diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 34edacfa9..addf36f43 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -3,7 +3,7 @@ name: Bug report about: Create a report to help us improve --- - + diff --git a/.github/classifier.yml b/.github/classifier.yml index eba4910f4..07b857207 100644 --- a/.github/classifier.yml +++ b/.github/classifier.yml @@ -15,7 +15,7 @@ css-less-scss: [ aeschli ], debug-console: [], debug: { - assignees: [ isidorn ], + assignees: [ weinand ], assignLabel: false }, diff-editor: [], @@ -78,10 +78,10 @@ formatting: [], git: [ joaomoreno ], grammar: [], - hot-exit: [ Tyriar ], + hot-exit: [], html: [ aeschli ], install-update: [], - integrated-terminal: [ Tyriar ], + integrated-terminal: [], integration-test: [], intellisense-config: [], issue-reporter: [ RMacfarlane ], diff --git a/.github/commands.yml b/.github/commands.yml index ba6ab0e86..95a3df06c 100644 --- a/.github/commands.yml +++ b/.github/commands.yml @@ -46,6 +46,7 @@ { type: 'comment', name: 'duplicate', + allowUsers: ['cleidigh', 'usernamehw'], action: 'updateLabels', addLabel: '*duplicate' }, @@ -59,18 +60,21 @@ { type: 'comment', name: 'confirm', + allowUsers: ['cleidigh', 'usernamehw'], action: 'updateLabels', addLabel: 'confirmed' }, { type: 'comment', name: 'findDuplicates', + allowUsers: ['cleidigh', 'usernamehw'], action: 'comment', comment: "Potential duplicates:\n${potentialDuplicates}" }, { type: 'comment', name: 'needsMoreInfo', + allowUsers: ['cleidigh', 'usernamehw'], action: 'updateLabels', addLabel: 'needs more info', comment: "Thanks for creating this issue! We figured it's missing some basic information or in some other way doesn't follow our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines. Please take the time to review these and update the issue.\n\nHappy Coding!" diff --git a/.vscode/cglicenses.schema.json b/.vscode/cglicenses.schema.json new file mode 100644 index 000000000..238f4803f --- /dev/null +++ b/.vscode/cglicenses.schema.json @@ -0,0 +1,23 @@ +{ + "type": "array", + "items": { + "type": "object", + "required": [ + "name", + "licenseDetail" + ], + "properties": { + "name": { + "type": "string", + "description": "The name of the dependency" + }, + "licenseDetail": { + "type": "array", + "description": "The complete license text of the dependency", + "items": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/.vscode/cgmanifest.schema.json b/.vscode/cgmanifest.schema.json new file mode 100644 index 000000000..2e719b023 --- /dev/null +++ b/.vscode/cgmanifest.schema.json @@ -0,0 +1,142 @@ +{ + "type": "object", + "properties": { + "registrations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "component": { + "oneOf": [ + { + "type": "object", + "required": [ + "type", + "git" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "git" + ] + }, + "git": { + "type": "object", + "required": [ + "name", + "repositoryUrl", + "commitHash" + ], + "properties": { + "name": { + "type": "string" + }, + "repositoryUrl": { + "type": "string" + }, + "commitHash": { + "type": "string" + } + } + } + } + }, + { + "type": "object", + "required": [ + "type", + "npm" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "npm" + ] + }, + "npm": { + "type": "object", + "required": [ + "name", + "version" + ], + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + } + } + } + } + }, + { + "type": "object", + "required": [ + "type", + "other" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "other" + ] + }, + "other": { + "type": "object", + "required": [ + "name", + "downloadUrl", + "version" + ], + "properties": { + "name": { + "type": "string" + }, + "downloadUrl": { + "type": "string" + }, + "version": { + "type": "string" + } + } + } + } + } + ] + }, + "repositoryUrl": { + "type": "string", + "description": "The git url of the component" + }, + "version": { + "type": "string", + "description": "The version of the component" + }, + "license": { + "type": "string", + "description": "The name of the license" + }, + "developmentDependency": { + "type": "boolean", + "description": "This component is inlined in the vscode repo and **is not shipped**." + }, + "isOnlyProductionDependency": { + "type": "boolean", + "description": "This component is shipped and **is not inlined in the vscode repo**." + }, + "licenseDetail": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The license text" + } + } + } + } + } +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 8ad783bcb..55cfea092 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,7 +2,7 @@ // See https://go.microsoft.com/fwlink/?LinkId=827846 // for the documentation about the extensions.json format "recommendations": [ - "eg2.tslint", + "ms-vscode.vscode-typescript-tslint-plugin", "dbaeumer.vscode-eslint", "msjsdiag.debugger-for-chrome" ] diff --git a/.vscode/launch.json b/.vscode/launch.json index a117e3ed4..89dc16fb1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,17 +9,14 @@ "stopOnEntry": true, "args": [ "hygiene" - ], - "cwd": "${workspaceFolder}" + ] }, { "type": "node", "request": "attach", "name": "Attach to Extension Host", - "protocol": "inspector", "port": 5870, "restart": true, - "smartStep": true, "outFiles": [ "${workspaceFolder}/out/**/*.js" ] @@ -28,9 +25,7 @@ "type": "node", "request": "attach", "name": "Attach to Shared Process", - "protocol": "inspector", "port": 5871, - "smartStep": true, "outFiles": [ "${workspaceFolder}/out/**/*.js" ] @@ -38,10 +33,8 @@ { "type": "node", "request": "attach", - "protocol": "inspector", "name": "Attach to Search Process", "port": 5876, - "smartStep": true, "outFiles": [ "${workspaceFolder}/out/**/*.js" ] @@ -50,9 +43,7 @@ "type": "node", "request": "attach", "name": "Attach to CLI Process", - "protocol": "inspector", "port": 5874, - "smartStep": true, "outFiles": [ "${workspaceFolder}/out/**/*.js" ] @@ -61,9 +52,7 @@ "type": "node", "request": "attach", "name": "Attach to Main Process", - "protocol": "inspector", "port": 5875, - "smartStep": true, "outFiles": [ "${workspaceFolder}/out/**/*.js" ] @@ -129,7 +118,6 @@ "type": "chrome", "request": "attach", "name": "Attach to VS Code", - "smartStep": true, "port": 9222 }, { @@ -149,7 +137,6 @@ "runtimeArgs": [ "--inspect=5875", "--no-cached-data" ], - "smartStep": true, "skipFiles": [ "**/winjs*.js" ], @@ -163,7 +150,6 @@ "runtimeArgs": [ "--no-cached-data" ], - "smartStep": true, "outFiles": [ "${workspaceFolder}/out/**/*.js" ] @@ -172,7 +158,6 @@ "type": "node", "request": "launch", "name": "Git Unit Tests", - "protocol": "inspector", "program": "${workspaceFolder}/extensions/git/node_modules/mocha/bin/_mocha", "stopOnEntry": false, "cwd": "${workspaceFolder}/extensions/git", @@ -212,7 +197,6 @@ "type": "node", "request": "launch", "name": "Unit Tests", - "protocol": "inspector", "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", "runtimeExecutable": "${workspaceFolder}/.build/electron/Code - OSS.app/Contents/MacOS/Electron", "windows": { @@ -289,4 +273,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 3edd3534d..8b9ca6cd0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,7 +10,7 @@ } }, "files.associations": { - "OSSREADME.json": "jsonc" + "cglicenses.json": "jsonc" }, "search.exclude": { "**/node_modules": true, @@ -24,7 +24,6 @@ "test/smoke/out/**": true, "src/vs/base/test/node/uri.test.data.txt": true }, - "tslint.enable": true, "lcov.path": [ "./.build/coverage/lcov.info", "./.build/coverage-single/lcov.info" @@ -42,5 +41,12 @@ "npm.exclude": "**/extensions/**", "emmet.excludeLanguages": [], "typescript.preferences.importModuleSpecifier": "non-relative", - "typescript.preferences.quoteStyle": "single" -} \ No newline at end of file + "typescript.preferences.quoteStyle": "single", + "json.schemas": [{ + "fileMatch": [ "cgmanifest.json" ], + "url": "./.vscode/cgmanifest.schema.json" + }, { + "fileMatch": [ "cglicenses.json" ], + "url": "./.vscode/cglicenses.schema.json" + }] +} diff --git a/.vscode/shared.code-snippets b/.vscode/shared.code-snippets index 6ff336abc..6d6690a02 100644 --- a/.vscode/shared.code-snippets +++ b/.vscode/shared.code-snippets @@ -31,7 +31,7 @@ "prefix": "emitter", "description": "Add emitter and event properties", "body": [ - "private _onDid$1 = new Emitter<$2>();", + "private readonly _onDid$1 = new Emitter<$2>();", "readonly onDid$1: Event<$2> = this._onDid$1.event;" ], } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b1650a08e..7775753e8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -101,4 +101,3 @@ please see [How to Contribute](https://github.com/Microsoft/vscode/wiki/How-to-C # Thank You! Your contributions to open source, large or small, make great projects like this possible. Thank you for taking the time to contribute. - diff --git a/README.md b/README.md index 934da0fb2..a7294e71b 100644 --- a/README.md +++ b/README.md @@ -5,17 +5,16 @@ [![Bugs](https://img.shields.io/github/issues/Microsoft/vscode/bug.svg)](https://github.com/Microsoft/vscode/issues?utf8=✓&q=is%3Aissue+is%3Aopen+label%3Abug) [![Gitter](https://img.shields.io/badge/chat-on%20gitter-yellow.svg)](https://gitter.im/Microsoft/vscode) -[VS Code](https://code.visualstudio.com) is a new type of tool that combines the simplicity of -a code editor with what developers need for their core edit-build-debug cycle. Code -provides comprehensive editing and debugging support, an extensibility model, and lightweight integration with existing tools. +[VS Code](https://code.visualstudio.com) is a type of tool that combines the simplicity of +a code editor with what developers need for their core edit-build-debug cycle. It provides comprehensive editing and debugging support, an extensibility model, and lightweight integration with existing tools. -VS Code is updated monthly with new features and bug fixes. You can download it for Windows, macOS, and Linux on [VS Code's website](https://code.visualstudio.com/Download). To get the latest releases every day, you can install the [Insiders version of VS Code](https://code.visualstudio.com/insiders). This builds from the master branch and is updated at least daily. +VS Code is updated monthly with new features and bug fixes. You can download it for Windows, macOS, and Linux on [VS Code's website](https://code.visualstudio.com/Download). To get the latest releases every day, you can install the [Insiders version of VS Code](https://code.visualstudio.com/insiders). This builds from the master branch and is updated daily at the very least.

VS Code in action

-The [`vscode`](https://github.com/microsoft/vscode) repository is where we do development and there are many ways you can participate in the project, for example: +The [`vscode`](https://github.com/microsoft/vscode) repository is where VS Code is developed and there are many ways you can participate in the project, for example: * [Submit bugs and feature requests](https://github.com/microsoft/vscode/issues) and help us verify as they are checked in. * Review [source code changes](https://github.com/microsoft/vscode/pulls). @@ -40,14 +39,19 @@ Please also see our [Code of Conduct](CODE_OF_CONDUCT.md). * Request a new feature on [GitHub](CONTRIBUTING.md). * Vote for [Popular Feature Requests](https://github.com/Microsoft/vscode/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc). * File a bug in [GitHub Issues](https://github.com/Microsoft/vscode/issues). -* [Tweet](https://twitter.com/code) us with other feedback. +* [Tweet](https://twitter.com/code) us with any other feedback. ## Related Projects -Many of the core components and extensions to Code live in their own repositories on GitHub. For example, the [node debug adapter](https://github.com/microsoft/vscode-node-debug) and the [mono debug adapter](https://github.com/microsoft/vscode-mono-debug). +Many of the core components and extensions to Code live in their own repositories on GitHub. For example, the [node debug adapter](https://github.com/microsoft/vscode-node-debug) and the [mono debug adapter](https://github.com/microsoft/vscode-mono-debug) have their own repositories. For a complete list, please visit the [Related Projects](https://github.com/Microsoft/vscode/wiki/Related-Projects) page on our [wiki](https://github.com/Microsoft/vscode/wiki). +## Bundled Extensions + +Code ships with a set of extensions. These extensions are located in the [extensions](extensions) folder. +These extensions include grammars and snippets for several languages. Extensions that provide rich language support (code completion, go to definition) for a language have the suffix 'language-features'. For example, the 'json' extension provides coloring for JSON and the 'json-language-features' provides rich language support for JSON. + ## License Copyright (c) Microsoft Corporation. All rights reserved. diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index ed7e5acda..cd087ee71 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -20,7 +20,7 @@ This project incorporates components from the projects listed below. The origina 13. davidrios/pug-tmbundle (https://github.com/davidrios/pug-tmbundle) 14. definitelytyped (https://github.com/DefinitelyTyped/DefinitelyTyped) 15. demyte/language-cshtml (https://github.com/demyte/language-cshtml) -16. Document Object Model () +16. Document Object Model (https://www.w3.org/DOM/) 17. dotnet/csharp-tmLanguage version 0.1.0 (https://github.com/dotnet/csharp-tmLanguage) 18. expand-abbreviation version 0.5.8 (https://github.com/emmetio/expand-abbreviation) 19. fadeevab/make.tmbundle (https://github.com/fadeevab/make.tmbundle) @@ -43,8 +43,8 @@ This project incorporates components from the projects listed below. The origina 36. Microsoft/vscode-JSON.tmLanguage (https://github.com/Microsoft/vscode-JSON.tmLanguage) 37. Microsoft/vscode-mssql (https://github.com/Microsoft/vscode-mssql) 38. mmims/language-batchfile (https://github.com/mmims/language-batchfile) -39. octicons-code version 3.1.0 (https://octicons.github.com) -40. octicons-font version 3.1.0 (https://octicons.github.com) +39. octicons-code version 3.1.0 (https://registry.npmjs.org/octicons/-/octicons-3.1.0.tgz) +40. octicons-font version 3.1.0 (https://registry.npmjs.org/octicons/-/octicons-3.1.0.tgz) 41. PowerShell/EditorSyntax (https://github.com/powershell/editorsyntax) 42. seti-ui version 0.1.0 (https://github.com/jesseweed/seti-ui) 43. shaders-tmLanguage version 0.1.0 (https://github.com/tgjones/shaders-tmLanguage) @@ -60,13 +60,12 @@ This project incorporates components from the projects listed below. The origina 53. textmate/markdown.tmbundle (https://github.com/textmate/markdown.tmbundle) 54. textmate/perl.tmbundle (https://github.com/textmate/perl.tmbundle) 55. textmate/ruby.tmbundle (https://github.com/textmate/ruby.tmbundle) -56. textmate/toml.tmbundle (https://github.com/textmate/toml.tmbundle) -57. textmate/yaml.tmbundle (https://github.com/textmate/yaml.tmbundle) -58. TypeScript-TmLanguage version 0.1.8 (https://github.com/Microsoft/TypeScript-TmLanguage) -59. Unicode () -60. vscode-logfile-highlighter version 1.2.0 (https://github.com/emilast/vscode-logfile-highlighter) -61. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) -62. Web Background Synchronization (https://github.com/WICG/BackgroundSync) +56. textmate/yaml.tmbundle (https://github.com/textmate/yaml.tmbundle) +57. TypeScript-TmLanguage version 0.1.8 (https://github.com/Microsoft/TypeScript-TmLanguage) +58. Unicode (http://www.unicode.org/) +59. vscode-logfile-highlighter version 1.2.0 (https://github.com/emilast/vscode-logfile-highlighter) +60. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) +61. Web Background Synchronization (https://github.com/WICG/BackgroundSync) %% atom/language-c NOTICES AND INFORMATION BEGIN HERE @@ -1282,7 +1281,49 @@ END OF MagicStack/MagicPython NOTICES AND INFORMATION %% marked NOTICES AND INFORMATION BEGIN HERE ========================================= -Copyright (c) 2011-2018, Christopher Jeffrey. (MIT License) +information + +## Contribution License Agreement + +If you contribute code to this project, you are implicitly allowing your code +to be distributed under the MIT license. You are also implicitly verifying that +all code is your original work. `` + +## Marked + +Copyright (c) 2011-2018, Christopher Jeffrey (https://github.com/chjj/) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +## Markdown + +Copyright © 2004, John Gruber +http://daringfireball.net/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name "Markdown" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +This software is provided by the copyright holders and contributors "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage. ========================================= END OF marked NOTICES AND INFORMATION @@ -2180,20 +2221,6 @@ to the base-name name of the original file, and an extension of txt, html, or si ========================================= END OF textmate/ruby.tmbundle NOTICES AND INFORMATION -%% textmate/toml.tmbundle NOTICES AND INFORMATION BEGIN HERE -========================================= -Copyright (c) https://github.com/infininight and https://github.com/mojombo -Permission to copy, use, modify, sell and distribute this -software is granted. This software is provided "as is" without -express or implied warranty, and with no claim as to its -suitability for any purpose - -An exception is made for files in readable text which contain their own license information, or files where an accompanying -file exists (in the same directory) with a "-license" suffix added to the base-name name of the original file, and an extension . -of txt, html, or similar. For example "tidy" is accompanied by "tidy-license.txt". -========================================= -END OF textmate/toml.tmbundle NOTICES AND INFORMATION - %% textmate/yaml.tmbundle NOTICES AND INFORMATION BEGIN HERE ========================================= Copyright (c) 2015 FichteFoll diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 000000000..e52afca60 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,18 @@ +jobs: +- job: Windows + pool: + vmImage: VS2017-Win2016 + steps: + - template: build/azure-pipelines/win32/continuous-build-win32.yml + +- job: Linux + pool: + vmImage: 'Ubuntu-16.04' + steps: + - template: build/azure-pipelines/linux/continuous-build-linux.yml + +- job: macOS + pool: + vmImage: macOS 10.13 + steps: + - template: build/azure-pipelines/darwin/continuous-build-darwin.yml \ No newline at end of file diff --git a/build/tfs/common/.gitignore b/build/azure-pipelines/common/.gitignore similarity index 100% rename from build/tfs/common/.gitignore rename to build/azure-pipelines/common/.gitignore diff --git a/build/tfs/common/installDistro.ts b/build/azure-pipelines/common/installDistro.ts similarity index 100% rename from build/tfs/common/installDistro.ts rename to build/azure-pipelines/common/installDistro.ts diff --git a/build/tfs/common/publish.ts b/build/azure-pipelines/common/publish.ts similarity index 98% rename from build/tfs/common/publish.ts rename to build/azure-pipelines/common/publish.ts index 7a74dc610..2095c2d25 100644 --- a/build/tfs/common/publish.ts +++ b/build/azure-pipelines/common/publish.ts @@ -265,6 +265,11 @@ async function publish(commit: string, quality: string, platform: string, type: } function main(): void { + if (process.env['VSCODE_BUILD_SKIP_PUBLISH']) { + console.warn('Skipping publish due to VSCODE_BUILD_SKIP_PUBLISH'); + return; + } + const opts = minimist(process.argv.slice(2), { boolean: ['upload-only'] }); diff --git a/build/tfs/common/symbols.ts b/build/azure-pipelines/common/symbols.ts similarity index 100% rename from build/tfs/common/symbols.ts rename to build/azure-pipelines/common/symbols.ts diff --git a/build/tfs/darwin/continuous-build-darwin.yml b/build/azure-pipelines/darwin/continuous-build-darwin.yml similarity index 100% rename from build/tfs/darwin/continuous-build-darwin.yml rename to build/azure-pipelines/darwin/continuous-build-darwin.yml diff --git a/build/tfs/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml similarity index 84% rename from build/tfs/darwin/product-build-darwin.yml rename to build/azure-pipelines/darwin/product-build-darwin.yml index f5520d060..c510636ef 100644 --- a/build/tfs/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -15,7 +15,7 @@ steps: yarn monaco-compile-check yarn strict-null-check VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" yarn gulp -- mixin - node build/tfs/common/installDistro.js + node build/azure-pipelines/common/installDistro.js node build/lib/builtInExtensions.js displayName: Prepare build @@ -69,7 +69,7 @@ steps: AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \ - node build/tfs/common/publish.js \ + node build/azure-pipelines/common/publish.js \ "$(VSCODE_QUALITY)" \ darwin \ archive \ @@ -79,9 +79,12 @@ steps: ../VSCode-darwin.zip # publish hockeyapp symbols - node build/tfs/common/symbols.js "$(VSCODE_MIXIN_PASSWORD)" "$(VSCODE_HOCKEYAPP_TOKEN)" "$(VSCODE_ARCH)" "$(VSCODE_HOCKEYAPP_ID_MACOS)" + node build/azure-pipelines/common/symbols.js "$(VSCODE_MIXIN_PASSWORD)" "$(VSCODE_HOCKEYAPP_TOKEN)" "$(VSCODE_ARCH)" "$(VSCODE_HOCKEYAPP_ID_MACOS)" # upload configuration AZURE_STORAGE_ACCESS_KEY="$(AZURE_STORAGE_ACCESS_KEY)" \ yarn gulp -- upload-vscode-configuration - displayName: Publish \ No newline at end of file + displayName: Publish + +- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: 'Component Detection' diff --git a/build/azure-pipelines/linux/.gitignore b/build/azure-pipelines/linux/.gitignore new file mode 100644 index 000000000..0f46fa708 --- /dev/null +++ b/build/azure-pipelines/linux/.gitignore @@ -0,0 +1 @@ +pat \ No newline at end of file diff --git a/build/tfs/linux/continuous-build-linux.yml b/build/azure-pipelines/linux/continuous-build-linux.yml similarity index 72% rename from build/tfs/linux/continuous-build-linux.yml rename to build/azure-pipelines/linux/continuous-build-linux.yml index 459b82b9a..91891e6da 100644 --- a/build/tfs/linux/continuous-build-linux.yml +++ b/build/azure-pipelines/linux/continuous-build-linux.yml @@ -1,14 +1,12 @@ steps: - script: | set -e - apt-get update - apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 libgconf-2-4 dbus xvfb libgtk-3-0 - cp build/tfs/linux/x64/xvfb.init /etc/init.d/xvfb - chmod +x /etc/init.d/xvfb - update-rc.d xvfb defaults - ln -sf /bin/dbus-daemon /usr/bin/dbus-daemon - service xvfb start - service dbus start + sudo apt-get update + sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 libgconf-2-4 dbus xvfb libgtk-3-0 + sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb + sudo chmod +x /etc/init.d/xvfb + sudo update-rc.d xvfb defaults + sudo service xvfb start - task: NodeTool@0 inputs: versionSpec: "8.12.0" diff --git a/build/azure-pipelines/linux/frozen-check.js b/build/azure-pipelines/linux/frozen-check.js new file mode 100644 index 000000000..281632424 --- /dev/null +++ b/build/azure-pipelines/linux/frozen-check.js @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +const documentdb_1 = require("documentdb"); +function createDefaultConfig(quality) { + return { + id: quality, + frozen: false + }; +} +function getConfig(quality) { + const client = new documentdb_1.DocumentClient(process.env['AZURE_DOCUMENTDB_ENDPOINT'], { masterKey: process.env['AZURE_DOCUMENTDB_MASTERKEY'] }); + const collection = 'dbs/builds/colls/config'; + const query = { + query: `SELECT TOP 1 * FROM c WHERE c.id = @quality`, + parameters: [ + { name: '@quality', value: quality } + ] + }; + return new Promise((c, e) => { + client.queryDocuments(collection, query).toArray((err, results) => { + if (err && err.code !== 409) { + return e(err); + } + c(!results || results.length === 0 ? createDefaultConfig(quality) : results[0]); + }); + }); +} +getConfig(process.argv[2]) + .then(config => { + console.log(config.frozen); + process.exit(0); +}) + .catch(err => { + console.error(err); + process.exit(1); +}); diff --git a/build/tfs/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml similarity index 70% rename from build/tfs/linux/product-build-linux.yml rename to build/azure-pipelines/linux/product-build-linux.yml index 7a8c637ef..ff877d440 100644 --- a/build/tfs/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -20,7 +20,7 @@ steps: npm run monaco-compile-check npm run strict-null-check VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" npm run gulp -- mixin - node build/tfs/common/installDistro.js + node build/azure-pipelines/common/installDistro.js node build/lib/builtInExtensions.js - script: | @@ -63,10 +63,10 @@ steps: AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \ - node build/tfs/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_LINUX" archive-unsigned "$TARBALL_FILENAME" "$VERSION" true "$TARBALL_PATH" + node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_LINUX" archive-unsigned "$TARBALL_FILENAME" "$VERSION" true "$TARBALL_PATH" # Publish hockeyapp symbols - node build/tfs/common/symbols.js "$(VSCODE_MIXIN_PASSWORD)" "$(VSCODE_HOCKEYAPP_TOKEN)" "$(VSCODE_ARCH)" "$(VSCODE_HOCKEYAPP_ID_LINUX64)" + node build/azure-pipelines/common/symbols.js "$(VSCODE_MIXIN_PASSWORD)" "$(VSCODE_HOCKEYAPP_TOKEN)" "$(VSCODE_ARCH)" "$(VSCODE_HOCKEYAPP_ID_LINUX64)" # Publish DEB npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-deb" @@ -78,7 +78,7 @@ steps: AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \ - node build/tfs/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_DEB" package "$DEB_FILENAME" "$VERSION" true "$DEB_PATH" + node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_DEB" package "$DEB_FILENAME" "$VERSION" true "$DEB_PATH" # Publish RPM npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-build-rpm" @@ -90,7 +90,22 @@ steps: AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \ - node build/tfs/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_RPM" package "$RPM_FILENAME" "$VERSION" true "$RPM_PATH" + node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_RPM" package "$RPM_FILENAME" "$VERSION" true "$RPM_PATH" - # SNAP_FILENAME="$(ls $REPO/.build/linux/snap/$ARCH/ | grep .snap)" - # SNAP_PATH="$REPO/.build/linux/snap/$ARCH/$SNAP_FILENAME" + # Publish Snap + npm run gulp -- "vscode-linux-$(VSCODE_ARCH)-prepare-snap" + + # Pack snap tarball artifact, in order to preserve file perms + mkdir -p $REPO/.build/linux/snap-tarball + SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-$(VSCODE_ARCH).tar.gz" + rm -rf $SNAP_TARBALL_PATH + (cd .build/linux && tar -czf $SNAP_TARBALL_PATH snap) + +- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: 'Component Detection' + +- task: PublishPipelineArtifact@0 + displayName: 'Publish Pipeline Artifact' + inputs: + artifactName: snap-$(VSCODE_ARCH) + targetPath: .build/linux/snap-tarball diff --git a/build/azure-pipelines/linux/snap-build-linux.yml b/build/azure-pipelines/linux/snap-build-linux.yml new file mode 100644 index 000000000..29252107f --- /dev/null +++ b/build/azure-pipelines/linux/snap-build-linux.yml @@ -0,0 +1,42 @@ +steps: +- task: NodeTool@0 + inputs: + versionSpec: "8.12.0" + +- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.10.1" + +- task: DownloadPipelineArtifact@0 + displayName: 'Download Pipeline Artifact' + inputs: + artifactName: snap-$(VSCODE_ARCH) + targetPath: .build/linux/snap-tarball + +- script: | + set -e + + REPO="$(pwd)" + ARCH="$(VSCODE_ARCH)" + SNAP_ROOT="$REPO/.build/linux/snap/$ARCH" + + # Install build dependencies + (cd build && yarn) + + # Unpack snap tarball artifact, in order to preserve file perms + SNAP_TARBALL_PATH="$REPO/.build/linux/snap-tarball/snap-$ARCH.tar.gz" + (cd .build/linux && tar -xzf $SNAP_TARBALL_PATH) + + # Create snap package + BUILD_VERSION="$(date +%s)" + SNAP_FILENAME="code-$VSCODE_QUALITY-$BUILD_VERSION.snap" + PACKAGEJSON="$(ls $SNAP_ROOT/code*/usr/share/code*/resources/app/package.json)" + VERSION=$(node -p "require(\"$PACKAGEJSON\").version") + SNAP_PATH="$SNAP_ROOT/$SNAP_FILENAME" + (cd $SNAP_ROOT/code-* && snapcraft snap --output "$SNAP_PATH") + + # Publish snap package + AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ + MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \ + node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "linux-snap-$ARCH" package "$SNAP_FILENAME" "$VERSION" true "$SNAP_PATH" \ No newline at end of file diff --git a/build/tfs/linux/ia32/xvfb.init b/build/azure-pipelines/linux/xvfb.init similarity index 100% rename from build/tfs/linux/ia32/xvfb.init rename to build/azure-pipelines/linux/xvfb.init diff --git a/build/tfs/product-build.yml b/build/azure-pipelines/product-build.yml similarity index 77% rename from build/tfs/product-build.yml rename to build/azure-pipelines/product-build.yml index 417ae4f78..afebc973c 100644 --- a/build/tfs/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -4,6 +4,8 @@ resources: image: joaomoreno/vscode-linux-build-agent:x64 - container: vscode-ia32 image: joaomoreno/vscode-linux-build-agent:ia32 + - container: snapcraft + image: snapcore/snapcraft jobs: - job: Windows @@ -34,6 +36,17 @@ jobs: steps: - template: linux/product-build-linux.yml +- job: LinuxSnap + condition: eq(variables['VSCODE_BUILD_LINUX'], 'true') + pool: + vmImage: 'Ubuntu-16.04' + variables: + VSCODE_ARCH: x64 + container: snapcraft + dependsOn: Linux + steps: + - template: linux/snap-build-linux.yml + - job: Linux32 condition: eq(variables['VSCODE_BUILD_LINUX_32BIT'], 'true') pool: diff --git a/build/tfs/win32/ESRPClient/NuGet.config b/build/azure-pipelines/win32/ESRPClient/NuGet.config similarity index 100% rename from build/tfs/win32/ESRPClient/NuGet.config rename to build/azure-pipelines/win32/ESRPClient/NuGet.config diff --git a/build/tfs/win32/ESRPClient/packages.config b/build/azure-pipelines/win32/ESRPClient/packages.config similarity index 100% rename from build/tfs/win32/ESRPClient/packages.config rename to build/azure-pipelines/win32/ESRPClient/packages.config diff --git a/build/tfs/win32/continuous-build-win32.yml b/build/azure-pipelines/win32/continuous-build-win32.yml similarity index 100% rename from build/tfs/win32/continuous-build-win32.yml rename to build/azure-pipelines/win32/continuous-build-win32.yml diff --git a/build/tfs/win32/exec.ps1 b/build/azure-pipelines/win32/exec.ps1 similarity index 100% rename from build/tfs/win32/exec.ps1 rename to build/azure-pipelines/win32/exec.ps1 diff --git a/build/tfs/win32/import-esrp-auth-cert.ps1 b/build/azure-pipelines/win32/import-esrp-auth-cert.ps1 similarity index 100% rename from build/tfs/win32/import-esrp-auth-cert.ps1 rename to build/azure-pipelines/win32/import-esrp-auth-cert.ps1 diff --git a/build/tfs/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml similarity index 71% rename from build/tfs/win32/product-build-win32.yml rename to build/azure-pipelines/win32/product-build-win32.yml index 9dd2d4c65..c4922708c 100644 --- a/build/tfs/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -13,7 +13,7 @@ steps: addToPath: true - powershell: | - . build/tfs/win32/exec.ps1 + . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" "machine monacotools.visualstudio.com password $(VSO_PAT)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII $env:npm_config_arch="$(VSCODE_ARCH)" @@ -24,11 +24,11 @@ steps: exec { npm run monaco-compile-check } exec { npm run strict-null-check } exec { npm run gulp -- mixin } - exec { node build/tfs/common/installDistro.js } + exec { node build/azure-pipelines/common/installDistro.js } exec { node build/lib/builtInExtensions.js } - powershell: | - . build/tfs/win32/exec.ps1 + . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" $env:VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" exec { npm run gulp -- "vscode-win32-$(VSCODE_ARCH)-min" } @@ -36,7 +36,7 @@ steps: name: build - powershell: | - . build/tfs/win32/exec.ps1 + . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" exec { npm run gulp -- "electron-$(VSCODE_ARCH)" } exec { .\scripts\test.bat --build --tfs "Unit Tests" } @@ -51,30 +51,6 @@ steps: signConfigType: inlineSignParams inlineOperation: | [ - { - "keyCode": "CP-229803", - "operationSetCode": "SigntoolSign", - "parameters": [ - { - "parameterName": "OpusName", - "parameterValue": "VS Code" - }, - { - "parameterName": "OpusInfo", - "parameterValue": "https://code.visualstudio.com/" - }, - { - "parameterName": "PageHash", - "parameterValue": "/NPH" - }, - { - "parameterName": "TimeStamp", - "parameterValue": "/t \"http://ts4096.gtm.microsoft.com/TSS/AuthenticodeTS\"" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - }, { "keyCode": "CP-230012", "operationSetCode": "SigntoolSign", @@ -125,9 +101,9 @@ steps: - task: NuGetCommand@2 displayName: Install ESRPClient.exe inputs: - restoreSolution: 'build\tfs\win32\ESRPClient\packages.config' + restoreSolution: 'build\azure-pipelines\win32\ESRPClient\packages.config' feedsToUse: config - nugetConfigPath: 'build\tfs\win32\ESRPClient\NuGet.config' + nugetConfigPath: 'build\azure-pipelines\win32\ESRPClient\NuGet.config' externalFeedCredentials: 3fc0b7f7-da09-4ae7-a9c8-d69824b1819b restoreDirectory: packages @@ -138,11 +114,11 @@ steps: - powershell: | $ErrorActionPreference = "Stop" - .\build\tfs\win32\import-esrp-auth-cert.ps1 -AuthCertificateBase64 $(ESRP_AUTH_CERTIFICATE) -AuthCertificateKey $(ESRP_AUTH_CERTIFICATE_KEY) + .\build\azure-pipelines\win32\import-esrp-auth-cert.ps1 -AuthCertificateBase64 $(ESRP_AUTH_CERTIFICATE) -AuthCertificateKey $(ESRP_AUTH_CERTIFICATE_KEY) displayName: Import ESRP Auth Certificate - powershell: | - . build/tfs/win32/exec.ps1 + . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" exec { npm run gulp -- "vscode-win32-$(VSCODE_ARCH)-archive" "vscode-win32-$(VSCODE_ARCH)-system-setup" "vscode-win32-$(VSCODE_ARCH)-user-setup" --sign } @@ -163,10 +139,13 @@ steps: $assetPlatform = if ("$(VSCODE_ARCH)" -eq "ia32") { "win32" } else { "win32-x64" } - exec { node build/tfs/common/publish.js $Quality "$global:assetPlatform-archive" archive "VSCode-win32-$(VSCODE_ARCH)-$Version.zip" $Version true $Zip } - exec { node build/tfs/common/publish.js $Quality "$global:assetPlatform" setup "VSCodeSetup-$(VSCODE_ARCH)-$Version.exe" $Version true $SystemExe } - exec { node build/tfs/common/publish.js $Quality "$global:assetPlatform-user" setup "VSCodeUserSetup-$(VSCODE_ARCH)-$Version.exe" $Version true $UserExe } + exec { node build/azure-pipelines/common/publish.js $Quality "$global:assetPlatform-archive" archive "VSCode-win32-$(VSCODE_ARCH)-$Version.zip" $Version true $Zip } + exec { node build/azure-pipelines/common/publish.js $Quality "$global:assetPlatform" setup "VSCodeSetup-$(VSCODE_ARCH)-$Version.exe" $Version true $SystemExe } + exec { node build/azure-pipelines/common/publish.js $Quality "$global:assetPlatform-user" setup "VSCodeUserSetup-$(VSCODE_ARCH)-$Version.exe" $Version true $UserExe } # publish hockeyapp symbols $hockeyAppId = if ("$(VSCODE_ARCH)" -eq "ia32") { "$(VSCODE_HOCKEYAPP_ID_WIN32)" } else { "$(VSCODE_HOCKEYAPP_ID_WIN64)" } - exec { node build/tfs/common/symbols.js "$(VSCODE_MIXIN_PASSWORD)" "$(VSCODE_HOCKEYAPP_TOKEN)" "$(VSCODE_ARCH)" $hockeyAppId } + exec { node build/azure-pipelines/common/symbols.js "$(VSCODE_MIXIN_PASSWORD)" "$(VSCODE_HOCKEYAPP_TOKEN)" "$(VSCODE_ARCH)" $hockeyAppId } + +- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: 'Component Detection' diff --git a/build/tfs/win32/sign.ps1 b/build/azure-pipelines/win32/sign.ps1 similarity index 80% rename from build/tfs/win32/sign.ps1 rename to build/azure-pipelines/win32/sign.ps1 index d888a7d10..00c4d42d9 100644 --- a/build/tfs/win32/sign.ps1 +++ b/build/azure-pipelines/win32/sign.ps1 @@ -36,18 +36,6 @@ $Input = Create-TmpJson @{ ) SigningInfo = @{ Operations = @( - @{ - KeyCode = "CP-229803" - OperationCode = "SigntoolSign" - Parameters = @{ - OpusName = "VS Code" - OpusInfo = "https://code.visualstudio.com/" - PageHash = "/NPH" - TimeStamp = "/t `"http://ts4096.gtm.microsoft.com/TSS/AuthenticodeTS`"" - } - ToolName = "sign" - ToolVersion = "1.0" - }, @{ KeyCode = "CP-230012" OperationCode = "SigntoolSign" diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index e6bf85d5e..829f3bf13 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -1,7 +1,7 @@ [ { "name": "ms-vscode.node-debug", - "version": "1.29.0", + "version": "1.30.1", "repo": "https://github.com/Microsoft/vscode-node-debug", "metadata": { "id": "b6ded8fb-a0a0-4c1c-acbd-ab2a3bc995a6", @@ -16,7 +16,7 @@ }, { "name": "ms-vscode.node-debug2", - "version": "1.29.2", + "version": "1.30.1", "repo": "https://github.com/Microsoft/vscode-node-debug2", "metadata": { "id": "36d19e17-7569-4841-a001-947eb18602b2", @@ -28,5 +28,20 @@ }, "publisherDisplayName": "Microsoft" } + }, + { + "name": "ms-vscode.references-view", + "version": "0.0.11", + "repo": "https://github.com/Microsoft/vscode-reference-view", + "metadata": { + "id": "dc489f46-520d-4556-ae85-1f9eab3c412d", + "publisherId": { + "publisherId": "5f5636e7-69ed-4afe-b5d6-8d231fb3d3ee", + "publisherName": "ms-vscode", + "displayName": "Microsoft", + "flags": "verified" + }, + "publisherDisplayName": "Microsoft" + } } -] \ No newline at end of file +] diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 1e492d0f5..1e19fe65e 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -152,10 +152,19 @@ gulp.task('extract-editor-esm', ['clean-editor-esm', 'clean-editor-distro', 'ext }); }); gulp.task('compile-editor-esm', ['extract-editor-esm', 'clean-editor-distro'], function () { - const result = cp.spawnSync(`node`, [`../node_modules/.bin/tsc`], { - cwd: path.join(__dirname, '../out-editor-esm') - }); - console.log(result.stdout.toString()); + if (process.platform === 'win32') { + const result = cp.spawnSync(`..\\node_modules\\.bin\\tsc.cmd`, { + cwd: path.join(__dirname, '../out-editor-esm') + }); + console.log(result.stdout.toString()); + console.log(result.stderr.toString()); + } else { + const result = cp.spawnSync(`node`, [`../node_modules/.bin/tsc`], { + cwd: path.join(__dirname, '../out-editor-esm') + }); + console.log(result.stdout.toString()); + console.log(result.stderr.toString()); + } }); function toExternalDTS(contents) { diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js index 85f9827e6..16d187ffc 100644 --- a/build/gulpfile.hygiene.js +++ b/build/gulpfile.hygiene.js @@ -43,6 +43,7 @@ const indentationFilter = [ // except specific files '!ThirdPartyNotices.txt', '!LICENSE.txt', + '!**/LICENSE', '!src/vs/nls.js', '!src/vs/nls.build.js', '!src/vs/css.js', @@ -83,9 +84,11 @@ const indentationFilter = [ '!**/*.{svg,exe,png,bmp,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns}', '!build/{lib,tslintRules}/**/*.js', '!build/**/*.sh', - '!build/tfs/**/*.js', - '!build/tfs/**/*.config', + '!build/azure-pipelines/**/*.js', + '!build/azure-pipelines/**/*.config', '!**/Dockerfile', + '!**/*.Dockerfile', + '!**/*.dockerfile', '!extensions/markdown-language-features/media/*.js' ]; diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 1b725d132..326fac6bc 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -393,6 +393,8 @@ function packageTask(platform, arch, opts) { result = es.merge(result, gulp.src('resources/win32/bin/code.sh', { base: 'resources/win32' }) .pipe(replace('@@NAME@@', product.nameShort)) + .pipe(replace('@@COMMIT@@', commit)) + .pipe(replace('@@APPNAME@@', product.applicationName)) .pipe(rename(function (f) { f.basename = product.applicationName; f.extname = ''; }))); result = es.merge(result, gulp.src('resources/win32/VisualElementsManifest.xml', { base: 'resources/win32' }) diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index 349a2b979..50258a98f 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -118,12 +118,12 @@ function prepareRpmPackage(arch) { const desktopUrlHandler = gulp.src('resources/linux/code-url-handler.desktop', { base: '.' }) .pipe(rename('BUILD/usr/share/applications/' + product.applicationName + '-url-handler.desktop')); - const desktops = es.merge(desktop, desktopUrlHandler) - .pipe(replace('@@NAME_LONG@@', product.nameLong)) - .pipe(replace('@@NAME_SHORT@@', product.nameShort)) - .pipe(replace('@@NAME@@', product.applicationName)) - .pipe(replace('@@ICON@@', product.applicationName)) - .pipe(replace('@@URLPROTOCOL@@', product.urlProtocol)); + const desktops = es.merge(desktop, desktopUrlHandler) + .pipe(replace('@@NAME_LONG@@', product.nameLong)) + .pipe(replace('@@NAME_SHORT@@', product.nameShort)) + .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(replace('@@ICON@@', product.applicationName)) + .pipe(replace('@@URLPROTOCOL@@', product.urlProtocol)); const appdata = gulp.src('resources/linux/code.appdata.xml', { base: '.' }) .pipe(replace('@@NAME_LONG@@', product.nameLong)) @@ -172,6 +172,7 @@ function buildRpmPackage(arch) { 'cp "' + rpmOut + '/$(ls ' + rpmOut + ')" ' + destination + '/' ]); } + function getSnapBuildPath(arch) { return `.build/linux/snap/${arch}/${product.applicationName}-${arch}`; } @@ -192,17 +193,21 @@ function prepareSnapPackage(arch) { .pipe(rename(`usr/share/pixmaps/${product.applicationName}.png`)); const code = gulp.src(binaryDir + '/**/*', { base: binaryDir }) - .pipe(rename(function (p) { p.dirname = 'usr/share/' + product.applicationName + '/' + p.dirname; })); + .pipe(rename(function (p) { p.dirname = `usr/share/${product.applicationName}/${p.dirname}`; })); const snapcraft = gulp.src('resources/linux/snap/snapcraft.yaml', { base: '.' }) .pipe(replace('@@NAME@@', product.applicationName)) - .pipe(replace('@@VERSION@@', packageJson.version)) + .pipe(replace('@@VERSION@@', `${packageJson.version}-${linuxPackageRevision}`)) .pipe(rename('snap/snapcraft.yaml')); + const snapUpdate = gulp.src('resources/linux/snap/snapUpdate.sh', { base: '.' }) + .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(rename(`usr/share/${product.applicationName}/snapUpdate.sh`)); + const electronLaunch = gulp.src('resources/linux/snap/electron-launch', { base: '.' }) .pipe(rename('electron-launch')); - const all = es.merge(desktop, icon, code, snapcraft, electronLaunch); + const all = es.merge(desktop, icon, code, snapcraft, electronLaunch, snapUpdate); return all.pipe(vfs.dest(destination)); }; @@ -210,11 +215,7 @@ function prepareSnapPackage(arch) { function buildSnapPackage(arch) { const snapBuildPath = getSnapBuildPath(arch); - const snapFilename = `${product.applicationName}-${packageJson.version}-${linuxPackageRevision}-${arch}.snap`; - return shell.task([ - `chmod +x ${snapBuildPath}/electron-launch`, - `cd ${snapBuildPath} && snapcraft snap --output ../${snapFilename}` - ]); + return shell.task(`cd ${snapBuildPath} && snapcraft build`); } gulp.task('clean-vscode-linux-ia32-deb', util.rimraf('.build/linux/deb/i386')); diff --git a/build/gulpfile.vscode.win32.js b/build/gulpfile.vscode.win32.js index b73b4f3d3..1cfc5b4c4 100644 --- a/build/gulpfile.vscode.win32.js +++ b/build/gulpfile.vscode.win32.js @@ -25,7 +25,7 @@ const zipPath = arch => path.join(zipDir(arch), `VSCode-win32-${arch}.zip`); const setupDir = (arch, target) => path.join(repoPath, '.build', `win32-${arch}`, `${target}-setup`); const issPath = path.join(__dirname, 'win32', 'code.iss'); const innoSetupPath = path.join(path.dirname(path.dirname(require.resolve('innosetup-compiler'))), 'bin', 'ISCC.exe'); -const signPS1 = path.join(repoPath, 'build', 'tfs', 'win32', 'sign.ps1'); +const signPS1 = path.join(repoPath, 'build', 'azure-pipelines', 'win32', 'sign.ps1'); function packageInnoSetup(iss, options, cb) { options = options || {}; diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 5e5cadf48..9cc984633 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -11,7 +11,6 @@ const bom = require("gulp-bom"); const sourcemaps = require("gulp-sourcemaps"); const tsb = require("gulp-tsb"); const path = require("path"); -const ts = require("typescript"); const _ = require("underscore"); const monacodts = require("../monaco/api"); const nls = require("./nls"); @@ -109,87 +108,61 @@ exports.watchTask = watchTask; const REPO_SRC_FOLDER = path.join(__dirname, '../../src'); class MonacoGenerator { constructor(isWatch) { + this._executeSoonTimer = null; this._isWatch = isWatch; this.stream = es.through(); - this._inputFiles = monacodts.getIncludesInRecipe().map((moduleId) => { - if (/\.d\.ts$/.test(moduleId)) { - // This source file is already in .d.ts form - return path.join(REPO_SRC_FOLDER, moduleId); + this._watchers = []; + this._watchedFiles = {}; + let onWillReadFile = (moduleId, filePath) => { + if (!this._isWatch) { + return; } - else { - return path.join(REPO_SRC_FOLDER, `${moduleId}.ts`); + if (this._watchedFiles[filePath]) { + return; } - }); - // Install watchers - this._watchers = []; - if (this._isWatch) { - this._inputFiles.forEach((filePath) => { - const watcher = fs.watch(filePath); - watcher.addListener('change', () => { - this._inputFileChanged[filePath] = true; - // Avoid hitting empty files... :/ - setTimeout(() => this.execute(), 10); - }); - this._watchers.push(watcher); + this._watchedFiles[filePath] = true; + const watcher = fs.watch(filePath); + watcher.addListener('change', () => { + this._declarationResolver.invalidateCache(moduleId); + this._executeSoon(); }); + this._watchers.push(watcher); + }; + this._fsProvider = new class extends monacodts.FSProvider { + readFileSync(moduleId, filePath) { + onWillReadFile(moduleId, filePath); + return super.readFileSync(moduleId, filePath); + } + }; + this._declarationResolver = new monacodts.DeclarationResolver(this._fsProvider); + if (this._isWatch) { const recipeWatcher = fs.watch(monacodts.RECIPE_PATH); recipeWatcher.addListener('change', () => { - this._recipeFileChanged = true; - // Avoid hitting empty files... :/ - setTimeout(() => this.execute(), 10); + this._executeSoon(); }); this._watchers.push(recipeWatcher); } - this._inputFileChanged = {}; - this._inputFiles.forEach(file => this._inputFileChanged[file] = true); - this._recipeFileChanged = true; - this._dtsFilesContents = {}; - this._dtsFilesContents2 = {}; + } + _executeSoon() { + if (this._executeSoonTimer !== null) { + clearTimeout(this._executeSoonTimer); + this._executeSoonTimer = null; + } + this._executeSoonTimer = setTimeout(() => { + this._executeSoonTimer = null; + this.execute(); + }, 20); } dispose() { this._watchers.forEach(watcher => watcher.close()); } _run() { - let somethingChanged = false; - const setDTSFileContent = (file, contents) => { - if (this._dtsFilesContents[file] === contents) { - return; - } - this._dtsFilesContents[file] = contents; - this._dtsFilesContents2[file] = ts.createSourceFile(file, contents, ts.ScriptTarget.ES5); - somethingChanged = true; - }; - const fileMap = {}; - this._inputFiles.forEach((inputFile) => { - if (!this._inputFileChanged[inputFile]) { - return; - } - this._inputFileChanged[inputFile] = false; - const inputFileContents = fs.readFileSync(inputFile).toString(); - if (/\.d\.ts$/.test(inputFile)) { - // This is a .d.ts file - setDTSFileContent(inputFile, inputFileContents); - return; - } - fileMap[inputFile] = inputFileContents; - }); - if (Object.keys(fileMap).length > 0) { - const service = ts.createLanguageService(new monacodts.TypeScriptLanguageServiceHost({}, fileMap, {})); - Object.keys(fileMap).forEach((fileName) => { - const output = service.getEmitOutput(fileName, true).outputFiles[0].text; - const destFileName = fileName.replace(/\.ts$/, '.d.ts'); - setDTSFileContent(destFileName, output); - }); - } - if (this._recipeFileChanged) { - this._recipeFileChanged = false; - somethingChanged = true; - } - if (!somethingChanged) { - // Nothing changed - return null; + let r = monacodts.run3(this._declarationResolver); + if (!r && !this._isWatch) { + // The build must always be able to generate the monaco.d.ts + throw new Error(`monaco.d.ts generation error - Cannot continue`); } - return monacodts.run2('src', this._dtsFilesContents2); + return r; } _log(message, ...rest) { util2.log(util2.colors.cyan('[monaco.d.ts]'), message, ...rest); @@ -199,11 +172,9 @@ class MonacoGenerator { const result = this._run(); if (!result) { // nothing really changed - this._log(`monaco.d.ts is unchanged - total time took ${Date.now() - startTime} ms`); return; } if (result.isTheSame) { - this._log(`monaco.d.ts is unchanged - total time took ${Date.now() - startTime} ms`); return; } fs.writeFileSync(result.filePath, result.content); diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index 991e24bf7..393bc56bd 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -12,7 +12,6 @@ import * as bom from 'gulp-bom'; import * as sourcemaps from 'gulp-sourcemaps'; import * as tsb from 'gulp-tsb'; import * as path from 'path'; -import * as ts from 'typescript'; import * as _ from 'underscore'; import * as monacodts from '../monaco/api'; import * as nls from './nls'; @@ -135,57 +134,60 @@ const REPO_SRC_FOLDER = path.join(__dirname, '../../src'); class MonacoGenerator { private readonly _isWatch: boolean; public readonly stream: NodeJS.ReadWriteStream; - /** - * This list is never changed for the lifetime of this object. - */ - private readonly _inputFiles: string[]; - private readonly _watchers: fs.FSWatcher[]; - - private _inputFileChanged: { [filePath: string]: boolean; }; - private _recipeFileChanged: boolean; - private _dtsFilesContents: { [filePath: string]: string; }; - private _dtsFilesContents2: { [filePath: string]: ts.SourceFile; }; + private readonly _watchers: fs.FSWatcher[]; + private readonly _watchedFiles: { [filePath: string]: boolean; }; + private readonly _fsProvider: monacodts.FSProvider; + private readonly _declarationResolver: monacodts.DeclarationResolver; constructor(isWatch: boolean) { this._isWatch = isWatch; this.stream = es.through(); - this._inputFiles = monacodts.getIncludesInRecipe().map((moduleId) => { - if (/\.d\.ts$/.test(moduleId)) { - // This source file is already in .d.ts form - return path.join(REPO_SRC_FOLDER, moduleId); - } else { - return path.join(REPO_SRC_FOLDER, `${moduleId}.ts`); + this._watchers = []; + this._watchedFiles = {}; + let onWillReadFile = (moduleId: string, filePath: string) => { + if (!this._isWatch) { + return; } - }); + if (this._watchedFiles[filePath]) { + return; + } + this._watchedFiles[filePath] = true; - // Install watchers - this._watchers = []; - if (this._isWatch) { - this._inputFiles.forEach((filePath) => { - const watcher = fs.watch(filePath); - watcher.addListener('change', () => { - this._inputFileChanged[filePath] = true; - // Avoid hitting empty files... :/ - setTimeout(() => this.execute(), 10); - }); - this._watchers.push(watcher); + const watcher = fs.watch(filePath); + watcher.addListener('change', () => { + this._declarationResolver.invalidateCache(moduleId); + this._executeSoon(); }); + this._watchers.push(watcher); + }; + this._fsProvider = new class extends monacodts.FSProvider { + public readFileSync(moduleId: string, filePath: string): Buffer { + onWillReadFile(moduleId, filePath); + return super.readFileSync(moduleId, filePath); + } + }; + this._declarationResolver = new monacodts.DeclarationResolver(this._fsProvider); + if (this._isWatch) { const recipeWatcher = fs.watch(monacodts.RECIPE_PATH); recipeWatcher.addListener('change', () => { - this._recipeFileChanged = true; - // Avoid hitting empty files... :/ - setTimeout(() => this.execute(), 10); + this._executeSoon(); }); this._watchers.push(recipeWatcher); } + } - this._inputFileChanged = {}; - this._inputFiles.forEach(file => this._inputFileChanged[file] = true); - this._recipeFileChanged = true; - this._dtsFilesContents = {}; - this._dtsFilesContents2 = {}; + private _executeSoonTimer: NodeJS.Timer | null = null; + private _executeSoon(): void { + if (this._executeSoonTimer !== null) { + clearTimeout(this._executeSoonTimer); + this._executeSoonTimer = null; + } + this._executeSoonTimer = setTimeout(() => { + this._executeSoonTimer = null; + this.execute(); + }, 20); } public dispose(): void { @@ -193,56 +195,12 @@ class MonacoGenerator { } private _run(): monacodts.IMonacoDeclarationResult | null { - let somethingChanged = false; - - const setDTSFileContent = (file: string, contents: string): void => { - if (this._dtsFilesContents[file] === contents) { - return; - } - this._dtsFilesContents[file] = contents; - this._dtsFilesContents2[file] = ts.createSourceFile(file, contents, ts.ScriptTarget.ES5); - somethingChanged = true; - }; - - const fileMap: { [fileName: string]: string; } = {}; - - this._inputFiles.forEach((inputFile) => { - if (!this._inputFileChanged[inputFile]) { - return; - } - this._inputFileChanged[inputFile] = false; - - const inputFileContents = fs.readFileSync(inputFile).toString(); - if (/\.d\.ts$/.test(inputFile)) { - // This is a .d.ts file - setDTSFileContent(inputFile, inputFileContents); - return; - } - - fileMap[inputFile] = inputFileContents; - }); - - if (Object.keys(fileMap).length > 0) { - const service = ts.createLanguageService(new monacodts.TypeScriptLanguageServiceHost({}, fileMap, {})); - - Object.keys(fileMap).forEach((fileName) => { - const output = service.getEmitOutput(fileName, true).outputFiles[0].text; - const destFileName = fileName.replace(/\.ts$/, '.d.ts'); - setDTSFileContent(destFileName, output); - }); - } - - if (this._recipeFileChanged) { - this._recipeFileChanged = false; - somethingChanged = true; - } - - if (!somethingChanged) { - // Nothing changed - return null; + let r = monacodts.run3(this._declarationResolver); + if (!r && !this._isWatch) { + // The build must always be able to generate the monaco.d.ts + throw new Error(`monaco.d.ts generation error - Cannot continue`); } - - return monacodts.run2('src', this._dtsFilesContents2); + return r; } private _log(message: any, ...rest: any[]): void { @@ -254,11 +212,9 @@ class MonacoGenerator { const result = this._run(); if (!result) { // nothing really changed - this._log(`monaco.d.ts is unchanged - total time took ${Date.now() - startTime} ms`); return; } if (result.isTheSame) { - this._log(`monaco.d.ts is unchanged - total time took ${Date.now() - startTime} ms`); return; } diff --git a/build/lib/extensions.js b/build/lib/extensions.js index 293c1f0c2..994d1336e 100644 --- a/build/lib/extensions.js +++ b/build/lib/extensions.js @@ -36,10 +36,12 @@ function fromLocalWebpack(extensionPath, sourceMappingURLBase) { const result = es.through(); const packagedDependencies = []; const packageJsonConfig = require(path.join(extensionPath, 'package.json')); - const webpackRootConfig = require(path.join(extensionPath, 'extension.webpack.config.js')); - for (const key in webpackRootConfig.externals) { - if (key in packageJsonConfig.dependencies) { - packagedDependencies.push(key); + if (packageJsonConfig.dependencies) { + const webpackRootConfig = require(path.join(extensionPath, 'extension.webpack.config.js')); + for (const key in webpackRootConfig.externals) { + if (key in packageJsonConfig.dependencies) { + packagedDependencies.push(key); + } } } vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.Yarn, packagedDependencies }).then(fileNames => { @@ -68,12 +70,14 @@ function fromLocalWebpack(extensionPath, sourceMappingURLBase) { .pipe(packageJsonFilter) .pipe(buffer()) .pipe(json((data) => { - // hardcoded entry point directory! - data.main = data.main.replace('/out/', /dist/); + if (data.main) { + // hardcoded entry point directory! + data.main = data.main.replace('/out/', /dist/); + } return data; })) .pipe(packageJsonFilter.restore); - const webpackStreams = webpackConfigLocations.map(webpackConfigPath => { + const webpackStreams = webpackConfigLocations.map(webpackConfigPath => () => { const webpackDone = (err, stats) => { util.log(`Bundled extension: ${util.colors.yellow(path.join(path.basename(extensionPath), path.relative(extensionPath, webpackConfigPath)))}...`); if (err) { @@ -114,7 +118,7 @@ function fromLocalWebpack(extensionPath, sourceMappingURLBase) { this.emit('data', data); })); }); - es.merge(...webpackStreams, patchFilesStream) + es.merge(sequence(webpackStreams), patchFilesStream) // .pipe(es.through(function (data) { // // debug // console.log('out', data.path, data.contents.length); @@ -212,10 +216,10 @@ function packageExtensionsStream(optsIn) { .filter(({ name }) => excludedExtensions.indexOf(name) === -1) .filter(({ name }) => opts.desiredExtensions ? opts.desiredExtensions.indexOf(name) >= 0 : true) .filter(({ name }) => builtInExtensions.every(b => b.name !== name)); - const localExtensions = () => es.merge(...localExtensionDescriptions.map(extension => { - return fromLocal(extension.path, opts.sourceMappingURLBase) - .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); - })); + const localExtensions = () => sequence([...localExtensionDescriptions.map(extension => () => { + return fromLocal(extension.path, opts.sourceMappingURLBase) + .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); + })]); const localExtensionDependencies = () => gulp.src('extensions/node_modules/**', { base: '.' }); const marketplaceExtensions = () => es.merge(...builtInExtensions .filter(({ name }) => opts.desiredExtensions ? opts.desiredExtensions.indexOf(name) >= 0 : true) diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts index 7d3bc6630..844b711a8 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts @@ -39,14 +39,15 @@ function fromLocalWebpack(extensionPath: string, sourceMappingURLBase: string | const packagedDependencies: string[] = []; const packageJsonConfig = require(path.join(extensionPath, 'package.json')); - const webpackRootConfig = require(path.join(extensionPath, 'extension.webpack.config.js')); - for (const key in webpackRootConfig.externals) { - if (key in packageJsonConfig.dependencies) { - packagedDependencies.push(key); + if (packageJsonConfig.dependencies) { + const webpackRootConfig = require(path.join(extensionPath, 'extension.webpack.config.js')); + for (const key in webpackRootConfig.externals) { + if (key in packageJsonConfig.dependencies) { + packagedDependencies.push(key); + } } } - vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.Yarn, packagedDependencies }).then(fileNames => { const files = fileNames .map(fileName => path.join(extensionPath, fileName)) @@ -80,14 +81,16 @@ function fromLocalWebpack(extensionPath: string, sourceMappingURLBase: string | .pipe(packageJsonFilter) .pipe(buffer()) .pipe(json((data: any) => { - // hardcoded entry point directory! - data.main = data.main.replace('/out/', /dist/); + if (data.main) { + // hardcoded entry point directory! + data.main = data.main.replace('/out/', /dist/); + } return data; })) .pipe(packageJsonFilter.restore); - const webpackStreams = webpackConfigLocations.map(webpackConfigPath => { + const webpackStreams = webpackConfigLocations.map(webpackConfigPath => () => { const webpackDone = (err: any, stats: any) => { util.log(`Bundled extension: ${util.colors.yellow(path.join(path.basename(extensionPath), path.relative(extensionPath, webpackConfigPath)))}...`); @@ -136,7 +139,7 @@ function fromLocalWebpack(extensionPath: string, sourceMappingURLBase: string | })); }); - es.merge(...webpackStreams, patchFilesStream) + es.merge(sequence(webpackStreams), patchFilesStream) // .pipe(es.through(function (data) { // // debug // console.log('out', data.path, data.contents.length); @@ -267,10 +270,10 @@ export function packageExtensionsStream(optsIn?: IPackageExtensionsOptions): Nod .filter(({ name }) => opts.desiredExtensions ? opts.desiredExtensions.indexOf(name) >= 0 : true) .filter(({ name }) => builtInExtensions.every(b => b.name !== name)); - const localExtensions = () => es.merge(...localExtensionDescriptions.map(extension => { + const localExtensions = () => sequence([...localExtensionDescriptions.map(extension => () => { return fromLocal(extension.path, opts.sourceMappingURLBase) .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); - })); + })]); const localExtensionDependencies = () => gulp.src('extensions/node_modules/**', { base: '.' }); diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 40d686c08..296d1286f 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -214,6 +214,10 @@ "name": "vs/workbench/services/progress", "project": "vscode-workbench" }, + { + "name": "vs/workbench/services/remote", + "project": "vscode-workbench" + }, { "name": "vs/workbench/services/textfile", "project": "vscode-workbench" diff --git a/build/lib/snapshotLoader.js b/build/lib/snapshotLoader.js index 9a047e0f6..ee626a0f7 100644 --- a/build/lib/snapshotLoader.js +++ b/build/lib/snapshotLoader.js @@ -24,6 +24,7 @@ var snaps; case 'linux': loaderFilepath = `VSCode-${process.platform}-${arch}/resources/app/out/vs/loader.js`; startupBlobFilepath = `VSCode-${process.platform}-${arch}/snapshot_blob.bin`; + break; default: throw new Error('Unknown platform'); } diff --git a/build/lib/snapshotLoader.ts b/build/lib/snapshotLoader.ts index 3bc379697..40b06d67d 100644 --- a/build/lib/snapshotLoader.ts +++ b/build/lib/snapshotLoader.ts @@ -30,6 +30,7 @@ namespace snaps { case 'linux': loaderFilepath = `VSCode-${process.platform}-${arch}/resources/app/out/vs/loader.js`; startupBlobFilepath = `VSCode-${process.platform}-${arch}/snapshot_blob.bin`; + break; default: throw new Error('Unknown platform'); diff --git a/build/lib/standalone.js b/build/lib/standalone.js index c01e0f461..121a5e192 100644 --- a/build/lib/standalone.js +++ b/build/lib/standalone.js @@ -28,12 +28,22 @@ function writeFile(filePath, contents) { } function extractEditor(options) { const tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.json')).toString()); - tsConfig.compilerOptions.noUnusedLocals = false; - tsConfig.compilerOptions.preserveConstEnums = false; - tsConfig.compilerOptions.declaration = false; - delete tsConfig.compilerOptions.types; + let compilerOptions; + if (tsConfig.extends) { + compilerOptions = Object.assign({}, require(path.join(options.sourcesRoot, tsConfig.extends)).compilerOptions, tsConfig.compilerOptions); + } + else { + compilerOptions = tsConfig.compilerOptions; + } + tsConfig.compilerOptions = compilerOptions; + compilerOptions.noUnusedLocals = false; + compilerOptions.preserveConstEnums = false; + compilerOptions.declaration = false; + compilerOptions.moduleResolution = ts.ModuleResolutionKind.Classic; + delete compilerOptions.types; + delete tsConfig.extends; tsConfig.exclude = []; - options.compilerOptions = tsConfig.compilerOptions; + options.compilerOptions = compilerOptions; let result = tss.shake(options); for (let fileName in result) { if (result.hasOwnProperty(fileName)) { @@ -80,6 +90,7 @@ function extractEditor(options) { } } } + delete tsConfig.compilerOptions.moduleResolution; writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); [ 'vs/css.build.js', @@ -116,7 +127,7 @@ function createESMSourcesAndResources2(options) { if (file === 'tsconfig.json') { const tsConfig = JSON.parse(fs.readFileSync(path.join(SRC_FOLDER, file)).toString()); tsConfig.compilerOptions.module = 'es6'; - tsConfig.compilerOptions.outDir = path.join(path.relative(OUT_FOLDER, OUT_RESOURCES_FOLDER), 'vs'); + tsConfig.compilerOptions.outDir = path.join(path.relative(OUT_FOLDER, OUT_RESOURCES_FOLDER), 'vs').replace(/\\/g, '/'); write(getDestAbsoluteFilePath(file), JSON.stringify(tsConfig, null, '\t')); continue; } @@ -144,15 +155,16 @@ function createESMSourcesAndResources2(options) { importedFilepath = path.join(path.dirname(file), importedFilepath); } let relativePath; - if (importedFilepath === path.dirname(file)) { + if (importedFilepath === path.dirname(file).replace(/\\/g, '/')) { relativePath = '../' + path.basename(path.dirname(file)); } - else if (importedFilepath === path.dirname(path.dirname(file))) { + else if (importedFilepath === path.dirname(path.dirname(file)).replace(/\\/g, '/')) { relativePath = '../../' + path.basename(path.dirname(path.dirname(file))); } else { relativePath = path.relative(path.dirname(file), importedFilepath); } + relativePath = relativePath.replace(/\\/g, '/'); if (!/(^\.\/)|(^\.\.\/)/.test(relativePath)) { relativePath = './' + relativePath; } diff --git a/build/lib/standalone.ts b/build/lib/standalone.ts index aba118ab5..6ec0aa1c9 100644 --- a/build/lib/standalone.ts +++ b/build/lib/standalone.ts @@ -32,13 +32,24 @@ function writeFile(filePath: string, contents: Buffer | string): void { export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: string }): void { const tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.json')).toString()); - tsConfig.compilerOptions.noUnusedLocals = false; - tsConfig.compilerOptions.preserveConstEnums = false; - tsConfig.compilerOptions.declaration = false; - delete tsConfig.compilerOptions.types; + let compilerOptions: { [key: string]: any }; + if (tsConfig.extends) { + compilerOptions = Object.assign({}, require(path.join(options.sourcesRoot, tsConfig.extends)).compilerOptions, tsConfig.compilerOptions); + } else { + compilerOptions = tsConfig.compilerOptions; + } + tsConfig.compilerOptions = compilerOptions; + + compilerOptions.noUnusedLocals = false; + compilerOptions.preserveConstEnums = false; + compilerOptions.declaration = false; + compilerOptions.moduleResolution = ts.ModuleResolutionKind.Classic; + + delete compilerOptions.types; + delete tsConfig.extends; tsConfig.exclude = []; - options.compilerOptions = tsConfig.compilerOptions; + options.compilerOptions = compilerOptions; let result = tss.shake(options); for (let fileName in result) { @@ -88,6 +99,7 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str } } + delete tsConfig.compilerOptions.moduleResolution; writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); [ @@ -137,7 +149,7 @@ export function createESMSourcesAndResources2(options: IOptions2): void { if (file === 'tsconfig.json') { const tsConfig = JSON.parse(fs.readFileSync(path.join(SRC_FOLDER, file)).toString()); tsConfig.compilerOptions.module = 'es6'; - tsConfig.compilerOptions.outDir = path.join(path.relative(OUT_FOLDER, OUT_RESOURCES_FOLDER), 'vs'); + tsConfig.compilerOptions.outDir = path.join(path.relative(OUT_FOLDER, OUT_RESOURCES_FOLDER), 'vs').replace(/\\/g, '/'); write(getDestAbsoluteFilePath(file), JSON.stringify(tsConfig, null, '\t')); continue; } @@ -170,13 +182,14 @@ export function createESMSourcesAndResources2(options: IOptions2): void { } let relativePath: string; - if (importedFilepath === path.dirname(file)) { + if (importedFilepath === path.dirname(file).replace(/\\/g, '/')) { relativePath = '../' + path.basename(path.dirname(file)); - } else if (importedFilepath === path.dirname(path.dirname(file))) { + } else if (importedFilepath === path.dirname(path.dirname(file)).replace(/\\/g, '/')) { relativePath = '../../' + path.basename(path.dirname(path.dirname(file))); } else { relativePath = path.relative(path.dirname(file), importedFilepath); } + relativePath = relativePath.replace(/\\/g, '/'); if (!/(^\.\/)|(^\.\.\/)/.test(relativePath)) { relativePath = './' + relativePath; } diff --git a/build/lib/treeshaking.js b/build/lib/treeshaking.js index b80454156..884d28279 100644 --- a/build/lib/treeshaking.js +++ b/build/lib/treeshaking.js @@ -57,7 +57,7 @@ function createTypeScriptLanguageService(options) { const FILES = discoverAndReadFiles(options); // Add fake usage files options.inlineEntryPoints.forEach((inlineEntryPoint, index) => { - FILES[`inlineEntryPoint:${index}.ts`] = inlineEntryPoint; + FILES[`inlineEntryPoint.${index}.ts`] = inlineEntryPoint; }); // Add additional typings options.typings.forEach((typing) => { @@ -70,7 +70,8 @@ function createTypeScriptLanguageService(options) { const filepath = path.join(TYPESCRIPT_LIB_FOLDER, filename); RESOLVED_LIBS[`defaultLib:${filename}`] = fs.readFileSync(filepath).toString(); }); - const host = new TypeScriptLanguageServiceHost(RESOLVED_LIBS, FILES, ts.convertCompilerOptionsFromJson(options.compilerOptions, ``).options); + const compilerOptions = ts.convertCompilerOptionsFromJson(options.compilerOptions, options.sourcesRoot).options; + const host = new TypeScriptLanguageServiceHost(RESOLVED_LIBS, FILES, compilerOptions); return ts.createLanguageService(host); } /** @@ -335,7 +336,7 @@ function markNodes(languageService, options) { } options.entryPoints.forEach(moduleId => enqueueFile(moduleId + '.ts')); // Add fake usage files - options.inlineEntryPoints.forEach((_, index) => enqueueFile(`inlineEntryPoint:${index}.ts`)); + options.inlineEntryPoints.forEach((_, index) => enqueueFile(`inlineEntryPoint.${index}.ts`)); let step = 0; const checker = program.getTypeChecker(); while (black_queue.length > 0 || gray_queue.length > 0) { diff --git a/build/lib/treeshaking.ts b/build/lib/treeshaking.ts index 3439b1437..9c142793a 100644 --- a/build/lib/treeshaking.ts +++ b/build/lib/treeshaking.ts @@ -110,7 +110,7 @@ function createTypeScriptLanguageService(options: ITreeShakingOptions): ts.Langu // Add fake usage files options.inlineEntryPoints.forEach((inlineEntryPoint, index) => { - FILES[`inlineEntryPoint:${index}.ts`] = inlineEntryPoint; + FILES[`inlineEntryPoint.${index}.ts`] = inlineEntryPoint; }); // Add additional typings @@ -126,7 +126,9 @@ function createTypeScriptLanguageService(options: ITreeShakingOptions): ts.Langu RESOLVED_LIBS[`defaultLib:${filename}`] = fs.readFileSync(filepath).toString(); }); - const host = new TypeScriptLanguageServiceHost(RESOLVED_LIBS, FILES, ts.convertCompilerOptionsFromJson(options.compilerOptions, ``).options); + const compilerOptions = ts.convertCompilerOptionsFromJson(options.compilerOptions, options.sourcesRoot).options; + + const host = new TypeScriptLanguageServiceHost(RESOLVED_LIBS, FILES, compilerOptions); return ts.createLanguageService(host); } @@ -443,7 +445,7 @@ function markNodes(languageService: ts.LanguageService, options: ITreeShakingOpt options.entryPoints.forEach(moduleId => enqueueFile(moduleId + '.ts')); // Add fake usage files - options.inlineEntryPoints.forEach((_, index) => enqueueFile(`inlineEntryPoint:${index}.ts`)); + options.inlineEntryPoints.forEach((_, index) => enqueueFile(`inlineEntryPoint.${index}.ts`)); let step = 0; diff --git a/build/lib/tslint/noNewBufferRule.js b/build/lib/tslint/noNewBufferRule.js new file mode 100644 index 000000000..ae9b02d45 --- /dev/null +++ b/build/lib/tslint/noNewBufferRule.js @@ -0,0 +1,22 @@ +"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 ts = require("typescript"); +const Lint = require("tslint"); +class Rule extends Lint.Rules.AbstractRule { + apply(sourceFile) { + return this.applyWithWalker(new NewBufferRuleWalker(sourceFile, this.getOptions())); + } +} +exports.Rule = Rule; +class NewBufferRuleWalker extends Lint.RuleWalker { + visitNewExpression(node) { + if (node.expression.kind === ts.SyntaxKind.Identifier && node.expression && node.expression.text === 'Buffer') { + this.addFailureAtNode(node, '`new Buffer` is deprecated. Consider Buffer.From or Buffer.alloc instead.'); + } + super.visitNewExpression(node); + } +} diff --git a/build/lib/tslint/noNewBufferRule.ts b/build/lib/tslint/noNewBufferRule.ts new file mode 100644 index 000000000..7b0dd43e5 --- /dev/null +++ b/build/lib/tslint/noNewBufferRule.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as ts from 'typescript'; +import * as Lint from 'tslint'; + +export class Rule extends Lint.Rules.AbstractRule { + apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { + return this.applyWithWalker(new NewBufferRuleWalker(sourceFile, this.getOptions())); + } +} + +class NewBufferRuleWalker extends Lint.RuleWalker { + visitNewExpression(node: ts.NewExpression) { + if (node.expression.kind === ts.SyntaxKind.Identifier && node.expression && (node.expression as ts.Identifier).text === 'Buffer') { + this.addFailureAtNode(node, '`new Buffer` is deprecated. Consider Buffer.From or Buffer.alloc instead.'); + } + + super.visitNewExpression(node); + } +} \ No newline at end of file diff --git a/build/lib/typings/OSSREADME.json b/build/lib/typings/OSSREADME.json deleted file mode 100644 index cdb8c7f51..000000000 --- a/build/lib/typings/OSSREADME.json +++ /dev/null @@ -1,10 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -// All OSS in this folder is development time only -[{ - "name": "definitelytyped", - "repositoryURL": "https://github.com/DefinitelyTyped/DefinitelyTyped", - "license": "MIT", - "isDev": true -} -] \ No newline at end of file diff --git a/build/lib/typings/cgmanifest.json b/build/lib/typings/cgmanifest.json new file mode 100644 index 000000000..6e529a79f --- /dev/null +++ b/build/lib/typings/cgmanifest.json @@ -0,0 +1,16 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "definitelytyped", + "repositoryUrl": "https://github.com/DefinitelyTyped/DefinitelyTyped", + "commitHash": "69e3ac6bec3008271f76bbfa7cf69aa9198c4ff0" + } + }, + "license": "MIT" + } + ], + "version": 1 +} diff --git a/build/lib/util.ts b/build/lib/util.ts index f5dc8cd84..4617cc83f 100644 --- a/build/lib/util.ts +++ b/build/lib/util.ts @@ -91,7 +91,7 @@ export function fixWin32DirectoryPermissions(): NodeJS.ReadWriteStream { }); } -export function setExecutableBit(pattern: string | string[]): NodeJS.ReadWriteStream { +export function setExecutableBit(pattern?: string | string[]): NodeJS.ReadWriteStream { const setBit = es.mapSync(f => { f.stat.mode = /* 100755 */ 33261; return f; diff --git a/build/monaco/api.js b/build/monaco/api.js index cc9fc40e9..716d1f566 100644 --- a/build/monaco/api.js +++ b/build/monaco/api.js @@ -8,24 +8,13 @@ const fs = require("fs"); const ts = require("typescript"); const path = require("path"); const util = require("gulp-util"); +const dtsv = '2'; const tsfmt = require('../../tsfmt.json'); -function log(message, ...rest) { - util.log(util.colors.cyan('[monaco.d.ts]'), message, ...rest); -} const SRC = path.join(__dirname, '../../src'); -const OUT_ROOT = path.join(__dirname, '../../'); exports.RECIPE_PATH = path.join(__dirname, './monaco.d.ts.recipe'); const DECLARATION_PATH = path.join(__dirname, '../../src/vs/monaco.d.ts'); -var CURRENT_PROCESSING_RULE = ''; function logErr(message, ...rest) { - util.log(util.colors.red('[monaco.d.ts]'), 'WHILE HANDLING RULE: ', CURRENT_PROCESSING_RULE); - util.log(util.colors.red('[monaco.d.ts]'), message, ...rest); -} -function moduleIdToPath(out, moduleId) { - if (/\.d\.ts/.test(moduleId)) { - return path.join(SRC, moduleId); - } - return path.join(OUT_ROOT, out, moduleId) + '.d.ts'; + util.log(util.colors.yellow(`[monaco.d.ts]`), message, ...rest); } function isDeclaration(a) { return (a.kind === ts.SyntaxKind.InterfaceDeclaration @@ -315,6 +304,7 @@ function generateDeclarationFile(recipe, sourceFileGetter) { let usageCounter = 0; let usageImports = []; let usage = []; + let failed = false; usage.push(`var a;`); usage.push(`var b;`); const generateUsageImport = (moduleId) => { @@ -323,13 +313,23 @@ function generateDeclarationFile(recipe, sourceFileGetter) { return importName; }; let enums = []; + let version = null; lines.forEach(line => { + if (failed) { + return; + } + let m0 = line.match(/^\/\/dtsv=(\d+)$/); + if (m0) { + version = m0[1]; + } let m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/); if (m1) { - CURRENT_PROCESSING_RULE = line; let moduleId = m1[1]; const sourceFile = sourceFileGetter(moduleId); if (!sourceFile) { + logErr(`While handling ${line}`); + logErr(`Cannot find ${moduleId}`); + failed = true; return; } const importName = generateUsageImport(moduleId); @@ -342,7 +342,9 @@ function generateDeclarationFile(recipe, sourceFileGetter) { } let declaration = getTopLevelDeclaration(sourceFile, typeName); if (!declaration) { - logErr('Cannot find type ' + typeName); + logErr(`While handling ${line}`); + logErr(`Cannot find ${typeName}`); + failed = true; return; } result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums))); @@ -351,10 +353,12 @@ function generateDeclarationFile(recipe, sourceFileGetter) { } let m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/); if (m2) { - CURRENT_PROCESSING_RULE = line; let moduleId = m2[1]; const sourceFile = sourceFileGetter(moduleId); if (!sourceFile) { + logErr(`While handling ${line}`); + logErr(`Cannot find ${moduleId}`); + failed = true; return; } const importName = generateUsageImport(moduleId); @@ -391,10 +395,24 @@ function generateDeclarationFile(recipe, sourceFileGetter) { } result.push(line); }); + if (failed) { + return null; + } + if (version !== dtsv) { + if (!version) { + logErr(`gulp watch restart required. 'monaco.d.ts.recipe' is written before versioning was introduced.`); + } + else { + logErr(`gulp watch restart required. 'monaco.d.ts.recipe' v${version} does not match runtime v${dtsv}.`); + } + return null; + } let resultTxt = result.join(endl); resultTxt = resultTxt.replace(/\bURI\b/g, 'Uri'); resultTxt = resultTxt.replace(/\bEvent { - let m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/); - if (m1) { - let moduleId = m1[1]; - result.push(moduleId); - return; - } - let m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/); - if (m2) { - let moduleId = m2[1]; - result.push(moduleId); - return; - } - }); - return result; -} -exports.getIncludesInRecipe = getIncludesInRecipe; -function getFilesToWatch(out) { - return getIncludesInRecipe().map((moduleId) => moduleIdToPath(out, moduleId)); + resultEnums = resultEnums.split(/\r\n|\n|\r/).join(endl); + return { + result: resultTxt, + usageContent: `${usageImports.join('\n')}\n\n${usage.join('\n')}`, + enums: resultEnums + }; } -exports.getFilesToWatch = getFilesToWatch; function _run(sourceFileGetter) { - log('Starting monaco.d.ts generation'); const recipe = fs.readFileSync(exports.RECIPE_PATH).toString(); - const [result, usageContent, enums] = generateDeclarationFile(recipe, sourceFileGetter); + const t = generateDeclarationFile(recipe, sourceFileGetter); + if (!t) { + return null; + } + const result = t.result; + const usageContent = t.usageContent; + const enums = t.enums; const currentContent = fs.readFileSync(DECLARATION_PATH).toString(); const one = currentContent.replace(/\r\n/gm, '\n'); const other = result.replace(/\r\n/gm, '\n'); const isTheSame = (one === other); - log('Finished monaco.d.ts generation'); return { content: result, usageContent: usageContent, @@ -453,36 +452,57 @@ function _run(sourceFileGetter) { isTheSame }; } -function run(out, inputFiles) { - let SOURCE_FILE_MAP = {}; - const sourceFileGetter = (moduleId) => { - if (!SOURCE_FILE_MAP[moduleId]) { - let filePath = path.normalize(moduleIdToPath(out, moduleId)); - if (!inputFiles.hasOwnProperty(filePath)) { - logErr('CANNOT FIND FILE ' + filePath + '. YOU MIGHT NEED TO RESTART gulp'); +class FSProvider { + existsSync(filePath) { + return fs.existsSync(filePath); + } + readFileSync(_moduleId, filePath) { + return fs.readFileSync(filePath); + } +} +exports.FSProvider = FSProvider; +class DeclarationResolver { + constructor(_fsProvider) { + this._fsProvider = _fsProvider; + this._sourceFileCache = Object.create(null); + } + invalidateCache(moduleId) { + this._sourceFileCache[moduleId] = null; + } + getDeclarationSourceFile(moduleId) { + if (!this._sourceFileCache[moduleId]) { + this._sourceFileCache[moduleId] = this._getDeclarationSourceFile(moduleId); + } + return this._sourceFileCache[moduleId]; + } + _getDeclarationSourceFile(moduleId) { + if (/\.d\.ts$/.test(moduleId)) { + const fileName = path.join(SRC, moduleId); + if (!this._fsProvider.existsSync(fileName)) { return null; } - let fileContents = inputFiles[filePath]; - let sourceFile = ts.createSourceFile(filePath, fileContents, ts.ScriptTarget.ES5); - SOURCE_FILE_MAP[moduleId] = sourceFile; + const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString(); + return ts.createSourceFile(fileName, fileContents, ts.ScriptTarget.ES5); } - return SOURCE_FILE_MAP[moduleId]; - }; - return _run(sourceFileGetter); + const fileName = path.join(SRC, `${moduleId}.ts`); + if (!this._fsProvider.existsSync(fileName)) { + return null; + } + const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString(); + const fileMap = { + 'file.ts': fileContents + }; + const service = ts.createLanguageService(new TypeScriptLanguageServiceHost({}, fileMap, {})); + const text = service.getEmitOutput('file.ts', true).outputFiles[0].text; + return ts.createSourceFile(fileName, text, ts.ScriptTarget.ES5); + } } -exports.run = run; -function run2(out, sourceFileMap) { - const sourceFileGetter = (moduleId) => { - let filePath = path.normalize(moduleIdToPath(out, moduleId)); - return sourceFileMap[filePath]; - }; +exports.DeclarationResolver = DeclarationResolver; +function run3(resolver) { + const sourceFileGetter = (moduleId) => resolver.getDeclarationSourceFile(moduleId); return _run(sourceFileGetter); } -exports.run2 = run2; -function complainErrors() { - logErr('Not running monaco.d.ts generation due to compile errors'); -} -exports.complainErrors = complainErrors; +exports.run3 = run3; class TypeScriptLanguageServiceHost { constructor(libs, files, compilerOptions) { this._libs = libs; @@ -528,28 +548,11 @@ class TypeScriptLanguageServiceHost { return fileName === this.getDefaultLibFileName(this._compilerOptions); } } -exports.TypeScriptLanguageServiceHost = TypeScriptLanguageServiceHost; function execute() { - const OUTPUT_FILES = {}; - const SRC_FILES = {}; - const SRC_FILE_TO_EXPECTED_NAME = {}; - getIncludesInRecipe().forEach((moduleId) => { - if (/\.d\.ts$/.test(moduleId)) { - let fileName = path.join(SRC, moduleId); - OUTPUT_FILES[moduleIdToPath('src', moduleId)] = fs.readFileSync(fileName).toString(); - return; - } - let fileName = path.join(SRC, moduleId) + '.ts'; - SRC_FILES[fileName] = fs.readFileSync(fileName).toString(); - SRC_FILE_TO_EXPECTED_NAME[fileName] = moduleIdToPath('src', moduleId); - }); - const languageService = ts.createLanguageService(new TypeScriptLanguageServiceHost({}, SRC_FILES, {})); - var t1 = Date.now(); - Object.keys(SRC_FILES).forEach((fileName) => { - const emitOutput = languageService.getEmitOutput(fileName, true); - OUTPUT_FILES[SRC_FILE_TO_EXPECTED_NAME[fileName]] = emitOutput.outputFiles[0].text; - }); - console.log(`Generating .d.ts took ${Date.now() - t1} ms`); - return run('src', OUTPUT_FILES); + let r = run3(new DeclarationResolver(new FSProvider())); + if (!r) { + throw new Error(`monaco.d.ts generation error - Cannot continue`); + } + return r; } exports.execute = execute; diff --git a/build/monaco/api.ts b/build/monaco/api.ts index b9016913f..d12b77787 100644 --- a/build/monaco/api.ts +++ b/build/monaco/api.ts @@ -8,34 +8,19 @@ import * as ts from 'typescript'; import * as path from 'path'; import * as util from 'gulp-util'; -const tsfmt = require('../../tsfmt.json'); +const dtsv = '2'; -function log(message: any, ...rest: any[]): void { - util.log(util.colors.cyan('[monaco.d.ts]'), message, ...rest); -} +const tsfmt = require('../../tsfmt.json'); const SRC = path.join(__dirname, '../../src'); -const OUT_ROOT = path.join(__dirname, '../../'); export const RECIPE_PATH = path.join(__dirname, './monaco.d.ts.recipe'); const DECLARATION_PATH = path.join(__dirname, '../../src/vs/monaco.d.ts'); -var CURRENT_PROCESSING_RULE = ''; function logErr(message: any, ...rest: any[]): void { - util.log(util.colors.red('[monaco.d.ts]'), 'WHILE HANDLING RULE: ', CURRENT_PROCESSING_RULE); - util.log(util.colors.red('[monaco.d.ts]'), message, ...rest); + util.log(util.colors.yellow(`[monaco.d.ts]`), message, ...rest); } -function moduleIdToPath(out: string, moduleId: string): string { - if (/\.d\.ts/.test(moduleId)) { - return path.join(SRC, moduleId); - } - return path.join(OUT_ROOT, out, moduleId) + '.d.ts'; -} - -export interface ISourceFileMap { - [moduleId: string]: ts.SourceFile; -} -export type SourceFileGetter = (moduleId: string) => ts.SourceFile | null; +type SourceFileGetter = (moduleId: string) => ts.SourceFile | null; type TSTopLevelDeclaration = ts.InterfaceDeclaration | ts.EnumDeclaration | ts.ClassDeclaration | ts.TypeAliasDeclaration | ts.FunctionDeclaration | ts.ModuleDeclaration; type TSTopLevelDeclare = TSTopLevelDeclaration | ts.VariableStatement; @@ -362,7 +347,13 @@ function createReplacer(data: string): (str: string) => string { }; } -function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGetter): [string, string, string] { +interface ITempResult { + result: string; + usageContent: string; + enums: string; +} + +function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGetter): ITempResult | null { const endl = /\r\n/.test(recipe) ? '\r\n' : '\n'; let lines = recipe.split(endl); @@ -372,6 +363,8 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet let usageImports: string[] = []; let usage: string[] = []; + let failed = false; + usage.push(`var a;`); usage.push(`var b;`); @@ -382,15 +375,27 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet }; let enums: string[] = []; + let version: string | null = null; lines.forEach(line => { + if (failed) { + return; + } + + let m0 = line.match(/^\/\/dtsv=(\d+)$/); + if (m0) { + version = m0[1]; + } + let m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/); if (m1) { - CURRENT_PROCESSING_RULE = line; let moduleId = m1[1]; const sourceFile = sourceFileGetter(moduleId); if (!sourceFile) { + logErr(`While handling ${line}`); + logErr(`Cannot find ${moduleId}`); + failed = true; return; } @@ -406,7 +411,9 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet } let declaration = getTopLevelDeclaration(sourceFile, typeName); if (!declaration) { - logErr('Cannot find type ' + typeName); + logErr(`While handling ${line}`); + logErr(`Cannot find ${typeName}`); + failed = true; return; } result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage, enums))); @@ -416,10 +423,12 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet let m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/); if (m2) { - CURRENT_PROCESSING_RULE = line; let moduleId = m2[1]; const sourceFile = sourceFileGetter(moduleId); if (!sourceFile) { + logErr(`While handling ${line}`); + logErr(`Cannot find ${moduleId}`); + failed = true; return; } @@ -461,10 +470,25 @@ function generateDeclarationFile(recipe: string, sourceFileGetter: SourceFileGet result.push(line); }); + if (failed) { + return null; + } + + if (version !== dtsv) { + if (!version) { + logErr(`gulp watch restart required. 'monaco.d.ts.recipe' is written before versioning was introduced.`); + } else { + logErr(`gulp watch restart required. 'monaco.d.ts.recipe' v${version} does not match runtime v${dtsv}.`); + } + return null; + } + let resultTxt = result.join(endl); resultTxt = resultTxt.replace(/\bURI\b/g, 'Uri'); resultTxt = resultTxt.replace(/\bEvent { - - let m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/); - if (m1) { - let moduleId = m1[1]; - result.push(moduleId); - return; - } - - let m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/); - if (m2) { - let moduleId = m2[1]; - result.push(moduleId); - return; - } - }); - - return result; -} - -export function getFilesToWatch(out: string): string[] { - return getIncludesInRecipe().map((moduleId) => moduleIdToPath(out, moduleId)); + return { + result: resultTxt, + usageContent: `${usageImports.join('\n')}\n\n${usage.join('\n')}`, + enums: resultEnums + }; } export interface IMonacoDeclarationResult { @@ -521,19 +518,22 @@ export interface IMonacoDeclarationResult { isTheSame: boolean; } -function _run(sourceFileGetter: SourceFileGetter): IMonacoDeclarationResult { - log('Starting monaco.d.ts generation'); - +function _run(sourceFileGetter: SourceFileGetter): IMonacoDeclarationResult | null { const recipe = fs.readFileSync(RECIPE_PATH).toString(); - const [result, usageContent, enums] = generateDeclarationFile(recipe, sourceFileGetter); + const t = generateDeclarationFile(recipe, sourceFileGetter); + if (!t) { + return null; + } + + const result = t.result; + const usageContent = t.usageContent; + const enums = t.enums; const currentContent = fs.readFileSync(DECLARATION_PATH).toString(); const one = currentContent.replace(/\r\n/gm, '\n'); const other = result.replace(/\r\n/gm, '\n'); const isTheSame = (one === other); - log('Finished monaco.d.ts generation'); - return { content: result, usageContent: usageContent, @@ -543,48 +543,69 @@ function _run(sourceFileGetter: SourceFileGetter): IMonacoDeclarationResult { }; } -export function run(out: string, inputFiles: { [file: string]: string; }): IMonacoDeclarationResult { +export class FSProvider { + public existsSync(filePath: string): boolean { + return fs.existsSync(filePath); + } + public readFileSync(_moduleId: string, filePath: string): Buffer { + return fs.readFileSync(filePath); + } +} - let SOURCE_FILE_MAP: { [moduleId: string]: ts.SourceFile; } = {}; - const sourceFileGetter = (moduleId: string): ts.SourceFile | null => { - if (!SOURCE_FILE_MAP[moduleId]) { - let filePath = path.normalize(moduleIdToPath(out, moduleId)); +export class DeclarationResolver { - if (!inputFiles.hasOwnProperty(filePath)) { - logErr('CANNOT FIND FILE ' + filePath + '. YOU MIGHT NEED TO RESTART gulp'); - return null; - } + private _sourceFileCache: { [moduleId: string]: ts.SourceFile | null; }; + + constructor(private readonly _fsProvider: FSProvider) { + this._sourceFileCache = Object.create(null); + } - let fileContents = inputFiles[filePath]; - let sourceFile = ts.createSourceFile(filePath, fileContents, ts.ScriptTarget.ES5); + public invalidateCache(moduleId: string): void { + this._sourceFileCache[moduleId] = null; + } - SOURCE_FILE_MAP[moduleId] = sourceFile; + public getDeclarationSourceFile(moduleId: string): ts.SourceFile | null { + if (!this._sourceFileCache[moduleId]) { + this._sourceFileCache[moduleId] = this._getDeclarationSourceFile(moduleId); } - return SOURCE_FILE_MAP[moduleId]; - }; + return this._sourceFileCache[moduleId]; + } - return _run(sourceFileGetter); + private _getDeclarationSourceFile(moduleId: string): ts.SourceFile | null { + if (/\.d\.ts$/.test(moduleId)) { + const fileName = path.join(SRC, moduleId); + if (!this._fsProvider.existsSync(fileName)) { + return null; + } + const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString(); + return ts.createSourceFile(fileName, fileContents, ts.ScriptTarget.ES5); + } + const fileName = path.join(SRC, `${moduleId}.ts`); + if (!this._fsProvider.existsSync(fileName)) { + return null; + } + const fileContents = this._fsProvider.readFileSync(moduleId, fileName).toString(); + const fileMap: IFileMap = { + 'file.ts': fileContents + }; + const service = ts.createLanguageService(new TypeScriptLanguageServiceHost({}, fileMap, {})); + const text = service.getEmitOutput('file.ts', true).outputFiles[0].text; + return ts.createSourceFile(fileName, text, ts.ScriptTarget.ES5); + } } -export function run2(out: string, sourceFileMap: ISourceFileMap): IMonacoDeclarationResult { - const sourceFileGetter = (moduleId: string): ts.SourceFile | null => { - let filePath = path.normalize(moduleIdToPath(out, moduleId)); - return sourceFileMap[filePath]; - }; - +export function run3(resolver: DeclarationResolver): IMonacoDeclarationResult | null { + const sourceFileGetter = (moduleId: string) => resolver.getDeclarationSourceFile(moduleId); return _run(sourceFileGetter); } -export function complainErrors() { - logErr('Not running monaco.d.ts generation due to compile errors'); -} interface ILibMap { [libName: string]: string; } interface IFileMap { [fileName: string]: string; } -export class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost { +class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost { private readonly _libs: ILibMap; private readonly _files: IFileMap; @@ -638,30 +659,9 @@ export class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost { } export function execute(): IMonacoDeclarationResult { - - const OUTPUT_FILES: { [file: string]: string; } = {}; - const SRC_FILES: IFileMap = {}; - const SRC_FILE_TO_EXPECTED_NAME: { [filename: string]: string; } = {}; - getIncludesInRecipe().forEach((moduleId) => { - if (/\.d\.ts$/.test(moduleId)) { - let fileName = path.join(SRC, moduleId); - OUTPUT_FILES[moduleIdToPath('src', moduleId)] = fs.readFileSync(fileName).toString(); - return; - } - - let fileName = path.join(SRC, moduleId) + '.ts'; - SRC_FILES[fileName] = fs.readFileSync(fileName).toString(); - SRC_FILE_TO_EXPECTED_NAME[fileName] = moduleIdToPath('src', moduleId); - }); - - const languageService = ts.createLanguageService(new TypeScriptLanguageServiceHost({}, SRC_FILES, {})); - - var t1 = Date.now(); - Object.keys(SRC_FILES).forEach((fileName) => { - const emitOutput = languageService.getEmitOutput(fileName, true); - OUTPUT_FILES[SRC_FILE_TO_EXPECTED_NAME[fileName]] = emitOutput.outputFiles[0].text; - }); - console.log(`Generating .d.ts took ${Date.now() - t1} ms`); - - return run('src', OUTPUT_FILES); + let r = run3(new DeclarationResolver(new FSProvider())); + if (!r) { + throw new Error(`monaco.d.ts generation error - Cannot continue`); + } + return r; } diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index 51ed5fbae..356ef3ff0 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -85,3 +85,5 @@ declare namespace monaco.worker { #includeAll(vs/editor/common/services/editorSimpleWorker;): } + +//dtsv=2 \ No newline at end of file diff --git a/build/monaco/yarn.lock b/build/monaco/yarn.lock deleted file mode 100644 index d54e6ddb6..000000000 --- a/build/monaco/yarn.lock +++ /dev/null @@ -1,4668 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@gulp-sourcemaps/map-sources@1.X": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz#890ae7c5d8c877f6d384860215ace9d7ec945bda" - dependencies: - normalize-path "^2.0.1" - through2 "^2.0.3" - -"@types/minimist@1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.0.tgz#69a23a3ad29caf0097f06eda59b361ee2f0639f6" - -"@types/mocha@2.2.39": - version "2.2.39" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.39.tgz#f68d63db8b69c38e9558b4073525cf96c4f7a829" - -"@types/semver@5.3.30": - version "5.3.30" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.3.30.tgz#b55a3bd07b6b8b35f9d4472e1fc3318b68a493b2" - -"@types/sinon@1.16.34": - version "1.16.34" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-1.16.34.tgz#a9761fff33d0f7b3fe61875b577778a2576a9a03" - -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - -abbrev@1.0.x: - version "1.0.9" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" - -acorn-jsx@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" - dependencies: - acorn "^3.0.4" - -acorn@4.X: - version "4.0.13" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" - -acorn@^3.0.4: - version "3.3.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" - -acorn@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.2.1.tgz#317ac7821826c22c702d66189ab8359675f135d7" - -ajv-keywords@^1.0.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" - -ajv@^4.7.0, ajv@^4.9.1: - version "4.11.8" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" - dependencies: - co "^4.6.0" - json-stable-stringify "^1.0.1" - -ajv@^5.1.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.3.0.tgz#4414ff74a50879c208ee5fdc826e32c303549eda" - dependencies: - co "^4.6.0" - fast-deep-equal "^1.0.0" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.3.0" - -align-text@^0.1.1, align-text@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" - dependencies: - kind-of "^3.0.2" - longest "^1.0.1" - repeat-string "^1.5.2" - -alphanum-sort@^1.0.1, alphanum-sort@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" - -amdefine@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.0.tgz#fd17474700cb5cc9c2b709f0be9d23ce3c198c33" - -amdefine@>=0.0.4: - version "1.0.1" - resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" - -ansi-align@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" - dependencies: - string-width "^2.0.0" - -ansi-escapes@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" - -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" - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - -ansi-styles@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.1.0.tgz#eaecbf66cd706882760b2f4691582b8f55d7a7de" - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - -ansi-styles@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88" - 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" - dependencies: - micromatch "^2.1.5" - normalize-path "^2.0.0" - -aproba@^1.0.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - -archy@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" - -are-we-there-yet@~1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - -argparse@^1.0.7: - version "1.0.9" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" - 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" - dependencies: - arr-flatten "^1.0.1" - -arr-flatten@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - -array-differ@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" - -array-each@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" - -array-find-index@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" - -array-slice@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-1.0.0.tgz#e73034f00dcc1f40876008fd20feae77bd4b7c2f" - -array-union@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" - dependencies: - array-uniq "^1.0.1" - -array-uniq@^1.0.1, array-uniq@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" - -array-unique@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" - -arrify@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" - -asn1@~0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - -assert-plus@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" - -async-each@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" - -async@1.x, async@^1.4.0: - version "1.5.2" - resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - -atob@~1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/atob/-/atob-1.1.3.tgz#95f13629b12c3a51a5d215abdce2aa9f32f80773" - -autoprefixer@^6.3.1: - version "6.7.7" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014" - dependencies: - browserslist "^1.7.6" - caniuse-db "^1.0.30000634" - normalize-range "^0.1.2" - num2fraction "^1.2.2" - postcss "^5.2.16" - postcss-value-parser "^3.2.3" - -aws-sign2@~0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - -aws4@^1.2.1, aws4@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" - -babel-code-frame@^6.16.0, babel-code-frame@^6.20.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" - dependencies: - chalk "^1.1.3" - esutils "^2.0.2" - js-tokens "^3.0.2" - -balanced-match@^0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - -bcrypt-pbkdf@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" - dependencies: - tweetnacl "^0.14.3" - -beeper@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809" - -binary-extensions@^1.0.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.10.0.tgz#9aeb9a6c5e88638aad171e167f5900abe24835d0" - -block-stream@*: - version "0.0.9" - resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" - dependencies: - inherits "~2.0.0" - -bluebird@^3.0.5: - version "3.5.1" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" - -boom@2.x.x: - version "2.10.1" - resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" - dependencies: - hoek "2.x.x" - -boom@4.x.x: - version "4.3.1" - resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31" - dependencies: - hoek "4.x.x" - -boom@5.x.x: - version "5.2.0" - resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02" - dependencies: - hoek "4.x.x" - -boxen@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.2.2.tgz#3f1d4032c30ffea9d4b02c322eaf2ea741dcbce5" - dependencies: - ansi-align "^2.0.0" - camelcase "^4.0.0" - chalk "^2.0.1" - cli-boxes "^1.0.0" - string-width "^2.0.0" - term-size "^1.2.0" - widest-line "^1.0.0" - -brace-expansion@^1.0.0, brace-expansion@^1.1.7: - version "1.1.8" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^1.8.2: - version "1.8.5" - resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" - dependencies: - expand-range "^1.8.1" - preserve "^0.2.0" - repeat-element "^1.1.2" - -"browser-request@>= 0.3.1 < 0.4.0": - version "0.3.3" - resolved "https://registry.yarnpkg.com/browser-request/-/browser-request-0.3.3.tgz#9ece5b5aca89a29932242e18bf933def9876cc17" - -browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: - version "1.7.7" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9" - dependencies: - caniuse-db "^1.0.30000639" - electron-to-chromium "^1.2.7" - -builtin-modules@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" - -caller-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" - dependencies: - callsites "^0.2.0" - -callsites@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" - -camelcase-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" - dependencies: - camelcase "^2.0.0" - map-obj "^1.0.0" - -camelcase@^1.0.2: - version "1.2.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" - -camelcase@^2.0.0, camelcase@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" - -camelcase@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" - -caniuse-api@^1.5.2: - version "1.6.1" - resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c" - dependencies: - browserslist "^1.3.6" - caniuse-db "^1.0.30000529" - lodash.memoize "^4.1.2" - lodash.uniq "^4.5.0" - -caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: - version "1.0.30000764" - resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000764.tgz#d73ab11ae62f6a9e2f69867d6d9c23ae3f2e5d8d" - -capture-stack-trace@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d" - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - -center-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" - dependencies: - align-text "^0.1.3" - lazy-cache "^1.0.3" - -chalk@^0.5.0: - version "0.5.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.5.1.tgz#663b3a648b68b55d04690d49167aa837858f2174" - dependencies: - ansi-styles "^1.1.0" - escape-string-regexp "^1.0.0" - has-ansi "^0.1.0" - strip-ansi "^0.3.0" - supports-color "^0.2.0" - -chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^2.0.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba" - dependencies: - ansi-styles "^3.1.0" - escape-string-regexp "^1.0.5" - supports-color "^4.0.0" - -chokidar@^1.6.1: - version "1.7.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" - dependencies: - anymatch "^1.3.0" - async-each "^1.0.0" - glob-parent "^2.0.0" - inherits "^2.0.1" - is-binary-path "^1.0.0" - is-glob "^2.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.0.0" - optionalDependencies: - fsevents "^1.0.0" - -circular-json@^0.3.1: - version "0.3.3" - resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" - -clap@^1.0.9: - version "1.2.3" - resolved "https://registry.yarnpkg.com/clap/-/clap-1.2.3.tgz#4f36745b32008492557f46412d66d50cb99bce51" - dependencies: - chalk "^1.1.3" - -cli-boxes@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" - -cli-cursor@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" - dependencies: - restore-cursor "^1.0.1" - -cli-width@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" - -cliui@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" - dependencies: - center-align "^0.1.1" - right-align "^0.1.1" - wordwrap "0.0.2" - -cliui@^3.0.3: - version "3.2.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi "^2.0.0" - -clone-buffer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" - -clone-stats@^0.0.1, clone-stats@~0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" - -clone-stats@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" - -clone@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/clone/-/clone-0.2.0.tgz#c6126a90ad4f72dbf5acdb243cc37724fe93fc1f" - -clone@^1.0.0, clone@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.3.tgz#298d7e2231660f40c003c2ed3140decf3f53085f" - -clone@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb" - -cloneable-readable@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.0.0.tgz#a6290d413f217a61232f95e458ff38418cfb0117" - dependencies: - inherits "^2.0.1" - process-nextick-args "^1.0.6" - through2 "^2.0.1" - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - -coa@~1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.4.tgz#a9ef153660d6a86a8bdec0289a5c684d217432fd" - dependencies: - q "^1.1.2" - -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" - -color-convert@^1.3.0, color-convert@^1.9.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed" - dependencies: - color-name "^1.1.1" - -color-name@^1.0.0, color-name@^1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - -color-string@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991" - dependencies: - color-name "^1.0.0" - -color@^0.11.0: - version "0.11.4" - resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764" - dependencies: - clone "^1.0.2" - color-convert "^1.3.0" - color-string "^0.3.0" - -colormin@^1.0.5: - version "1.1.2" - resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133" - dependencies: - color "^0.11.0" - css-color-names "0.0.4" - has "^1.0.1" - -colors@^1.1.2, colors@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" - -colors@~0.6.0-1: - version "0.6.2" - resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc" - -combined-stream@^1.0.5, combined-stream@~1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" - dependencies: - delayed-stream "~1.0.0" - -commander@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-0.6.1.tgz#fa68a14f6a945d54dbbe50d8cdb3320e9e3b1a06" - -commander@2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.3.0.tgz#fd430e889832ec353b9acd1de217c11cb3eef873" - -commander@^2.9.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" - -commander@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.1.0.tgz#d121bbae860d9992a3d517ba96f56588e47c6781" - -commandpost@^1.0.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/commandpost/-/commandpost-1.2.1.tgz#2e9c4c7508b9dc704afefaa91cab92ee6054cc68" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - -concat-stream@^1.5.2: - version "1.6.0" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" - dependencies: - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -concat-with-sourcemaps@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.0.4.tgz#f55b3be2aeb47601b10a2d5259ccfb70fd2f1dd6" - dependencies: - source-map "^0.5.1" - -configstore@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.1.tgz#094ee662ab83fad9917678de114faaea8fcdca90" - dependencies: - dot-prop "^4.1.0" - graceful-fs "^4.1.2" - make-dir "^1.0.0" - unique-string "^1.0.0" - write-file-atomic "^2.0.0" - xdg-basedir "^3.0.0" - -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - -convert-source-map@1.X: - version "1.5.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5" - -core-util-is@1.0.2, core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - -create-error-class@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" - dependencies: - capture-stack-trace "^1.0.0" - -cross-spawn@^5.0.1: - version "5.1.0" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" - dependencies: - lru-cache "^4.0.1" - shebang-command "^1.2.0" - which "^1.2.9" - -cryptiles@2.x.x: - version "2.0.5" - resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" - dependencies: - boom "2.x.x" - -cryptiles@3.x.x: - version "3.1.2" - resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe" - dependencies: - boom "5.x.x" - -crypto-random-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" - -css-color-names@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" - -css@2.X: - version "2.2.1" - resolved "https://registry.yarnpkg.com/css/-/css-2.2.1.tgz#73a4c81de85db664d4ee674f7d47085e3b2d55dc" - dependencies: - inherits "^2.0.1" - source-map "^0.1.38" - source-map-resolve "^0.3.0" - urix "^0.1.0" - -cssnano@^3.0.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38" - dependencies: - autoprefixer "^6.3.1" - decamelize "^1.1.2" - defined "^1.0.0" - has "^1.0.1" - object-assign "^4.0.1" - postcss "^5.0.14" - postcss-calc "^5.2.0" - postcss-colormin "^2.1.8" - postcss-convert-values "^2.3.4" - postcss-discard-comments "^2.0.4" - postcss-discard-duplicates "^2.0.1" - postcss-discard-empty "^2.0.1" - postcss-discard-overridden "^0.1.1" - postcss-discard-unused "^2.2.1" - postcss-filter-plugins "^2.0.0" - postcss-merge-idents "^2.1.5" - postcss-merge-longhand "^2.0.1" - postcss-merge-rules "^2.0.3" - postcss-minify-font-values "^1.0.2" - postcss-minify-gradients "^1.0.1" - postcss-minify-params "^1.0.4" - postcss-minify-selectors "^2.0.4" - postcss-normalize-charset "^1.1.0" - postcss-normalize-url "^3.0.7" - postcss-ordered-values "^2.1.0" - postcss-reduce-idents "^2.2.2" - postcss-reduce-initial "^1.0.0" - postcss-reduce-transforms "^1.0.3" - postcss-svgo "^2.1.1" - postcss-unique-selectors "^2.0.2" - postcss-value-parser "^3.2.3" - postcss-zindex "^2.0.1" - -csso@~2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/csso/-/csso-2.3.2.tgz#ddd52c587033f49e94b71fc55569f252e8ff5f85" - dependencies: - clap "^1.0.9" - source-map "^0.5.3" - -cssom@0.3.x, "cssom@>= 0.3.0 < 0.4.0": - version "0.3.2" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b" - -"cssstyle@>= 0.2.21 < 0.3.0": - version "0.2.37" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54" - dependencies: - cssom "0.3.x" - -currently-unhandled@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" - dependencies: - array-find-index "^1.0.1" - -d@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" - dependencies: - es5-ext "^0.10.9" - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - dependencies: - assert-plus "^1.0.0" - -dateformat@^1.0.11, dateformat@^1.0.7-1.2.3: - version "1.0.12" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" - dependencies: - get-stdin "^4.0.1" - meow "^3.3.0" - -dateformat@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.2.0.tgz#4065e2013cf9fb916ddfd82efb506ad4c6769062" - -debounce@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.1.0.tgz#6a1a4ee2a9dc4b7c24bb012558dbcdb05b37f408" - -debug-fabulous@0.0.X: - version "0.0.4" - resolved "https://registry.yarnpkg.com/debug-fabulous/-/debug-fabulous-0.0.4.tgz#fa071c5d87484685424807421ca4b16b0b1a0763" - dependencies: - debug "2.X" - lazy-debug-legacy "0.0.X" - object-assign "4.1.0" - -debug@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" - dependencies: - ms "0.7.1" - -debug@2.X, debug@^2.1.1, debug@^2.2.0: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - dependencies: - ms "2.0.0" - -decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - -deep-extend@~0.4.0: - version "0.4.2" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" - -deep-is@~0.1.2, deep-is@~0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" - -defaults@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" - dependencies: - clone "^1.0.2" - -defined@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" - -del@^2.0.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" - dependencies: - globby "^5.0.0" - is-path-cwd "^1.0.0" - is-path-in-cwd "^1.0.0" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - rimraf "^2.2.8" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - -deprecated@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/deprecated/-/deprecated-0.0.1.tgz#f9c9af5464afa1e7a971458a8bdef2aa94d5bb19" - -detect-file@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-0.1.0.tgz#4935dedfd9488648e006b0129566e9386711ea63" - dependencies: - fs-exists-sync "^0.1.0" - -detect-libc@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.2.tgz#71ad5d204bf17a6a6ca8f450c61454066ef461e1" - -detect-newline@2.X: - version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" - -diff@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" - -diff@^3.0.1: - version "3.4.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.4.0.tgz#b1d85507daf3964828de54b37d0d73ba67dda56c" - -doctrine@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63" - dependencies: - esutils "^2.0.2" - isarray "^1.0.0" - -dom-serializer@0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" - dependencies: - domelementtype "~1.1.1" - entities "~1.1.1" - -domelementtype@1, domelementtype@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" - -domelementtype@~1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" - -domhandler@^2.3.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.1.tgz#892e47000a99be55bbf3774ffea0561d8879c259" - dependencies: - domelementtype "1" - -domutils@^1.5.1: - version "1.6.2" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.6.2.tgz#1958cc0b4c9426e9ed367fb1c8e854891b0fa3ff" - dependencies: - dom-serializer "0" - domelementtype "1" - -dot-prop@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" - dependencies: - is-obj "^1.0.0" - -duplexer2@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" - dependencies: - readable-stream "~1.1.9" - -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - -duplexer@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" - -ecc-jsbn@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" - dependencies: - jsbn "~0.1.0" - -editorconfig@^0.13.2: - version "0.13.3" - resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.13.3.tgz#e5219e587951d60958fd94ea9a9a008cdeff1b34" - dependencies: - bluebird "^3.0.5" - commander "^2.9.0" - lru-cache "^3.2.0" - semver "^5.1.0" - sigmund "^1.0.1" - -electron-to-chromium@^1.2.7: - version "1.3.27" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.27.tgz#78ecb8a399066187bb374eede35d9c70565a803d" - -end-of-stream@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.0.tgz#7a90d833efda6cfa6eac0f4949dbb0fad3a63206" - dependencies: - once "^1.4.0" - -end-of-stream@~0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-0.1.5.tgz#8e177206c3c80837d85632e8b9359dfe8b2f6eaf" - dependencies: - once "~1.3.0" - -entities@^1.1.1, entities@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" - -error-ex@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" - dependencies: - is-arrayish "^0.2.1" - -es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14: - version "0.10.35" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.35.tgz#18ee858ce6a3c45c7d79e91c15fcca9ec568494f" - dependencies: - es6-iterator "~2.0.1" - es6-symbol "~3.1.1" - -es6-iterator@^2.0.1, es6-iterator@~2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" - dependencies: - d "1" - es5-ext "^0.10.35" - es6-symbol "^3.1.1" - -es6-map@^0.1.3: - version "0.1.5" - resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" - dependencies: - d "1" - es5-ext "~0.10.14" - es6-iterator "~2.0.1" - es6-set "~0.1.5" - es6-symbol "~3.1.1" - event-emitter "~0.3.5" - -es6-set@~0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" - dependencies: - d "1" - es5-ext "~0.10.14" - es6-iterator "~2.0.1" - es6-symbol "3.1.1" - event-emitter "~0.3.5" - -es6-symbol@3.1.1, es6-symbol@^3.1.1, es6-symbol@~3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" - dependencies: - d "1" - es5-ext "~0.10.14" - -es6-weak-map@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f" - dependencies: - d "1" - es5-ext "^0.10.14" - es6-iterator "^2.0.1" - es6-symbol "^3.1.1" - -escape-string-regexp@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1" - -escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.2, 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" - -escodegen@1.7.x: - version "1.7.1" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.7.1.tgz#30ecfcf66ca98dc67cd2fd162abeb6eafa8ce6fc" - dependencies: - esprima "^1.2.2" - estraverse "^1.9.1" - esutils "^2.0.2" - optionator "^0.5.0" - optionalDependencies: - source-map "~0.2.0" - -escodegen@1.8.x: - version "1.8.1" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" - dependencies: - esprima "^2.7.1" - estraverse "^1.9.1" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.2.0" - -escope@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" - dependencies: - es6-map "^0.1.3" - es6-weak-map "^2.0.1" - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint@^3.4.0: - version "3.19.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.19.0.tgz#c8fc6201c7f40dd08941b87c085767386a679acc" - dependencies: - babel-code-frame "^6.16.0" - chalk "^1.1.3" - concat-stream "^1.5.2" - debug "^2.1.1" - doctrine "^2.0.0" - escope "^3.6.0" - espree "^3.4.0" - esquery "^1.0.0" - estraverse "^4.2.0" - esutils "^2.0.2" - file-entry-cache "^2.0.0" - glob "^7.0.3" - globals "^9.14.0" - ignore "^3.2.0" - imurmurhash "^0.1.4" - inquirer "^0.12.0" - is-my-json-valid "^2.10.0" - is-resolvable "^1.0.0" - js-yaml "^3.5.1" - json-stable-stringify "^1.0.0" - levn "^0.3.0" - lodash "^4.0.0" - mkdirp "^0.5.0" - natural-compare "^1.4.0" - optionator "^0.8.2" - path-is-inside "^1.0.1" - pluralize "^1.2.1" - progress "^1.1.8" - require-uncached "^1.0.2" - shelljs "^0.7.5" - strip-bom "^3.0.0" - strip-json-comments "~2.0.1" - table "^3.7.8" - text-table "~0.2.0" - user-home "^2.0.0" - -espree@^3.4.0: - version "3.5.2" - resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.2.tgz#756ada8b979e9dcfcdb30aad8d1a9304a905e1ca" - dependencies: - acorn "^5.2.1" - acorn-jsx "^3.0.0" - -esprima@2.5.x: - version "2.5.0" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.5.0.tgz#f387a46fd344c1b1a39baf8c20bfb43b6d0058cc" - -esprima@2.7.x, esprima@^2.6.0, esprima@^2.7.1: - version "2.7.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" - -esprima@^1.2.2: - version "1.2.5" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.2.5.tgz#0993502feaf668138325756f30f9a51feeec11e9" - -esprima@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" - -esquery@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.0.tgz#cfba8b57d7fba93f17298a8a006a04cda13d80fa" - dependencies: - estraverse "^4.0.0" - -esrecurse@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163" - dependencies: - estraverse "^4.1.0" - object-assign "^4.0.1" - -estraverse@^1.9.1: - version "1.9.3" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" - -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" - -esutils@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" - -event-emitter@~0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" - dependencies: - d "1" - es5-ext "~0.10.14" - -event-stream@^3.1.7, event-stream@^3.3.2: - version "3.3.4" - resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" - dependencies: - duplexer "~0.1.1" - from "~0" - map-stream "~0.1.0" - pause-stream "0.0.11" - split "0.3" - stream-combiner "~0.0.4" - through "~2.3.1" - -execa@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -exit-hook@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" - -expand-brackets@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" - dependencies: - is-posix-bracket "^0.1.0" - -expand-range@^1.8.1: - version "1.8.2" - resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" - dependencies: - fill-range "^2.1.0" - -expand-tilde@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-1.2.2.tgz#0b81eba897e5a3d31d1c3d102f8f01441e559449" - dependencies: - os-homedir "^1.0.1" - -expand-tilde@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" - dependencies: - homedir-polyfill "^1.0.1" - -extend@^3.0.0, extend@~3.0.0, extend@~3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" - -extglob@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" - dependencies: - is-extglob "^1.0.0" - -extsprintf@1.3.0, extsprintf@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - -fancy-log@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.0.tgz#45be17d02bb9917d60ccffd4995c999e6c8c9948" - dependencies: - chalk "^1.1.1" - time-stamp "^1.0.0" - -fast-deep-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" - -fast-json-stable-stringify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" - -fast-levenshtein@~1.0.0: - version "1.0.7" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-1.0.7.tgz#0178dcdee023b92905193af0959e8a7639cfdcb9" - -fast-levenshtein@~2.0.4: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - -figures@^1.3.5: - version "1.7.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" - dependencies: - escape-string-regexp "^1.0.5" - object-assign "^4.1.0" - -file-entry-cache@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" - dependencies: - flat-cache "^1.2.1" - object-assign "^4.0.1" - -filename-regex@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" - -fileset@0.2.x: - version "0.2.1" - resolved "https://registry.yarnpkg.com/fileset/-/fileset-0.2.1.tgz#588ef8973c6623b2a76df465105696b96aac8067" - dependencies: - glob "5.x" - minimatch "2.x" - -fill-range@^2.1.0: - version "2.2.3" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" - dependencies: - is-number "^2.1.0" - isobject "^2.0.0" - randomatic "^1.1.3" - repeat-element "^1.1.2" - repeat-string "^1.5.2" - -find-index@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4" - -find-up@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" - dependencies: - path-exists "^2.0.0" - pinkie-promise "^2.0.0" - -findup-sync@^0.4.2: - version "0.4.3" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.4.3.tgz#40043929e7bc60adf0b7f4827c4c6e75a0deca12" - dependencies: - detect-file "^0.1.0" - is-glob "^2.0.1" - micromatch "^2.3.7" - resolve-dir "^0.1.0" - -findup-sync@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.3.0.tgz#37930aa5d816b777c03445e1966cc6790a4c0b16" - dependencies: - glob "~5.0.0" - -findup@0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/findup/-/findup-0.1.5.tgz#8ad929a3393bac627957a7e5de4623b06b0e2ceb" - dependencies: - colors "~0.6.0-1" - commander "~2.1.0" - -fined@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fined/-/fined-1.1.0.tgz#b37dc844b76a2f5e7081e884f7c0ae344f153476" - dependencies: - expand-tilde "^2.0.2" - is-plain-object "^2.0.3" - object.defaults "^1.1.0" - object.pick "^1.2.0" - parse-filepath "^1.0.1" - -first-chunk-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz#59bfb50cd905f60d7c394cd3d9acaab4e6ad934e" - -first-chunk-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz#1bdecdb8e083c0664b91945581577a43a9f31d70" - dependencies: - readable-stream "^2.0.2" - -flagged-respawn@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-0.3.2.tgz#ff191eddcd7088a675b2610fffc976be9b8074b5" - -flat-cache@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481" - dependencies: - circular-json "^0.3.1" - del "^2.0.2" - graceful-fs "^4.1.2" - write "^0.2.1" - -flatten@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" - -for-in@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - -for-own@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" - dependencies: - for-in "^1.0.1" - -for-own@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b" - dependencies: - for-in "^1.0.1" - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - -form-data@~2.1.1: - version "2.1.4" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.5" - mime-types "^2.1.12" - -form-data@~2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf" - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.5" - mime-types "^2.1.12" - -formatio@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.1.1.tgz#5ed3ccd636551097383465d996199100e86161e9" - dependencies: - samsam "~1.1" - -from@~0: - version "0.1.7" - resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" - -fs-exists-sync@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - -fsevents@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.3.tgz#11f82318f5fe7bb2cd22965a108e9306208216d8" - dependencies: - nan "^2.3.0" - node-pre-gyp "^0.6.39" - -fstream-ignore@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" - dependencies: - fstream "^1.0.0" - inherits "2" - minimatch "^3.0.0" - -fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: - version "1.0.11" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" - -function-bind@^1.0.2: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - -gaze@^0.5.1: - version "0.5.2" - resolved "https://registry.yarnpkg.com/gaze/-/gaze-0.5.2.tgz#40b709537d24d1d45767db5a908689dfe69ac44f" - dependencies: - globule "~0.1.0" - -generate-function@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" - -generate-object-property@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" - dependencies: - is-property "^1.0.0" - -get-stdin@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" - -get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - dependencies: - assert-plus "^1.0.0" - -ghooks@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/ghooks/-/ghooks-1.0.3.tgz#0a98c6bdef04f092d901306dde35a945c48b3119" - dependencies: - findup "0.1.5" - lodash.clone "3.0.3" - manage-path "2.0.0" - spawn-command "0.0.2" - -glob-base@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" - dependencies: - glob-parent "^2.0.0" - is-glob "^2.0.0" - -glob-expand@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/glob-expand/-/glob-expand-0.2.1.tgz#1b088ac272b57158870b76816111da4618a66a0f" - dependencies: - glob "~4.5.x" - lodash "~4.13.x" - -glob-parent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" - dependencies: - is-glob "^2.0.0" - -glob-parent@^3.0.1: - version "3.1.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" - dependencies: - is-glob "^3.1.0" - path-dirname "^1.0.0" - -glob-stream@^3.1.5: - version "3.1.18" - resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-3.1.18.tgz#9170a5f12b790306fdfe598f313f8f7954fd143b" - dependencies: - glob "^4.3.1" - glob2base "^0.0.12" - minimatch "^2.0.1" - ordered-read-streams "^0.1.0" - through2 "^0.6.1" - unique-stream "^1.0.0" - -glob-watcher@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/glob-watcher/-/glob-watcher-0.0.6.tgz#b95b4a8df74b39c83298b0c05c978b4d9a3b710b" - dependencies: - gaze "^0.5.1" - -glob2base@^0.0.12: - version "0.0.12" - resolved "https://registry.yarnpkg.com/glob2base/-/glob2base-0.0.12.tgz#9d419b3e28f12e83a362164a277055922c9c0d56" - dependencies: - find-index "^0.1.1" - -glob@3.2.11: - version "3.2.11" - resolved "https://registry.yarnpkg.com/glob/-/glob-3.2.11.tgz#4a973f635b9190f715d10987d5c00fd2815ebe3d" - dependencies: - inherits "2" - minimatch "0.3" - -glob@5.x, glob@^5.0.13, glob@~5.0.0: - version "5.0.15" - resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^4.3.1, glob@~4.5.x: - version "4.5.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-4.5.3.tgz#c6cb73d3226c1efef04de3c56d012f03377ee15f" - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "^2.0.1" - once "^1.3.0" - -glob@^6.0.4: - version "6.0.4" - resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1: - version "7.1.2" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" - 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@~3.1.21: - version "3.1.21" - resolved "https://registry.yarnpkg.com/glob/-/glob-3.1.21.tgz#d29e0a055dea5138f4d07ed40e8982e83c2066cd" - dependencies: - graceful-fs "~1.2.0" - inherits "1" - minimatch "~0.2.11" - -global-dirs@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.0.tgz#10d34039e0df04272e262cf24224f7209434df4f" - dependencies: - ini "^1.3.4" - -global-modules@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-0.2.3.tgz#ea5a3bed42c6d6ce995a4f8a1269b5dae223828d" - dependencies: - global-prefix "^0.1.4" - is-windows "^0.2.0" - -global-prefix@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-0.1.5.tgz#8d3bc6b8da3ca8112a160d8d496ff0462bfef78f" - dependencies: - homedir-polyfill "^1.0.0" - ini "^1.3.4" - is-windows "^0.2.0" - which "^1.2.12" - -globals@^9.14.0: - version "9.18.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" - -globby@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" - dependencies: - array-union "^1.0.1" - arrify "^1.0.0" - glob "^7.0.3" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -globule@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/globule/-/globule-0.1.0.tgz#d9c8edde1da79d125a151b79533b978676346ae5" - dependencies: - glob "~3.1.21" - lodash "~1.0.1" - minimatch "~0.2.11" - -glogg@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.0.tgz#7fe0f199f57ac906cf512feead8f90ee4a284fc5" - dependencies: - sparkles "^1.0.0" - -got@^6.7.1: - version "6.7.1" - resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" - dependencies: - create-error-class "^3.0.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - is-redirect "^1.0.0" - is-retry-allowed "^1.0.0" - is-stream "^1.0.0" - lowercase-keys "^1.0.0" - safe-buffer "^5.0.1" - timed-out "^4.0.0" - unzip-response "^2.0.1" - url-parse-lax "^1.0.0" - -graceful-fs@4.X, graceful-fs@^4.1.11, graceful-fs@^4.1.2: - version "4.1.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" - -graceful-fs@^3.0.0: - version "3.0.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-3.0.11.tgz#7613c778a1afea62f25c630a086d7f3acbbdd818" - dependencies: - natives "^1.1.0" - -graceful-fs@~1.2.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-1.2.3.tgz#15a4806a57547cb2d2dbf27f42e89a8c3451b364" - -growl@1.9.2: - version "1.9.2" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" - -gulp-bom@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/gulp-bom/-/gulp-bom-1.0.0.tgz#38a183a07187bd57a7922d37977441f379df2abf" - dependencies: - gulp-util "^3.0.0" - through2 "^2.0.0" - -gulp-concat@^2.6.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/gulp-concat/-/gulp-concat-2.6.1.tgz#633d16c95d88504628ad02665663cee5a4793353" - dependencies: - concat-with-sourcemaps "^1.0.0" - through2 "^2.0.0" - vinyl "^2.0.0" - -gulp-cssnano@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/gulp-cssnano/-/gulp-cssnano-2.1.2.tgz#e08a09771ec5454a549f1a005bdd256cb8e5e0a3" - dependencies: - cssnano "^3.0.0" - gulp-util "^3.0.6" - object-assign "^4.0.1" - vinyl-sourcemaps-apply "^0.2.1" - -gulp-filter@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/gulp-filter/-/gulp-filter-3.0.1.tgz#7c6ffce5b563e89de7a90dfceff16ec8a8cb1562" - dependencies: - gulp-util "^3.0.6" - multimatch "^2.0.0" - streamfilter "^1.0.5" - -gulp-flatmap@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/gulp-flatmap/-/gulp-flatmap-1.0.0.tgz#e634e03cffb263aebacfdc22dd8ce2f3d76ffe97" - dependencies: - gulp-util "~2.2.14" - through2 "~0.6.3" - -gulp-rename@^1.2.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/gulp-rename/-/gulp-rename-1.2.2.tgz#3ad4428763f05e2764dec1c67d868db275687817" - -gulp-sourcemaps@^1.11.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-1.12.0.tgz#786f97c94a0f968492465d70558e04242c679598" - dependencies: - "@gulp-sourcemaps/map-sources" "1.X" - acorn "4.X" - convert-source-map "1.X" - css "2.X" - debug-fabulous "0.0.X" - detect-newline "2.X" - graceful-fs "4.X" - source-map "0.X" - strip-bom "2.X" - through2 "2.X" - vinyl "1.X" - -gulp-tsb@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/gulp-tsb/-/gulp-tsb-2.0.4.tgz#0b298092d4dfd4e5cfd80679ed4c1d93bfdba64a" - dependencies: - gulp-util "^3.0.1" - through "^2.3.6" - vinyl "^0.4.3" - -gulp-tslint@^7.0.1: - version "7.1.0" - resolved "https://registry.yarnpkg.com/gulp-tslint/-/gulp-tslint-7.1.0.tgz#9bd3ff4fbc16d4cbd9abb08ff786db89b563e93d" - dependencies: - gulp-util "~3.0.8" - map-stream "~0.1.0" - through "~2.3.8" - -gulp-uglify@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/gulp-uglify/-/gulp-uglify-2.1.2.tgz#6db85b1d0ee63d18058592b658649d65c2ec4541" - dependencies: - gulplog "^1.0.0" - has-gulplog "^0.1.0" - lodash "^4.13.1" - make-error-cause "^1.1.1" - through2 "^2.0.0" - uglify-js "~2.8.10" - uglify-save-license "^0.4.1" - vinyl-sourcemaps-apply "^0.2.0" - -gulp-util@3.0.7: - version "3.0.7" - resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.7.tgz#78925c4b8f8b49005ac01a011c557e6218941cbb" - dependencies: - array-differ "^1.0.0" - array-uniq "^1.0.2" - beeper "^1.0.0" - chalk "^1.0.0" - dateformat "^1.0.11" - fancy-log "^1.1.0" - gulplog "^1.0.0" - has-gulplog "^0.1.0" - lodash._reescape "^3.0.0" - lodash._reevaluate "^3.0.0" - lodash._reinterpolate "^3.0.0" - lodash.template "^3.0.0" - minimist "^1.1.0" - multipipe "^0.1.2" - object-assign "^3.0.0" - replace-ext "0.0.1" - through2 "^2.0.0" - vinyl "^0.5.0" - -gulp-util@^3.0.0, gulp-util@^3.0.1, gulp-util@^3.0.6, gulp-util@^3.0.7, gulp-util@~3.0.8: - version "3.0.8" - resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f" - dependencies: - array-differ "^1.0.0" - array-uniq "^1.0.2" - beeper "^1.0.0" - chalk "^1.0.0" - dateformat "^2.0.0" - fancy-log "^1.1.0" - gulplog "^1.0.0" - has-gulplog "^0.1.0" - lodash._reescape "^3.0.0" - lodash._reevaluate "^3.0.0" - lodash._reinterpolate "^3.0.0" - lodash.template "^3.0.0" - minimist "^1.1.0" - multipipe "^0.1.2" - object-assign "^3.0.0" - replace-ext "0.0.1" - through2 "^2.0.0" - vinyl "^0.5.0" - -gulp-util@~2.2.14: - version "2.2.20" - resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-2.2.20.tgz#d7146e5728910bd8f047a6b0b1e549bc22dbd64c" - dependencies: - chalk "^0.5.0" - dateformat "^1.0.7-1.2.3" - lodash._reinterpolate "^2.4.1" - lodash.template "^2.4.1" - minimist "^0.2.0" - multipipe "^0.1.0" - through2 "^0.5.0" - vinyl "^0.2.1" - -gulp-watch@^4.3.9: - version "4.3.11" - resolved "https://registry.yarnpkg.com/gulp-watch/-/gulp-watch-4.3.11.tgz#162fc563de9fc770e91f9a7ce3955513a9a118c0" - dependencies: - anymatch "^1.3.0" - chokidar "^1.6.1" - glob-parent "^3.0.1" - gulp-util "^3.0.7" - object-assign "^4.1.0" - path-is-absolute "^1.0.1" - readable-stream "^2.2.2" - slash "^1.0.0" - vinyl "^1.2.0" - vinyl-file "^2.0.0" - -gulp@^3.8.9: - version "3.9.1" - resolved "https://registry.yarnpkg.com/gulp/-/gulp-3.9.1.tgz#571ce45928dd40af6514fc4011866016c13845b4" - dependencies: - archy "^1.0.0" - chalk "^1.0.0" - deprecated "^0.0.1" - gulp-util "^3.0.0" - interpret "^1.0.0" - liftoff "^2.1.0" - minimist "^1.1.0" - orchestrator "^0.3.0" - pretty-hrtime "^1.0.0" - semver "^4.1.0" - tildify "^1.0.0" - v8flags "^2.0.2" - vinyl-fs "^0.3.0" - -gulplog@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" - dependencies: - glogg "^1.0.0" - -handlebars@^4.0.1: - version "4.0.11" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc" - dependencies: - async "^1.4.0" - optimist "^0.6.1" - source-map "^0.4.4" - optionalDependencies: - uglify-js "^2.6" - -har-schema@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" - -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - -har-validator@~4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" - dependencies: - ajv "^4.9.1" - har-schema "^1.0.5" - -har-validator@~5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" - dependencies: - ajv "^5.1.0" - har-schema "^2.0.0" - -has-ansi@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-0.1.0.tgz#84f265aae8c0e6a88a12d7022894b7568894c62e" - dependencies: - ansi-regex "^0.2.0" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - dependencies: - ansi-regex "^2.0.0" - -has-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" - -has-flag@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" - -has-gulplog@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" - dependencies: - sparkles "^1.0.0" - -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - -has@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" - dependencies: - function-bind "^1.0.2" - -hawk@3.1.3, hawk@~3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" - dependencies: - boom "2.x.x" - cryptiles "2.x.x" - hoek "2.x.x" - sntp "1.x.x" - -hawk@~6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038" - dependencies: - boom "4.x.x" - cryptiles "3.x.x" - hoek "4.x.x" - sntp "2.x.x" - -hoek@2.x.x: - version "2.16.3" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" - -hoek@4.x.x: - version "4.2.0" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d" - -homedir-polyfill@^1.0.0, homedir-polyfill@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" - dependencies: - parse-passwd "^1.0.0" - -hosted-git-info@^2.1.4: - version "2.5.0" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" - -html-comment-regex@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e" - -"htmlparser2@>= 3.7.3 < 4.0.0": - version "3.9.2" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" - dependencies: - domelementtype "^1.3.0" - domhandler "^2.3.0" - domutils "^1.5.1" - entities "^1.1.1" - inherits "^2.0.1" - readable-stream "^2.0.2" - -http-signature@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" - dependencies: - assert-plus "^0.2.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -iconv-lite@^0.4.15: - version "0.4.19" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" - -ignore@^3.2.0: - version "3.3.7" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021" - -import-lazy@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - -indent-string@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" - dependencies: - repeating "^2.0.0" - -indexes-of@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b" - -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - -inherits@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" - -ini@^1.3.4, ini@~1.3.0: - version "1.3.4" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" - -inquirer@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" - dependencies: - ansi-escapes "^1.1.0" - ansi-regex "^2.0.0" - chalk "^1.0.0" - cli-cursor "^1.0.1" - cli-width "^2.0.0" - figures "^1.3.5" - lodash "^4.3.0" - readline2 "^1.0.1" - run-async "^0.1.0" - rx-lite "^3.1.2" - string-width "^1.0.1" - strip-ansi "^3.0.0" - through "^2.3.6" - -interpret@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.4.tgz#820cdd588b868ffb191a809506d6c9c8f212b1b0" - -invert-kv@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" - -is-absolute-url@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" - -is-absolute@^0.2.3: - version "0.2.6" - resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-0.2.6.tgz#20de69f3db942ef2d87b9c2da36f172235b1b5eb" - dependencies: - is-relative "^0.2.1" - is-windows "^0.2.0" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - -is-binary-path@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" - dependencies: - binary-extensions "^1.0.0" - -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - -is-builtin-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" - dependencies: - builtin-modules "^1.0.0" - -is-dotfile@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" - -is-equal-shallow@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" - dependencies: - is-primitive "^2.0.0" - -is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - -is-extglob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" - -is-extglob@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - -is-finite@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - dependencies: - number-is-nan "^1.0.0" - -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" - -is-glob@^2.0.0, is-glob@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" - dependencies: - is-extglob "^1.0.0" - -is-glob@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" - dependencies: - is-extglob "^2.1.0" - -is-installed-globally@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80" - dependencies: - global-dirs "^0.1.0" - is-path-inside "^1.0.0" - -is-my-json-valid@^2.10.0: - version "2.16.1" - resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz#5a846777e2c2620d1e69104e5d3a03b1f6088f11" - dependencies: - generate-function "^2.0.0" - generate-object-property "^1.1.0" - jsonpointer "^4.0.0" - xtend "^4.0.0" - -is-npm@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" - -is-number@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" - dependencies: - kind-of "^3.0.2" - -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - dependencies: - kind-of "^3.0.2" - -is-obj@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" - -is-path-cwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" - -is-path-in-cwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc" - dependencies: - is-path-inside "^1.0.0" - -is-path-inside@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f" - dependencies: - path-is-inside "^1.0.1" - -is-plain-obj@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" - -is-plain-object@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - dependencies: - isobject "^3.0.1" - -is-posix-bracket@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" - -is-primitive@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" - -is-property@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" - -is-redirect@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" - -is-relative@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-0.2.1.tgz#d27f4c7d516d175fb610db84bbeef23c3bc97aa5" - dependencies: - is-unc-path "^0.1.1" - -is-resolvable@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62" - dependencies: - tryit "^1.0.1" - -is-retry-allowed@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" - -is-stream@^1.0.0, is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - -is-svg@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9" - dependencies: - html-comment-regex "^1.1.0" - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - -is-unc-path@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-0.1.2.tgz#6ab053a72573c10250ff416a3814c35178af39b9" - dependencies: - unc-path-regex "^0.1.0" - -is-utf8@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - -is-windows@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" - -is@^3.1.0, is@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/is/-/is-3.2.1.tgz#d0ac2ad55eb7b0bec926a5266f6c662aaa83dca5" - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - dependencies: - isarray "1.0.0" - -isobject@^3.0.0, isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - -istanbul@0.4.3: - version "0.4.3" - resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.3.tgz#5b714ee0ae493ac5ef204b99f3872bceef73d53a" - dependencies: - abbrev "1.0.x" - async "1.x" - escodegen "1.8.x" - esprima "2.7.x" - fileset "0.2.x" - handlebars "^4.0.1" - js-yaml "3.x" - mkdirp "0.5.x" - nopt "3.x" - once "1.x" - resolve "1.1.x" - supports-color "^3.1.0" - which "^1.1.1" - wordwrap "^1.0.0" - -istanbul@^0.3.17: - version "0.3.22" - resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.3.22.tgz#3e164d85021fe19c985d1f0e7ef0c3e22d012eb6" - dependencies: - abbrev "1.0.x" - async "1.x" - escodegen "1.7.x" - esprima "2.5.x" - fileset "0.2.x" - handlebars "^4.0.1" - js-yaml "3.x" - mkdirp "0.5.x" - nopt "3.x" - once "1.x" - resolve "1.1.x" - supports-color "^3.1.0" - which "^1.1.1" - wordwrap "^1.0.0" - -jade@0.26.3: - version "0.26.3" - resolved "https://registry.yarnpkg.com/jade/-/jade-0.26.3.tgz#8f10d7977d8d79f2f6ff862a81b0513ccb25686c" - dependencies: - commander "0.6.1" - mkdirp "0.3.0" - -js-base64@^2.1.9: - version "2.3.2" - resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.3.2.tgz#a79a923666372b580f8e27f51845c6f7e8fbfbaf" - -js-tokens@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" - -js-yaml@3.x, js-yaml@^3.5.1: - version "3.10.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc" - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -js-yaml@~3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80" - dependencies: - argparse "^1.0.7" - esprima "^2.6.0" - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - -jsdom-no-contextify@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/jsdom-no-contextify/-/jsdom-no-contextify-3.1.0.tgz#0d8beaf610c2ff23894f54dfa7f89dd22fd0f7ab" - dependencies: - browser-request ">= 0.3.1 < 0.4.0" - cssom ">= 0.3.0 < 0.4.0" - cssstyle ">= 0.2.21 < 0.3.0" - htmlparser2 ">= 3.7.3 < 4.0.0" - nwmatcher ">= 1.3.4 < 2.0.0" - parse5 ">= 1.3.1 < 2.0.0" - request ">= 2.44.0 < 3.0.0" - xml-name-validator "^1.0.0" - xmlhttprequest ">= 1.6.0 < 2.0.0" - -json-schema-traverse@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - -json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" - dependencies: - jsonify "~0.0.0" - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - -jsonify@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" - -jsonpointer@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - -kind-of@^3.0.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - dependencies: - is-buffer "^1.1.5" - -latest-version@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15" - dependencies: - package-json "^4.0.0" - -lazy-cache@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" - -lazy-debug-legacy@0.0.X: - version "0.0.1" - resolved "https://registry.yarnpkg.com/lazy-debug-legacy/-/lazy-debug-legacy-0.0.1.tgz#537716c0776e4cf79e3ed1b621f7658c2911b1b1" - -lazy.js@^0.4.2: - version "0.4.3" - resolved "https://registry.yarnpkg.com/lazy.js/-/lazy.js-0.4.3.tgz#87f67a07ad36555121e4fff1520df31be66786d8" - -lcid@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" - dependencies: - invert-kv "^1.0.0" - -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - -levn@~0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.2.5.tgz#ba8d339d0ca4a610e3a3f145b9caf48807155054" - dependencies: - prelude-ls "~1.1.0" - type-check "~0.3.1" - -liftoff@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-2.3.0.tgz#a98f2ff67183d8ba7cfaca10548bd7ff0550b385" - dependencies: - extend "^3.0.0" - findup-sync "^0.4.2" - fined "^1.0.1" - flagged-respawn "^0.3.2" - lodash.isplainobject "^4.0.4" - lodash.isstring "^4.0.1" - lodash.mapvalues "^4.4.0" - rechoir "^0.6.2" - resolve "^1.1.7" - -load-json-file@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" - -lodash._arraycopy@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz#76e7b7c1f1fb92547374878a562ed06a3e50f6e1" - -lodash._arrayeach@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._arrayeach/-/lodash._arrayeach-3.0.0.tgz#bab156b2a90d3f1bbd5c653403349e5e5933ef9e" - -lodash._baseassign@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" - dependencies: - lodash._basecopy "^3.0.0" - lodash.keys "^3.0.0" - -lodash._baseclone@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/lodash._baseclone/-/lodash._baseclone-3.3.0.tgz#303519bf6393fe7e42f34d8b630ef7794e3542b7" - dependencies: - lodash._arraycopy "^3.0.0" - lodash._arrayeach "^3.0.0" - lodash._baseassign "^3.0.0" - lodash._basefor "^3.0.0" - lodash.isarray "^3.0.0" - lodash.keys "^3.0.0" - -lodash._basecopy@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" - -lodash._basefor@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash._basefor/-/lodash._basefor-3.0.3.tgz#7550b4e9218ef09fad24343b612021c79b4c20c2" - -lodash._basetostring@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz#d1861d877f824a52f669832dcaf3ee15566a07d5" - -lodash._basevalues@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz#5b775762802bde3d3297503e26300820fdf661b7" - -lodash._bindcallback@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" - -lodash._escapehtmlchar@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._escapehtmlchar/-/lodash._escapehtmlchar-2.4.1.tgz#df67c3bb6b7e8e1e831ab48bfa0795b92afe899d" - dependencies: - lodash._htmlescapes "~2.4.1" - -lodash._escapestringchar@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._escapestringchar/-/lodash._escapestringchar-2.4.1.tgz#ecfe22618a2ade50bfeea43937e51df66f0edb72" - -lodash._getnative@^3.0.0: - version "3.9.1" - resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" - -lodash._htmlescapes@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._htmlescapes/-/lodash._htmlescapes-2.4.1.tgz#32d14bf0844b6de6f8b62a051b4f67c228b624cb" - -lodash._isiterateecall@^3.0.0: - version "3.0.9" - resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" - -lodash._isnative@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._isnative/-/lodash._isnative-2.4.1.tgz#3ea6404b784a7be836c7b57580e1cdf79b14832c" - -lodash._objecttypes@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz#7c0b7f69d98a1f76529f890b0cdb1b4dfec11c11" - -lodash._reescape@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reescape/-/lodash._reescape-3.0.0.tgz#2b1d6f5dfe07c8a355753e5f27fac7f1cde1616a" - -lodash._reevaluate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz#58bc74c40664953ae0b124d806996daca431e2ed" - -lodash._reinterpolate@^2.4.1, lodash._reinterpolate@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-2.4.1.tgz#4f1227aa5a8711fc632f5b07a1f4607aab8b3222" - -lodash._reinterpolate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" - -lodash._reunescapedhtml@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._reunescapedhtml/-/lodash._reunescapedhtml-2.4.1.tgz#747c4fc40103eb3bb8a0976e571f7a2659e93ba7" - dependencies: - lodash._htmlescapes "~2.4.1" - lodash.keys "~2.4.1" - -lodash._root@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" - -lodash._shimkeys@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz#6e9cc9666ff081f0b5a6c978b83e242e6949d203" - dependencies: - lodash._objecttypes "~2.4.1" - -lodash.clone@3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-3.0.3.tgz#84688c73d32b5a90ca25616963f189252a997043" - dependencies: - lodash._baseclone "^3.0.0" - lodash._bindcallback "^3.0.0" - lodash._isiterateecall "^3.0.0" - -lodash.defaults@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-2.4.1.tgz#a7e8885f05e68851144b6e12a8f3678026bc4c54" - dependencies: - lodash._objecttypes "~2.4.1" - lodash.keys "~2.4.1" - -lodash.escape@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698" - dependencies: - lodash._root "^3.0.0" - -lodash.escape@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-2.4.1.tgz#2ce12c5e084db0a57dda5e5d1eeeb9f5d175a3b4" - dependencies: - lodash._escapehtmlchar "~2.4.1" - lodash._reunescapedhtml "~2.4.1" - lodash.keys "~2.4.1" - -lodash.isarguments@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" - -lodash.isarray@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" - -lodash.isobject@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-2.4.1.tgz#5a2e47fe69953f1ee631a7eba1fe64d2d06558f5" - dependencies: - lodash._objecttypes "~2.4.1" - -lodash.isplainobject@^4.0.4: - version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" - -lodash.isstring@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" - -lodash.keys@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" - dependencies: - lodash._getnative "^3.0.0" - lodash.isarguments "^3.0.0" - lodash.isarray "^3.0.0" - -lodash.keys@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-2.4.1.tgz#48dea46df8ff7632b10d706b8acb26591e2b3727" - dependencies: - lodash._isnative "~2.4.1" - lodash._shimkeys "~2.4.1" - lodash.isobject "~2.4.1" - -lodash.mapvalues@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c" - -lodash.memoize@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - -lodash.restparam@^3.0.0: - version "3.6.1" - resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" - -lodash.template@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-2.4.1.tgz#9e611007edf629129a974ab3c48b817b3e1cf20d" - dependencies: - lodash._escapestringchar "~2.4.1" - lodash._reinterpolate "~2.4.1" - lodash.defaults "~2.4.1" - lodash.escape "~2.4.1" - lodash.keys "~2.4.1" - lodash.templatesettings "~2.4.1" - lodash.values "~2.4.1" - -lodash.template@^3.0.0: - version "3.6.2" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-3.6.2.tgz#f8cdecc6169a255be9098ae8b0c53d378931d14f" - dependencies: - lodash._basecopy "^3.0.0" - lodash._basetostring "^3.0.0" - lodash._basevalues "^3.0.0" - lodash._isiterateecall "^3.0.0" - lodash._reinterpolate "^3.0.0" - lodash.escape "^3.0.0" - lodash.keys "^3.0.0" - lodash.restparam "^3.0.0" - lodash.templatesettings "^3.0.0" - -lodash.templatesettings@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz#fb307844753b66b9f1afa54e262c745307dba8e5" - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.escape "^3.0.0" - -lodash.templatesettings@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-2.4.1.tgz#ea76c75d11eb86d4dbe89a83893bb861929ac699" - dependencies: - lodash._reinterpolate "~2.4.1" - lodash.escape "~2.4.1" - -lodash.uniq@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" - -lodash.values@~2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-2.4.1.tgz#abf514436b3cb705001627978cbcf30b1280eea4" - dependencies: - lodash.keys "~2.4.1" - -lodash@^4.0.0, lodash@^4.13.1, lodash@^4.3.0: - version "4.17.4" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" - -lodash@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551" - -lodash@~4.13.x: - version "4.13.1" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.13.1.tgz#83e4b10913f48496d4d16fec4a560af2ee744b68" - -lolex@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.3.2.tgz#7c3da62ffcb30f0f5a80a2566ca24e45d8a01f31" - -longest@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" - -loud-rejection@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" - dependencies: - currently-unhandled "^0.4.1" - signal-exit "^3.0.0" - -lowercase-keys@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" - -lru-cache@2: - version "2.7.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" - -lru-cache@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-3.2.0.tgz#71789b3b7f5399bec8565dda38aa30d2a097efee" - dependencies: - pseudomap "^1.0.1" - -lru-cache@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55" - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -macaddress@^0.2.8: - version "0.2.8" - resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12" - -make-dir@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.1.0.tgz#19b4369fe48c116f53c2af95ad102c0e39e85d51" - dependencies: - pify "^3.0.0" - -make-error-cause@^1.1.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/make-error-cause/-/make-error-cause-1.2.2.tgz#df0388fcd0b37816dff0a5fb8108939777dcbc9d" - dependencies: - make-error "^1.2.0" - -make-error@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.0.tgz#52ad3a339ccf10ce62b4040b708fe707244b8b96" - -manage-path@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/manage-path/-/manage-path-2.0.0.tgz#f4cf8457b926eeee2a83b173501414bc76eb9597" - -map-cache@^0.2.0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - -map-obj@^1.0.0, map-obj@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" - -map-stream@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" - -math-expression-evaluator@^1.2.14: - version "1.2.17" - resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac" - -meow@^3.3.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" - dependencies: - camelcase-keys "^2.0.0" - decamelize "^1.1.2" - loud-rejection "^1.0.0" - map-obj "^1.0.1" - minimist "^1.1.3" - normalize-package-data "^2.3.4" - object-assign "^4.0.1" - read-pkg-up "^1.0.1" - redent "^1.0.0" - trim-newlines "^1.0.0" - -micromatch@^2.1.5, micromatch@^2.3.7: - version "2.3.11" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" - dependencies: - arr-diff "^2.0.0" - array-unique "^0.2.1" - braces "^1.8.2" - expand-brackets "^0.1.4" - extglob "^0.3.1" - filename-regex "^2.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.1" - kind-of "^3.0.2" - normalize-path "^2.0.1" - object.omit "^2.0.0" - parse-glob "^3.0.4" - regex-cache "^0.4.2" - -mime-db@~1.30.0: - version "1.30.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" - -mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.7: - version "2.1.17" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" - dependencies: - mime-db "~1.30.0" - -minimatch@0.3: - version "0.3.0" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.3.0.tgz#275d8edaac4f1bb3326472089e7949c8394699dd" - dependencies: - lru-cache "2" - sigmund "~1.0.0" - -"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - dependencies: - brace-expansion "^1.1.7" - -minimatch@2.x, minimatch@^2.0.1, minimatch@^2.0.10: - version "2.0.10" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-2.0.10.tgz#8d087c39c6b38c001b97fca7ce6d0e1e80afbac7" - dependencies: - brace-expansion "^1.0.0" - -minimatch@~0.2.11: - version "0.2.14" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.2.14.tgz#c74e780574f63c6f9a090e90efbe6ef53a6a756a" - dependencies: - lru-cache "2" - sigmund "~1.0.0" - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - -minimist@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.2.0.tgz#4dffe525dae2b864c66c2e23c6271d7afdecefce" - -minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - -minimist@~0.0.1: - version "0.0.10" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" - -mkdirp@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" - -mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - dependencies: - minimist "0.0.8" - -mocha@^2.2.5: - version "2.5.3" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-2.5.3.tgz#161be5bdeb496771eb9b35745050b622b5aefc58" - dependencies: - commander "2.3.0" - debug "2.2.0" - diff "1.4.0" - escape-string-regexp "1.0.2" - glob "3.2.11" - growl "1.9.2" - jade "0.26.3" - mkdirp "0.5.1" - supports-color "1.2.0" - to-iso-string "0.0.2" - -ms@0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - -multimatch@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-2.1.0.tgz#9c7906a22fb4c02919e2f5f75161b4cdbd4b2a2b" - dependencies: - array-differ "^1.0.0" - array-union "^1.0.1" - arrify "^1.0.0" - minimatch "^3.0.0" - -multipipe@^0.1.0, multipipe@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b" - dependencies: - duplexer2 "0.0.2" - -mute-stream@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" - -nan@^2.3.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.7.0.tgz#d95bf721ec877e08db276ed3fc6eb78f9083ad46" - -natives@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/natives/-/natives-1.1.0.tgz#e9ff841418a6b2ec7a495e939984f78f163e6e31" - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - -node-pre-gyp@^0.6.39: - version "0.6.39" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649" - dependencies: - detect-libc "^1.0.2" - hawk "3.1.3" - mkdirp "^0.5.1" - nopt "^4.0.1" - npmlog "^4.0.2" - rc "^1.1.7" - request "2.81.0" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^2.2.1" - tar-pack "^3.4.0" - -nopt@3.x: - version "3.0.6" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" - dependencies: - abbrev "1" - -nopt@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" - dependencies: - abbrev "1" - osenv "^0.1.4" - -normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: - version "2.4.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" - dependencies: - hosted-git-info "^2.1.4" - is-builtin-module "^1.0.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -normalize-path@^2.0.0, normalize-path@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - dependencies: - remove-trailing-separator "^1.0.1" - -normalize-range@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" - -normalize-url@^1.4.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" - dependencies: - object-assign "^4.0.1" - prepend-http "^1.0.0" - query-string "^4.1.0" - sort-keys "^1.0.0" - -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - dependencies: - path-key "^2.0.0" - -npmlog@^4.0.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - -num2fraction@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - -"nwmatcher@>= 1.3.4 < 2.0.0": - version "1.4.3" - resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.3.tgz#64348e3b3d80f035b40ac11563d278f8b72db89c" - -oauth-sign@~0.8.1, oauth-sign@~0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" - -object-assign@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" - -object-assign@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" - -object-assign@^4.0.1, object-assign@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - -object.defaults@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/object.defaults/-/object.defaults-1.1.0.tgz#3a7f868334b407dea06da16d88d5cd29e435fecf" - dependencies: - array-each "^1.0.1" - array-slice "^1.0.0" - for-own "^1.0.0" - isobject "^3.0.0" - -object.omit@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" - dependencies: - for-own "^0.1.4" - is-extendable "^0.1.1" - -object.pick@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" - dependencies: - isobject "^3.0.1" - -once@1.x, once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - dependencies: - wrappy "1" - -once@~1.3.0: - version "1.3.3" - resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" - dependencies: - wrappy "1" - -onetime@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" - -optimist@^0.6.1, optimist@~0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" - dependencies: - minimist "~0.0.1" - wordwrap "~0.0.2" - -optionator@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.5.0.tgz#b75a8995a2d417df25b6e4e3862f50aa88651368" - dependencies: - deep-is "~0.1.2" - fast-levenshtein "~1.0.0" - levn "~0.2.5" - prelude-ls "~1.1.1" - type-check "~0.3.1" - wordwrap "~0.0.2" - -optionator@^0.8.1, optionator@^0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.4" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - wordwrap "~1.0.0" - -orchestrator@^0.3.0: - version "0.3.8" - resolved "https://registry.yarnpkg.com/orchestrator/-/orchestrator-0.3.8.tgz#14e7e9e2764f7315fbac184e506c7aa6df94ad7e" - dependencies: - end-of-stream "~0.1.5" - sequencify "~0.0.7" - stream-consume "~0.1.0" - -ordered-read-streams@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz#fd565a9af8eb4473ba69b6ed8a34352cb552f126" - -os-homedir@^1.0.0, os-homedir@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - -os-locale@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" - dependencies: - lcid "^1.0.0" - -os-tmpdir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - -osenv@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - -package-json@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed" - dependencies: - got "^6.7.1" - registry-auth-token "^3.0.1" - registry-url "^3.0.3" - semver "^5.1.0" - -parse-filepath@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.1.tgz#159d6155d43904d16c10ef698911da1e91969b73" - dependencies: - is-absolute "^0.2.3" - map-cache "^0.2.0" - path-root "^0.1.1" - -parse-glob@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" - dependencies: - glob-base "^0.3.0" - is-dotfile "^1.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.0" - -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - dependencies: - error-ex "^1.2.0" - -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" - -"parse5@>= 1.3.1 < 2.0.0": - version "1.5.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94" - -path-dirname@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" - -path-exists@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" - dependencies: - pinkie-promise "^2.0.0" - -path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - -path-is-inside@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - -path-key@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - -path-parse@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" - -path-root-regex@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" - -path-root@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" - dependencies: - path-root-regex "^0.1.0" - -path-type@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" - dependencies: - graceful-fs "^4.1.2" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -pause-stream@0.0.11: - version "0.0.11" - resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" - dependencies: - through "~2.3" - -performance-now@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - -pify@^2.0.0, pify@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - -pluralize@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" - -postcss-calc@^5.2.0: - version "5.3.1" - resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e" - dependencies: - postcss "^5.0.2" - postcss-message-helpers "^2.0.0" - reduce-css-calc "^1.2.6" - -postcss-colormin@^2.1.8: - version "2.2.2" - resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.2.tgz#6631417d5f0e909a3d7ec26b24c8a8d1e4f96e4b" - dependencies: - colormin "^1.0.5" - postcss "^5.0.13" - postcss-value-parser "^3.2.3" - -postcss-convert-values@^2.3.4: - version "2.6.1" - resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz#bbd8593c5c1fd2e3d1c322bb925dcae8dae4d62d" - dependencies: - postcss "^5.0.11" - postcss-value-parser "^3.1.2" - -postcss-discard-comments@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d" - dependencies: - postcss "^5.0.14" - -postcss-discard-duplicates@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz#b9abf27b88ac188158a5eb12abcae20263b91932" - dependencies: - postcss "^5.0.4" - -postcss-discard-empty@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz#d2b4bd9d5ced5ebd8dcade7640c7d7cd7f4f92b5" - dependencies: - postcss "^5.0.14" - -postcss-discard-overridden@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz#8b1eaf554f686fb288cd874c55667b0aa3668d58" - dependencies: - postcss "^5.0.16" - -postcss-discard-unused@^2.2.1: - version "2.2.3" - resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz#bce30b2cc591ffc634322b5fb3464b6d934f4433" - dependencies: - postcss "^5.0.14" - uniqs "^2.0.0" - -postcss-filter-plugins@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz#6d85862534d735ac420e4a85806e1f5d4286d84c" - dependencies: - postcss "^5.0.4" - uniqid "^4.0.0" - -postcss-merge-idents@^2.1.5: - version "2.1.7" - resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270" - dependencies: - has "^1.0.1" - postcss "^5.0.10" - postcss-value-parser "^3.1.1" - -postcss-merge-longhand@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz#23d90cd127b0a77994915332739034a1a4f3d658" - dependencies: - postcss "^5.0.4" - -postcss-merge-rules@^2.0.3: - version "2.1.2" - resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz#d1df5dfaa7b1acc3be553f0e9e10e87c61b5f721" - dependencies: - browserslist "^1.5.2" - caniuse-api "^1.5.2" - postcss "^5.0.4" - postcss-selector-parser "^2.2.2" - vendors "^1.0.0" - -postcss-message-helpers@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz#a4f2f4fab6e4fe002f0aed000478cdf52f9ba60e" - -postcss-minify-font-values@^1.0.2: - version "1.0.5" - resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz#4b58edb56641eba7c8474ab3526cafd7bbdecb69" - dependencies: - object-assign "^4.0.1" - postcss "^5.0.4" - postcss-value-parser "^3.0.2" - -postcss-minify-gradients@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz#5dbda11373703f83cfb4a3ea3881d8d75ff5e6e1" - dependencies: - postcss "^5.0.12" - postcss-value-parser "^3.3.0" - -postcss-minify-params@^1.0.4: - version "1.2.2" - resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz#ad2ce071373b943b3d930a3fa59a358c28d6f1f3" - dependencies: - alphanum-sort "^1.0.1" - postcss "^5.0.2" - postcss-value-parser "^3.0.2" - uniqs "^2.0.0" - -postcss-minify-selectors@^2.0.4: - version "2.1.1" - resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz#b2c6a98c0072cf91b932d1a496508114311735bf" - dependencies: - alphanum-sort "^1.0.2" - has "^1.0.1" - postcss "^5.0.14" - postcss-selector-parser "^2.0.0" - -postcss-normalize-charset@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz#ef9ee71212d7fe759c78ed162f61ed62b5cb93f1" - dependencies: - postcss "^5.0.5" - -postcss-normalize-url@^3.0.7: - version "3.0.8" - resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz#108f74b3f2fcdaf891a2ffa3ea4592279fc78222" - dependencies: - is-absolute-url "^2.0.0" - normalize-url "^1.4.0" - postcss "^5.0.14" - postcss-value-parser "^3.2.3" - -postcss-ordered-values@^2.1.0: - version "2.2.3" - resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz#eec6c2a67b6c412a8db2042e77fe8da43f95c11d" - dependencies: - postcss "^5.0.4" - postcss-value-parser "^3.0.1" - -postcss-reduce-idents@^2.2.2: - version "2.4.0" - resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz#c2c6d20cc958284f6abfbe63f7609bf409059ad3" - dependencies: - postcss "^5.0.4" - postcss-value-parser "^3.0.2" - -postcss-reduce-initial@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz#68f80695f045d08263a879ad240df8dd64f644ea" - dependencies: - postcss "^5.0.4" - -postcss-reduce-transforms@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz#ff76f4d8212437b31c298a42d2e1444025771ae1" - dependencies: - has "^1.0.1" - postcss "^5.0.8" - postcss-value-parser "^3.0.1" - -postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2: - version "2.2.3" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz#f9437788606c3c9acee16ffe8d8b16297f27bb90" - dependencies: - flatten "^1.0.2" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-svgo@^2.1.1: - version "2.1.6" - resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d" - dependencies: - is-svg "^2.0.0" - postcss "^5.0.14" - postcss-value-parser "^3.2.3" - svgo "^0.7.0" - -postcss-unique-selectors@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz#981d57d29ddcb33e7b1dfe1fd43b8649f933ca1d" - dependencies: - alphanum-sort "^1.0.1" - postcss "^5.0.4" - uniqs "^2.0.0" - -postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15" - -postcss-zindex@^2.0.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz#d2109ddc055b91af67fc4cb3b025946639d2af22" - dependencies: - has "^1.0.1" - postcss "^5.0.4" - uniqs "^2.0.0" - -postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.8, postcss@^5.2.16: - version "5.2.18" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5" - dependencies: - chalk "^1.1.3" - js-base64 "^2.1.9" - source-map "^0.5.6" - supports-color "^3.2.3" - -prelude-ls@~1.1.0, prelude-ls@~1.1.1, prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - -prepend-http@^1.0.0, prepend-http@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" - -preserve@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" - -pretty-hrtime@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" - -process-nextick-args@^1.0.6, process-nextick-args@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" - -progress@^1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" - -pseudomap@^1.0.1, pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - -pump@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.2.tgz#3b3ee6512f94f0e575538c17995f9f16990a5d51" - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - -q@^1.1.2: - version "1.5.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" - -qs@~6.4.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" - -qs@~6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" - -query-string@^4.1.0: - version "4.3.4" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" - dependencies: - object-assign "^4.1.0" - strict-uri-encode "^1.0.0" - -randomatic@^1.1.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -rc@^1.0.1, rc@^1.1.6, rc@^1.1.7: - version "1.2.2" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.2.tgz#d8ce9cb57e8d64d9c7badd9876c7c34cbe3c7077" - dependencies: - deep-extend "~0.4.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - -read-pkg-up@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" - dependencies: - find-up "^1.0.0" - read-pkg "^1.0.0" - -read-pkg@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" - dependencies: - load-json-file "^1.0.0" - normalize-package-data "^2.3.2" - path-type "^1.0.0" - -"readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@~1.0.17: - version "1.0.34" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - safe-buffer "~5.1.1" - string_decoder "~1.0.3" - util-deprecate "~1.0.1" - -readable-stream@~1.1.9: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readable-stream@~2.0.0: - version "2.0.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - string_decoder "~0.10.x" - util-deprecate "~1.0.1" - -readdirp@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" - dependencies: - graceful-fs "^4.1.2" - minimatch "^3.0.2" - readable-stream "^2.0.2" - set-immediate-shim "^1.0.1" - -readline2@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35" - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - mute-stream "0.0.5" - -rechoir@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" - dependencies: - resolve "^1.1.6" - -redent@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" - dependencies: - indent-string "^2.1.0" - strip-indent "^1.0.1" - -reduce-css-calc@^1.2.6: - version "1.3.0" - resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" - dependencies: - balanced-match "^0.4.2" - math-expression-evaluator "^1.2.14" - reduce-function-call "^1.0.1" - -reduce-function-call@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.2.tgz#5a200bf92e0e37751752fe45b0ab330fd4b6be99" - dependencies: - balanced-match "^0.4.2" - -regex-cache@^0.4.2: - version "0.4.4" - resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" - dependencies: - is-equal-shallow "^0.1.3" - -registry-auth-token@^3.0.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.1.tgz#fb0d3289ee0d9ada2cbb52af5dfe66cb070d3006" - dependencies: - rc "^1.1.6" - safe-buffer "^5.0.1" - -registry-url@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" - dependencies: - rc "^1.0.1" - -remap-istanbul@^0.6.4: - version "0.6.4" - resolved "https://registry.yarnpkg.com/remap-istanbul/-/remap-istanbul-0.6.4.tgz#ac551eff1aa641504b4f318d0303dda61e3bb695" - dependencies: - amdefine "1.0.0" - gulp-util "3.0.7" - istanbul "0.4.3" - source-map ">=0.5.6" - through2 "2.0.1" - -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - -repeat-element@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" - -repeat-string@^1.5.2: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - -repeating@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" - dependencies: - is-finite "^1.0.0" - -replace-ext@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" - -replace-ext@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" - -request@2.81.0: - version "2.81.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" - dependencies: - aws-sign2 "~0.6.0" - aws4 "^1.2.1" - caseless "~0.12.0" - combined-stream "~1.0.5" - extend "~3.0.0" - forever-agent "~0.6.1" - form-data "~2.1.1" - har-validator "~4.2.1" - hawk "~3.1.3" - http-signature "~1.1.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.7" - oauth-sign "~0.8.1" - performance-now "^0.2.0" - qs "~6.4.0" - safe-buffer "^5.0.1" - stringstream "~0.0.4" - tough-cookie "~2.3.0" - tunnel-agent "^0.6.0" - uuid "^3.0.0" - -"request@>= 2.44.0 < 3.0.0": - version "2.83.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356" - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.6.0" - caseless "~0.12.0" - combined-stream "~1.0.5" - extend "~3.0.1" - forever-agent "~0.6.1" - form-data "~2.3.1" - har-validator "~5.0.3" - hawk "~6.0.2" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.17" - oauth-sign "~0.8.2" - performance-now "^2.1.0" - qs "~6.5.1" - safe-buffer "^5.1.1" - stringstream "~0.0.5" - tough-cookie "~2.3.3" - tunnel-agent "^0.6.0" - uuid "^3.1.0" - -require-uncached@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" - dependencies: - caller-path "^0.1.0" - resolve-from "^1.0.0" - -resolve-dir@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-0.1.1.tgz#b219259a5602fac5c5c496ad894a6e8cc430261e" - dependencies: - expand-tilde "^1.2.2" - global-modules "^0.2.3" - -resolve-from@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" - -resolve-url@~0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - -resolve@1.1.x: - version "1.1.7" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" - -resolve@^1.1.6, resolve@^1.1.7: - version "1.5.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36" - dependencies: - path-parse "^1.0.5" - -restore-cursor@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" - dependencies: - exit-hook "^1.0.0" - onetime "^1.0.0" - -right-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" - dependencies: - align-text "^0.1.1" - -rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.6.1: - version "2.6.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" - dependencies: - glob "^7.0.5" - -run-async@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" - dependencies: - once "^1.3.0" - -rx-lite@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" - -safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" - -samsam@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.2.tgz#bec11fdc83a9fda063401210e40176c3024d1567" - -samsam@~1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.3.tgz#9f5087419b4d091f232571e7fa52e90b0f552621" - -sax@>=0.6.0, sax@~1.2.1: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - -semver-diff@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" - dependencies: - semver "^5.0.3" - -"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0: - version "5.4.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" - -semver@^4.1.0: - version "4.3.6" - resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" - -sequencify@~0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/sequencify/-/sequencify-0.0.7.tgz#90cff19d02e07027fd767f5ead3e7b95d1e7380c" - -set-blocking@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - -set-immediate-shim@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - dependencies: - shebang-regex "^1.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - -shelljs@^0.7.5: - version "0.7.8" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.8.tgz#decbcf874b0d1e5fb72e14b164a9683048e9acb3" - dependencies: - glob "^7.0.0" - interpret "^1.0.0" - rechoir "^0.6.2" - -sigmund@^1.0.1, sigmund@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" - -signal-exit@^3.0.0, signal-exit@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - -sinon@^1.17.2: - version "1.17.7" - resolved "https://registry.yarnpkg.com/sinon/-/sinon-1.17.7.tgz#4542a4f49ba0c45c05eb2e9dd9d203e2b8efe0bf" - dependencies: - formatio "1.1.1" - lolex "1.3.2" - samsam "1.1.2" - util ">=0.10.3 <1" - -slash@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" - -slice-ansi@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" - -sntp@1.x.x: - version "1.0.9" - resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" - dependencies: - hoek "2.x.x" - -sntp@2.x.x: - version "2.1.0" - resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8" - dependencies: - hoek "4.x.x" - -sort-keys@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" - dependencies: - is-plain-obj "^1.0.0" - -source-map-resolve@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.3.1.tgz#610f6122a445b8dd51535a2a71b783dfc1248761" - dependencies: - atob "~1.1.0" - resolve-url "~0.2.1" - source-map-url "~0.3.0" - urix "~0.1.0" - -source-map-url@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.3.0.tgz#7ecaf13b57bcd09da8a40c5d269db33799d4aaf9" - -source-map@0.X, source-map@>=0.5.6: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - -source-map@^0.1.38: - version "0.1.43" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" - dependencies: - amdefine ">=0.0.4" - -source-map@^0.4.4: - version "0.4.4" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" - dependencies: - amdefine ">=0.0.4" - -source-map@^0.5.1, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - -source-map@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" - dependencies: - amdefine ">=0.0.4" - -sparkles@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.0.tgz#1acbbfb592436d10bbe8f785b7cc6f82815012c3" - -spawn-command@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2.tgz#9544e1a43ca045f8531aac1a48cb29bdae62338e" - -spdx-correct@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" - dependencies: - spdx-license-ids "^1.0.2" - -spdx-expression-parse@~1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c" - -spdx-license-ids@^1.0.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" - -split@0.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" - dependencies: - through "2" - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - -sshpk@^1.7.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - dashdash "^1.12.0" - getpass "^0.1.1" - optionalDependencies: - bcrypt-pbkdf "^1.0.0" - ecc-jsbn "~0.1.1" - jsbn "~0.1.0" - tweetnacl "~0.14.0" - -stream-combiner@~0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" - dependencies: - duplexer "~0.1.1" - -stream-consume@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/stream-consume/-/stream-consume-0.1.0.tgz#a41ead1a6d6081ceb79f65b061901b6d8f3d1d0f" - -streamfilter@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/streamfilter/-/streamfilter-1.0.5.tgz#87507111beb8e298451717b511cfed8f002abf53" - dependencies: - readable-stream "^2.0.2" - -strict-uri-encode@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" - -string-width@^1.0.1, string-width@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -string-width@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - -string_decoder@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" - dependencies: - safe-buffer "~5.1.0" - -stringstream@~0.0.4, stringstream@~0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" - -strip-ansi@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.3.0.tgz#25f48ea22ca79187f3174a4db8759347bb126220" - dependencies: - ansi-regex "^0.2.1" - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - dependencies: - ansi-regex "^3.0.0" - -strip-bom-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz#f87db5ef2613f6968aa545abfe1ec728b6a829ca" - dependencies: - first-chunk-stream "^2.0.0" - strip-bom "^2.0.0" - -strip-bom@2.X, strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - dependencies: - is-utf8 "^0.2.0" - -strip-bom@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-1.0.0.tgz#85b8862f3844b5a6d5ec8467a93598173a36f794" - dependencies: - first-chunk-stream "^1.0.0" - is-utf8 "^0.2.0" - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - -strip-indent@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" - dependencies: - get-stdin "^4.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" - -supports-color@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.2.0.tgz#ff1ed1e61169d06b3cf2d588e188b18d8847e17e" - -supports-color@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a" - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - -supports-color@^3.1.0, supports-color@^3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" - dependencies: - has-flag "^1.0.0" - -supports-color@^4.0.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b" - dependencies: - has-flag "^2.0.0" - -svgo@^0.7.0: - version "0.7.2" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5" - dependencies: - coa "~1.0.1" - colors "~1.1.2" - csso "~2.3.1" - js-yaml "~3.7.0" - mkdirp "~0.5.1" - sax "~1.2.1" - whet.extend "~0.9.9" - -table@^3.7.8: - version "3.8.3" - resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" - dependencies: - ajv "^4.7.0" - ajv-keywords "^1.0.0" - chalk "^1.1.1" - lodash "^4.0.0" - slice-ansi "0.0.4" - string-width "^2.0.0" - -tar-pack@^3.4.0: - version "3.4.1" - resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f" - dependencies: - debug "^2.2.0" - fstream "^1.0.10" - fstream-ignore "^1.0.5" - once "^1.3.3" - readable-stream "^2.1.4" - rimraf "^2.5.1" - tar "^2.2.1" - uid-number "^0.0.6" - -tar@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" - dependencies: - block-stream "*" - fstream "^1.0.2" - inherits "2" - -term-size@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" - dependencies: - execa "^0.7.0" - -text-table@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - -through2@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.1.tgz#384e75314d49f32de12eebb8136b8eb6b5d59da9" - dependencies: - readable-stream "~2.0.0" - xtend "~4.0.0" - -through2@2.X, through2@^2.0.0, through2@^2.0.1, through2@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" - dependencies: - readable-stream "^2.1.5" - xtend "~4.0.1" - -through2@^0.5.0: - version "0.5.1" - resolved "https://registry.yarnpkg.com/through2/-/through2-0.5.1.tgz#dfdd012eb9c700e2323fd334f38ac622ab372da7" - dependencies: - readable-stream "~1.0.17" - xtend "~3.0.0" - -through2@^0.6.1, through2@~0.6.3: - version "0.6.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" - dependencies: - readable-stream ">=1.0.33-1 <1.1.0-0" - xtend ">=4.0.0 <4.1.0-0" - -through@2, through@^2.3.6, through@~2.3, through@~2.3.1, through@~2.3.8: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - -tildify@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/tildify/-/tildify-1.2.0.tgz#dcec03f55dca9b7aa3e5b04f21817eb56e63588a" - dependencies: - os-homedir "^1.0.0" - -time-stamp@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" - -timed-out@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" - -to-iso-string@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/to-iso-string/-/to-iso-string-0.0.2.tgz#4dc19e664dfccbe25bd8db508b00c6da158255d1" - -tough-cookie@~2.3.0, tough-cookie@~2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561" - dependencies: - punycode "^1.4.1" - -trim-newlines@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" - -tryit@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb" - -tslint@^4.3.1: - version "4.5.1" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-4.5.1.tgz#05356871bef23a434906734006fc188336ba824b" - dependencies: - babel-code-frame "^6.20.0" - colors "^1.1.2" - diff "^3.0.1" - findup-sync "~0.3.0" - glob "^7.1.1" - optimist "~0.6.0" - resolve "^1.1.7" - tsutils "^1.1.0" - update-notifier "^2.0.0" - -tsutils@^1.1.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-1.9.1.tgz#b9f9ab44e55af9681831d5f28d0aeeaf5c750cb0" - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - -type-check@~0.3.1, type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - dependencies: - prelude-ls "~1.1.2" - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - -typescript-formatter@4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/typescript-formatter/-/typescript-formatter-4.0.1.tgz#ed82daf856cc9a379bb16b7f1aac9affee2974cd" - dependencies: - commandpost "^1.0.0" - editorconfig "^0.13.2" - glob-expand "^0.2.1" - -typescript@2.6.1, typescript@^2.0.3: - version "2.6.1" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.1.tgz#ef39cdea27abac0b500242d6726ab90e0c846631" - -uglify-js@^2.6, uglify-js@~2.8.10: - version "2.8.29" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" - dependencies: - source-map "~0.5.1" - yargs "~3.10.0" - optionalDependencies: - uglify-to-browserify "~1.0.0" - -uglify-save-license@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/uglify-save-license/-/uglify-save-license-0.4.1.tgz#95726c17cc6fd171c3617e3bf4d8d82aa8c4cce1" - -uglify-to-browserify@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" - -uid-number@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" - -unc-path-regex@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" - -underscore@^1.8.2: - version "1.8.3" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" - -uniq@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" - -uniqid@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/uniqid/-/uniqid-4.1.1.tgz#89220ddf6b751ae52b5f72484863528596bb84c1" - dependencies: - macaddress "^0.2.8" - -uniqs@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" - -unique-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-1.0.0.tgz#d59a4a75427447d9aa6c91e70263f8d26a4b104b" - -unique-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a" - dependencies: - crypto-random-string "^1.0.0" - -unzip-response@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" - -update-notifier@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.3.0.tgz#4e8827a6bb915140ab093559d7014e3ebb837451" - dependencies: - boxen "^1.2.1" - chalk "^2.0.1" - configstore "^3.0.0" - import-lazy "^2.1.0" - is-installed-globally "^0.1.0" - is-npm "^1.0.0" - latest-version "^3.0.0" - semver-diff "^2.0.0" - xdg-basedir "^3.0.0" - -urix@^0.1.0, urix@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" - -url-parse-lax@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" - dependencies: - prepend-http "^1.0.1" - -user-home@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" - -user-home@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" - dependencies: - os-homedir "^1.0.0" - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - -"util@>=0.10.3 <1": - version "0.10.3" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" - dependencies: - inherits "2.0.1" - -uuid@^3.0.0, uuid@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" - -v8flags@^2.0.2: - version "2.1.1" - resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.1.1.tgz#aab1a1fa30d45f88dd321148875ac02c0b55e5b4" - dependencies: - user-home "^1.1.1" - -validate-npm-package-license@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" - dependencies: - spdx-correct "~1.0.0" - spdx-expression-parse "~1.0.0" - -vendors@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22" - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -vinyl-file@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/vinyl-file/-/vinyl-file-2.0.0.tgz#a7ebf5ffbefda1b7d18d140fcb07b223efb6751a" - dependencies: - graceful-fs "^4.1.2" - pify "^2.3.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" - strip-bom-stream "^2.0.0" - vinyl "^1.1.0" - -vinyl-fs@^0.3.0: - version "0.3.14" - resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-0.3.14.tgz#9a6851ce1cac1c1cea5fe86c0931d620c2cfa9e6" - dependencies: - defaults "^1.0.0" - glob-stream "^3.1.5" - glob-watcher "^0.0.6" - graceful-fs "^3.0.0" - mkdirp "^0.5.0" - strip-bom "^1.0.0" - through2 "^0.6.1" - vinyl "^0.4.0" - -vinyl-sourcemaps-apply@^0.2.0, vinyl-sourcemaps-apply@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz#ab6549d61d172c2b1b87be5c508d239c8ef87705" - dependencies: - source-map "^0.5.1" - -vinyl@1.X, vinyl@^1.1.0, vinyl@^1.1.1, vinyl@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-1.2.0.tgz#5c88036cf565e5df05558bfc911f8656df218884" - dependencies: - clone "^1.0.0" - clone-stats "^0.0.1" - replace-ext "0.0.1" - -vinyl@^0.2.1: - version "0.2.3" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.2.3.tgz#bca938209582ec5a49ad538a00fa1f125e513252" - dependencies: - clone-stats "~0.0.1" - -vinyl@^0.4.0, vinyl@^0.4.3, vinyl@^0.4.5: - version "0.4.6" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.4.6.tgz#2f356c87a550a255461f36bbeb2a5ba8bf784847" - dependencies: - clone "^0.2.0" - clone-stats "^0.0.1" - -vinyl@^0.5.0: - version "0.5.3" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.5.3.tgz#b0455b38fc5e0cf30d4325132e461970c2091cde" - dependencies: - clone "^1.0.0" - clone-stats "^0.0.1" - replace-ext "0.0.1" - -vinyl@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.1.0.tgz#021f9c2cf951d6b939943c89eb5ee5add4fd924c" - dependencies: - clone "^2.1.1" - clone-buffer "^1.0.0" - clone-stats "^1.0.0" - cloneable-readable "^1.0.0" - remove-trailing-separator "^1.0.1" - replace-ext "^1.0.0" - -vscode-nls-dev@^2.0.1: - version "2.1.5" - resolved "https://registry.yarnpkg.com/vscode-nls-dev/-/vscode-nls-dev-2.1.5.tgz#19faa3b18a7f302201039a4c967bbd22fa12844d" - dependencies: - clone "^1.0.2" - event-stream "^3.3.2" - glob "^6.0.4" - gulp-util "^3.0.7" - iconv-lite "^0.4.15" - is "^3.2.1" - source-map "^0.5.3" - typescript "^2.0.3" - vinyl "^1.1.1" - xml2js "^0.4.17" - yargs "^3.32.0" - -whet.extend@~0.9.9: - version "0.9.9" - resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1" - -which@^1.1.1, which@^1.2.12, which@^1.2.9: - version "1.3.0" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" - dependencies: - isexe "^2.0.0" - -wide-align@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710" - dependencies: - string-width "^1.0.2" - -widest-line@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-1.0.0.tgz#0c09c85c2a94683d0d7eaf8ee097d564bf0e105c" - dependencies: - string-width "^1.0.1" - -window-size@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" - -window-size@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" - -wordwrap@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" - -wordwrap@^1.0.0, wordwrap@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - -wordwrap@~0.0.2: - version "0.0.3" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" - -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - -write-file-atomic@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.3.0.tgz#1ff61575c2e2a4e8e510d6fa4e243cce183999ab" - dependencies: - graceful-fs "^4.1.11" - imurmurhash "^0.1.4" - signal-exit "^3.0.2" - -write@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" - dependencies: - mkdirp "^0.5.1" - -xdg-basedir@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" - -xml-name-validator@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-1.0.0.tgz#dcf82ee092322951ef8cc1ba596c9cbfd14a83f1" - -xml2js@^0.4.17: - version "0.4.19" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" - dependencies: - sax ">=0.6.0" - xmlbuilder "~9.0.1" - -xmlbuilder@~9.0.1: - version "9.0.4" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.4.tgz#519cb4ca686d005a8420d3496f3f0caeecca580f" - -"xmlhttprequest@>= 1.6.0 < 2.0.0": - version "1.8.0" - resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" - -"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@~4.0.0, xtend@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" - -xtend@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-3.0.0.tgz#5cce7407baf642cba7becda568111c493f59665a" - -y18n@^3.2.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" - -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - -yargs@^3.32.0: - version "3.32.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995" - dependencies: - camelcase "^2.0.1" - cliui "^3.0.3" - decamelize "^1.1.1" - os-locale "^1.4.0" - string-width "^1.0.1" - window-size "^0.1.4" - y18n "^3.2.0" - -yargs@~3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" - dependencies: - camelcase "^1.0.2" - cliui "^2.1.0" - decamelize "^1.0.0" - window-size "0.1.0" diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index 6df0b4ecb..ea2d2d9a2 100644 --- a/build/npm/postinstall.js +++ b/build/npm/postinstall.js @@ -60,4 +60,11 @@ runtime "${runtime}"`; yarnInstall(`build`); // node modules required for build yarnInstall('test/smoke'); // node modules required for smoketest -yarnInstallBuildDependencies(); // node modules for watching, specific to host node version, not electron \ No newline at end of file +yarnInstallBuildDependencies(); // node modules for watching, specific to host node version, not electron + +// Remove the windows process tree typings as this causes duplicate identifier errors in tsc builds +const processTreeDts = path.join('node_modules', 'windows-process-tree', 'typings', 'windows-process-tree.d.ts'); +if (fs.existsSync(processTreeDts)) { + console.log('Removing windows-process-tree.d.ts'); + fs.unlinkSync(processTreeDts); +} \ No newline at end of file diff --git a/build/package.json b/build/package.json index 9ad3ffbe1..7e096ee9b 100644 --- a/build/package.json +++ b/build/package.json @@ -16,6 +16,8 @@ "@types/gulp-util": "^3.0.34", "@types/mime": "0.0.29", "@types/minimatch": "^3.0.3", + "@types/minimist": "^1.2.0", + "@types/mocha": "2.2.39", "@types/node": "8.0.33", "@types/pump": "^1.0.1", "@types/request": "^2.47.0", @@ -25,15 +27,19 @@ "@types/uglify-es": "^3.0.0", "@types/underscore": "^1.8.9", "@types/xml2js": "0.0.33", + "applicationinsights": "1.0.6", "azure-storage": "^2.1.0", "documentdb": "1.13.0", "github-releases": "^0.4.1", "gulp-bom": "^1.0.0", "gulp-sourcemaps": "^1.11.0", + "iconv-lite": "0.4.23", "mime": "^1.3.4", "minimist": "^1.2.0", "request": "^2.85.0", - "typescript": "3.1.1", + "tslint": "^5.9.1", + "typescript": "3.1.4", + "vsce": "1.48.0", "xml2js": "^0.4.17" }, "scripts": { diff --git a/build/tfs/continuous-build.yml b/build/tfs/continuous-build.yml deleted file mode 100644 index a1d4be713..000000000 --- a/build/tfs/continuous-build.yml +++ /dev/null @@ -1,17 +0,0 @@ -jobs: -- job: Windows - pool: - vmImage: VS2017-Win2016 - steps: - - template: win32/continuous-build-win32.yml - -- job: Linux - pool: Hosted Linux Preview - steps: - - template: linux/continuous-build-linux.yml - -- job: macOS - pool: - vmImage: macOS 10.13 - steps: - - template: darwin/continuous-build-darwin.yml \ No newline at end of file diff --git a/build/tfs/darwin/enqueue.js b/build/tfs/darwin/enqueue.js deleted file mode 100644 index d2243ad6a..000000000 --- a/build/tfs/darwin/enqueue.js +++ /dev/null @@ -1,29 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; -Object.defineProperty(exports, "__esModule", { value: true }); -const child_process_1 = require("child_process"); -const azure = require("azure-storage"); -function queueSigningRequest(quality, commit) { - const retryOperations = new azure.ExponentialRetryPolicyFilter(); - const queueSvc = azure - .createQueueService(process.env['AZURE_STORAGE_ACCOUNT_2'], process.env['AZURE_STORAGE_ACCESS_KEY_2']) - .withFilter(retryOperations); - queueSvc.messageEncoder = new azure.QueueMessageEncoder.TextBase64QueueMessageEncoder(); - const message = `${quality}/${commit}`; - return new Promise((c, e) => queueSvc.createMessage('sign-darwin', message, err => err ? e(err) : c())); -} -async function main(quality) { - const commit = child_process_1.execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim(); - console.log(`Queueing signing request for '${quality}/${commit}'...`); - await queueSigningRequest(quality, commit); - // console.log('Waiting on signed build...'); - // await waitForSignedBuild(quality, commit); - // console.log('Found signed build!'); -} -main(process.argv[2]).catch(err => { - console.error(err); - process.exit(1); -}); diff --git a/build/tfs/darwin/enqueue.ts b/build/tfs/darwin/enqueue.ts deleted file mode 100644 index 2da255b0b..000000000 --- a/build/tfs/darwin/enqueue.ts +++ /dev/null @@ -1,41 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -'use strict'; - -import { execSync } from 'child_process'; -import * as azure from 'azure-storage'; - - -function queueSigningRequest(quality: string, commit: string): Promise { - const retryOperations = new azure.ExponentialRetryPolicyFilter(); - const queueSvc = azure - .createQueueService(process.env['AZURE_STORAGE_ACCOUNT_2']!, process.env['AZURE_STORAGE_ACCESS_KEY_2']!) - .withFilter(retryOperations); - - queueSvc.messageEncoder = new azure.QueueMessageEncoder.TextBase64QueueMessageEncoder(); - - const message = `${quality}/${commit}`; - - return new Promise((c, e) => queueSvc.createMessage('sign-darwin', message, err => err ? e(err) : c())); -} - - -async function main(quality: string): Promise { - const commit = execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim(); - - console.log(`Queueing signing request for '${quality}/${commit}'...`); - await queueSigningRequest(quality, commit); - - // console.log('Waiting on signed build...'); - // await waitForSignedBuild(quality, commit); - - // console.log('Found signed build!'); -} - -main(process.argv[2]).catch(err => { - console.error(err); - process.exit(1); -}); \ No newline at end of file diff --git a/build/tfs/linux/.gitignore b/build/tfs/linux/.gitignore deleted file mode 100644 index 5ca5f22fc..000000000 --- a/build/tfs/linux/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -pat -*.js \ No newline at end of file diff --git a/build/tfs/linux/frozen-check.ts b/build/tfs/linux/frozen-check.ts deleted file mode 100644 index 09bc36a24..000000000 --- a/build/tfs/linux/frozen-check.ts +++ /dev/null @@ -1,49 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -'use strict'; - -import { DocumentClient } from 'documentdb'; - -interface Config { - id: string; - frozen: boolean; -} - -function createDefaultConfig(quality: string): Config { - return { - id: quality, - frozen: false - }; -} - -function getConfig(quality: string): Promise { - const client = new DocumentClient(process.env['AZURE_DOCUMENTDB_ENDPOINT']!, { masterKey: process.env['AZURE_DOCUMENTDB_MASTERKEY'] }); - const collection = 'dbs/builds/colls/config'; - const query = { - query: `SELECT TOP 1 * FROM c WHERE c.id = @quality`, - parameters: [ - { name: '@quality', value: quality } - ] - }; - - return new Promise((c, e) => { - client.queryDocuments(collection, query).toArray((err, results) => { - if (err && err.code !== 409) { return e(err); } - - c(!results || results.length === 0 ? createDefaultConfig(quality) : results[0] as any as Config); - }); - }); -} - -getConfig(process.argv[2]) - .then(config => { - console.log(config.frozen); - process.exit(0); - }) - .catch(err => { - console.error(err); - process.exit(1); - }); \ No newline at end of file diff --git a/build/tfs/linux/ia32/Dockerfile b/build/tfs/linux/ia32/Dockerfile deleted file mode 100644 index 25d621d99..000000000 --- a/build/tfs/linux/ia32/Dockerfile +++ /dev/null @@ -1,53 +0,0 @@ -FROM microsoft/vsts-agent:ubuntu-14.04-standard -MAINTAINER Joao Moreno - -ARG DEBIAN_FRONTEND=noninteractive -RUN dpkg --add-architecture i386 -RUN apt-get update - -# Dependencies -RUN apt-get install -y build-essential -RUN apt-get install -y gcc-multilib g++-multilib -RUN apt-get install -y git -RUN apt-get install -y zip -RUN apt-get install -y rpm -RUN apt-get install -y createrepo -RUN apt-get install -y python-gtk2 -RUN apt-get install -y jq -RUN apt-get install -y xvfb -RUN apt-get install -y fakeroot -RUN apt-get install -y libgtk2.0-0:i386 -RUN apt-get install -y libgconf-2-4:i386 -RUN apt-get install -y libnss3:i386 -RUN apt-get install -y libasound2:i386 -RUN apt-get install -y libxtst6:i386 -RUN apt-get install -y libfuse2 -RUN apt-get install -y libnotify-bin -RUN apt-get install -y libnotify4:i386 -RUN apt-get install -y libx11-dev:i386 -RUN apt-get install -y libxkbfile-dev:i386 -RUN apt-get install -y libxss1:i386 -RUN apt-get install -y libx11-xcb-dev:i386 -RUN apt-get install -y libgl1-mesa-glx:i386 libgl1-mesa-dri:i386 -RUN apt-get install -y libxkbfile-dev -RUN apt-get install -y bc bsdmainutils -RUN apt-get install -y libgirepository-1.0-1:i386 gir1.2-glib-2.0:i386 gir1.2-secret-1:i386 libsecret-1-dev:i386 -RUN apt-get install -y dpkg-dev:i386 - -# Xvfb -# Thanks https://medium.com/@griggheo/running-headless-selenium-webdriver-tests-in-docker-containers-342fdbabf756 -ADD xvfb.init /etc/init.d/xvfb -RUN chmod +x /etc/init.d/xvfb -RUN update-rc.d xvfb defaults - -# dbus -RUN ln -sf /bin/dbus-daemon /usr/bin/dbus-daemon - -# nvm -ENV NVM_DIR /usr/local/nvm -RUN curl https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash - -# for libsecret -ENV PKG_CONFIG_PATH /usr/lib/i386-linux-gnu/pkgconfig - -CMD (service xvfb start; service dbus start; export DISPLAY=:10; ./start.sh) \ No newline at end of file diff --git a/build/tfs/linux/ia32/run-agent.sh b/build/tfs/linux/ia32/run-agent.sh deleted file mode 100755 index bcf9017f3..000000000 --- a/build/tfs/linux/ia32/run-agent.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -if [ ! -f pat ]; then - echo "Error: file pat not found" - exit 1 -fi - -docker run \ - -e VSTS_ACCOUNT="monacotools" \ - -e VSTS_TOKEN="$(cat pat)" \ - -e VSTS_AGENT="tb-lnx-ia32-local" \ - -e VSTS_POOL="linux-ia32" \ - -e VSTS_WORK="/var/vsts/work" \ - --name "tb-lnx-ia32-local" \ - -it joaomoreno/vscode-vso-agent-ia32:latest \ No newline at end of file diff --git a/build/tfs/linux/new_package.json.template b/build/tfs/linux/new_package.json.template deleted file mode 100644 index 77e2ada92..000000000 --- a/build/tfs/linux/new_package.json.template +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "PACKAGENAME", - "version": "PACKAGEVERSION", - "repositoryId": "REPOSITORYID", - "sourceUrl": "PACKAGEURL" -} \ No newline at end of file diff --git a/build/tfs/linux/repoapi_client.sh b/build/tfs/linux/repoapi_client.sh deleted file mode 100755 index b700aceff..000000000 --- a/build/tfs/linux/repoapi_client.sh +++ /dev/null @@ -1,365 +0,0 @@ -#!/bin/bash -e -# This is a VERY basic script for Create/Delete operations on repos and packages -# -cmd=$1 -docDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # chrmarti: Changed to script's directory. -packageJsonTemplate=$docDir/new_package.json.template -repoJsonTemplate=$docDir/new_repo.json.template - -function Bail -{ - echo "ERROR: $@" - exit 1 -} - -function BailIfFileMissing { - file="$1" - if [ ! -f "$file" ]; then - Bail "File $file does not exist" - fi -} - -function Usage { - echo "USAGE: Manage repos and packages in an apt repository" - echo "$0 -config FILENAME -listrepos | -listpkgs | -addrepo FILENAME | -addpkg FILENAME |" - echo "-addpkgs FILENAME | -check ID | -delrepo REPOID | -delpkg PKGID" - echo -e "\t-config FILENAME : JSON file containing API server name and creds" - echo -e "Package Operations:" - echo -e "\t-listpkgs [REGEX] : List packages, optionally filter by REGEX" - echo -e "\t-addpkg FILENAME : Add package to repo using the specified JSON file" - echo -e "\t-addpkgs FILENAME : Add packages to repo using urls contained in FILENAME" - echo -e "\t-check ID : Check upload operation by ID" - echo -e "\t-delpkg PKGID : Delete the specified package by ID" - echo -e "File Operations:" - echo -e "\t-uploadfile FILENAME: Upload FILENAME (does not publish) " - echo -e "\t-addfile FILENAME : Upload FILENAME AND publish to the repo" - echo -e "\t-listfiles : List uploaded files" - echo -e "\t-delfile FILEID : Delete uploaded file by ID" - echo -e "Repository Operations:" - echo -e "\t-listrepos : List repositories" - echo -e "\t-addrepo FILENAME : Create a new repo using the specified JSON file" - echo -e "\t-delrepo REPOID : Delete the specified repo by ID" - exit 1 -} - -function ParseFromJson { - if [ -z "$secretContents" ]; then - Bail "Unable to parse value because no JSON contents were specified" - elif [ -z "$1" ]; then - Bail "Unable to parse value from JSON because no key was specified" - fi - # Write value directly to stdout to be used by caller - echo $secretContents | jq "$1" | tr -d '"' -} - -function ParseConfigFile { - configFile="$1" - if [ -z "$configFile" ]; then - echo "Must specify -config option" - Usage - fi - BailIfFileMissing "$configFile" - secretContents=$(cat "$configFile") - - server=$(ParseFromJson .server) - protocol=$(ParseFromJson .protocol) - port=$(ParseFromJson .port) - repositoryId=$(ParseFromJson .repositoryId) - user=$(ParseFromJson .username) - pass=$(ParseFromJson .password) - baseurl="$protocol://$user:$pass@$server:$port" -} - -# List Repositories -function ListRepositories -{ - echo "Fetching repo list from $server..." - curl -k "$baseurl/v1/repositories" | sed 's/,/,\n/g' | sed 's/^"/\t"/g' - echo "" -} - -# List packages, using $1 as a regex to filter results -function ListPackages -{ - echo "Fetching package list from $server" - curl -k "$baseurl/v1/packages" | sed 's/{/\n{/g' | egrep "$1" | sed 's/,/,\n/g' | sed 's/^"/\t"/g' - echo "" -} - -# Create a new Repo using the specified JSON file -function AddRepo -{ - repoFile=$1 - if [ -z $repoFile ]; then - Bail "Error: Must specify a JSON-formatted file. Reference $repoJsonTemplate" - fi - if [ ! -f $repoFile ]; then - Bail "Error: Cannot create repo - $repoFile does not exist" - fi - packageUrl=$(grep "url" $repoFile | head -n 1 | awk '{print $2}' | tr -d ',') - echo "Creating new repo on $server [$packageUrl]" - curl -i -k "$baseurl/v1/repositories" --data @$repoFile -H "Content-Type: application/json" - echo "" -} - -# Upload AND publish the file -function AddFile -{ - packageFile=$1 - # Validity checks are performed by UploadFile - echo "Uploading package to $server [$packageFile]" - response=$(UploadFile $packageFile "true") - id=$(echo $response | jq -r ".id") - - # Parse package metadata first to confirm it's a valid deb/rpm - # Needs to be performed in this function so we can use it to publish the package - jsonFile=$(WritePackageInfoToFile $packageFile) - - sed -i "s/REPOSITORYID/$repositoryId/g" $jsonFile - # Replace the url field with fileId - sed -i "s/PACKAGEURL/$id/g" $jsonFile - sed -i "s/sourceUrl/fileId/g" $jsonFile - - AddPackage $jsonFile - rm -f $jsonFile - echo "" -} - -# Upload a file -function UploadFile -{ - packageFile=$1 - quick=$2 - if [ -z $packageFile ]; then - Bail "Error: Must specify the path to a file to upload " - fi - if [ ! -f $packageFile ]; then - Bail "Error: Cannot upload - $packageFile does not exist" - fi - - # Additional validation and output if quick mode isn't enabled - # Basically, if this is part of a publish operation, these steps are handled elsewhere - if [ "$quick" != "true" ]; then - # Parse package metadata first to confirm it's a valid deb/rpm - jsonFile=$(WritePackageInfoToFile $packageFile) - rm -f $jsonFile - - echo "Uploading package to $server [$packageFile]" - fi - curl -s -k -X POST -F file=@$packageFile "$baseurl/v1/files" - echo "" -} - -function ListFiles -{ - curl -s -k "$baseurl/v1/files" | jq -} - -function DeleteFile -{ - fileId=$1 - if [ -z "$fileId" ]; then - Bail "Error: Must specify an ID to delete" - fi - curl -s -X DELETE "$baseurl/v1/files/$fileId" -} - -# Upload a single package using the specified JSON file -function AddPackage -{ - packageFile=$1 - if [ -z $packageFile ]; then - Bail "Error: Must specify a JSON-formatted file. Reference $packageJsonTemplate" - fi - if [ ! -f $packageFile ]; then - Bail "Error: Cannot add package - $packageFile does not exist" - fi - packageUrl=$(grep "sourceUrl" $packageFile | head -n 1 | awk '{print $2}') - echo "Adding package to $server [$packageUrl]" - curl -i -k "$baseurl/v1/packages" --data @$packageFile -H "Content-Type: application/json" - echo "" -} - -# Gets the package name and version and writes it to a file -function WritePackageInfoToFile -{ - packageFile=$1 - tmpOut=$(mktemp) - if [ -z "$packageFile" ]; then - Bail "Error: Must specify path to a deb/rpm package" - elif [ ! -f "$packageFile" ]; then - Bail "Error: Specified file $packageFile does not exist" - fi - if dpkg -I $packageFile > $tmpOut 2> /dev/null; then - >&2 echo "File is deb format" - pkgName=$(grep "^\s*Package:" $tmpOut | awk '{print $2}') - pkgVer=$(grep "^\s*Version:" $tmpOut | awk '{print $2}') - elif rpm -qpi $packageFile > $tmpOut 2> /dev/null; then - >&2 echo "File is rpm format" - pkgName=$(egrep "^Name" $tmpOut | tr -d ':' | awk '{print $2}') - pkgVer=$(egrep "^Version" $tmpOut | tr -d ':' | awk '{print $2}') - else - rm -f $tmpOut - Bail "File is not a valid deb/rpm package $url" - fi - - rm -f $tmpOut - if [ -z "$pkgName" ]; then - Bail "Unable to parse package name for $url" - elif [ -z "$pkgVer" ]; then - Bail "Unable to parse package version number for $url" - fi - - # Create Package .json file - outJson=$(mktemp) - escapedUrl=$(echo "$url" | sed 's/\//\\\//g' | sed 's/\&/\\\&/g') - cp $packageJsonTemplate $outJson - sed -i "s/PACKAGENAME/$pkgName/g" $outJson - sed -i "s/PACKAGEVERSION/$pkgVer/g" $outJson - - # Return path to json file - echo $outJson -} - -# Upload a single package by dynamically creating a JSON file using a provided URL -function AddPackageByUrl -{ - url=$(echo "$1") - if [ -z "$url" ]; then - Bail "Unable to publish package because no URL was specified" - fi - tmpFile=$(mktemp) - if ! wget -q "$url" -O $tmpFile; then - rm -f $tmpFile - Bail "Unable to download URL $url" - fi - - jsonFile=$(WritePackageInfoToFile $tmpFile) - # Create Package .json file - escapedUrl=$(echo "$url" | sed 's/\//\\\//g' | sed 's/\&/\\\&/g') - sed -i "s/PACKAGEURL/$escapedUrl/g" $jsonFile - sed -i "s/REPOSITORYID/$repositoryId/g" $jsonFile - # Perform Upload - AddPackage $jsonFile - # Cleanup - rm -f $jsonFile -} - -# Upload multiple packages by reading urls line-by-line from the specified file -function AddPackages -{ - urlFile=$1 - if [ -z $urlFile ]; then - Bail "Must specify a flat text file containing one or more URLs" - fi - if [ ! -f $urlFile ]; then - Bail "Cannot add packages. File $urlFile does not exist" - fi - for url in $(cat $urlFile); do - if [ -n "$url" ]; then - AddPackageByUrl "$url" - fi - sleep 5 - done -} - -# Check upload by ID -function CheckUpload { - id=$1 - if [ -z "$id" ]; then - Bail "Must specify an ID" - fi - curl -s -k $baseurl/v1/packages/queue/$id | jq - echo "" -} - -# Delete the specified repo -function DeleteRepo -{ - repoId=$1 - if [ -z $repoId ]; then - Bail "Please specify repository ID. Run -listrepos for a list of IDs" - fi - curl -I -k -X DELETE "$baseurl/v1/repositories/$repoId" -} - -# Delete the specified package -function DeletePackage -{ - packageId=$1 - if [ -z $packageId ]; then - Bail "Please specify package ID. Run -listpkgs for a list of IDs" - fi - echo Removing pkgId $packageId from repo $repositoryId - curl -I -k -X DELETE "$baseurl/v1/packages/$packageId" -} - -# Parse params -# Not using getopts because this uses multi-char flags -operation= -while (( "$#" )); do - if [[ "$1" == "-config" ]]; then - shift - configFile="$1" - elif [[ "$1" == "-listrepos" ]]; then - operation=ListRepositories - elif [[ "$1" == "-listpkgs" ]]; then - operation=ListPackages - if [ -n "$2" ]; then - shift - operand="$1" - fi - elif [[ "$1" == "-addrepo" ]]; then - operation=AddRepo - shift - operand="$1" - elif [[ "$1" == "-addpkg" ]]; then - operation=AddPackage - shift - operand="$1" - elif [[ "$1" == "-addpkgs" ]]; then - operation=AddPackages - shift - operand="$1" - elif [[ "$1" == "-addfile" ]]; then - operation=AddFile - shift - operand="$1" - elif [[ "$1" == "-uploadfile" ]]; then - operation=UploadFile - shift - operand="$1" - elif [[ "$1" == "-listfiles" ]]; then - operation=ListFiles - elif [[ "$1" == "-delfile" ]]; then - operation=DeleteFile - shift - operand="$1" - elif [[ "$1" == "-check" ]]; then - operation=CheckUpload - shift - operand="$1" - elif [[ "$1" == "-delrepo" ]]; then - operation=DeleteRepo - shift - operand="$1" - elif [[ "$1" == "-delpkg" ]]; then - operation=DeletePackage - shift - operand="$1" - else - Usage - fi - shift -done - -echo "Performing $operation $operand" -# Parse config file -ParseConfigFile "$configFile" - -# Exit if no operation was specified -if [ -z "operation" ]; then - Usage -fi - -$operation "$operand" diff --git a/build/tfs/linux/x64/Dockerfile b/build/tfs/linux/x64/Dockerfile deleted file mode 100644 index ae9190c29..000000000 --- a/build/tfs/linux/x64/Dockerfile +++ /dev/null @@ -1,46 +0,0 @@ -FROM microsoft/vsts-agent:ubuntu-14.04-standard -MAINTAINER Joao Moreno - -ARG DEBIAN_FRONTEND=noninteractive -RUN apt-get update - -# Dependencies -RUN apt-get install -y build-essential -RUN apt-get install -y gcc-multilib g++-multilib -RUN apt-get install -y git -RUN apt-get install -y dpkg-dev -RUN apt-get install -y zip -RUN apt-get install -y rpm -RUN apt-get install -y createrepo -RUN apt-get install -y python-gtk2 -RUN apt-get install -y jq -RUN apt-get install -y xvfb -RUN apt-get install -y fakeroot -RUN apt-get install -y libgtk2.0-0 -RUN apt-get install -y libgconf-2-4 -RUN apt-get install -y libnss3 -RUN apt-get install -y libasound2 -RUN apt-get install -y libxtst6 -RUN apt-get install -y libfuse2 -RUN apt-get install -y libnotify-bin -RUN apt-get install -y libx11-dev -RUN apt-get install -y libxss1 -RUN apt-get install -y libx11-xcb-dev -RUN apt-get install -y libxkbfile-dev -RUN apt-get install -y bc bsdmainutils -RUN apt-get install -y libsecret-1-dev - -# Xvfb -# Thanks https://medium.com/@griggheo/running-headless-selenium-webdriver-tests-in-docker-containers-342fdbabf756 -ADD xvfb.init /etc/init.d/xvfb -RUN chmod +x /etc/init.d/xvfb -RUN update-rc.d xvfb defaults - -# dbus -RUN ln -sf /bin/dbus-daemon /usr/bin/dbus-daemon - -# nvm -ENV NVM_DIR /usr/local/nvm -RUN curl https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash - -CMD (service xvfb start; service dbus start; export DISPLAY=:10; ./start.sh) \ No newline at end of file diff --git a/build/tfs/linux/x64/run-agent.sh b/build/tfs/linux/x64/run-agent.sh deleted file mode 100755 index 76978ce2b..000000000 --- a/build/tfs/linux/x64/run-agent.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -if [ ! -f pat ]; then - echo "Error: file pat not found" - exit 1 -fi - -docker run \ - -e VSTS_ACCOUNT="monacotools" \ - -e VSTS_TOKEN="$(cat pat)" \ - -e VSTS_AGENT="tb-lnx-x64-local" \ - -e VSTS_POOL="linux-x64" \ - -e VSTS_WORK="/var/vsts/work" \ - --name "tb-lnx-x64-local" \ - -it joaomoreno/vscode-vso-agent-x64:latest \ No newline at end of file diff --git a/build/tfs/linux/x64/xvfb.init b/build/tfs/linux/x64/xvfb.init deleted file mode 100644 index 4d77d253a..000000000 --- a/build/tfs/linux/x64/xvfb.init +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash -# -# /etc/rc.d/init.d/xvfbd -# -# chkconfig: 345 95 28 -# description: Starts/Stops X Virtual Framebuffer server -# processname: Xvfb -# -### BEGIN INIT INFO -# Provides: xvfb -# Required-Start: $remote_fs $syslog -# Required-Stop: $remote_fs $syslog -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Start xvfb at boot time -# Description: Enable xvfb provided by daemon. -### END INIT INFO - -[ "${NETWORKING}" = "no" ] && exit 0 - -PROG="/usr/bin/Xvfb" -PROG_OPTIONS=":10 -ac" -PROG_OUTPUT="/tmp/Xvfb.out" - -case "$1" in - start) - echo "Starting : X Virtual Frame Buffer " - $PROG $PROG_OPTIONS>>$PROG_OUTPUT 2>&1 & - disown -ar - ;; - stop) - echo "Shutting down : X Virtual Frame Buffer" - killproc $PROG - RETVAL=$? - [ $RETVAL -eq 0 ] && /bin/rm -f /var/lock/subsys/Xvfb - /var/run/Xvfb.pid - echo - ;; - restart|reload) - $0 stop - $0 start - RETVAL=$? - ;; - status) - status Xvfb - RETVAL=$? - ;; - *) - echo $"Usage: $0 (start|stop|restart|reload|status)" - exit 1 -esac - -exit $RETVAL \ No newline at end of file diff --git a/build/win32/Cargo.lock b/build/win32/Cargo.lock new file mode 100644 index 000000000..dcc1440b3 --- /dev/null +++ b/build/win32/Cargo.lock @@ -0,0 +1,256 @@ +[[package]] +name = "build_const" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "chrono" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crc" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "build_const 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "inno_updater" +version = "0.7.1" +dependencies = [ + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "slog 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "slog-async 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "slog-term 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "isatty" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazy_static" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", + "num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-integer" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-iter" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_syscall" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_termios" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "slog" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "slog-async" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "slog 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "take_mut 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "slog-term" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "isatty 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "slog 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "take_mut" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "term" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "termion" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thread_local" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.1.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum build_const 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e90dc84f5e62d2ebe7676b83c22d33b6db8bd27340fb6ffbff0a364efa0cb9c9" +"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" +"checksum chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c20ebe0b2b08b0aeddba49c609fe7957ba2e33449882cb186a180bc60682fa9" +"checksum crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd5d02c0aac6bd68393ed69e00bbc2457f3e89075c6349db7189618dc4ddc1d7" +"checksum isatty 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8f2a233726c7bb76995cec749d59582e5664823b7245d4970354408f1d79a7a2" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" +"checksum libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1e5d97d6708edaa407429faa671b942dc0f2727222fb6b6539bf1db936e4b121" +"checksum num 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "cc4083e14b542ea3eb9b5f33ff48bd373a92d78687e74f4cc0a30caeb754f0ca" +"checksum num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "d1452e8b06e448a07f0e6ebb0bb1d92b8890eea63288c0b627331d53514d0fba" +"checksum num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "7485fcc84f85b4ecd0ea527b14189281cf27d60e583ae65ebc9c088b13dffe01" +"checksum num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "9936036cc70fe4a8b2d338ab665900323290efb03983c86cbe235ae800ad8017" +"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +"checksum slog 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0a6b13b17f4225771f7f15cece704a4e68d3a5f31278ed26367f497133398a18" +"checksum slog-async 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e319a30c08b004618d5f7ca2f2b1dad7b4623ba7fcb1a12846fc3b01e9eaa10" +"checksum slog-term 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5bb5d9360b2b279b326824b3b4ca2402ead8a8138f0e5ec1900605c861bb6671" +"checksum take_mut 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50b910a1174df4aeb5738e8a0e7253883cf7801de40d094175a5a557e487f4c5" +"checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" +"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" +"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" +"checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098" +"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/build/win32/OSSREADME.json b/build/win32/OSSREADME.json deleted file mode 100755 index e895321f1..000000000 --- a/build/win32/OSSREADME.json +++ /dev/null @@ -1,1098 +0,0 @@ -[ - { - "name": "Amanieu/thread_local-rs", - "version": "0.3.5", - "repositoryUrl": "https://github.com/Amanieu/thread_local-rs", - "licenseDetail": [ - "Copyright (c) 2016 The Rust Project Developers", - "", - "Permission is hereby granted, free of charge, to any", - "person obtaining a copy of this software and associated", - "documentation files (the \"Software\"), to deal in the", - "Software without restriction, including without", - "limitation the rights to use, copy, modify, merge,", - "publish, distribute, sublicense, and/or sell copies of", - "the Software, and to permit persons to whom the Software", - "is furnished to do so, subject to the following", - "conditions:", - "", - "The above copyright notice and this permission notice", - "shall be included in all copies or substantial portions", - "of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF", - "ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED", - "TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A", - "PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT", - "SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY", - "CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", - "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR", - "IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER", - "DEALINGS IN THE SOFTWARE." - ], - "isProd": true - }, - { - "name": "BurntSushi/byteorder", - "version": "1.2.1", - "repositoryUrl": "https://github.com/BurntSushi/byteorder", - "licenseDetail": [ - "The MIT License (MIT)", - "", - "Copyright (c) 2015 Andrew Gallant", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in", - "all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN", - "THE SOFTWARE." - ], - "isProd": true - }, - { - "name": "Sgeo/take_mut", - "version": "0.2.0", - "repositoryUrl": "https://github.com/Sgeo/take_mut", - "licenseDetail": [ - "The MIT License (MIT)", - "", - "Copyright (c) 2016 Sgeo", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all", - "copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", - "SOFTWARE." - ], - "isProd": true - }, - { - "name": "Stebalien/term", - "version": "0.4.6", - "repositoryUrl": "https://github.com/Stebalien/term", - "licenseDetail": [ - "Copyright (c) 2014 The Rust Project Developers", - "", - "Permission is hereby granted, free of charge, to any", - "person obtaining a copy of this software and associated", - "documentation files (the \"Software\"), to deal in the", - "Software without restriction, including without", - "limitation the rights to use, copy, modify, merge,", - "publish, distribute, sublicense, and/or sell copies of", - "the Software, and to permit persons to whom the Software", - "is furnished to do so, subject to the following", - "conditions:", - "", - "The above copyright notice and this permission notice", - "shall be included in all copies or substantial portions", - "of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF", - "ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED", - "TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A", - "PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT", - "SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY", - "CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", - "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR", - "IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER", - "DEALINGS IN THE SOFTWARE." - ], - "isProd": true - }, - { - "name": "chronotope/chrono", - "version": "0.4.0", - "repositoryUrl": "https://github.com/chronotope/chrono", - "licenseDetail": [ - "Rust-chrono is dual-licensed under The MIT License [1] and", - "Apache 2.0 License [2]. Copyright (c) 2014--2017, Kang Seonghoon and", - "contributors.", - "", - "Nota Bene: This is same as the Rust Project's own license.", - "", - "", - "[1]: , which is reproduced below:", - "", - "~~~~", - "The MIT License (MIT)", - "", - "Copyright (c) 2014, Kang Seonghoon.", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in", - "all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN", - "THE SOFTWARE.", - "~~~~", - "", - "", - "[2]: , which is reproduced below:", - "", - "~~~~", - " Apache License", - " Version 2.0, January 2004", - " http://www.apache.org/licenses/", - "", - "TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION", - "", - "1. Definitions.", - "", - " \"License\" shall mean the terms and conditions for use, reproduction,", - " and distribution as defined by Sections 1 through 9 of this document.", - "", - " \"Licensor\" shall mean the copyright owner or entity authorized by", - " the copyright owner that is granting the License.", - "", - " \"Legal Entity\" shall mean the union of the acting entity and all", - " other entities that control, are controlled by, or are under common", - " control with that entity. For the purposes of this definition,", - " \"control\" means (i) the power, direct or indirect, to cause the", - " direction or management of such entity, whether by contract or", - " otherwise, or (ii) ownership of fifty percent (50%) or more of the", - " outstanding shares, or (iii) beneficial ownership of such entity.", - "", - " \"You\" (or \"Your\") shall mean an individual or Legal Entity", - " exercising permissions granted by this License.", - "", - " \"Source\" form shall mean the preferred form for making modifications,", - " including but not limited to software source code, documentation", - " source, and configuration files.", - "", - " \"Object\" form shall mean any form resulting from mechanical", - " transformation or translation of a Source form, including but", - " not limited to compiled object code, generated documentation,", - " and conversions to other media types.", - "", - " \"Work\" shall mean the work of authorship, whether in Source or", - " Object form, made available under the License, as indicated by a", - " copyright notice that is included in or attached to the work", - " (an example is provided in the Appendix below).", - "", - " \"Derivative Works\" shall mean any work, whether in Source or Object", - " form, that is based on (or derived from) the Work and for which the", - " editorial revisions, annotations, elaborations, or other modifications", - " represent, as a whole, an original work of authorship. For the purposes", - " of this License, Derivative Works shall not include works that remain", - " separable from, or merely link (or bind by name) to the interfaces of,", - " the Work and Derivative Works thereof.", - "", - " \"Contribution\" shall mean any work of authorship, including", - " the original version of the Work and any modifications or additions", - " to that Work or Derivative Works thereof, that is intentionally", - " submitted to Licensor for inclusion in the Work by the copyright owner", - " or by an individual or Legal Entity authorized to submit on behalf of", - " the copyright owner. For the purposes of this definition, \"submitted\"", - " means any form of electronic, verbal, or written communication sent", - " to the Licensor or its representatives, including but not limited to", - " communication on electronic mailing lists, source code control systems,", - " and issue tracking systems that are managed by, or on behalf of, the", - " Licensor for the purpose of discussing and improving the Work, but", - " excluding communication that is conspicuously marked or otherwise", - " designated in writing by the copyright owner as \"Not a Contribution.\"", - "", - " \"Contributor\" shall mean Licensor and any individual or Legal Entity", - " on behalf of whom a Contribution has been received by Licensor and", - " subsequently incorporated within the Work.", - "", - "2. Grant of Copyright License. Subject to the terms and conditions of", - " this License, each Contributor hereby grants to You a perpetual,", - " worldwide, non-exclusive, no-charge, royalty-free, irrevocable", - " copyright license to reproduce, prepare Derivative Works of,", - " publicly display, publicly perform, sublicense, and distribute the", - " Work and such Derivative Works in Source or Object form.", - "", - "3. Grant of Patent License. Subject to the terms and conditions of", - " this License, each Contributor hereby grants to You a perpetual,", - " worldwide, non-exclusive, no-charge, royalty-free, irrevocable", - " (except as stated in this section) patent license to make, have made,", - " use, offer to sell, sell, import, and otherwise transfer the Work,", - " where such license applies only to those patent claims licensable", - " by such Contributor that are necessarily infringed by their", - " Contribution(s) alone or by combination of their Contribution(s)", - " with the Work to which such Contribution(s) was submitted. If You", - " institute patent litigation against any entity (including a", - " cross-claim or counterclaim in a lawsuit) alleging that the Work", - " or a Contribution incorporated within the Work constitutes direct", - " or contributory patent infringement, then any patent licenses", - " granted to You under this License for that Work shall terminate", - " as of the date such litigation is filed.", - "", - "4. Redistribution. You may reproduce and distribute copies of the", - " Work or Derivative Works thereof in any medium, with or without", - " modifications, and in Source or Object form, provided that You", - " meet the following conditions:", - "", - " (a) You must give any other recipients of the Work or", - " Derivative Works a copy of this License; and", - "", - " (b) You must cause any modified files to carry prominent notices", - " stating that You changed the files; and", - "", - " (c) You must retain, in the Source form of any Derivative Works", - " that You distribute, all copyright, patent, trademark, and", - " attribution notices from the Source form of the Work,", - " excluding those notices that do not pertain to any part of", - " the Derivative Works; and", - "", - " (d) If the Work includes a \"NOTICE\" text file as part of its", - " distribution, then any Derivative Works that You distribute must", - " include a readable copy of the attribution notices contained", - " within such NOTICE file, excluding those notices that do not", - " pertain to any part of the Derivative Works, in at least one", - " of the following places: within a NOTICE text file distributed", - " as part of the Derivative Works; within the Source form or", - " documentation, if provided along with the Derivative Works; or,", - " within a display generated by the Derivative Works, if and", - " wherever such third-party notices normally appear. The contents", - " of the NOTICE file are for informational purposes only and", - " do not modify the License. You may add Your own attribution", - " notices within Derivative Works that You distribute, alongside", - " or as an addendum to the NOTICE text from the Work, provided", - " that such additional attribution notices cannot be construed", - " as modifying the License.", - "", - " You may add Your own copyright statement to Your modifications and", - " may provide additional or different license terms and conditions", - " for use, reproduction, or distribution of Your modifications, or", - " for any such Derivative Works as a whole, provided Your use,", - " reproduction, and distribution of the Work otherwise complies with", - " the conditions stated in this License.", - "", - "5. Submission of Contributions. Unless You explicitly state otherwise,", - " any Contribution intentionally submitted for inclusion in the Work", - " by You to the Licensor shall be under the terms and conditions of", - " this License, without any additional terms or conditions.", - " Notwithstanding the above, nothing herein shall supersede or modify", - " the terms of any separate license agreement you may have executed", - " with Licensor regarding such Contributions.", - "", - "6. Trademarks. This License does not grant permission to use the trade", - " names, trademarks, service marks, or product names of the Licensor,", - " except as required for reasonable and customary use in describing the", - " origin of the Work and reproducing the content of the NOTICE file.", - "", - "7. Disclaimer of Warranty. Unless required by applicable law or", - " agreed to in writing, Licensor provides the Work (and each", - " Contributor provides its Contributions) on an \"AS IS\" BASIS,", - " WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or", - " implied, including, without limitation, any warranties or conditions", - " of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A", - " PARTICULAR PURPOSE. You are solely responsible for determining the", - " appropriateness of using or redistributing the Work and assume any", - " risks associated with Your exercise of permissions under this License.", - "", - "8. Limitation of Liability. In no event and under no legal theory,", - " whether in tort (including negligence), contract, or otherwise,", - " unless required by applicable law (such as deliberate and grossly", - " negligent acts) or agreed to in writing, shall any Contributor be", - " liable to You for damages, including any direct, indirect, special,", - " incidental, or consequential damages of any character arising as a", - " result of this License or out of the use or inability to use the", - " Work (including but not limited to damages for loss of goodwill,", - " work stoppage, computer failure or malfunction, or any and all", - " other commercial damages or losses), even if such Contributor", - " has been advised of the possibility of such damages.", - "", - "9. Accepting Warranty or Additional Liability. While redistributing", - " the Work or Derivative Works thereof, You may choose to offer,", - " and charge a fee for, acceptance of support, warranty, indemnity,", - " or other liability obligations and/or rights consistent with this", - " License. However, in accepting such obligations, You may act only", - " on Your own behalf and on Your sole responsibility, not on behalf", - " of any other Contributor, and only if You agree to indemnify,", - " defend, and hold each Contributor harmless for any liability", - " incurred by, or claims asserted against, such Contributor by reason", - " of your accepting any such warranty or additional liability.", - "", - "END OF TERMS AND CONDITIONS", - "", - "APPENDIX: How to apply the Apache License to your work.", - "", - " To apply the Apache License to your work, attach the following", - " boilerplate notice, with the fields enclosed by brackets \"[]\"", - " replaced with your own identifying information. (Don't include", - " the brackets!) The text should be enclosed in the appropriate", - " comment syntax for the file format. We also recommend that a", - " file or class name and description of purpose be included on the", - " same \"printed page\" as the copyright notice for easier", - " identification within third-party archives.", - "", - "Copyright [yyyy] [name of copyright owner]", - "", - "Licensed under the Apache License, Version 2.0 (the \"License\");", - "you may not use this file except in compliance with the License.", - "You may obtain a copy of the License at", - "", - "\thttp://www.apache.org/licenses/LICENSE-2.0", - "", - "Unless required by applicable law or agreed to in writing, software", - "distributed under the License is distributed on an \"AS IS\" BASIS,", - "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", - "See the License for the specific language governing permissions and", - "limitations under the License.", - "~~~~" - ], - "isProd": true - }, - { - "name": "dtolnay/isatty", - "version": "0.1.6", - "repositoryUrl": "https://github.com/dtolnay/isatty", - "licenseDetail": [ - "Copyright (c) 2016", - "", - "Permission is hereby granted, free of charge, to any", - "person obtaining a copy of this software and associated", - "documentation files (the \"Software\"), to deal in the", - "Software without restriction, including without", - "limitation the rights to use, copy, modify, merge,", - "publish, distribute, sublicense, and/or sell copies of", - "the Software, and to permit persons to whom the Software", - "is furnished to do so, subject to the following", - "conditions:", - "", - "The above copyright notice and this permission notice", - "shall be included in all copies or substantial portions", - "of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF", - "ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED", - "TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A", - "PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT", - "SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY", - "CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", - "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR", - "IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER", - "DEALINGS IN THE SOFTWARE." - ], - "isProd": true - }, - { - "name": "mrhooray/crc-rs", - "version": "1.7.0", - "repositoryUrl": "https://github.com/mrhooray/crc-rs.git", - "licenseDetail": [ - "MIT License", - "", - "Copyright (c) 2017 crc-rs Developers", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all", - "copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", - "SOFTWARE." - ], - "isProd": true - }, - { - "name": "redox-os/syscall", - "version": "0.1.37", - "repositoryUrl": "https://github.com/redox-os/syscall", - "licenseDetail": [ - "Copyright (c) 2017 Redox OS Developers", - "", - "MIT License", - "", - "Permission is hereby granted, free of charge, to any person obtaining", - "a copy of this software and associated documentation files (the", - "\"Software\"), to deal in the Software without restriction, including", - "without limitation the rights to use, copy, modify, merge, publish,", - "distribute, sublicense, and/or sell copies of the Software, and to", - "permit persons to whom the Software is furnished to do so, subject to", - "the following conditions:", - "", - "The above copyright notice and this permission notice shall be", - "included in all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,", - "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", - "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND", - "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE", - "LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", - "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION", - "WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." - ], - "isProd": true - }, - { - "name": "redox-os/termios", - "version": "0.1.1", - "repositoryUrl": "https://github.com/redox-os/termios", - "licenseDetail": [ - "MIT License", - "", - "Copyright (c) 2017 Redox OS", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all", - "copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", - "SOFTWARE." - ], - "isProd": true - }, - { - "name": "reem/rust-unreachable", - "version": "1.0.0", - "repositoryUrl": "https://github.com/reem/rust-unreachable.git", - "licenseDetail": [ - "Copyright (c) 2015 The rust-unreachable Developers", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all", - "copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", - "SOFTWARE." - ], - "isProd": true - }, - { - "name": "reem/rust-void", - "version": "1.0.2", - "repositoryUrl": "https://github.com/reem/rust-void.git", - "licenseDetail": [ - "Copyright (c) 2015 The rust-void Developers", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all", - "copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", - "SOFTWARE." - ], - "isProd": true - }, - { - "name": "retep998/winapi-rs", - "version": "0.1.1", - "repositoryUrl": "https://github.com/retep998/winapi-rs", - "licenseDetail": [ - "Copyright (c) 2015 The winapi-rs Developers", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all", - "copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", - "SOFTWARE." - ], - "isProd": true - }, - { - "name": "retep998/winapi-rs", - "version": "0.2.2", - "repositoryUrl": "https://github.com/retep998/winapi-rs", - "licenseDetail": [ - "Copyright (c) 2015 The winapi-rs Developers", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all", - "copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", - "SOFTWARE." - ], - "isProd": true - }, - { - "name": "retep998/winapi-rs", - "version": "0.2.8", - "repositoryUrl": "https://github.com/retep998/winapi-rs", - "licenseDetail": [ - "Copyright (c) 2015 The winapi-rs Developers", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all", - "copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", - "SOFTWARE." - ], - "isProd": true - }, - { - "name": "retep998/winapi-rs", - "version": "0.3.4", - "repositoryUrl": "https://github.com/retep998/winapi-rs", - "licenseDetail": [ - "Copyright (c) 2015 The winapi-rs Developers", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all", - "copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", - "SOFTWARE." - ], - "isProd": true - }, - { - "name": "retep998/winapi-rs", - "version": "0.4.0", - "repositoryUrl": "https://github.com/retep998/winapi-rs", - "licenseDetail": [ - "Copyright (c) 2015 The winapi-rs Developers", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all", - "copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", - "SOFTWARE." - ], - "isProd": true - }, - { - "name": "retep998/winapi-rs", - "version": "0.4.0", - "repositoryUrl": "https://github.com/retep998/winapi-rs", - "licenseDetail": [ - "Copyright (c) 2015 The winapi-rs Developers", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all", - "copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", - "SOFTWARE." - ], - "isProd": true - }, - { - "name": "rust-lang-nursery/lazy-static.rs", - "version": "1.0.0", - "repositoryUrl": "https://github.com/rust-lang-nursery/lazy-static.rs", - "licenseDetail": [ - "Copyright (c) 2010 The Rust Project Developers", - "", - "Permission is hereby granted, free of charge, to any", - "person obtaining a copy of this software and associated", - "documentation files (the \"Software\"), to deal in the", - "Software without restriction, including without", - "limitation the rights to use, copy, modify, merge,", - "publish, distribute, sublicense, and/or sell copies of", - "the Software, and to permit persons to whom the Software", - "is furnished to do so, subject to the following", - "conditions:", - "", - "The above copyright notice and this permission notice", - "shall be included in all copies or substantial portions", - "of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF", - "ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED", - "TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A", - "PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT", - "SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY", - "CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", - "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR", - "IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER", - "DEALINGS IN THE SOFTWARE." - ], - "isProd": true - }, - { - "name": "rust-lang/libc", - "version": "0.2.36", - "repositoryUrl": "https://github.com/rust-lang/libc", - "licenseDetail": [ - "Copyright (c) 2014 The Rust Project Developers", - "", - "Permission is hereby granted, free of charge, to any", - "person obtaining a copy of this software and associated", - "documentation files (the \"Software\"), to deal in the", - "Software without restriction, including without", - "limitation the rights to use, copy, modify, merge,", - "publish, distribute, sublicense, and/or sell copies of", - "the Software, and to permit persons to whom the Software", - "is furnished to do so, subject to the following", - "conditions:", - "", - "The above copyright notice and this permission notice", - "shall be included in all copies or substantial portions", - "of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF", - "ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED", - "TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A", - "PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT", - "SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY", - "CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", - "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR", - "IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER", - "DEALINGS IN THE SOFTWARE." - ], - "isProd": true - }, - { - "name": "rust-lang/time", - "version": "0.1.39", - "repositoryUrl": "https://github.com/rust-lang/time", - "licenseDetail": [ - "Copyright (c) 2014 The Rust Project Developers", - "", - "Permission is hereby granted, free of charge, to any", - "person obtaining a copy of this software and associated", - "documentation files (the \"Software\"), to deal in the", - "Software without restriction, including without", - "limitation the rights to use, copy, modify, merge,", - "publish, distribute, sublicense, and/or sell copies of", - "the Software, and to permit persons to whom the Software", - "is furnished to do so, subject to the following", - "conditions:", - "", - "The above copyright notice and this permission notice", - "shall be included in all copies or substantial portions", - "of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF", - "ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED", - "TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A", - "PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT", - "SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY", - "CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", - "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR", - "IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER", - "DEALINGS IN THE SOFTWARE." - ], - "isProd": true - }, - { - "name": "rust-num/num", - "version": "0.1.41", - "repositoryUrl": "https://github.com/rust-num/num", - "licenseDetail": [ - "Copyright (c) 2014 The Rust Project Developers", - "", - "Permission is hereby granted, free of charge, to any", - "person obtaining a copy of this software and associated", - "documentation files (the \"Software\"), to deal in the", - "Software without restriction, including without", - "limitation the rights to use, copy, modify, merge,", - "publish, distribute, sublicense, and/or sell copies of", - "the Software, and to permit persons to whom the Software", - "is furnished to do so, subject to the following", - "conditions:", - "", - "The above copyright notice and this permission notice", - "shall be included in all copies or substantial portions", - "of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF", - "ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED", - "TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A", - "PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT", - "SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY", - "CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", - "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR", - "IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER", - "DEALINGS IN THE SOFTWARE." - ], - "isProd": true - }, - { - "name": "rust-num/num-integer", - "version": "0.1.35", - "repositoryUrl": "https://github.com/rust-num/num-integer", - "licenseDetail": [ - "Copyright (c) 2014 The Rust Project Developers", - "", - "Permission is hereby granted, free of charge, to any", - "person obtaining a copy of this software and associated", - "documentation files (the \"Software\"), to deal in the", - "Software without restriction, including without", - "limitation the rights to use, copy, modify, merge,", - "publish, distribute, sublicense, and/or sell copies of", - "the Software, and to permit persons to whom the Software", - "is furnished to do so, subject to the following", - "conditions:", - "", - "The above copyright notice and this permission notice", - "shall be included in all copies or substantial portions", - "of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF", - "ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED", - "TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A", - "PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT", - "SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY", - "CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", - "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR", - "IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER", - "DEALINGS IN THE SOFTWARE." - ], - "isProd": true - }, - { - "name": "rust-num/num-iter", - "version": "0.1.34", - "repositoryUrl": "https://github.com/rust-num/num-iter", - "licenseDetail": [ - "Copyright (c) 2014 The Rust Project Developers", - "", - "Permission is hereby granted, free of charge, to any", - "person obtaining a copy of this software and associated", - "documentation files (the \"Software\"), to deal in the", - "Software without restriction, including without", - "limitation the rights to use, copy, modify, merge,", - "publish, distribute, sublicense, and/or sell copies of", - "the Software, and to permit persons to whom the Software", - "is furnished to do so, subject to the following", - "conditions:", - "", - "The above copyright notice and this permission notice", - "shall be included in all copies or substantial portions", - "of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF", - "ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED", - "TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A", - "PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT", - "SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY", - "CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", - "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR", - "IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER", - "DEALINGS IN THE SOFTWARE." - ], - "isProd": true - }, - { - "name": "rust-num/num-traits", - "version": "0.1.42", - "repositoryUrl": "https://github.com/rust-num/num-traits", - "licenseDetail": [ - "Copyright (c) 2014 The Rust Project Developers", - "", - "Permission is hereby granted, free of charge, to any", - "person obtaining a copy of this software and associated", - "documentation files (the \"Software\"), to deal in the", - "Software without restriction, including without", - "limitation the rights to use, copy, modify, merge,", - "publish, distribute, sublicense, and/or sell copies of", - "the Software, and to permit persons to whom the Software", - "is furnished to do so, subject to the following", - "conditions:", - "", - "The above copyright notice and this permission notice", - "shall be included in all copies or substantial portions", - "of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF", - "ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED", - "TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A", - "PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT", - "SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY", - "CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", - "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR", - "IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER", - "DEALINGS IN THE SOFTWARE." - ], - "isProd": true - }, - { - "name": "slog-rs/async", - "version": "2.2.0", - "repositoryUrl": "https://github.com/slog-rs/async", - "licenseDetail": [ - "Copyright (c) 2014 The Rust Project Developers", - "", - "Permission is hereby granted, free of charge, to any", - "person obtaining a copy of this software and associated", - "documentation files (the \"Software\"), to deal in the", - "Software without restriction, including without", - "limitation the rights to use, copy, modify, merge,", - "publish, distribute, sublicense, and/or sell copies of", - "the Software, and to permit persons to whom the Software", - "is furnished to do so, subject to the following", - "conditions:", - "", - "The above copyright notice and this permission notice", - "shall be included in all copies or substantial portions", - "of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF", - "ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED", - "TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A", - "PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT", - "SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY", - "CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", - "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR", - "IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER", - "DEALINGS IN THE SOFTWARE." - ], - "isProd": true - }, - { - "name": "slog-rs/slog", - "version": "2.1.1", - "repositoryUrl": "https://github.com/slog-rs/slog", - "licenseDetail": [ - "Copyright (c) 2014 The Rust Project Developers", - "", - "Permission is hereby granted, free of charge, to any", - "person obtaining a copy of this software and associated", - "documentation files (the \"Software\"), to deal in the", - "Software without restriction, including without", - "limitation the rights to use, copy, modify, merge,", - "publish, distribute, sublicense, and/or sell copies of", - "the Software, and to permit persons to whom the Software", - "is furnished to do so, subject to the following", - "conditions:", - "", - "The above copyright notice and this permission notice", - "shall be included in all copies or substantial portions", - "of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF", - "ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED", - "TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A", - "PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT", - "SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY", - "CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", - "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR", - "IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER", - "DEALINGS IN THE SOFTWARE." - ], - "isProd": true - }, - { - "name": "slog-rs/term", - "version": "2.3.0", - "repositoryUrl": "https://github.com/slog-rs/term", - "licenseDetail": [ - "Copyright (c) 2014 The Rust Project Developers", - "", - "Permission is hereby granted, free of charge, to any", - "person obtaining a copy of this software and associated", - "documentation files (the \"Software\"), to deal in the", - "Software without restriction, including without", - "limitation the rights to use, copy, modify, merge,", - "publish, distribute, sublicense, and/or sell copies of", - "the Software, and to permit persons to whom the Software", - "is furnished to do so, subject to the following", - "conditions:", - "", - "The above copyright notice and this permission notice", - "shall be included in all copies or substantial portions", - "of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF", - "ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED", - "TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A", - "PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT", - "SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY", - "CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", - "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR", - "IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER", - "DEALINGS IN THE SOFTWARE." - ], - "isProd": true - }, - { - "name": "ticki/termion", - "version": "1.5.1", - "repositoryUrl": "https://github.com/ticki/termion", - "licenseDetail": [ - "The MIT License (MIT)", - "", - "Copyright (c) 2016 Ticki", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all", - "copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", - "SOFTWARE." - ], - "isProd": true - }, - { - "name": "vitiral/build_const", - "version": "0.2.0", - "repositoryUrl": "https://github.com/vitiral/build_const", - "licenseDetail": [ - "The MIT License (MIT)", - "", - "Copyright (c) 2017 Garrett Berg, vitiral@gmail.com", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in", - "all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN", - "THE SOFTWARE." - ], - "isProd": true - } -] diff --git a/build/yarn.lock b/build/yarn.lock index 9ec0f9fe9..45a096572 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -151,6 +151,16 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== +"@types/minimist@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.0.tgz#69a23a3ad29caf0097f06eda59b361ee2f0639f6" + integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY= + +"@types/mocha@2.2.39": + version "2.2.39" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.39.tgz#f68d63db8b69c38e9558b4073525cf96c4f7a829" + integrity sha1-9o1j24tpw46VWLQHNSXPlsT3qCk= + "@types/node@*": version "8.0.51" resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.51.tgz#b31d716fb8d58eeb95c068a039b9b6292817d5fb" @@ -318,6 +328,22 @@ ansi-wrap@0.1.0: resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= +applicationinsights@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.6.tgz#bc201810de91cea910dab34e8ad35ecde488edeb" + integrity sha512-VQT3kBpJVPw5fCO5n+WUeSx0VHjxFtD7znYbILBlVgOS9/cMDuGFmV2Br3ObzFyZUDGNbEfW36fD1y2/vAiCKw== + dependencies: + diagnostic-channel "0.2.0" + diagnostic-channel-publishers "0.2.1" + zone.js "0.7.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" + array-differ@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" @@ -390,6 +416,15 @@ azure-storage@^2.1.0: xml2js "0.2.7" xmlbuilder "0.4.3" +babel-code-frame@^6.22.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.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" @@ -412,6 +447,11 @@ binary-search-bounds@2.0.3: resolved "https://registry.yarnpkg.com/binary-search-bounds/-/binary-search-bounds-2.0.3.tgz#5ff8616d6dd2ca5388bc85b2d6266e2b9da502dc" integrity sha1-X/hhbW3SylOIvIWy1iZuK52lAtw= +boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + boom@2.x.x: version "2.10.1" resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" @@ -446,12 +486,22 @@ browserify-mime@~1.2.9: resolved "https://registry.yarnpkg.com/browserify-mime/-/browserify-mime-1.2.9.tgz#aeb1af28de6c0d7a6a2ce40adb68ff18422af31f" integrity sha1-rrGvKN5sDXpqLOQK22j/GEIq8x8= +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + +builtin-modules@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chalk@^1.0.0: +chalk@^1.0.0, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= @@ -462,7 +512,7 @@ chalk@^1.0.0: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.2.0: +chalk@^2.2.0, chalk@^2.3.0: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ== @@ -471,6 +521,18 @@ chalk@^2.2.0: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +cheerio@^1.0.0-rc.1: + version "1.0.0-rc.2" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.2.tgz#4b9f53a81b27e4d5dac31c0ffd0cfa03cc6830db" + integrity sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs= + dependencies: + css-select "~1.2.0" + dom-serializer "~0.1.0" + entities "~1.1.1" + htmlparser2 "^3.9.1" + lodash "^4.15.0" + parse5 "^3.0.1" + clone-stats@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" @@ -522,6 +584,11 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" +commander@^2.12.1, commander@^2.8.1: + version "2.19.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" + integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -553,6 +620,21 @@ cryptiles@3.x.x: dependencies: boom "5.x.x" +css-select@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + +css-what@2.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.2.tgz#c0876d9d0480927d7d4920dcd72af3595649554d" + integrity sha512-wan8dMWQ0GUeF7DGEPVjhHemVW/vy6xUYmFzRY8RYqgA0JtXC9rJmbScBjqSu6dg9q0lwPQy6ZAmJVr3PPTvqQ== + css@2.X: version "2.2.4" resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929" @@ -601,11 +683,33 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +denodeify@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631" + integrity sha1-OjYof1A05pnnV3kBBSwubJQlFjE= + detect-newline@2.X: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= +diagnostic-channel-publishers@0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3" + integrity sha1-ji1geottef6IC1SLxYzGvrKIxPM= + +diagnostic-channel@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-0.2.0.tgz#cc99af9612c23fb1fff13612c72f2cbfaa8d5a17" + integrity sha1-zJmvlhLCP7H/8TYSxy8sv6qNWhc= + dependencies: + semver "^5.3.0" + +diff@^3.2.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + documentdb@1.13.0: version "1.13.0" resolved "https://registry.yarnpkg.com/documentdb/-/documentdb-1.13.0.tgz#bba6f03150b2f42498cec4261bc439d834a33f8b" @@ -616,6 +720,52 @@ documentdb@1.13.0: semaphore "1.0.5" underscore "1.8.3" +dom-serializer@0, dom-serializer@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" + integrity sha1-BzxpdUbOB4DOI75KKOKT5AvDDII= + dependencies: + domelementtype "~1.1.1" + entities "~1.1.1" + +domelementtype@1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.2.1.tgz#578558ef23befac043a1abb0db07635509393479" + integrity sha512-SQVCLFS2E7G5CRCMdn6K9bIhRj1bS6QBWZfF0TUPh4V/BbqrQ619IdSS3/izn0FZ+9l+uODzaZjb08fjOfablA== + +domelementtype@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" + integrity sha1-sXrtguirWeUt2cGbF1bg/BhyBMI= + +domelementtype@~1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" + integrity sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs= + +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== + dependencies: + domelementtype "1" + +domutils@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^1.5.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== + dependencies: + dom-serializer "0" + domelementtype "1" + duplexer2@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" @@ -630,11 +780,26 @@ ecc-jsbn@~0.1.1: dependencies: jsbn "~0.1.0" +entities@^1.1.1, entities@~1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" + integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== + escape-string-regexp@^1.0.2, 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== + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= + extend@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/extend/-/extend-1.2.1.tgz#a0f5fd6cfc83a5fe49ef698d60ec8a624dd4576c" @@ -669,6 +834,13 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -692,6 +864,11 @@ form-data@~2.3.1: combined-stream "1.0.6" mime-types "^2.1.12" +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= + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -709,6 +886,18 @@ github-releases@^0.4.1: prettyjson "1.2.1" request "2.81.0" +glob@^7.0.6, glob@^7.1.1: + 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" + glogg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.1.tgz#dcf758e44789cc3f3d32c1f3562a3676e6a34810" @@ -860,6 +1049,18 @@ hoek@4.x.x: resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb" integrity sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA== +htmlparser2@^3.9.1: + version "3.10.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.0.tgz#5f5e422dcf6119c0d983ed36260ce9ded0bee464" + integrity sha512-J1nEUGv+MkXS0weHNWVKJJ+UrLfePxRWpN3C9bEi9fLxL2+ggW94DQvgYVXsaT30PGwYRIZKNZXuyMhp3Di4bQ== + dependencies: + domelementtype "^1.3.0" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^3.0.6" + http-signature@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" @@ -878,7 +1079,22 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" -inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: +iconv-lite@0.4.23: + version "0.4.23" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" + integrity sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= @@ -908,6 +1124,19 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= + +js-yaml@^3.7.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" + integrity sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -967,6 +1196,13 @@ lazy-debug-legacy@0.0.X: resolved "https://registry.yarnpkg.com/lazy-debug-legacy/-/lazy-debug-legacy-0.0.1.tgz#537716c0776e4cf79e3ed1b621f7658c2911b1b1" integrity sha1-U3cWwHduTPeePtG2IfdljCkRsbE= +linkify-it@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.0.3.tgz#d94a4648f9b1c179d64fa97291268bdb6ce9434f" + integrity sha1-2UpGSPmxwXnWT6lykSaL22zpQ08= + dependencies: + uc.micro "^1.0.1" + lodash._basecopy@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" @@ -1066,6 +1302,22 @@ lodash.templatesettings@^3.0.0: lodash._reinterpolate "^3.0.0" lodash.escape "^3.0.0" +lodash@^4.15.0, lodash@^4.17.10: + version "4.17.11" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" + integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== + +markdown-it@^8.3.1: + version "8.4.2" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.2.tgz#386f98998dc15a37722aa7722084f4020bdd9b54" + integrity sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ== + dependencies: + argparse "^1.0.7" + entities "~1.1.1" + linkify-it "^2.0.0" + mdurl "^1.0.1" + uc.micro "^1.0.5" + md5.js@1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d" @@ -1074,6 +1326,11 @@ md5.js@1.3.4: hash-base "^3.0.0" inherits "^2.0.1" +mdurl@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= + mime-db@~1.30.0: version "1.30.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" @@ -1103,7 +1360,7 @@ mime@^1.3.4: resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== -minimatch@3.0.4: +minimatch@3.0.4, minimatch@^3.0.3, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -1132,6 +1389,11 @@ multipipe@^0.1.2: dependencies: duplexer2 "0.0.2" +mute-stream@~0.0.4: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= + normalize-path@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" @@ -1139,6 +1401,13 @@ normalize-path@^2.0.1: dependencies: remove-trailing-separator "^1.0.1" +nth-check@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== + dependencies: + boolbase "~1.0.0" + oauth-sign@~0.8.1, oauth-sign@~0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" @@ -1154,6 +1423,13 @@ object-assign@^3.0.0: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" integrity sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I= +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + optimist@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" @@ -1162,6 +1438,53 @@ optimist@0.6.1: minimist "~0.0.1" wordwrap "~0.0.2" +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + +os-tmpdir@^1.0.0, os-tmpdir@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +osenv@^0.1.3: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +parse-semver@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/parse-semver/-/parse-semver-1.1.1.tgz#9a4afd6df063dc4826f93fba4a99cf223f666cb8" + integrity sha1-mkr9bfBj3Egm+T+6SpnPIj9mbLg= + dependencies: + semver "^5.1.0" + +parse5@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" + integrity sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA== + dependencies: + "@types/node" "*" + +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= + +path-parse@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + performance-now@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" @@ -1200,6 +1523,11 @@ punycode@^1.4.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= +q@^1.0.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= + qs@~6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" @@ -1210,6 +1538,13 @@ qs@~6.5.1: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" integrity sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A== +read@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" + integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ= + dependencies: + mute-stream "~0.0.4" + readable-stream@^2.1.5: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" @@ -1223,6 +1558,15 @@ readable-stream@^2.1.5: string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.0.6.tgz#351302e4c68b5abd6a2ed55376a7f9a25be3057a" + integrity sha512-9E1oLoOWfhSXHGv6QlwXJim7uNzd9EVlWK+21tCU9Ju/kR0/p2AZYPz4qSchgO8PlLIH4FpZYfzwS+rEksZjIg== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readable-stream@~1.1.9: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" @@ -1316,6 +1660,13 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= +resolve@^1.3.2: + version "1.8.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" + integrity sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA== + dependencies: + path-parse "^1.0.5" + safe-buffer@^5.0.1, safe-buffer@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" @@ -1326,6 +1677,11 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + sax@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.2.tgz#735ffaa39a1cff8ffb9598f0223abdb03a9fb2ea" @@ -1341,6 +1697,11 @@ semaphore@1.0.5: resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.0.5.tgz#b492576e66af193db95d65e25ec53f5f19798d60" integrity sha1-tJJXbmavGT25XWXiXsU/Xxl5jWA= +semver@^5.1.0, semver@^5.3.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" + integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== + sntp@1.x.x: version "1.0.9" resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" @@ -1381,6 +1742,11 @@ sparkles@^1.0.0: resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.1.tgz#008db65edce6c50eec0c5e228e1945061dd0437c" integrity sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw== +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= + sshpk@^1.7.0: version "1.13.1" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" @@ -1396,18 +1762,18 @@ sshpk@^1.7.0: jsbn "~0.1.0" tweetnacl "~0.14.0" -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - -string_decoder@~1.1.1: +string_decoder@^1.1.1, string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== dependencies: safe-buffer "~5.1.0" +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + stringstream@~0.0.4, stringstream@~0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" @@ -1452,6 +1818,13 @@ time-stamp@^1.0.0: resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" integrity sha1-dkpaEa9QVhkhsTPztE5hhofg9cM= +tmp@0.0.29: + version "0.0.29" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.29.tgz#f25125ff0dd9da3ccb0c2dd371ee1288bb9128c0" + integrity sha1-8lEl/w3Z2jzLDC3Tce4SiLuRKMA= + dependencies: + os-tmpdir "~1.0.1" + tough-cookie@~2.3.0: version "2.3.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561" @@ -1466,6 +1839,36 @@ tough-cookie@~2.3.3: dependencies: punycode "^1.4.1" +tslib@^1.8.0, tslib@^1.8.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" + integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== + +tslint@^5.9.1: + version "5.11.0" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.11.0.tgz#98f30c02eae3cde7006201e4c33cb08b48581eed" + integrity sha1-mPMMAurjzecAYgHkwzywi0hYHu0= + dependencies: + babel-code-frame "^6.22.0" + builtin-modules "^1.1.1" + chalk "^2.3.0" + commander "^2.12.1" + diff "^3.2.0" + glob "^7.1.1" + js-yaml "^3.7.0" + minimatch "^3.0.4" + resolve "^1.3.2" + semver "^5.3.0" + tslib "^1.8.0" + tsutils "^2.27.2" + +tsutils@^2.27.2: + version "2.29.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" + integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== + dependencies: + tslib "^1.8.1" + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -1473,27 +1876,55 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tunnel@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.4.tgz#2d3785a158c174c9a16dc2c046ec5fc5f1742213" + integrity sha1-LTeFoVjBdMmhbcLARuxfxfF0IhM= + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= -typescript@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.1.tgz#3362ba9dd1e482ebb2355b02dfe8bcd19a2c7c96" - integrity sha512-Veu0w4dTc/9wlWNf2jeRInNodKlcdLgemvPsrNpfu5Pq39sgfFjvIIgTsvUHCoLBnMhPoUA+tFxsXjU6VexVRQ== +typed-rest-client@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/typed-rest-client/-/typed-rest-client-0.9.0.tgz#f768cc0dc3f4e950f06e04825c36b3e7834aa1f2" + integrity sha1-92jMDcP06VDwbgSCXDaz54NKofI= + dependencies: + tunnel "0.0.4" + underscore "1.8.3" + +typescript@3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.4.tgz#c74ef7b3c2da65beff548b903022cb8c3cd997ed" + integrity sha512-JZHJtA6ZL15+Q3Dqkbh8iCUmvxD3iJ7ujXS+fVkKnwIVAdHc5BJTDNM0aTrnr2luKulFjU7W+SRhDZvi66Ru7Q== + +uc.micro@^1.0.1, uc.micro@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.5.tgz#0c65f15f815aa08b560a61ce8b4db7ffc3f45376" + integrity sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg== underscore@1.8.3, underscore@~1.8.3: version "1.8.3" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" integrity sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI= +underscore@^1.8.3: + version "1.9.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" + integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg== + urix@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= -util-deprecate@~1.0.1: +url-join@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/url-join/-/url-join-1.1.0.tgz#741c6c2f4596c4830d6718460920d0c92202dc78" + integrity sha1-dBxsL0WWxIMNZxhGCSDQySIC3Hg= + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= @@ -1540,11 +1971,49 @@ vinyl@^0.5.0: clone-stats "^0.0.1" replace-ext "0.0.1" +vsce@1.48.0: + version "1.48.0" + resolved "https://registry.yarnpkg.com/vsce/-/vsce-1.48.0.tgz#31c1a4c6909c3b8bdc48b3d32cc8c8e94c7113a2" + integrity sha512-1qJn6QLRTu26FIvvMbK/gzHLLdxJVTg9CUTSnCjJHObCCF5CQ0F3FUv7t+5cT7i0J5v5YljrsRY09u7dPBcEnA== + dependencies: + cheerio "^1.0.0-rc.1" + commander "^2.8.1" + denodeify "^1.2.1" + glob "^7.0.6" + lodash "^4.17.10" + markdown-it "^8.3.1" + mime "^1.3.4" + minimatch "^3.0.3" + osenv "^0.1.3" + parse-semver "^1.1.1" + read "^1.0.7" + semver "^5.1.0" + tmp "0.0.29" + url-join "^1.1.0" + vso-node-api "6.1.2-preview" + yauzl "^2.3.1" + yazl "^2.2.2" + +vso-node-api@6.1.2-preview: + version "6.1.2-preview" + resolved "https://registry.yarnpkg.com/vso-node-api/-/vso-node-api-6.1.2-preview.tgz#aab3546df2451ecd894e071bb99b5df19c5fa78f" + integrity sha1-qrNUbfJFHs2JTgcbuZtd8Zxfp48= + dependencies: + q "^1.0.1" + tunnel "0.0.4" + typed-rest-client "^0.9.0" + underscore "^1.8.3" + wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + xml2js@0.2.7: version "0.2.7" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.2.7.tgz#1838518bb01741cae0878bab4915e494c32306af" @@ -1574,3 +2043,23 @@ xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= + +yauzl@^2.3.1: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + +yazl@^2.2.2: + version "2.4.3" + resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.4.3.tgz#ec26e5cc87d5601b9df8432dbdd3cd2e5173a071" + integrity sha1-7CblzIfVYBud+EMtvdPNLlFzoHE= + dependencies: + buffer-crc32 "~0.2.3" + +zone.js@0.7.6: + version "0.7.6" + resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009" + integrity sha1-+7w50+AmHQmG8boGMG6zrrDSIAk= diff --git a/OSSREADME.json b/cglicenses.json similarity index 62% rename from OSSREADME.json rename to cglicenses.json index 0f9881512..3d96813f3 100644 --- a/OSSREADME.json +++ b/cglicenses.json @@ -1,117 +1,14 @@ -// Listing in here platform dependencies that come in at build time +// ----------------------------------------------------------------------------------------- +// ----------------------------------------------------------------------------------------- +// This file overrides licenses only for OSS components which do not appear in `cgmanifest.json`. +// i.e. for OSS components that are detected from `yarn.lock` or `Cargo.lock` files. +// +// DO NOT EDIT THIS FILE UNLESS THE OSS TOOL INDICATES THAT YOU SHOULD. +// [ -{ - "name": "chromium", - "version": "61.0.3163.100", - "repositoryURL": "http://www.chromium.org/Home", - "licenseDetail": [ - "BSD License", - "", - "Copyright 2015 The Chromium Authors. All rights reserved.", - "", - "Redistribution and use in source and binary forms, with or without modification,", - "are permitted provided that the following conditions are met:", - "", - " * Redistributions of source code must retain the above copyright notice, this", - " list of conditions and the following disclaimer.", - "", - " * Redistributions in binary form must reproduce the above copyright notice,", - " this list of conditions and the following disclaimer in the documentation", - " and/or other materials provided with the distribution.", - "", - " * Neither the name Google Inc. nor the names of its contributors may be used to", - " endorse or promote products derived from this software without specific", - " prior written permission.", - "", - "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND", - "ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED", - "WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE", - "DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR", - "ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES", - "(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;", - "LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON", - "ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT", - "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS", - "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." - ], - "isProd": true -}, -{ - "name": "libchromiumcontent", - "version": "61.0.3163.100", - "license": "MIT", - "repositoryURL": "https://github.com/electron/libchromiumcontent", - "isProd": true -}, -{ - "name": "nodejs", - "version": "8.9.3", - "repositoryURL": "https://github.com/nodejs/node", - "isProd": true -}, -{ - "name": "electron", - "version": "2.0.12", - "license": "MIT", - "repositoryURL": "https://github.com/electron/electron", - "isProd": true -}, -{ - "name": "inno setup", - "version": "5.5.6", - "repositoryURL": "https://github.com/jrsoftware/issrc", - "isProd": true -}, -{ - "name": "spdlog original", - "version": "0.14.0", - "repositoryURL": "https://github.com/gabime/spdlog", - "license": "MIT", - "isProd": true -}, - -// -------------------------------------------------------------------------------------- -// -------------------------------------------------------------------------------------- -// ONLY LICENSE TEXT AFTER THIS MARKER -// Each license entry should contain precisely the following fields: -// "isLicense": true -// "name": string -// "licenseDetail": string[] -// Furthermore, each license entry should contain a clear reason for existance. - -{ - // Reason: Added here because the repo at https://github.com/paulmillr/async-each - // does not include a LICENSE file that can be machine processed. - "isLicense": true, - "name": "async-each", - "licenseDetail": [ - "The MIT License (MIT)", - "", - "Copyright (c) 2016 Paul Miller [(paulmillr.com)](http://paulmillr.com)", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the “Software”), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in", - "all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN", - "THE SOFTWARE." - ] -}, { // Reason: The license at https://github.com/aadsm/jschardet/blob/master/LICENSE // does not include a clear Copyright statement and does not credit authors. - "isLicense": true, "name": "jschardet", "licenseDetail": [ "Chardet was originally ported from C++ by Mark Pilgrim. It is now maintained", @@ -632,7 +529,6 @@ // Added here because the module `parse5` has a dependency to it. // The module `parse5` is shipped via the `extension-editing` built-in extension. // The module `parse5` does not want to remove it https://github.com/inikulin/parse5/issues/225 - "isLicense": true, "name": "@types/node", "licenseDetail": [ "This project is licensed under the MIT license.", @@ -649,7 +545,6 @@ // We override the license that gets discovered at // https://github.com/Microsoft/TypeScript/blob/master/LICENSE.txt // because it does not contain a Copyright statement - "isLicense": true, "name": "typescript", "licenseDetail": [ "Copyright (c) Microsoft Corporation. All rights reserved.", @@ -712,10 +607,8 @@ ] }, { - "isLicense": true, + // This module comes in from https://github.com/Microsoft/vscode-node-debug2/blob/master/package-lock.json "name": "@types/source-map", - "repositoryURL": "https://www.github.com/DefinitelyTyped/DefinitelyTyped", - "license": "MIT", "licenseDetail": [ "This project is licensed under the MIT license.", "Copyrights are respective of each contributor listed at the beginning of each definition file.", @@ -728,508 +621,6 @@ ] }, { - "isLicense": true, - "name": "markdown-it-named-headers", - "licenseDetail": [ - "Copyright (c) markdown-it-named-headers authors", - "", - "This is free and unencumbered software released into the public domain.", - "", - "Anyone is free to copy, modify, publish, use, compile, sell, or", - "distribute this software, either in source code form or as a compiled", - "binary, for any purpose, commercial or non-commercial, and by any", - "means.", - "", - "In jurisdictions that recognize copyright laws, the author or authors", - "of this software dedicate any and all copyright interest in the", - "software to the public domain. We make this dedication for the benefit", - "of the public at large and to the detriment of our heirs and", - "successors. We intend this dedication to be an overt act of", - "relinquishment in perpetuity of all present and future rights to this", - "software under copyright law.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,", - "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", - "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.", - "IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR", - "OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,", - "ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR", - "OTHER DEALINGS IN THE SOFTWARE.", - "", - "For more information, please refer to " - ] -}, -{ - "isLicense": true, - "name": "uc.micro", - "licenseDetail": [ - " DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE", - " Version 2, December 2004", - "", - " Copyright (C) 2004 Sam Hocevar ", - "", - " Everyone is permitted to copy and distribute verbatim or modified", - " copies of this license document, and changing it is allowed as long", - " as the name is changed.", - "", - " DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE", - " TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION", - "", - " 0. You just DO WHAT THE FUCK YOU WANT TO." - ] -}, -{ - "name": "mdn-data", - "version": "1.1.12", - "repositoryURL": "https://github.com/mdn/data", - "license": "MPL", - "licenseDetail": [ - "Mozilla Public License Version 2.0", - "", - "Copyright (c) 2018 Mozilla Corporation", - "", - "==================================", - "", - "1. Definitions", - "--------------", - "", - "1.1. \"Contributor\"", - " means each individual or legal entity that creates, contributes to", - " the creation of, or owns Covered Software.", - "", - "1.2. \"Contributor Version\"", - " means the combination of the Contributions of others (if any) used", - " by a Contributor and that particular Contributor's Contribution.", - "", - "1.3. \"Contribution\"", - " means Covered Software of a particular Contributor.", - "", - "1.4. \"Covered Software\"", - " means Source Code Form to which the initial Contributor has attached", - " the notice in Exhibit A, the Executable Form of such Source Code", - " Form, and Modifications of such Source Code Form, in each case", - " including portions thereof.", - "", - "1.5. \"Incompatible With Secondary Licenses\"", - " means", - "", - " (a) that the initial Contributor has attached the notice described", - " in Exhibit B to the Covered Software; or", - "", - " (b) that the Covered Software was made available under the terms of", - " version 1.1 or earlier of the License, but not also under the", - " terms of a Secondary License.", - "", - "1.6. \"Executable Form\"", - " means any form of the work other than Source Code Form.", - "", - "1.7. \"Larger Work\"", - " means a work that combines Covered Software with other material, in", - " a separate file or files, that is not Covered Software.", - "", - "1.8. \"License\"", - " means this document.", - "", - "1.9. \"Licensable\"", - " means having the right to grant, to the maximum extent possible,", - " whether at the time of the initial grant or subsequently, any and", - " all of the rights conveyed by this License.", - "", - "1.10. \"Modifications\"", - " means any of the following:", - "", - " (a) any file in Source Code Form that results from an addition to,", - " deletion from, or modification of the contents of Covered", - " Software; or", - "", - " (b) any new file in Source Code Form that contains any Covered", - " Software.", - "", - "1.11. \"Patent Claims\" of a Contributor", - " means any patent claim(s), including without limitation, method,", - " process, and apparatus claims, in any patent Licensable by such", - " Contributor that would be infringed, but for the grant of the", - " License, by the making, using, selling, offering for sale, having", - " made, import, or transfer of either its Contributions or its", - " Contributor Version.", - "", - "1.12. \"Secondary License\"", - " means either the GNU General Public License, Version 2.0, the GNU", - " Lesser General Public License, Version 2.1, the GNU Affero General", - " Public License, Version 3.0, or any later versions of those", - " licenses.", - "", - "1.13. \"Source Code Form\"", - " means the form of the work preferred for making modifications.", - "", - "1.14. \"You\" (or \"Your\")", - " means an individual or a legal entity exercising rights under this", - " License. For legal entities, \"You\" includes any entity that", - " controls, is controlled by, or is under common control with You. For", - " purposes of this definition, \"control\" means (a) the power, direct", - " or indirect, to cause the direction or management of such entity,", - " whether by contract or otherwise, or (b) ownership of more than", - " fifty percent (50%) of the outstanding shares or beneficial", - " ownership of such entity.", - "", - "2. License Grants and Conditions", - "--------------------------------", - "", - "2.1. Grants", - "", - "Each Contributor hereby grants You a world-wide, royalty-free,", - "non-exclusive license:", - "", - "(a) under intellectual property rights (other than patent or trademark)", - " Licensable by such Contributor to use, reproduce, make available,", - " modify, display, perform, distribute, and otherwise exploit its", - " Contributions, either on an unmodified basis, with Modifications, or", - " as part of a Larger Work; and", - "", - "(b) under Patent Claims of such Contributor to make, use, sell, offer", - " for sale, have made, import, and otherwise transfer either its", - " Contributions or its Contributor Version.", - "", - "2.2. Effective Date", - "", - "The licenses granted in Section 2.1 with respect to any Contribution", - "become effective for each Contribution on the date the Contributor first", - "distributes such Contribution.", - "", - "2.3. Limitations on Grant Scope", - "", - "The licenses granted in this Section 2 are the only rights granted under", - "this License. No additional rights or licenses will be implied from the", - "distribution or licensing of Covered Software under this License.", - "Notwithstanding Section 2.1(b) above, no patent license is granted by a", - "Contributor:", - "", - "(a) for any code that a Contributor has removed from Covered Software;", - " or", - "", - "(b) for infringements caused by: (i) Your and any other third party's", - " modifications of Covered Software, or (ii) the combination of its", - " Contributions with other software (except as part of its Contributor", - " Version); or", - "", - "(c) under Patent Claims infringed by Covered Software in the absence of", - " its Contributions.", - "", - "This License does not grant any rights in the trademarks, service marks,", - "or logos of any Contributor (except as may be necessary to comply with", - "the notice requirements in Section 3.4).", - "", - "2.4. Subsequent Licenses", - "", - "No Contributor makes additional grants as a result of Your choice to", - "distribute the Covered Software under a subsequent version of this", - "License (see Section 10.2) or under the terms of a Secondary License (if", - "permitted under the terms of Section 3.3).", - "", - "2.5. Representation", - "", - "Each Contributor represents that the Contributor believes its", - "Contributions are its original creation(s) or it has sufficient rights", - "to grant the rights to its Contributions conveyed by this License.", - "", - "2.6. Fair Use", - "", - "This License is not intended to limit any rights You have under", - "applicable copyright doctrines of fair use, fair dealing, or other", - "equivalents.", - "", - "2.7. Conditions", - "", - "Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted", - "in Section 2.1.", - "", - "3. Responsibilities", - "-------------------", - "", - "3.1. Distribution of Source Form", - "", - "All distribution of Covered Software in Source Code Form, including any", - "Modifications that You create or to which You contribute, must be under", - "the terms of this License. You must inform recipients that the Source", - "Code Form of the Covered Software is governed by the terms of this", - "License, and how they can obtain a copy of this License. You may not", - "attempt to alter or restrict the recipients' rights in the Source Code", - "Form.", - "", - "3.2. Distribution of Executable Form", - "", - "If You distribute Covered Software in Executable Form then:", - "", - "(a) such Covered Software must also be made available in Source Code", - " Form, as described in Section 3.1, and You must inform recipients of", - " the Executable Form how they can obtain a copy of such Source Code", - " Form by reasonable means in a timely manner, at a charge no more", - " than the cost of distribution to the recipient; and", - "", - "(b) You may distribute such Executable Form under the terms of this", - " License, or sublicense it under different terms, provided that the", - " license for the Executable Form does not attempt to limit or alter", - " the recipients' rights in the Source Code Form under this License.", - "", - "3.3. Distribution of a Larger Work", - "", - "You may create and distribute a Larger Work under terms of Your choice,", - "provided that You also comply with the requirements of this License for", - "the Covered Software. If the Larger Work is a combination of Covered", - "Software with a work governed by one or more Secondary Licenses, and the", - "Covered Software is not Incompatible With Secondary Licenses, this", - "License permits You to additionally distribute such Covered Software", - "under the terms of such Secondary License(s), so that the recipient of", - "the Larger Work may, at their option, further distribute the Covered", - "Software under the terms of either this License or such Secondary", - "License(s).", - "", - "3.4. Notices", - "", - "You may not remove or alter the substance of any license notices", - "(including copyright notices, patent notices, disclaimers of warranty,", - "or limitations of liability) contained within the Source Code Form of", - "the Covered Software, except that You may alter any license notices to", - "the extent required to remedy known factual inaccuracies.", - "", - "3.5. Application of Additional Terms", - "", - "You may choose to offer, and to charge a fee for, warranty, support,", - "indemnity or liability obligations to one or more recipients of Covered", - "Software. However, You may do so only on Your own behalf, and not on", - "behalf of any Contributor. You must make it absolutely clear that any", - "such warranty, support, indemnity, or liability obligation is offered by", - "You alone, and You hereby agree to indemnify every Contributor for any", - "liability incurred by such Contributor as a result of warranty, support,", - "indemnity or liability terms You offer. You may include additional", - "disclaimers of warranty and limitations of liability specific to any", - "jurisdiction.", - "", - "4. Inability to Comply Due to Statute or Regulation", - "---------------------------------------------------", - "", - "If it is impossible for You to comply with any of the terms of this", - "License with respect to some or all of the Covered Software due to", - "statute, judicial order, or regulation then You must: (a) comply with", - "the terms of this License to the maximum extent possible; and (b)", - "describe the limitations and the code they affect. Such description must", - "be placed in a text file included with all distributions of the Covered", - "Software under this License. Except to the extent prohibited by statute", - "or regulation, such description must be sufficiently detailed for a", - "recipient of ordinary skill to be able to understand it.", - "", - "5. Termination", - "--------------", - "", - "5.1. The rights granted under this License will terminate automatically", - "if You fail to comply with any of its terms. However, if You become", - "compliant, then the rights granted under this License from a particular", - "Contributor are reinstated (a) provisionally, unless and until such", - "Contributor explicitly and finally terminates Your grants, and (b) on an", - "ongoing basis, if such Contributor fails to notify You of the", - "non-compliance by some reasonable means prior to 60 days after You have", - "come back into compliance. Moreover, Your grants from a particular", - "Contributor are reinstated on an ongoing basis if such Contributor", - "notifies You of the non-compliance by some reasonable means, this is the", - "first time You have received notice of non-compliance with this License", - "from such Contributor, and You become compliant prior to 30 days after", - "Your receipt of the notice.", - "", - "5.2. If You initiate litigation against any entity by asserting a patent", - "infringement claim (excluding declaratory judgment actions,", - "counter-claims, and cross-claims) alleging that a Contributor Version", - "directly or indirectly infringes any patent, then the rights granted to", - "You by any and all Contributors for the Covered Software under Section", - "2.1 of this License shall terminate.", - "", - "5.3. In the event of termination under Sections 5.1 or 5.2 above, all", - "end user license agreements (excluding distributors and resellers) which", - "have been validly granted by You or Your distributors under this License", - "prior to termination shall survive termination.", - "", - "************************************************************************", - "* *", - "* 6. Disclaimer of Warranty *", - "* ------------------------- *", - "* *", - "* Covered Software is provided under this License on an \"as is\" *", - "* basis, without warranty of any kind, either expressed, implied, or *", - "* statutory, including, without limitation, warranties that the *", - "* Covered Software is free of defects, merchantable, fit for a *", - "* particular purpose or non-infringing. The entire risk as to the *", - "* quality and performance of the Covered Software is with You. *", - "* Should any Covered Software prove defective in any respect, You *", - "* (not any Contributor) assume the cost of any necessary servicing, *", - "* repair, or correction. This disclaimer of warranty constitutes an *", - "* essential part of this License. No use of any Covered Software is *", - "* authorized under this License except under this disclaimer. *", - "* *", - "************************************************************************", - "", - "************************************************************************", - "* *", - "* 7. Limitation of Liability *", - "* -------------------------- *", - "* *", - "* Under no circumstances and under no legal theory, whether tort *", - "* (including negligence), contract, or otherwise, shall any *", - "* Contributor, or anyone who distributes Covered Software as *", - "* permitted above, be liable to You for any direct, indirect, *", - "* special, incidental, or consequential damages of any character *", - "* including, without limitation, damages for lost profits, loss of *", - "* goodwill, work stoppage, computer failure or malfunction, or any *", - "* and all other commercial damages or losses, even if such party *", - "* shall have been informed of the possibility of such damages. This *", - "* limitation of liability shall not apply to liability for death or *", - "* personal injury resulting from such party's negligence to the *", - "* extent applicable law prohibits such limitation. Some *", - "* jurisdictions do not allow the exclusion or limitation of *", - "* incidental or consequential damages, so this exclusion and *", - "* limitation may not apply to You. *", - "* *", - "************************************************************************", - "", - "8. Litigation", - "-------------", - "", - "Any litigation relating to this License may be brought only in the", - "courts of a jurisdiction where the defendant maintains its principal", - "place of business and such litigation shall be governed by laws of that", - "jurisdiction, without reference to its conflict-of-law provisions.", - "Nothing in this Section shall prevent a party's ability to bring", - "cross-claims or counter-claims.", - "", - "9. Miscellaneous", - "----------------", - "", - "This License represents the complete agreement concerning the subject", - "matter hereof. If any provision of this License is held to be", - "unenforceable, such provision shall be reformed only to the extent", - "necessary to make it enforceable. Any law or regulation which provides", - "that the language of a contract shall be construed against the drafter", - "shall not be used to construe this License against a Contributor.", - "", - "10. Versions of the License", - "---------------------------", - "", - "10.1. New Versions", - "", - "Mozilla Foundation is the license steward. Except as provided in Section", - "10.3, no one other than the license steward has the right to modify or", - "publish new versions of this License. Each version will be given a", - "distinguishing version number.", - "", - "10.2. Effect of New Versions", - "", - "You may distribute the Covered Software under the terms of the version", - "of the License under which You originally received the Covered Software,", - "or under the terms of any subsequent version published by the license", - "steward.", - "", - "10.3. Modified Versions", - "", - "If you create software not governed by this License, and you want to", - "create a new license for such software, you may create and use a", - "modified version of this License if you rename the license and remove", - "any references to the name of the license steward (except to note that", - "such modified license differs from this License).", - "", - "10.4. Distributing Source Code Form that is Incompatible With Secondary", - "Licenses", - "", - "If You choose to distribute Source Code Form that is Incompatible With", - "Secondary Licenses under the terms of this version of the License, the", - "notice described in Exhibit B of this License must be attached.", - "", - "Exhibit A - Source Code Form License Notice", - "-------------------------------------------", - "", - " This Source Code Form is subject to the terms of the Mozilla Public", - " License, v. 2.0. If a copy of the MPL was not distributed with this", - " file, You can obtain one at http://mozilla.org/MPL/2.0/.", - "", - "If it is not possible or desirable to put the notice in a particular", - "file, then You may include the notice in a location (such as a LICENSE", - "file in a relevant directory) where a recipient would be likely to look", - "for such a notice.", - "", - "You may add additional accurate notices of copyright ownership.", - "", - "Exhibit B - \"Incompatible With Secondary Licenses\" Notice", - "---------------------------------------------------------", - "", - " This Source Code Form is \"Incompatible With Secondary Licenses\", as", - " defined by the Mozilla Public License, v. 2.0." - ] -}, -{ - "isLicense": true, - "name": "devtools-protocol", - "licenseDetail": [ - " Copyright 2015 The Chromium Authors. All rights reserved.", - "", - " Redistribution and use in source and binary forms, with or without", - " modification, are permitted provided that the following conditions are", - " met:", - "", - " * Redistributions of source code must retain the above copyright", - " notice, this list of conditions and the following disclaimer.", - " * Redistributions in binary form must reproduce the above", - " copyright notice, this list of conditions and the following disclaimer", - " in the documentation and/or other materials provided with the", - " distribution.", - " * Neither the name of Google Inc. nor the names of its", - " contributors may be used to endorse or promote products derived from", - " this software without specific prior written permission.", - "", - " THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS", - " \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT", - " LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR", - " A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT", - " OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,", - " SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT", - " LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,", - " DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY", - " THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT", - " (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE", - " OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." - ] -}, -{ - "isLicense": true, - "name": "buffer-alloc", - "licenseDetail": [ - "This project is licensed under the MIT license.", - "Copyrights are respective of each contributor listed at the beginning of each definition file.", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." - ] -}, -{ - "isLicense": true, - "name": "expand-template", - "licenseDetail": [ - " DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE", - " Version 2, December 2004", - "", - " Copyright (C) 2004 Sam Hocevar ", - "", - " Everyone is permitted to copy and distribute verbatim or modified", - " copies of this license document, and changing it is allowed as long", - " as the name is changed.", - "", - " DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE", - " TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION", - "", - " 0. You just DO WHAT THE FUCK YOU WANT TO." - ] -}, -{ - "isLicense": true, "name": "tunnel-agent", "licenseDetail": [ "Copyright (c) tunnel-agent authors", @@ -1292,7 +683,7 @@ ] }, { - "isLicense": true, + // Waiting for https://github.com/segmentio/noop-logger/issues/2 "name": "noop-logger", "licenseDetail": [ "This project is licensed under the MIT license.", @@ -1304,33 +695,5 @@ "", "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." ] -}, -{ - "isLicense": true, - "name": "buffer-alloc-unsafe", - "licenseDetail": [ - "This project is licensed under the MIT license.", - "Copyrights are respective of each contributor listed at the beginning of each definition file.", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." - ] -}, -{ - "isLicense": true, - "name": "buffer-fill", - "licenseDetail": [ - "This project is licensed under the MIT license.", - "Copyrights are respective of each contributor listed at the beginning of each definition file.", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." - ] } -] +] \ No newline at end of file diff --git a/cgmanifest.json b/cgmanifest.json new file mode 100644 index 000000000..637c7d2f5 --- /dev/null +++ b/cgmanifest.json @@ -0,0 +1,499 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "chromium", + "repositoryUrl": "https://chromium.googlesource.com/chromium/src", + "commitHash": "7accc8730b0f99b5e7c0702ea89d1fa7c17bfe33" + } + }, + "licenseDetail": [ + "BSD License", + "", + "Copyright 2015 The Chromium Authors. All rights reserved.", + "", + "Redistribution and use in source and binary forms, with or without modification,", + "are permitted provided that the following conditions are met:", + "", + " * Redistributions of source code must retain the above copyright notice, this", + " list of conditions and the following disclaimer.", + "", + " * Redistributions in binary form must reproduce the above copyright notice,", + " this list of conditions and the following disclaimer in the documentation", + " and/or other materials provided with the distribution.", + "", + " * Neither the name Google Inc. nor the names of its contributors may be used to", + " endorse or promote products derived from this software without specific", + " prior written permission.", + "", + "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND", + "ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED", + "WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE", + "DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR", + "ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES", + "(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;", + "LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON", + "ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT", + "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS", + "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + ], + "isOnlyProductionDependency": true, + "version": "61.0.3163.100" + }, + { + "component": { + "type": "git", + "git": { + "name": "libchromiumcontent", + "repositoryUrl": "https://github.com/electron/libchromiumcontent", + "commitHash": "ccdb085454b0a387ee96e0f81a7ca9a8ce07a710" + } + }, + "isOnlyProductionDependency": true, + "license": "MIT", + "version": "61.0.3163.100" + }, + { + "component": { + "type": "git", + "git": { + "name": "nodejs", + "repositoryUrl": "https://github.com/nodejs/node", + "commitHash": "8a44289089a08b7b19fa3c4651b5f1f5d1edd71b" + } + }, + "isOnlyProductionDependency": true, + "version": "8.9.3" + }, + { + "component": { + "type": "git", + "git": { + "name": "electron", + "repositoryUrl": "https://github.com/electron/electron", + "commitHash": "d281859cf59f12c7107a540a9f4cba0ecf5eff41" + } + }, + "isOnlyProductionDependency": true, + "license": "MIT", + "version": "2.0.12" + }, + { + "component": { + "type": "git", + "git": { + "name": "inno setup", + "repositoryUrl": "https://github.com/jrsoftware/issrc", + "commitHash": "03fe8f4edb3e96c7835c9483052625bbedb160f2" + } + }, + "isOnlyProductionDependency": true, + "version": "5.5.6" + }, + { + "component": { + "type": "git", + "git": { + "name": "spdlog original", + "repositoryUrl": "https://github.com/gabime/spdlog", + "commitHash": "4fba14c79f356ae48d6141c561bf9fd7ba33fabd" + } + }, + "isOnlyProductionDependency": true, + "license": "MIT", + "version": "0.14.0" + }, + { + "component": { + "type": "npm", + "npm": { + "name": "mdn-data", + "version": "1.1.12" + } + }, + "repositoryUrl": "https://github.com/mdn/data", + "licenseDetail": [ + "Mozilla Public License Version 2.0", + "", + "Copyright (c) 2018 Mozilla Corporation", + "", + "==================================", + "", + "1. Definitions", + "--------------", + "", + "1.1. \"Contributor\"", + " means each individual or legal entity that creates, contributes to", + " the creation of, or owns Covered Software.", + "", + "1.2. \"Contributor Version\"", + " means the combination of the Contributions of others (if any) used", + " by a Contributor and that particular Contributor's Contribution.", + "", + "1.3. \"Contribution\"", + " means Covered Software of a particular Contributor.", + "", + "1.4. \"Covered Software\"", + " means Source Code Form to which the initial Contributor has attached", + " the notice in Exhibit A, the Executable Form of such Source Code", + " Form, and Modifications of such Source Code Form, in each case", + " including portions thereof.", + "", + "1.5. \"Incompatible With Secondary Licenses\"", + " means", + "", + " (a) that the initial Contributor has attached the notice described", + " in Exhibit B to the Covered Software; or", + "", + " (b) that the Covered Software was made available under the terms of", + " version 1.1 or earlier of the License, but not also under the", + " terms of a Secondary License.", + "", + "1.6. \"Executable Form\"", + " means any form of the work other than Source Code Form.", + "", + "1.7. \"Larger Work\"", + " means a work that combines Covered Software with other material, in", + " a separate file or files, that is not Covered Software.", + "", + "1.8. \"License\"", + " means this document.", + "", + "1.9. \"Licensable\"", + " means having the right to grant, to the maximum extent possible,", + " whether at the time of the initial grant or subsequently, any and", + " all of the rights conveyed by this License.", + "", + "1.10. \"Modifications\"", + " means any of the following:", + "", + " (a) any file in Source Code Form that results from an addition to,", + " deletion from, or modification of the contents of Covered", + " Software; or", + "", + " (b) any new file in Source Code Form that contains any Covered", + " Software.", + "", + "1.11. \"Patent Claims\" of a Contributor", + " means any patent claim(s), including without limitation, method,", + " process, and apparatus claims, in any patent Licensable by such", + " Contributor that would be infringed, but for the grant of the", + " License, by the making, using, selling, offering for sale, having", + " made, import, or transfer of either its Contributions or its", + " Contributor Version.", + "", + "1.12. \"Secondary License\"", + " means either the GNU General Public License, Version 2.0, the GNU", + " Lesser General Public License, Version 2.1, the GNU Affero General", + " Public License, Version 3.0, or any later versions of those", + " licenses.", + "", + "1.13. \"Source Code Form\"", + " means the form of the work preferred for making modifications.", + "", + "1.14. \"You\" (or \"Your\")", + " means an individual or a legal entity exercising rights under this", + " License. For legal entities, \"You\" includes any entity that", + " controls, is controlled by, or is under common control with You. For", + " purposes of this definition, \"control\" means (a) the power, direct", + " or indirect, to cause the direction or management of such entity,", + " whether by contract or otherwise, or (b) ownership of more than", + " fifty percent (50%) of the outstanding shares or beneficial", + " ownership of such entity.", + "", + "2. License Grants and Conditions", + "--------------------------------", + "", + "2.1. Grants", + "", + "Each Contributor hereby grants You a world-wide, royalty-free,", + "non-exclusive license:", + "", + "(a) under intellectual property rights (other than patent or trademark)", + " Licensable by such Contributor to use, reproduce, make available,", + " modify, display, perform, distribute, and otherwise exploit its", + " Contributions, either on an unmodified basis, with Modifications, or", + " as part of a Larger Work; and", + "", + "(b) under Patent Claims of such Contributor to make, use, sell, offer", + " for sale, have made, import, and otherwise transfer either its", + " Contributions or its Contributor Version.", + "", + "2.2. Effective Date", + "", + "The licenses granted in Section 2.1 with respect to any Contribution", + "become effective for each Contribution on the date the Contributor first", + "distributes such Contribution.", + "", + "2.3. Limitations on Grant Scope", + "", + "The licenses granted in this Section 2 are the only rights granted under", + "this License. No additional rights or licenses will be implied from the", + "distribution or licensing of Covered Software under this License.", + "Notwithstanding Section 2.1(b) above, no patent license is granted by a", + "Contributor:", + "", + "(a) for any code that a Contributor has removed from Covered Software;", + " or", + "", + "(b) for infringements caused by: (i) Your and any other third party's", + " modifications of Covered Software, or (ii) the combination of its", + " Contributions with other software (except as part of its Contributor", + " Version); or", + "", + "(c) under Patent Claims infringed by Covered Software in the absence of", + " its Contributions.", + "", + "This License does not grant any rights in the trademarks, service marks,", + "or logos of any Contributor (except as may be necessary to comply with", + "the notice requirements in Section 3.4).", + "", + "2.4. Subsequent Licenses", + "", + "No Contributor makes additional grants as a result of Your choice to", + "distribute the Covered Software under a subsequent version of this", + "License (see Section 10.2) or under the terms of a Secondary License (if", + "permitted under the terms of Section 3.3).", + "", + "2.5. Representation", + "", + "Each Contributor represents that the Contributor believes its", + "Contributions are its original creation(s) or it has sufficient rights", + "to grant the rights to its Contributions conveyed by this License.", + "", + "2.6. Fair Use", + "", + "This License is not intended to limit any rights You have under", + "applicable copyright doctrines of fair use, fair dealing, or other", + "equivalents.", + "", + "2.7. Conditions", + "", + "Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted", + "in Section 2.1.", + "", + "3. Responsibilities", + "-------------------", + "", + "3.1. Distribution of Source Form", + "", + "All distribution of Covered Software in Source Code Form, including any", + "Modifications that You create or to which You contribute, must be under", + "the terms of this License. You must inform recipients that the Source", + "Code Form of the Covered Software is governed by the terms of this", + "License, and how they can obtain a copy of this License. You may not", + "attempt to alter or restrict the recipients' rights in the Source Code", + "Form.", + "", + "3.2. Distribution of Executable Form", + "", + "If You distribute Covered Software in Executable Form then:", + "", + "(a) such Covered Software must also be made available in Source Code", + " Form, as described in Section 3.1, and You must inform recipients of", + " the Executable Form how they can obtain a copy of such Source Code", + " Form by reasonable means in a timely manner, at a charge no more", + " than the cost of distribution to the recipient; and", + "", + "(b) You may distribute such Executable Form under the terms of this", + " License, or sublicense it under different terms, provided that the", + " license for the Executable Form does not attempt to limit or alter", + " the recipients' rights in the Source Code Form under this License.", + "", + "3.3. Distribution of a Larger Work", + "", + "You may create and distribute a Larger Work under terms of Your choice,", + "provided that You also comply with the requirements of this License for", + "the Covered Software. If the Larger Work is a combination of Covered", + "Software with a work governed by one or more Secondary Licenses, and the", + "Covered Software is not Incompatible With Secondary Licenses, this", + "License permits You to additionally distribute such Covered Software", + "under the terms of such Secondary License(s), so that the recipient of", + "the Larger Work may, at their option, further distribute the Covered", + "Software under the terms of either this License or such Secondary", + "License(s).", + "", + "3.4. Notices", + "", + "You may not remove or alter the substance of any license notices", + "(including copyright notices, patent notices, disclaimers of warranty,", + "or limitations of liability) contained within the Source Code Form of", + "the Covered Software, except that You may alter any license notices to", + "the extent required to remedy known factual inaccuracies.", + "", + "3.5. Application of Additional Terms", + "", + "You may choose to offer, and to charge a fee for, warranty, support,", + "indemnity or liability obligations to one or more recipients of Covered", + "Software. However, You may do so only on Your own behalf, and not on", + "behalf of any Contributor. You must make it absolutely clear that any", + "such warranty, support, indemnity, or liability obligation is offered by", + "You alone, and You hereby agree to indemnify every Contributor for any", + "liability incurred by such Contributor as a result of warranty, support,", + "indemnity or liability terms You offer. You may include additional", + "disclaimers of warranty and limitations of liability specific to any", + "jurisdiction.", + "", + "4. Inability to Comply Due to Statute or Regulation", + "---------------------------------------------------", + "", + "If it is impossible for You to comply with any of the terms of this", + "License with respect to some or all of the Covered Software due to", + "statute, judicial order, or regulation then You must: (a) comply with", + "the terms of this License to the maximum extent possible; and (b)", + "describe the limitations and the code they affect. Such description must", + "be placed in a text file included with all distributions of the Covered", + "Software under this License. Except to the extent prohibited by statute", + "or regulation, such description must be sufficiently detailed for a", + "recipient of ordinary skill to be able to understand it.", + "", + "5. Termination", + "--------------", + "", + "5.1. The rights granted under this License will terminate automatically", + "if You fail to comply with any of its terms. However, if You become", + "compliant, then the rights granted under this License from a particular", + "Contributor are reinstated (a) provisionally, unless and until such", + "Contributor explicitly and finally terminates Your grants, and (b) on an", + "ongoing basis, if such Contributor fails to notify You of the", + "non-compliance by some reasonable means prior to 60 days after You have", + "come back into compliance. Moreover, Your grants from a particular", + "Contributor are reinstated on an ongoing basis if such Contributor", + "notifies You of the non-compliance by some reasonable means, this is the", + "first time You have received notice of non-compliance with this License", + "from such Contributor, and You become compliant prior to 30 days after", + "Your receipt of the notice.", + "", + "5.2. If You initiate litigation against any entity by asserting a patent", + "infringement claim (excluding declaratory judgment actions,", + "counter-claims, and cross-claims) alleging that a Contributor Version", + "directly or indirectly infringes any patent, then the rights granted to", + "You by any and all Contributors for the Covered Software under Section", + "2.1 of this License shall terminate.", + "", + "5.3. In the event of termination under Sections 5.1 or 5.2 above, all", + "end user license agreements (excluding distributors and resellers) which", + "have been validly granted by You or Your distributors under this License", + "prior to termination shall survive termination.", + "", + "************************************************************************", + "* *", + "* 6. Disclaimer of Warranty *", + "* ------------------------- *", + "* *", + "* Covered Software is provided under this License on an \"as is\" *", + "* basis, without warranty of any kind, either expressed, implied, or *", + "* statutory, including, without limitation, warranties that the *", + "* Covered Software is free of defects, merchantable, fit for a *", + "* particular purpose or non-infringing. The entire risk as to the *", + "* quality and performance of the Covered Software is with You. *", + "* Should any Covered Software prove defective in any respect, You *", + "* (not any Contributor) assume the cost of any necessary servicing, *", + "* repair, or correction. This disclaimer of warranty constitutes an *", + "* essential part of this License. No use of any Covered Software is *", + "* authorized under this License except under this disclaimer. *", + "* *", + "************************************************************************", + "", + "************************************************************************", + "* *", + "* 7. Limitation of Liability *", + "* -------------------------- *", + "* *", + "* Under no circumstances and under no legal theory, whether tort *", + "* (including negligence), contract, or otherwise, shall any *", + "* Contributor, or anyone who distributes Covered Software as *", + "* permitted above, be liable to You for any direct, indirect, *", + "* special, incidental, or consequential damages of any character *", + "* including, without limitation, damages for lost profits, loss of *", + "* goodwill, work stoppage, computer failure or malfunction, or any *", + "* and all other commercial damages or losses, even if such party *", + "* shall have been informed of the possibility of such damages. This *", + "* limitation of liability shall not apply to liability for death or *", + "* personal injury resulting from such party's negligence to the *", + "* extent applicable law prohibits such limitation. Some *", + "* jurisdictions do not allow the exclusion or limitation of *", + "* incidental or consequential damages, so this exclusion and *", + "* limitation may not apply to You. *", + "* *", + "************************************************************************", + "", + "8. Litigation", + "-------------", + "", + "Any litigation relating to this License may be brought only in the", + "courts of a jurisdiction where the defendant maintains its principal", + "place of business and such litigation shall be governed by laws of that", + "jurisdiction, without reference to its conflict-of-law provisions.", + "Nothing in this Section shall prevent a party's ability to bring", + "cross-claims or counter-claims.", + "", + "9. Miscellaneous", + "----------------", + "", + "This License represents the complete agreement concerning the subject", + "matter hereof. If any provision of this License is held to be", + "unenforceable, such provision shall be reformed only to the extent", + "necessary to make it enforceable. Any law or regulation which provides", + "that the language of a contract shall be construed against the drafter", + "shall not be used to construe this License against a Contributor.", + "", + "10. Versions of the License", + "---------------------------", + "", + "10.1. New Versions", + "", + "Mozilla Foundation is the license steward. Except as provided in Section", + "10.3, no one other than the license steward has the right to modify or", + "publish new versions of this License. Each version will be given a", + "distinguishing version number.", + "", + "10.2. Effect of New Versions", + "", + "You may distribute the Covered Software under the terms of the version", + "of the License under which You originally received the Covered Software,", + "or under the terms of any subsequent version published by the license", + "steward.", + "", + "10.3. Modified Versions", + "", + "If you create software not governed by this License, and you want to", + "create a new license for such software, you may create and use a", + "modified version of this License if you rename the license and remove", + "any references to the name of the license steward (except to note that", + "such modified license differs from this License).", + "", + "10.4. Distributing Source Code Form that is Incompatible With Secondary", + "Licenses", + "", + "If You choose to distribute Source Code Form that is Incompatible With", + "Secondary Licenses under the terms of this version of the License, the", + "notice described in Exhibit B of this License must be attached.", + "", + "Exhibit A - Source Code Form License Notice", + "-------------------------------------------", + "", + " This Source Code Form is subject to the terms of the Mozilla Public", + " License, v. 2.0. If a copy of the MPL was not distributed with this", + " file, You can obtain one at http://mozilla.org/MPL/2.0/.", + "", + "If it is not possible or desirable to put the notice in a particular", + "file, then You may include the notice in a location (such as a LICENSE", + "file in a relevant directory) where a recipient would be likely to look", + "for such a notice.", + "", + "You may add additional accurate notices of copyright ownership.", + "", + "Exhibit B - \"Incompatible With Secondary Licenses\" Notice", + "---------------------------------------------------------", + "", + " This Source Code Form is \"Incompatible With Secondary Licenses\", as", + " defined by the Mozilla Public License, v. 2.0." + ], + "license": "MPL" + } + ], + "version": 1 +} diff --git a/extensions/OSSREADME.json b/extensions/OSSREADME.json deleted file mode 100644 index 518d360d2..000000000 --- a/extensions/OSSREADME.json +++ /dev/null @@ -1,7 +0,0 @@ -[{ - "name": "typescript", - "version": "2.6.2", - "license": "Apache-2.0", - "repositoryURL": "https://github.com/Microsoft/TypeScript", - "isProd": true -}] diff --git a/extensions/bat/.vscodeignore b/extensions/bat/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/bat/.vscodeignore +++ b/extensions/bat/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/bat/OSSREADME.json b/extensions/bat/OSSREADME.json deleted file mode 100644 index 58abee75f..000000000 --- a/extensions/bat/OSSREADME.json +++ /dev/null @@ -1,7 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "mmims/language-batchfile", - "version": "0.0.0", - "license": "MIT", - "repositoryURL": "https://github.com/mmims/language-batchfile" -}] diff --git a/extensions/bat/cgmanifest.json b/extensions/bat/cgmanifest.json new file mode 100644 index 000000000..32d7db263 --- /dev/null +++ b/extensions/bat/cgmanifest.json @@ -0,0 +1,17 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "mmims/language-batchfile", + "repositoryUrl": "https://github.com/mmims/language-batchfile", + "commitHash": "4b67596631b4ecd2c89c2ec1b2e08a6623438903" + } + }, + "license": "MIT", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/cgmanifest.json b/extensions/cgmanifest.json new file mode 100644 index 000000000..6c12dba86 --- /dev/null +++ b/extensions/cgmanifest.json @@ -0,0 +1,18 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "typescript", + "repositoryUrl": "https://github.com/Microsoft/TypeScript", + "commitHash": "54426a14f4c232da8e563d20ca8e71263e1c96b5" + } + }, + "isOnlyProductionDependency": true, + "license": "Apache-2.0", + "version": "2.6.2" + } + ], + "version": 1 +} diff --git a/extensions/clojure/.vscodeignore b/extensions/clojure/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/clojure/.vscodeignore +++ b/extensions/clojure/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/clojure/OSSREADME.json b/extensions/clojure/OSSREADME.json deleted file mode 100644 index 61cc0b52b..000000000 --- a/extensions/clojure/OSSREADME.json +++ /dev/null @@ -1,8 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "atom/language-clojure", - "version": "0.0.0", - "license": "MIT", - "repositoryURL": "https://github.com/atom/language-clojure", - "description": "The file syntaxes/clojure.tmLanguage.json was derived from the Atom package https://github.com/atom/language-clojure which was originally converted from the TextMate bundle https://github.com/mmcgrana/textmate-clojure." -}] diff --git a/extensions/clojure/cgmanifest.json b/extensions/clojure/cgmanifest.json new file mode 100644 index 000000000..bb14f456a --- /dev/null +++ b/extensions/clojure/cgmanifest.json @@ -0,0 +1,18 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "atom/language-clojure", + "repositoryUrl": "https://github.com/atom/language-clojure", + "commitHash": "ecc790326bc8e14220e4d2d72a392a30876c3219" + } + }, + "license": "MIT", + "version": "0.0.0", + "description": "The file syntaxes/clojure.tmLanguage.json was derived from the Atom package https://github.com/atom/language-clojure which was originally converted from the TextMate bundle https://github.com/mmcgrana/textmate-clojure." + } + ], + "version": 1 +} diff --git a/extensions/coffeescript/.vscodeignore b/extensions/coffeescript/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/coffeescript/.vscodeignore +++ b/extensions/coffeescript/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/coffeescript/OSSREADME.json b/extensions/coffeescript/OSSREADME.json deleted file mode 100644 index 92bff23d0..000000000 --- a/extensions/coffeescript/OSSREADME.json +++ /dev/null @@ -1,9 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "atom/language-coffee-script", - "version": "0.0.0", - "license": "MIT", - "repositoryURL": "https://github.com/atom/language-coffee-script", - "description": "The file syntaxes/coffeescript.tmLanguage.json was derived from the Atom package https://github.com/atom/language-coffee-script which was originally converted from the TextMate bundle https://github.com/jashkenas/coffee-script-tmbundle." - -}] diff --git a/extensions/coffeescript/cgmanifest.json b/extensions/coffeescript/cgmanifest.json new file mode 100644 index 000000000..b8f3fc4a7 --- /dev/null +++ b/extensions/coffeescript/cgmanifest.json @@ -0,0 +1,18 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "atom/language-coffee-script", + "repositoryUrl": "https://github.com/atom/language-coffee-script", + "commitHash": "a0da2a73ad817e2fc13c2ef8fcd2624017c39610" + } + }, + "license": "MIT", + "description": "The file syntaxes/coffeescript.tmLanguage.json was derived from the Atom package https://github.com/atom/language-coffee-script which was originally converted from the TextMate bundle https://github.com/jashkenas/coffee-script-tmbundle.", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/configuration-editing/src/extension.ts b/extensions/configuration-editing/src/extension.ts index abc38585c..b0a083e7e 100644 --- a/extensions/configuration-editing/src/extension.ts +++ b/extensions/configuration-editing/src/extension.ts @@ -283,4 +283,4 @@ vscode.languages.registerDocumentSymbolProvider({ pattern: '**/launch.json', lan return result; } -}); +}, { label: 'Launch Targets' }); diff --git a/extensions/cpp/.vscodeignore b/extensions/cpp/.vscodeignore index d41e74d16..d42f161c7 100644 --- a/extensions/cpp/.vscodeignore +++ b/extensions/cpp/.vscodeignore @@ -1,3 +1,3 @@ build/** test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/cpp/OSSREADME.json b/extensions/cpp/OSSREADME.json deleted file mode 100644 index 5137487d2..000000000 --- a/extensions/cpp/OSSREADME.json +++ /dev/null @@ -1,31 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[ - { - "name": "atom/language-c", - "version": "0.0.0", - "license": "MIT", - "repositoryURL": "https://github.com/atom/language-c", - "description": "The files syntaxes/c.json and syntaxes/c++.json were derived from the Atom package https://atom.io/packages/language-c which was originally converted from the C TextMate bundle https://github.com/textmate/c.tmbundle." - }, - { - "name": "textmate/c.tmbundle", - "version": "0.0.0", - "license": "TextMate Bundle License", - "repositoryURL": "https://github.com/textmate/c.tmbundle", - "licenseDetail": [ - "Copyright (c) textmate-c.tmbundle authors", - "", - "If not otherwise specified (see below), files in this repository fall under the following license:", - "", - "Permission to copy, use, modify, sell and distribute this", - "software is granted. This software is provided \"as is\" without", - "express or implied warranty, and with no claim as to its", - "suitability for any purpose.", - "", - "An exception is made for files in readable text which contain their own license information,", - "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", - "to the base-name name of the original file, and an extension of txt, html, or similar. For example", - "\"tidy\" is accompanied by \"tidy-license.txt\"." - ] - } -] \ No newline at end of file diff --git a/extensions/cpp/cgmanifest.json b/extensions/cpp/cgmanifest.json new file mode 100644 index 000000000..16d849387 --- /dev/null +++ b/extensions/cpp/cgmanifest.json @@ -0,0 +1,45 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "atom/language-c", + "repositoryUrl": "https://github.com/atom/language-c", + "commitHash": "9c0c5f202741a5647025db8d5df5fefba47b036c" + } + }, + "license": "MIT", + "version": "0.0.0", + "description": "The files syntaxes/c.json and syntaxes/c++.json were derived from the Atom package https://atom.io/packages/language-c which was originally converted from the C TextMate bundle https://github.com/textmate/c.tmbundle." + }, + { + "component": { + "type": "git", + "git": { + "name": "textmate/c.tmbundle", + "repositoryUrl": "https://github.com/textmate/c.tmbundle", + "commitHash": "9aa365882274ca52f01722f3dbb169b9539a20ee" + } + }, + "licenseDetail": [ + "Copyright (c) textmate-c.tmbundle authors", + "", + "If not otherwise specified (see below), files in this repository fall under the following license:", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose.", + "", + "An exception is made for files in readable text which contain their own license information,", + "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", + "to the base-name name of the original file, and an extension of txt, html, or similar. For example", + "\"tidy\" is accompanied by \"tidy-license.txt\"." + ], + "license": "TextMate Bundle License", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/csharp/.vscodeignore b/extensions/csharp/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/csharp/.vscodeignore +++ b/extensions/csharp/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/csharp/OSSREADME.json b/extensions/csharp/OSSREADME.json deleted file mode 100644 index f069b5f41..000000000 --- a/extensions/csharp/OSSREADME.json +++ /dev/null @@ -1,10 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[ - { - "name": "dotnet/csharp-tmLanguage", - "version": "0.1.0", - "license": "MIT", - "repositoryURL": "https://github.com/dotnet/csharp-tmLanguage", - "description": "The file syntaxes/csharp.tmLanguage.json was derived from https://github.com/dotnet/csharp-tmLanguage" - } -] \ No newline at end of file diff --git a/extensions/csharp/cgmanifest.json b/extensions/csharp/cgmanifest.json new file mode 100644 index 000000000..58cb2a2ce --- /dev/null +++ b/extensions/csharp/cgmanifest.json @@ -0,0 +1,18 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "dotnet/csharp-tmLanguage", + "repositoryUrl": "https://github.com/dotnet/csharp-tmLanguage", + "commitHash": "b95e4044ff1ac52e03f622de76f459dc5388954c" + } + }, + "license": "MIT", + "version": "0.1.0", + "description": "The file syntaxes/csharp.tmLanguage.json was derived from https://github.com/dotnet/csharp-tmLanguage" + } + ], + "version": 1 +} diff --git a/extensions/csharp/syntaxes/csharp.tmLanguage.json b/extensions/csharp/syntaxes/csharp.tmLanguage.json index 358d1b76c..31fb9c6b8 100644 --- a/extensions/csharp/syntaxes/csharp.tmLanguage.json +++ b/extensions/csharp/syntaxes/csharp.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/dotnet/csharp-tmLanguage/commit/822c147f65d9b009096d7163f7d624379812cd63", + "version": "https://github.com/dotnet/csharp-tmLanguage/commit/b95e4044ff1ac52e03f622de76f459dc5388954c", "name": "C#", "scopeName": "source.cs", "patterns": [ @@ -2607,10 +2607,14 @@ }, "patterns": [ { - "include": "#string-character-escape" + "include": "#char-character-escape" } ] }, + "char-character-escape": { + "name": "constant.character.escape.cs", + "match": "\\\\(['\"\\\\0abfnrtv]|x[0-9a-fA-F]{1,4}|u[0-9a-fA-F]{4})" + }, "string-literal": { "name": "string.quoted.double.cs", "begin": "(? { - registerCompletionProviders(context); - resolveUpdateExtensionsPath(); + context.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e) => { + if (e.affectsConfiguration('emmet.includeLanguages')) { + registerCompletionProviders(context); + } + if (e.affectsConfiguration('emmet.extensionsPath')) { + updateEmmetExtensionsPath(); + } })); } diff --git a/extensions/emmet/src/test/wrapWithAbbreviation.test.ts b/extensions/emmet/src/test/wrapWithAbbreviation.test.ts index 910f6ef0d..9fc924508 100644 --- a/extensions/emmet/src/test/wrapWithAbbreviation.test.ts +++ b/extensions/emmet/src/test/wrapWithAbbreviation.test.ts @@ -349,18 +349,46 @@ suite('Tests for Wrap with Abbreviations', () => { }); }); - test('Wrap individual lines with abbreviation and format set to false', () => { + test('Wrap with abbreviation and format set to false', () => { return workspace.getConfiguration('emmet').update('syntaxProfiles',{ 'html' : { 'format': false } } , ConfigurationTarget.Global).then(() => { return testWrapWithAbbreviation(multiCursors,'h1',wrapInlineElementExpectedFormatFalse).then(() => { return workspace.getConfiguration('emmet').update('syntaxProfiles',oldValueForSyntaxProfiles ? oldValueForSyntaxProfiles.globalValue : undefined, ConfigurationTarget.Global); }); }); }); + + test('Wrap multi line selections with abbreviation', () => { + const htmlContentsForWrapMultiLineTests = ` + + `; + + const wrapMultiLineExpected = ` + + `; + + return testWrapWithAbbreviation([new Selection(2, 4, 3, 9), new Selection(5, 4, 6, 9)], 'div', wrapMultiLineExpected, htmlContentsForWrapMultiLineTests); + }); }); -function testWrapWithAbbreviation(selections: Selection[], abbreviation: string, expectedContents: string): Thenable { - return withRandomFileEditor(htmlContentsForWrapTests, 'html', (editor, _) => { +function testWrapWithAbbreviation(selections: Selection[], abbreviation: string, expectedContents: string, input: string = htmlContentsForWrapTests): Thenable { + return withRandomFileEditor(input, 'html', (editor, _) => { editor.selections = selections; const promise = wrapWithAbbreviation({ abbreviation }); if (!promise) { diff --git a/extensions/emmet/src/util.ts b/extensions/emmet/src/util.ts index c74fb6511..6e2531c00 100644 --- a/extensions/emmet/src/util.ts +++ b/extensions/emmet/src/util.ts @@ -13,14 +13,19 @@ let _emmetHelper: any; let _currentExtensionsPath: string | undefined = undefined; export function getEmmetHelper() { + // Lazy load vscode-emmet-helper instead of importing it + // directly to reduce the start-up time of the extension if (!_emmetHelper) { _emmetHelper = require('vscode-emmet-helper'); } - resolveUpdateExtensionsPath(); + updateEmmetExtensionsPath(); return _emmetHelper; } -export function resolveUpdateExtensionsPath() { +/** + * Update Emmet Helper to use user snippets from the extensionsPath setting + */ +export function updateEmmetExtensionsPath() { if (!_emmetHelper) { return; } @@ -31,7 +36,10 @@ export function resolveUpdateExtensionsPath() { } } -export const LANGUAGE_MODES: any = { +/** + * Mapping between languages that support Emmet and completion trigger characters + */ +export const LANGUAGE_MODES: { [id: string]: string[] } = { 'html': ['!', '.', '}', ':', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], 'jade': ['!', '.', '}', ':', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], 'slim': ['!', '.', '}', ':', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'], @@ -47,19 +55,6 @@ export const LANGUAGE_MODES: any = { 'typescriptreact': ['!', '.', '}', '*', '$', ']', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] }; -export const allowedMimeTypesInScriptTag = ['text/html', 'text/plain', 'text/x-template', 'text/template', 'text/ng-template']; - -const emmetModes = ['html', 'pug', 'slim', 'haml', 'xml', 'xsl', 'jsx', 'css', 'scss', 'sass', 'less', 'stylus']; - -// Explicitly map languages that have built-in grammar in VS Code to their parent language -// to get emmet completion support -// For other languages, users will have to use `emmet.includeLanguages` or -// language specific extensions can provide emmet completion support -export const MAPPED_MODES: Object = { - 'handlebars': 'html', - 'php': 'html' -}; - export function isStyleSheet(syntax: string): boolean { let stylesheetSyntaxes = ['css', 'scss', 'sass', 'less', 'stylus']; return (stylesheetSyntaxes.indexOf(syntax) > -1); @@ -78,6 +73,15 @@ export function validate(allowStylesheet: boolean = true): boolean { } export function getMappingForIncludedLanguages(): any { + // Explicitly map languages that have built-in grammar in VS Code to their parent language + // to get emmet completion support + // For other languages, users will have to use `emmet.includeLanguages` or + // language specific extensions can provide emmet completion support + const MAPPED_MODES: Object = { + 'handlebars': 'html', + 'php': 'html' + }; + const finalMappedModes = Object.create(null); let includeLanguagesConfig = vscode.workspace.getConfiguration('emmet')['includeLanguages']; let includeLanguages = Object.assign({}, MAPPED_MODES, includeLanguagesConfig ? includeLanguagesConfig : {}); @@ -111,6 +115,7 @@ export function getEmmetMode(language: string, excludedLanguages: string[]): str if (language === 'jade') { return 'pug'; } + const emmetModes = ['html', 'pug', 'slim', 'haml', 'xml', 'xsl', 'jsx', 'css', 'scss', 'sass', 'less', 'stylus']; if (emmetModes.indexOf(language) > -1) { return language; } @@ -137,6 +142,12 @@ const openBrace = 123; const slash = 47; const star = 42; +/** + * Traverse the given document backward & forward from given position + * to find a complete ruleset, then parse just that to return a Stylesheet + * @param document vscode.TextDocument + * @param position vscode.Position + */ export function parsePartialStylesheet(document: vscode.TextDocument, position: vscode.Position): Stylesheet | undefined { const isCSS = document.languageId === 'css'; let startPosition = new vscode.Position(0, 0); @@ -145,6 +156,25 @@ export function parsePartialStylesheet(document: vscode.TextDocument, position: const limitPosition = limitCharacter > 0 ? document.positionAt(limitCharacter) : startPosition; const stream = new DocumentStreamReader(document, position); + function findOpeningCommentBeforePosition(pos: vscode.Position): vscode.Position | undefined { + let text = document.getText(new vscode.Range(0, 0, pos.line, pos.character)); + let offset = text.lastIndexOf('/*'); + if (offset === -1) { + return; + } + return document.positionAt(offset); + } + + function findClosingCommentAfterPosition(pos: vscode.Position): vscode.Position | undefined { + let text = document.getText(new vscode.Range(pos.line, pos.character, document.lineCount - 1, document.lineAt(document.lineCount - 1).text.length)); + let offset = text.indexOf('*/'); + if (offset === -1) { + return; + } + offset += 2 + document.offsetAt(pos); + return document.positionAt(offset); + } + function consumeLineCommentBackwards() { if (!isCSS && currentLine !== stream.pos.line) { currentLine = stream.pos.line; @@ -158,7 +188,7 @@ export function parsePartialStylesheet(document: vscode.TextDocument, position: function consumeBlockCommentBackwards() { if (stream.peek() === slash) { if (stream.backUp(1) === star) { - stream.pos = findOpeningCommentBeforePosition(document, stream.pos) || startPosition; + stream.pos = findOpeningCommentBeforePosition(stream.pos) || startPosition; } else { stream.next(); } @@ -170,7 +200,7 @@ export function parsePartialStylesheet(document: vscode.TextDocument, position: if (stream.eat(slash) && !isCSS) { stream.pos = new vscode.Position(stream.pos.line + 1, 0); } else if (stream.eat(star)) { - stream.pos = findClosingCommentAfterPosition(document, stream.pos) || endPosition; + stream.pos = findClosingCommentAfterPosition(stream.pos) || endPosition; } } } @@ -264,25 +294,6 @@ export function parsePartialStylesheet(document: vscode.TextDocument, position: } } -function findOpeningCommentBeforePosition(document: vscode.TextDocument, position: vscode.Position): vscode.Position | undefined { - let text = document.getText(new vscode.Range(0, 0, position.line, position.character)); - let offset = text.lastIndexOf('/*'); - if (offset === -1) { - return; - } - return document.positionAt(offset); -} - -function findClosingCommentAfterPosition(document: vscode.TextDocument, position: vscode.Position): vscode.Position | undefined { - let text = document.getText(new vscode.Range(position.line, position.character, document.lineCount - 1, document.lineAt(document.lineCount - 1).text.length)); - let offset = text.indexOf('*/'); - if (offset === -1) { - return; - } - offset += 2 + document.offsetAt(position); - return document.positionAt(offset); -} - /** * Returns node corresponding to given position in the given root node */ @@ -311,11 +322,22 @@ export function getNode(root: Node | undefined, position: vscode.Position, inclu return foundNode; } +export const allowedMimeTypesInScriptTag = ['text/html', 'text/plain', 'text/x-template', 'text/template', 'text/ng-template']; + +/** + * Returns HTML node corresponding to given position in the given root node + * If position is inside a script tag of type template, then it will be parsed to find the inner HTML node as well + */ export function getHtmlNode(document: vscode.TextDocument, root: Node | undefined, position: vscode.Position, includeNodeBoundary: boolean): HtmlNode | undefined { let currentNode = getNode(root, position, includeNodeBoundary); if (!currentNode) { return; } - if (isTemplateScript(currentNode) && currentNode.close && + const isTemplateScript = currentNode.name === 'script' && + (currentNode.attributes && + currentNode.attributes.some(x => x.name.toString() === 'type' + && allowedMimeTypesInScriptTag.indexOf(x.value.toString()) > -1)); + + if (isTemplateScript && currentNode.close && (position.isAfter(currentNode.open.end) && position.isBefore(currentNode.close.start))) { let buffer = new DocumentStreamReader(document, currentNode.open.end, new vscode.Range(currentNode.open.end, currentNode.close.start)); @@ -340,6 +362,10 @@ export function getInnerRange(currentNode: HtmlNode): vscode.Range | undefined { return new vscode.Range(currentNode.open.end, currentNode.close.start); } +/** + * Returns the deepest non comment node under given node + * @param node + */ export function getDeepestNode(node: Node | undefined): Node | undefined { if (!node || !node.children || node.children.length === 0 || !node.children.find(x => x.type !== 'comment')) { return node; @@ -434,37 +460,32 @@ export function getNodesInBetween(node1: Node, node2: Node): Node[] { return [node1]; } - // Same parent - if (sameNodes(node1.parent, node2.parent)) { - return getNextSiblingsTillPosition(node1, node2.end); - } - - // node2 is ancestor of node1 - if (node2.start.isBefore(node1.start)) { - return [node2]; - } + // Not siblings + if (!sameNodes(node1.parent, node2.parent)) { + // node2 is ancestor of node1 + if (node2.start.isBefore(node1.start)) { + return [node2]; + } - // node1 is ancestor of node2 - if (node2.start.isBefore(node1.end)) { - return [node1]; - } + // node1 is ancestor of node2 + if (node2.start.isBefore(node1.end)) { + return [node1]; + } - // Get the highest ancestor of node1 that should be commented - while (node1.parent && node1.parent.end.isBefore(node2.start)) { - node1 = node1.parent; - } + // Get the highest ancestor of node1 that should be commented + while (node1.parent && node1.parent.end.isBefore(node2.start)) { + node1 = node1.parent; + } - // Get the highest ancestor of node2 that should be commented - while (node2.parent && node2.parent.start.isAfter(node1.start)) { - node2 = node2.parent; + // Get the highest ancestor of node2 that should be commented + while (node2.parent && node2.parent.start.isAfter(node1.start)) { + node2 = node2.parent; + } } - return getNextSiblingsTillPosition(node1, node2.end); -} - -function getNextSiblingsTillPosition(node: Node, position: vscode.Position): Node[] { - let siblings: Node[] = []; - let currentNode = node; + const siblings: Node[] = []; + let currentNode = node1; + const position = node2.end; while (currentNode && position.isAfter(currentNode.start)) { siblings.push(currentNode); currentNode = currentNode.nextSibling; @@ -589,9 +610,4 @@ export function isStyleAttribute(currentNode: Node | null, position: vscode.Posi return position.isAfterOrEqual(styleAttribute.value.start) && position.isBeforeOrEqual(styleAttribute.value.end); } -export function isTemplateScript(currentNode: HtmlNode): boolean { - return currentNode.name === 'script' && - (currentNode.attributes && - currentNode.attributes.some(x => x.name.toString() === 'type' - && allowedMimeTypesInScriptTag.indexOf(x.value.toString()) > -1)); -} + diff --git a/extensions/fsharp/.vscodeignore b/extensions/fsharp/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/fsharp/.vscodeignore +++ b/extensions/fsharp/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/fsharp/OSSREADME.json b/extensions/fsharp/OSSREADME.json deleted file mode 100644 index 95d6c5b02..000000000 --- a/extensions/fsharp/OSSREADME.json +++ /dev/null @@ -1,8 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "ionide/ionide-fsgrammar", - "version": "0.0.0", - "license": "MIT", - "repositoryURL": "https://github.com/ionide/ionide-fsgrammar", - "description": "The file syntaxes/fsharp.json was included from https://github.com/ionide/ionide-fsgrammar/blob/master/grammar/fsharp.json." -}] diff --git a/extensions/fsharp/cgmanifest.json b/extensions/fsharp/cgmanifest.json new file mode 100644 index 000000000..df594e070 --- /dev/null +++ b/extensions/fsharp/cgmanifest.json @@ -0,0 +1,18 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "ionide/ionide-fsgrammar", + "repositoryUrl": "https://github.com/ionide/ionide-fsgrammar", + "commitHash": "24c1588529af144d205f66fbcec6889500f9aaa9" + } + }, + "license": "MIT", + "description": "The file syntaxes/fsharp.json was included from https://github.com/ionide/ionide-fsgrammar/blob/master/grammar/fsharp.json.", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json b/extensions/fsharp/syntaxes/fsharp.tmLanguage.json index 32bcaa286..067389b16 100644 --- a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json +++ b/extensions/fsharp/syntaxes/fsharp.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/ionide/ionide-fsgrammar/commit/a20d836fa67d6daf81fd63e8baa97aa5e1516006", + "version": "https://github.com/ionide/ionide-fsgrammar/commit/24c1588529af144d205f66fbcec6889500f9aaa9", "name": "fsharp", "scopeName": "source.fsharp", "patterns": [ @@ -276,6 +276,15 @@ } } }, + { + "match": "(\\|)", + "comments": "Prevent captures of `|>` as a keyword when defining custom operator like `<|>`", + "captures": { + "1": { + "name": "keyword.symbol.fsharp" + } + } + }, { "include": "#keywords" } diff --git a/extensions/git/.vscodeignore b/extensions/git/.vscodeignore index 46f8dd110..7462f7448 100644 --- a/extensions/git/.vscodeignore +++ b/extensions/git/.vscodeignore @@ -4,5 +4,5 @@ out/** tsconfig.json build/** extension.webpack.config.js -OSSREADME.json -yarn.lock \ No newline at end of file +cgmanifest.json +yarn.lock diff --git a/extensions/git/OSSREADME.json b/extensions/git/OSSREADME.json deleted file mode 100644 index 533d6ea68..000000000 --- a/extensions/git/OSSREADME.json +++ /dev/null @@ -1,52 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[ - { - "name": "textmate/git.tmbundle", - "version": "0.0.0", - "license": "MIT", - "repositoryURL": "https://github.com/textmate/git.tmbundle", - "licenseDetail": [ - "Copyright (c) 2008 Tim Harper", - "", - "Permission is hereby granted, free of charge, to any person obtaining", - "a copy of this software and associated documentation files (the\"", - "Software\"), to deal in the Software without restriction, including", - "without limitation the rights to use, copy, modify, merge, publish,", - "distribute, sublicense, and/or sell copies of the Software, and to", - "permit persons to whom the Software is furnished to do so, subject to", - "the following conditions:", - "", - "The above copyright notice and this permission notice shall be", - "included in all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,", - "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", - "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND", - "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE", - "LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", - "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION", - "WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." - ] - }, - { - "name": "textmate/diff.tmbundle", - "version": "0.0.0", - "license": "TextMate Bundle License", - "repositoryURL": "https://github.com/textmate/diff.tmbundle", - "licenseDetail": [ - "Copyright (c) textmate-diff.tmbundle project authors", - "", - "If not otherwise specified (see below), files in this repository fall under the following license:", - "", - "Permission to copy, use, modify, sell and distribute this", - "software is granted. This software is provided \"as is\" without", - "express or implied warranty, and with no claim as to its", - "suitability for any purpose.", - "", - "An exception is made for files in readable text which contain their own license information,", - "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", - "to the base-name name of the original file, and an extension of txt, html, or similar. For example", - "\"tidy\" is accompanied by \"tidy-license.txt\"." - ] - } -] \ No newline at end of file diff --git a/extensions/git/README.md b/extensions/git/README.md index bd7a8ef26..a20f32075 100644 --- a/extensions/git/README.md +++ b/extensions/git/README.md @@ -4,4 +4,17 @@ ## Features -See [Git support in VS Code](https://code.visualstudio.com/docs/editor/versioncontrol#_git-support) to learn about the features of this extension. \ No newline at end of file +See [Git support in VS Code](https://code.visualstudio.com/docs/editor/versioncontrol#_git-support) to learn about the features of this extension. + +## API + +The Git extension exposes an API, reachable by any other extension. + +1. Copy `src/api/git.d.ts` to your extension's sources; +2. Include `git.d.ts` in your extension's compilation. +3. Get a hold of the API with the following snippet: + + ```ts + const gitExtension = vscode.extensions.getExtension('vscode.git').exports; + const git = gitExtension.getAPI(1); + ``` \ No newline at end of file diff --git a/extensions/git/cgmanifest.json b/extensions/git/cgmanifest.json new file mode 100644 index 000000000..a7e0b63aa --- /dev/null +++ b/extensions/git/cgmanifest.json @@ -0,0 +1,66 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "textmate/git.tmbundle", + "repositoryUrl": "https://github.com/textmate/git.tmbundle", + "commitHash": "93897a78c6e52bef13dadc0d4091d203c5facb40" + } + }, + "licenseDetail": [ + "Copyright (c) 2008 Tim Harper", + "", + "Permission is hereby granted, free of charge, to any person obtaining", + "a copy of this software and associated documentation files (the\"", + "Software\"), to deal in the Software without restriction, including", + "without limitation the rights to use, copy, modify, merge, publish,", + "distribute, sublicense, and/or sell copies of the Software, and to", + "permit persons to whom the Software is furnished to do so, subject to", + "the following conditions:", + "", + "The above copyright notice and this permission notice shall be", + "included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,", + "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", + "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND", + "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE", + "LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION", + "OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION", + "WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ], + "license": "MIT", + "version": "0.0.0" + }, + { + "component": { + "type": "git", + "git": { + "name": "textmate/diff.tmbundle", + "repositoryUrl": "https://github.com/textmate/diff.tmbundle", + "commitHash": "0593bb775eab1824af97ef2172fd38822abd97d7" + } + }, + "licenseDetail": [ + "Copyright (c) textmate-diff.tmbundle project authors", + "", + "If not otherwise specified (see below), files in this repository fall under the following license:", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose.", + "", + "An exception is made for files in readable text which contain their own license information,", + "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", + "to the base-name name of the original file, and an extension of txt, html, or similar. For example", + "\"tidy\" is accompanied by \"tidy-license.txt\"." + ], + "license": "TextMate Bundle License", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/git/package.json b/extensions/git/package.json index 81734a94d..ef4385ef3 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -265,6 +265,11 @@ "title": "%command.fetch%", "category": "Git" }, + { + "command": "git.fetchPrune", + "title": "%command.fetchPrune%", + "category": "Git" + }, { "command": "git.fetchAll", "title": "%command.fetchAll%", @@ -533,6 +538,10 @@ "command": "git.fetch", "when": "config.git.enabled && gitOpenRepositoryCount != 0" }, + { + "command": "git.fetchPrune", + "when": "config.git.enabled && gitOpenRepositoryCount != 0" + }, { "command": "git.fetchAll", "when": "config.git.enabled && gitOpenRepositoryCount != 0" @@ -815,7 +824,12 @@ }, { "command": "git.openFile2", - "when": "scmProvider == git && scmResourceGroup == merge && config.git.showInlineOpenFileAction", + "when": "scmProvider == git && scmResourceGroup == merge && config.git.showInlineOpenFileAction && config.git.openDiffOnClick", + "group": "inline0" + }, + { + "command": "git.openChange", + "when": "scmProvider == git && scmResourceGroup == merge && config.git.showInlineOpenFileAction && !config.git.openDiffOnClick", "group": "inline0" }, { @@ -845,7 +859,12 @@ }, { "command": "git.openFile2", - "when": "scmProvider == git && scmResourceGroup == index && config.git.showInlineOpenFileAction", + "when": "scmProvider == git && scmResourceGroup == index && config.git.showInlineOpenFileAction && config.git.openDiffOnClick", + "group": "inline0" + }, + { + "command": "git.openChange", + "when": "scmProvider == git && scmResourceGroup == index && config.git.showInlineOpenFileAction && !config.git.openDiffOnClick", "group": "inline0" }, { @@ -885,7 +904,12 @@ }, { "command": "git.openFile2", - "when": "scmProvider == git && scmResourceGroup == workingTree && config.git.showInlineOpenFileAction", + "when": "scmProvider == git && scmResourceGroup == workingTree && config.git.showInlineOpenFileAction && config.git.openDiffOnClick", + "group": "inline0" + }, + { + "command": "git.openChange", + "when": "scmProvider == git && scmResourceGroup == workingTree && config.git.showInlineOpenFileAction && !config.git.openDiffOnClick", "group": "inline0" }, { @@ -1093,9 +1117,24 @@ "git.promptToSaveFilesBeforeCommit": { "type": "boolean", "scope": "resource", - "default": false, + "default": true, "description": "%config.promptToSaveFilesBeforeCommit%" }, + "git.postCommitCommand": { + "type": "string", + "enum": [ + "none", + "push", + "sync" + ], + "enumDescriptions": [ + "%config.postCommitCommand.none%", + "%config.postCommitCommand.push%", + "%config.postCommitCommand.sync%" + ], + "markdownDescription": "%config.postCommitCommand%", + "default": "none" + }, "git.showInlineOpenFileAction": { "type": "boolean", "default": true, @@ -1189,6 +1228,11 @@ "type": "boolean", "default": true, "description": "%config.confirmForcePush%" + }, + "git.openDiffOnClick": { + "type": "boolean", + "default": true, + "description": "%config.openDiffOnClick%" } } }, @@ -1339,7 +1383,7 @@ "dependencies": { "byline": "^5.0.0", "file-type": "^7.2.0", - "iconv-lite": "0.4.19", + "iconv-lite": "^0.4.24", "jschardet": "^1.6.0", "vscode-extension-telemetry": "0.1.0", "vscode-nls": "^4.0.0", @@ -1353,4 +1397,4 @@ "@types/which": "^1.0.28", "mocha": "^3.2.0" } -} +} \ No newline at end of file diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 4d73891f8..356d1473b 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -37,6 +37,7 @@ "command.merge": "Merge Branch...", "command.createTag": "Create Tag", "command.fetch": "Fetch", + "command.fetchPrune": "Fetch (Prune)", "command.fetchAll": "Fetch From All Remotes", "command.pull": "Pull", "command.pullRebase": "Pull (Rebase)", @@ -66,7 +67,7 @@ "config.autoRepositoryDetection.subFolders": "Scan for subfolders of the currently opened folder.", "config.autoRepositoryDetection.openEditors": "Scan for parent folders of open files.", "config.autorefresh": "Whether auto refreshing is enabled.", - "config.autofetch": "Whether auto fetching is enabled.", + "config.autofetch": "When enabled, commits will automatically be fetched from the default remote of the current Git repository.", "config.confirmSync": "Confirm before synchronizing git repositories.", "config.countBadge": "Controls the git badge counter.", "config.countBadge.all": "Count all changes.", @@ -88,6 +89,10 @@ "config.discardAllScope": "Controls what changes are discarded by the `Discard all changes` command. `all` discards all changes. `tracked` discards only tracked files. `prompt` shows a prompt dialog every time the action is run.", "config.decorations.enabled": "Controls whether Git contributes colors and badges to the explorer and the open editors view.", "config.promptToSaveFilesBeforeCommit": "Controls whether Git should check for unsaved files before committing.", + "config.postCommitCommand": "Runs a git command after a successful commit.", + "config.postCommitCommand.none": "Don't run any command after a commit.", + "config.postCommitCommand.push": "Run 'Git Push' after a successful commit.", + "config.postCommitCommand.sync": "Run 'Git Sync' after a successful commit.", "config.showInlineOpenFileAction": "Controls whether to show an inline Open File action in the Git changes view.", "config.showPushSuccessNotification": "Controls whether to show a notification when a push is successful.", "config.inputValidation": "Controls when to show commit message input validation.", @@ -105,6 +110,7 @@ "config.allowForcePush": "Controls whether force push (with or without lease) is enabled.", "config.useForcePushWithLease": "Controls whether force pushing uses the safer force-with-lease variant.", "config.confirmForcePush": "Controls whether to ask for confirmation before force-pushing.", + "config.openDiffOnClick": "Controls whether the diff editor should be opened when clicking a change. Otherwise the regular editor will be opened.", "colors.added": "Color for added resources.", "colors.modified": "Color for modified resources.", "colors.deleted": "Color for deleted resources.", diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index b12f5dcd6..9469580d9 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -60,6 +60,10 @@ export class ApiRepository implements Repository { constructor(private _repository: BaseRepository) { } + apply(patch: string, reverse?: boolean): Promise { + return this._repository.apply(patch, reverse); + } + getConfigs(): Promise<{ key: string; value: string; }[]> { return this._repository.getConfigs(); } @@ -96,6 +100,10 @@ export class ApiRepository implements Repository { return this._repository.clean(paths.map(p => Uri.file(p))); } + diff(cached?: boolean) { + return this._repository.diff(cached); + } + diffWithHEAD(path: string): Promise { return this._repository.diffWithHEAD(path); } @@ -128,8 +136,8 @@ export class ApiRepository implements Repository { return this._repository.branch(name, checkout, ref); } - deleteBranch(name: string): Promise { - return this._repository.deleteBranch(name); + deleteBranch(name: string, force?: boolean): Promise { + return this._repository.deleteBranch(name, force); } getBranch(name: string): Promise { @@ -167,6 +175,10 @@ export class ApiRepository implements Repository { pull(): Promise { return this._repository.pull(); } + + push(remoteName?: string, branchName?: string, setUpstream: boolean = false): Promise { + return this._repository.pushTo(remoteName, branchName, setUpstream); + } } export class ApiGit implements Git { diff --git a/extensions/git/src/api/extension.ts b/extensions/git/src/api/extension.ts index 1ca1892a7..8d7dc611e 100644 --- a/extensions/git/src/api/extension.ts +++ b/extensions/git/src/api/extension.ts @@ -6,6 +6,8 @@ import { Model } from '../model'; import { GitExtension, Repository, API } from './git'; import { ApiRepository, ApiImpl } from './api1'; +import { Event, EventEmitter } from 'vscode'; +import { latchEvent } from '../util'; export function deprecated(_target: any, key: string, descriptor: any): void { if (typeof descriptor.value !== 'function') { @@ -19,50 +21,56 @@ export function deprecated(_target: any, key: string, descriptor: any): void { }; } -class NoModelGitExtension implements GitExtension { +export class GitExtensionImpl implements GitExtension { - @deprecated - async getGitPath(): Promise { - throw new Error('Git model not found'); - } + enabled: boolean = false; - @deprecated - async getRepositories(): Promise { - throw new Error('Git model not found'); - } + private _onDidChangeEnablement = new EventEmitter(); + readonly onDidChangeEnablement: Event = latchEvent(this._onDidChangeEnablement.event); - getAPI(): API { - throw new Error('Git model not found'); - } -} + private _model: Model | undefined = undefined; + + set model(model: Model | undefined) { + this._model = model; -class GitExtensionImpl implements GitExtension { + this.enabled = !!model; + this._onDidChangeEnablement.fire(this.enabled); + } - constructor(private _model: Model) { } + constructor(model?: Model) { + if (model) { + this.enabled = true; + this._model = model; + } + } @deprecated async getGitPath(): Promise { + if (!this._model) { + throw new Error('Git model not found'); + } + return this._model.git.path; } @deprecated async getRepositories(): Promise { + if (!this._model) { + throw new Error('Git model not found'); + } + return this._model.repositories.map(repository => new ApiRepository(repository)); } getAPI(version: number): API { + if (!this._model) { + throw new Error('Git model not found'); + } + if (version !== 1) { throw new Error(`No API version ${version} found.`); } return new ApiImpl(this._model); } -} - -export function createGitExtension(model?: Model): GitExtension { - if (!model) { - return new NoModelGitExtension(); - } - - return new GitExtensionImpl(model); -} +} \ No newline at end of file diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index ced0b597a..ea4d1f97d 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -128,6 +128,8 @@ export interface Repository { clean(paths: string[]): Promise; + apply(patch: string, reverse?: boolean): Promise; + diff(cached?: boolean): Promise; diffWithHEAD(path: string): Promise; diffWith(ref: string, path: string): Promise; diffIndexWithHEAD(path: string): Promise; @@ -138,7 +140,7 @@ export interface Repository { hashObject(data: string): Promise; createBranch(name: string, checkout: boolean, ref?: string): Promise; - deleteBranch(name: string): Promise; + deleteBranch(name: string, force?: boolean): Promise; getBranch(name: string): Promise; setBranchUpstream(name: string, upstream: string): Promise; @@ -152,6 +154,7 @@ export interface Repository { fetch(remote?: string, ref?: string): Promise; pull(): Promise; + push(remoteName?: string, branchName?: string, setUpstream?: boolean): Promise; } export interface API { @@ -163,9 +166,16 @@ export interface API { export interface GitExtension { + readonly enabled: boolean; + readonly onDidChangeEnablement: Event; + /** * Returns a specific API version. * + * Throws error if git extension is disabled. You can listed to the + * [GitExtension.onDidChangeEnablement](#GitExtension.onDidChangeEnablement) event + * to know when the extension becomes enabled/disabled. + * * @param version Version number. * @returns API instance */ diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 10d296f5e..e41a8161c 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -163,6 +163,7 @@ enum PushType { interface PushOptions { pushType: PushType; forcePush?: boolean; + silent?: boolean; } export class CommandCenter { @@ -193,7 +194,20 @@ export class CommandCenter { @command('git.openResource') async openResource(resource: Resource): Promise { - await this._openResource(resource, undefined, true, false); + const repository = this.model.getRepository(resource.resourceUri); + + if (!repository) { + return; + } + + const config = workspace.getConfiguration('git', Uri.file(repository.root)); + const openDiffOnClick = config.get('openDiffOnClick'); + + if (openDiffOnClick) { + await this._openResource(resource, undefined, true, false); + } else { + await this.openFile(resource); + } } private async _openResource(resource: Resource, preview?: boolean, preserveFocus?: boolean, preserveSelection?: boolean): Promise { @@ -646,6 +660,7 @@ export class CommandCenter { @command('git.openHEADFile') async openHEADFile(arg?: Resource | Uri): Promise { let resource: Resource | undefined = undefined; + const preview = !(arg instanceof Resource); if (arg instanceof Resource) { resource = arg; @@ -666,12 +681,18 @@ export class CommandCenter { return; } - return await commands.executeCommand('vscode.open', HEAD); + const opts: TextDocumentShowOptions = { + preview + }; + + return await commands.executeCommand('vscode.open', HEAD, opts); } @command('git.openChange') async openChange(arg?: Resource | Uri, ...resourceStates: SourceControlResourceState[]): Promise { const preserveFocus = arg instanceof Resource; + const preview = !(arg instanceof Resource); + const preserveSelection = arg instanceof Uri || !arg; let resources: Resource[] | undefined = undefined; @@ -698,7 +719,6 @@ export class CommandCenter { return; } - const preview = resources.length === 1 ? undefined : false; for (const resource of resources) { await this._openResource(resource, preview, preserveFocus, preserveSelection); } @@ -1219,6 +1239,17 @@ export class CommandCenter { await repository.commit(message, opts); + const postCommitCommand = config.get<'none' | 'push' | 'sync'>('postCommitCommand'); + + switch (postCommitCommand) { + case 'push': + await this._push(repository, { pushType: PushType.Push, silent: true }); + break; + case 'sync': + await this.sync(repository); + break; + } + return true; } @@ -1539,6 +1570,17 @@ export class CommandCenter { await repository.fetchDefault(); } + @command('git.fetchPrune', { repository: true }) + async fetchPrune(repository: Repository): Promise { + if (repository.remotes.length === 0) { + window.showWarningMessage(localize('no remotes to fetch', "This repository has no remotes configured to fetch from.")); + return; + } + + await repository.fetchPrune(); + } + + @command('git.fetchAll', { repository: true }) async fetchAll(repository: Repository): Promise { if (repository.remotes.length === 0) { @@ -1609,7 +1651,9 @@ export class CommandCenter { const remotes = repository.remotes; if (remotes.length === 0) { - window.showWarningMessage(localize('no remotes to push', "Your repository has no remotes configured to push to.")); + if (!pushOptions.silent) { + window.showWarningMessage(localize('no remotes to push', "Your repository has no remotes configured to push to.")); + } return; } @@ -1646,7 +1690,9 @@ export class CommandCenter { } if (!repository.HEAD || !repository.HEAD.name) { - window.showWarningMessage(localize('nobranch', "Please check out a branch to push to a remote.")); + if (!pushOptions.silent) { + window.showWarningMessage(localize('nobranch', "Please check out a branch to push to a remote.")); + } return; } @@ -1658,6 +1704,10 @@ export class CommandCenter { throw err; } + if (pushOptions.silent) { + return; + } + const branchName = repository.HEAD.name; const message = localize('confirm publish branch', "The branch '{0}' has no upstream branch. Would you like to publish this branch?", branchName); const yes = localize('ok', "OK"); diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 0deb723c9..ff7e7bdd6 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -822,15 +822,23 @@ export class Repository { } } - async diff(path: string, cached = false): Promise { + async apply(patch: string, reverse?: boolean): Promise { + const args = ['apply', patch]; + + if (reverse) { + args.push('-R'); + } + + await this.run(args); + } + + async diff(cached = false): Promise { const args = ['diff']; if (cached) { args.push('--cached'); } - args.push('--', path); - const result = await this.run(args); return result.stdout; } @@ -1106,14 +1114,7 @@ export class Repository { } async reset(treeish: string, hard: boolean = false): Promise { - const args = ['reset']; - - if (hard) { - args.push('--hard'); - } - - args.push(treeish); - + const args = ['reset', hard ? '--hard' : '--soft', treeish]; await this.run(args); } @@ -1157,7 +1158,7 @@ export class Repository { await this.run(args); } - async fetch(options: { remote?: string, ref?: string, all?: boolean } = {}): Promise { + async fetch(options: { remote?: string, ref?: string, all?: boolean, prune?: boolean } = {}): Promise { const args = ['fetch']; if (options.remote) { @@ -1170,6 +1171,11 @@ export class Repository { args.push('--all'); } + if (options.prune) { + args.push('--prune'); + } + + try { await this.run(args); } catch (err) { diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 1ff683150..52b7c3c10 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -17,7 +17,7 @@ import { toDisposable, filterEvent, eventToPromise } from './util'; import TelemetryReporter from 'vscode-extension-telemetry'; import { GitExtension } from './api/git'; import { GitProtocolHandler } from './protocolHandler'; -import { createGitExtension } from './api/extension'; +import { GitExtensionImpl } from './api/extension'; import * as path from 'path'; import * as fs from 'fs'; @@ -137,12 +137,15 @@ export async function activate(context: ExtensionContext): Promise if (!enabled) { const onConfigChange = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git')); const onEnabled = filterEvent(onConfigChange, () => workspace.getConfiguration('git', null).get('enabled') === true); - await eventToPromise(onEnabled); + const result = new GitExtensionImpl(); + + eventToPromise(onEnabled).then(async () => result.model = await createModel(context, outputChannel, telemetryReporter, disposables)); + return result; } try { const model = await createModel(context, outputChannel, telemetryReporter, disposables); - return createGitExtension(model); + return new GitExtensionImpl(model); } catch (err) { if (!/Git installation not found/.test(err.message || '')) { throw err; @@ -151,9 +154,9 @@ export async function activate(context: ExtensionContext): Promise console.warn(err.message); outputChannel.appendLine(err.message); - await warnAboutMissingGit(); + warnAboutMissingGit(); - return createGitExtension(); + return new GitExtensionImpl(); } } diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 52bbb1ec6..d188eea02 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -295,6 +295,7 @@ export const enum Operation { GetObjectDetails = 'GetObjectDetails', SubmoduleUpdate = 'SubmoduleUpdate', RebaseContinue = 'RebaseContinue', + Apply = 'Apply' } function isReadOnly(operation: Operation): boolean { @@ -705,6 +706,10 @@ export class Repository implements Disposable { await this.run(Operation.Status); } + diff(cached?: boolean): Promise { + return this.run(Operation.Diff, () => this.repository.diff(cached)); + } + diffWithHEAD(path: string): Promise { return this.run(Operation.Diff, () => this.repository.diffWithHEAD(path)); } @@ -829,7 +834,7 @@ export class Repository implements Disposable { } async branch(name: string, _checkout: boolean, _ref?: string): Promise { - await this.run(Operation.Branch, () => this.repository.branch(name, true)); + await this.run(Operation.Branch, () => this.repository.branch(name, _checkout, _ref)); } async deleteBranch(name: string, force?: boolean): Promise { @@ -889,6 +894,11 @@ export class Repository implements Disposable { await this.run(Operation.Fetch, () => this.repository.fetch()); } + @throttle + async fetchPrune(): Promise { + await this.run(Operation.Fetch, () => this.repository.fetch({ prune: true })); + } + @throttle async fetchAll(): Promise { await this.run(Operation.Fetch, () => this.repository.fetch({ all: true })); @@ -1050,6 +1060,10 @@ export class Repository implements Disposable { return this.run(Operation.Show, () => this.repository.detectObjectType(object)); } + async apply(patch: string, reverse?: boolean): Promise { + return await this.run(Operation.Apply, () => this.repository.apply(patch, reverse)); + } + async getStashes(): Promise { return await this.repository.getStashes(); } diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index ea1ddb97a..14cbb0a65 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -33,7 +33,7 @@ export function combinedDisposable(disposables: IDisposable[]): IDisposable { export const EmptyDisposable = toDisposable(() => null); export function fireEvent(event: Event): Event { - return (listener, thisArgs = null, disposables?) => event(_ => listener.call(thisArgs), null, disposables); + return (listener, thisArgs = null, disposables?) => event(_ => (listener as any).call(thisArgs), null, disposables); } export function mapEvent(event: Event, map: (i: I) => O): Event { @@ -44,6 +44,18 @@ export function filterEvent(event: Event, filter: (e: T) => boolean): Even return (listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables); } +export function latchEvent(event: Event): Event { + let firstCall = true; + let cache: T; + + return filterEvent(event, value => { + let shouldEmit = firstCall || value !== cache; + firstCall = false; + cache = value; + return shouldEmit; + }); +} + export function anyEvent(...events: Event[]): Event { return (listener, thisArgs = null, disposables?) => { const result = combinedDisposable(events.map(event => event(i => listener.call(thisArgs, i)))); diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index 4a18c906e..616d42cad 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -151,10 +151,12 @@ he@1.1.1: resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= -iconv-lite@0.4.19: - version "0.4.19" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" - integrity sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ== +iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" inflight@^1.0.4: version "1.0.6" @@ -294,6 +296,11 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + semver@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" diff --git a/extensions/go/.vscodeignore b/extensions/go/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/go/.vscodeignore +++ b/extensions/go/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/go/OSSREADME.json b/extensions/go/OSSREADME.json deleted file mode 100644 index 389a61cdd..000000000 --- a/extensions/go/OSSREADME.json +++ /dev/null @@ -1,8 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "language-go", - "version": "0.39.0", - "license": "MIT", - "repositoryURL": "https://github.com/atom/language-go", - "description": "The file syntaxes/go.json was derived from the Atom package https://atom.io/packages/language-go." -}] diff --git a/extensions/go/cgmanifest.json b/extensions/go/cgmanifest.json new file mode 100644 index 000000000..46b1c1c43 --- /dev/null +++ b/extensions/go/cgmanifest.json @@ -0,0 +1,18 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "language-go", + "repositoryUrl": "https://github.com/atom/language-go", + "commitHash": "b6fd68f74efa109679e31fe6f4a41ac105262d0e" + } + }, + "license": "MIT", + "description": "The file syntaxes/go.json was derived from the Atom package https://atom.io/packages/language-go.", + "version": "0.39.0" + } + ], + "version": 1 +} diff --git a/extensions/groovy/.vscodeignore b/extensions/groovy/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/groovy/.vscodeignore +++ b/extensions/groovy/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/groovy/OSSREADME.json b/extensions/groovy/OSSREADME.json deleted file mode 100644 index 35222bdac..000000000 --- a/extensions/groovy/OSSREADME.json +++ /dev/null @@ -1,22 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "textmate/groovy.tmbundle", - "version": "0.0.0", - "license": "TextMate Bundle License", - "repositoryURL": "https://github.com/textmate/groovy.tmbundle", - "licenseDetail": [ - "Copyright (c) textmate-groovy.tmbundle project authors", - "", - "If not otherwise specified (see below), files in this repository fall under the following license:", - "", - "Permission to copy, use, modify, sell and distribute this", - "software is granted. This software is provided \"as is\" without", - "express or implied warranty, and with no claim as to its", - "suitability for any purpose.", - "", - "An exception is made for files in readable text which contain their own license information,", - "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", - "to the base-name name of the original file, and an extension of txt, html, or similar. For example", - "\"tidy\" is accompanied by \"tidy-license.txt\"." - ] -}] diff --git a/extensions/groovy/cgmanifest.json b/extensions/groovy/cgmanifest.json new file mode 100644 index 000000000..07dec53a6 --- /dev/null +++ b/extensions/groovy/cgmanifest.json @@ -0,0 +1,32 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "textmate/groovy.tmbundle", + "repositoryUrl": "https://github.com/textmate/groovy.tmbundle", + "commitHash": "85d8f7c97ae473ccb9473f6c8d27e4ec957f4be1" + } + }, + "licenseDetail": [ + "Copyright (c) textmate-groovy.tmbundle project authors", + "", + "If not otherwise specified (see below), files in this repository fall under the following license:", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose.", + "", + "An exception is made for files in readable text which contain their own license information,", + "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", + "to the base-name name of the original file, and an extension of txt, html, or similar. For example", + "\"tidy\" is accompanied by \"tidy-license.txt\"." + ], + "license": "TextMate Bundle License", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/grunt/src/main.ts b/extensions/grunt/src/main.ts index e90277cfe..38589e8a5 100644 --- a/extensions/grunt/src/main.ts +++ b/extensions/grunt/src/main.ts @@ -81,7 +81,7 @@ class FolderDetector { } public start(): void { - let pattern = path.join(this._workspaceFolder.uri.fsPath, '[Gg]runtfile.js'); + let pattern = path.join(this._workspaceFolder.uri.fsPath, '{node_modules,[Gg]runtfile.js}'); this.fileWatcher = vscode.workspace.createFileSystemWatcher(pattern); this.fileWatcher.onDidChange(() => this.promise = undefined); this.fileWatcher.onDidCreate(() => this.promise = undefined); diff --git a/extensions/gulp/images/gulp.png b/extensions/gulp/images/gulp.png index 894ee2306..2aa531f22 100644 Binary files a/extensions/gulp/images/gulp.png and b/extensions/gulp/images/gulp.png differ diff --git a/extensions/gulp/src/main.ts b/extensions/gulp/src/main.ts index c5adf07d1..b8612d511 100644 --- a/extensions/gulp/src/main.ts +++ b/extensions/gulp/src/main.ts @@ -82,7 +82,7 @@ class FolderDetector { } public start(): void { - let pattern = path.join(this._workspaceFolder.uri.fsPath, 'gulpfile{.babel.js,.js,.ts}'); + let pattern = path.join(this._workspaceFolder.uri.fsPath, '{node_modules,gulpfile{.babel.js,.js,.ts}}'); this.fileWatcher = vscode.workspace.createFileSystemWatcher(pattern); this.fileWatcher.onDidChange(() => this.promise = undefined); this.fileWatcher.onDidCreate(() => this.promise = undefined); diff --git a/extensions/handlebars/.vscodeignore b/extensions/handlebars/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/handlebars/.vscodeignore +++ b/extensions/handlebars/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/handlebars/OSSREADME.json b/extensions/handlebars/OSSREADME.json deleted file mode 100644 index 619107dcf..000000000 --- a/extensions/handlebars/OSSREADME.json +++ /dev/null @@ -1,26 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "daaain/Handlebars", - "version": "0.0.0", - "license": "MIT", - "repositoryURL": "https://github.com/daaain/Handlebars", - "licenseDetail": [ - "-- Credits", - "", - "Adapted from the great sublime-text-handlebars package by Nicholas Westlake.", - "", - "Thanks a lot to all the generous contributors (in alphabetical order): @bittersweetryan, @bradcliffe, @calumbrodie, @duncanbeevers, @hlvnst, @jonschlinkert, @Krutius, @samselikoff, @utkarshkukreti, @zeppelin", - "", - "-- License", - "", - "(The MIT License)", - "", - "Copyright (c) daaain/Handlebars project authors", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." - ] -}] diff --git a/extensions/handlebars/cgmanifest.json b/extensions/handlebars/cgmanifest.json new file mode 100644 index 000000000..5befa41e2 --- /dev/null +++ b/extensions/handlebars/cgmanifest.json @@ -0,0 +1,36 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "daaain/Handlebars", + "repositoryUrl": "https://github.com/daaain/Handlebars", + "commitHash": "790f2b0222098a3a236bd9e91bb9a039eeca4d8e" + } + }, + "licenseDetail": [ + "-- Credits", + "", + "Adapted from the great sublime-text-handlebars package by Nicholas Westlake.", + "", + "Thanks a lot to all the generous contributors (in alphabetical order): @bittersweetryan, @bradcliffe, @calumbrodie, @duncanbeevers, @hlvnst, @jonschlinkert, @Krutius, @samselikoff, @utkarshkukreti, @zeppelin", + "", + "-- License", + "", + "(The MIT License)", + "", + "Copyright (c) daaain/Handlebars project authors", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ], + "license": "MIT", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/hlsl/.vscodeignore b/extensions/hlsl/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/hlsl/.vscodeignore +++ b/extensions/hlsl/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/hlsl/OSSREADME.json b/extensions/hlsl/OSSREADME.json deleted file mode 100644 index 5acab3453..000000000 --- a/extensions/hlsl/OSSREADME.json +++ /dev/null @@ -1,8 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "shaders-tmLanguage", - "version": "0.1.0", - "license": "MIT", - "repositoryURL": "https://github.com/tgjones/shaders-tmLanguage" -}] diff --git a/extensions/hlsl/cgmanifest.json b/extensions/hlsl/cgmanifest.json new file mode 100644 index 000000000..92137372c --- /dev/null +++ b/extensions/hlsl/cgmanifest.json @@ -0,0 +1,17 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "shaders-tmLanguage", + "repositoryUrl": "https://github.com/tgjones/shaders-tmLanguage", + "commitHash": "cd1ef40f549f9ce2b9e6b73498688de114a85382" + } + }, + "license": "MIT", + "version": "0.1.0" + } + ], + "version": 1 +} diff --git a/extensions/html-language-features/.vscodeignore b/extensions/html-language-features/.vscodeignore index 98555b677..4215adf6e 100644 --- a/extensions/html-language-features/.vscodeignore +++ b/extensions/html-language-features/.vscodeignore @@ -17,4 +17,4 @@ yarn.lock server/extension.webpack.config.js extension.webpack.config.js CONTRIBUTING.md -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/html-language-features/OSSREADME.json b/extensions/html-language-features/OSSREADME.json deleted file mode 100644 index 95ad98fd9..000000000 --- a/extensions/html-language-features/OSSREADME.json +++ /dev/null @@ -1,95 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "js-beautify", - "version": "1.6.8", - "license": "MIT", - "repositoryURL": "https://github.com/beautify-web/js-beautify" -}, -{ - "name": "HTML 5.1 W3C Working Draft", - "version": "08 October 2015", - "license": "W3C Document License", - "repositoryURL": "http://www.w3.org/TR/2015/WD-html51-20151008/", - "licenseDetail": [ - "Copyright © 2015 W3C® (MIT, ERCIM, Keio, Beihang). This software or document includes material copied ", - "from or derived from HTML 5.1 W3C Working Draft (http://www.w3.org/TR/2015/WD-html51-20151008/.)", - "", - "THIS DOCUMENT IS PROVIDED \"AS IS,\" AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT ", - "NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, OR TITLE; THAT THE CONTENTS OF ", - "THE DOCUMENT ARE SUITABLE FOR ANY PURPOSE; NOR THAT THE IMPLEMENTATION OF SUCH CONTENTS WILL NOT INFRINGE ANY THIRD PARTY ", - "PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.", - "", - "COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE ", - "DOCUMENT OR THE PERFORMANCE OR IMPLEMENTATION OF THE CONTENTS THEREOF.", - "", - "The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to this document or its contents", - "without specific, written prior permission. Title to copyright in this document will at all times remain with copyright holders." - ] -}, -{ - "name": "Ionic documentation", - "version": "1.2.4", - "license": "Apache2", - "repositoryURL": "https://github.com/ionic-team/ionic-site", - "licenseDetail": [ - "Copyright Drifty Co. http://drifty.com/.", - "", - "Apache License", - "", - "Version 2.0, January 2004", - "", - "http://www.apache.org/licenses/", - "", - "TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION", - "", - "1. Definitions.", - "", - "\"License\" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.", - "", - "\"Licensor\" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.", - "", - "\"Legal Entity\" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, \"control\" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.", - "", - "\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising permissions granted by this License.", - "", - "\"Source\" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.", - "", - "\"Object\" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.", - "", - "\"Work\" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).", - "", - "\"Derivative Works\" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.", - "", - "\"Contribution\" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, \"submitted\" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as \"Not a Contribution.\"", - "", - "\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.", - "", - "2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.", - "", - "3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.", - "", - "4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:", - "", - "You must give any other recipients of the Work or Derivative Works a copy of this License; and", - "", - "You must cause any modified files to carry prominent notices stating that You changed the files; and", - "", - "You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and", - "", - "If the Work includes a \"NOTICE\" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.", - "", - "5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.", - "", - "6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.", - "", - "7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.", - "", - "8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.", - "", - "9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.", - "", - "END OF TERMS AND CONDITIONS" - ] -} - -] diff --git a/extensions/html-language-features/cgmanifest.json b/extensions/html-language-features/cgmanifest.json new file mode 100644 index 000000000..7f57d61af --- /dev/null +++ b/extensions/html-language-features/cgmanifest.json @@ -0,0 +1,115 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "js-beautify", + "repositoryUrl": "https://github.com/beautify-web/js-beautify", + "commitHash": "12e73365f9d0b203843c5b7c22d7017845a7c580" + } + }, + "license": "MIT", + "version": "1.6.8" + }, + { + "component": { + "type": "other", + "other": { + "name": "HTML 5.1 W3C Working Draft", + "downloadUrl": "http://www.w3.org/TR/2015/WD-html51-20151008/", + "version": "08 October 2015" + } + }, + "licenseDetail": [ + "Copyright © 2015 W3C® (MIT, ERCIM, Keio, Beihang). This software or document includes material copied ", + "from or derived from HTML 5.1 W3C Working Draft (http://www.w3.org/TR/2015/WD-html51-20151008/.)", + "", + "THIS DOCUMENT IS PROVIDED \"AS IS,\" AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT ", + "NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, OR TITLE; THAT THE CONTENTS OF ", + "THE DOCUMENT ARE SUITABLE FOR ANY PURPOSE; NOR THAT THE IMPLEMENTATION OF SUCH CONTENTS WILL NOT INFRINGE ANY THIRD PARTY ", + "PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.", + "", + "COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE ", + "DOCUMENT OR THE PERFORMANCE OR IMPLEMENTATION OF THE CONTENTS THEREOF.", + "", + "The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to this document or its contents", + "without specific, written prior permission. Title to copyright in this document will at all times remain with copyright holders." + ], + "license": "W3C Document License", + "version": "08 October 2015" + }, + { + "component": { + "type": "git", + "git": { + "name": "Ionic documentation", + "repositoryUrl": "https://github.com/ionic-team/ionic-site", + "commitHash": "e952bde103470738e19a456ec4acb0f1e650b619" + } + }, + "licenseDetail": [ + "Copyright Drifty Co. http://drifty.com/.", + "", + "Apache License", + "", + "Version 2.0, January 2004", + "", + "http://www.apache.org/licenses/", + "", + "TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION", + "", + "1. Definitions.", + "", + "\"License\" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.", + "", + "\"Licensor\" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.", + "", + "\"Legal Entity\" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, \"control\" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.", + "", + "\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising permissions granted by this License.", + "", + "\"Source\" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.", + "", + "\"Object\" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.", + "", + "\"Work\" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).", + "", + "\"Derivative Works\" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.", + "", + "\"Contribution\" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, \"submitted\" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as \"Not a Contribution.\"", + "", + "\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.", + "", + "2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.", + "", + "3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.", + "", + "4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:", + "", + "You must give any other recipients of the Work or Derivative Works a copy of this License; and", + "", + "You must cause any modified files to carry prominent notices stating that You changed the files; and", + "", + "You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and", + "", + "If the Work includes a \"NOTICE\" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.", + "", + "5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.", + "", + "6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.", + "", + "7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.", + "", + "8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.", + "", + "9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.", + "", + "END OF TERMS AND CONDITIONS" + ], + "license": "Apache2", + "version": "1.2.4" + } + ], + "version": 1 +} diff --git a/extensions/html-language-features/icons/html.png b/extensions/html-language-features/icons/html.png index f3f6d91b2..0d2fbe31d 100644 Binary files a/extensions/html-language-features/icons/html.png and b/extensions/html-language-features/icons/html.png differ diff --git a/extensions/html-language-features/server/lib/OSSREADME.json b/extensions/html-language-features/server/lib/OSSREADME.json deleted file mode 100644 index e5dbcd89d..000000000 --- a/extensions/html-language-features/server/lib/OSSREADME.json +++ /dev/null @@ -1,6 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "definitelytyped", - "repositoryURL": "https://github.com/DefinitelyTyped/DefinitelyTyped", - "license": "MIT" -}] \ No newline at end of file diff --git a/extensions/html-language-features/server/lib/cgmanifest.json b/extensions/html-language-features/server/lib/cgmanifest.json new file mode 100644 index 000000000..6e529a79f --- /dev/null +++ b/extensions/html-language-features/server/lib/cgmanifest.json @@ -0,0 +1,16 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "definitelytyped", + "repositoryUrl": "https://github.com/DefinitelyTyped/DefinitelyTyped", + "commitHash": "69e3ac6bec3008271f76bbfa7cf69aa9198c4ff0" + } + }, + "license": "MIT" + } + ], + "version": 1 +} diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index c46f30cf5..93882a7ec 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -9,8 +9,8 @@ }, "main": "./out/htmlServerMain", "dependencies": { - "vscode-css-languageservice": "^3.0.12-next.1", - "vscode-html-languageservice": "^2.1.9", + "vscode-css-languageservice": "^3.0.12", + "vscode-html-languageservice": "^2.1.10", "vscode-languageserver": "^5.1.0", "vscode-languageserver-types": "^3.13.0", "vscode-nls": "^4.0.0", diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index 48ff5aef2..6530713c4 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -229,18 +229,18 @@ supports-color@5.4.0: dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^3.0.12-next.1: - version "3.0.12-next.1" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.12-next.1.tgz#1bc76d04f68b6d3d9b25cf01592ba46cea91c26c" - integrity sha512-Be1pfmRlcRsKMl1O/5rci8lu8RlE5vwT8LOjUEfHZkz5eHL2n9rTLo3dzmbVGtSL7+T1XEArjqUks9MzzDUhcw== +vscode-css-languageservice@^3.0.12: + version "3.0.12" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.12.tgz#fb4aac5ae3c5b761b1db1d7224b78ff824284dc3" + integrity sha512-+FLQ9LcukIhnxaGTjDOqb3Nb1hesz9BLXf5yeoZxUsuK7joADPLPdxLwlZugFcMAvgmtnaFIGnzkQhGOVqf5yw== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" -vscode-html-languageservice@^2.1.9: - version "2.1.9" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-2.1.9.tgz#906f2f894b023d3a464739ebc5b4eb695ef1cf58" - integrity sha512-zHb6zqt55THIkHjywsjBqGwBr9vCOmBDh6mGyyawGi/8XH2Y6yIAH7KXTxN4Ov9A2M0CT2mwSA3tl+IKtIJtjg== +vscode-html-languageservice@^2.1.10: + version "2.1.10" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-2.1.10.tgz#3433fd53e188cb25d5ea190b61a148fe9965ff91" + integrity sha512-nuzLd7a3J+Ttvk/9Pg2H0vS7rV2oZRfsQYPRheHnUNJNqivkcieSI8ZCGvZjmr3NDBG2QQaRFambnCtceYAj3A== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" diff --git a/extensions/html/.vscodeignore b/extensions/html/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/html/.vscodeignore +++ b/extensions/html/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/html/OSSREADME.json b/extensions/html/OSSREADME.json deleted file mode 100644 index 6d2ab8bb1..000000000 --- a/extensions/html/OSSREADME.json +++ /dev/null @@ -1,22 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "textmate/html.tmbundle", - "version": "0.0.0", - "license": "TextMate Bundle License", - "repositoryURL": "https://github.com/textmate/html.tmbundle", - "licenseDetail": [ - "Copyright (c) textmate-html.tmbundle project authors", - "", - "If not otherwise specified (see below), files in this repository fall under the following license:", - "", - "Permission to copy, use, modify, sell and distribute this", - "software is granted. This software is provided \"as is\" without", - "express or implied warranty, and with no claim as to its", - "suitability for any purpose.", - "", - "An exception is made for files in readable text which contain their own license information,", - "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", - "to the base-name name of the original file, and an extension of txt, html, or similar. For example", - "\"tidy\" is accompanied by \"tidy-license.txt\"." - ] -}] diff --git a/extensions/html/cgmanifest.json b/extensions/html/cgmanifest.json new file mode 100644 index 000000000..7f3cc9396 --- /dev/null +++ b/extensions/html/cgmanifest.json @@ -0,0 +1,32 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "textmate/html.tmbundle", + "repositoryUrl": "https://github.com/textmate/html.tmbundle", + "commitHash": "390c8870273a2ae80244dae6db6ba064a802f407" + } + }, + "licenseDetail": [ + "Copyright (c) textmate-html.tmbundle project authors", + "", + "If not otherwise specified (see below), files in this repository fall under the following license:", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose.", + "", + "An exception is made for files in readable text which contain their own license information,", + "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", + "to the base-name name of the original file, and an extension of txt, html, or similar. For example", + "\"tidy\" is accompanied by \"tidy-license.txt\"." + ], + "license": "TextMate Bundle License", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/ini/.vscodeignore b/extensions/ini/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/ini/.vscodeignore +++ b/extensions/ini/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/ini/OSSREADME.json b/extensions/ini/OSSREADME.json deleted file mode 100644 index 2a916c76b..000000000 --- a/extensions/ini/OSSREADME.json +++ /dev/null @@ -1,22 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "textmate/ini.tmbundle", - "version": "0.0.0", - "license": "TextMate Bundle License", - "repositoryURL": "https://github.com/textmate/ini.tmbundle", - "licenseDetail": [ - "Copyright (c) textmate-ini.tmbundle project authors", - "", - "If not otherwise specified (see below), files in this folder fall under the following license: ", - "", - "Permission to copy, use, modify, sell and distribute this", - "software is granted. This software is provided \"as is\" without", - "express or implied warranty, and with no claim as to its", - "suitability for any purpose.", - "", - "An exception is made for files in readable text which contain their own license information, ", - "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added ", - "to the base-name name of the original file, and an extension of txt, html, or similar. For example ", - "\"tidy\" is accompanied by \"tidy-license.txt\"." - ] -}] diff --git a/extensions/ini/cgmanifest.json b/extensions/ini/cgmanifest.json new file mode 100644 index 000000000..ef9fa28b5 --- /dev/null +++ b/extensions/ini/cgmanifest.json @@ -0,0 +1,32 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "textmate/ini.tmbundle", + "repositoryUrl": "https://github.com/textmate/ini.tmbundle", + "commitHash": "2af0cbb0704940f967152616f2f1ff0aae6287a6" + } + }, + "licenseDetail": [ + "Copyright (c) textmate-ini.tmbundle project authors", + "", + "If not otherwise specified (see below), files in this folder fall under the following license: ", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose.", + "", + "An exception is made for files in readable text which contain their own license information, ", + "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added ", + "to the base-name name of the original file, and an extension of txt, html, or similar. For example ", + "\"tidy\" is accompanied by \"tidy-license.txt\"." + ], + "license": "TextMate Bundle License", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/jake/images/cowboy_hat.png b/extensions/jake/images/cowboy_hat.png index 89c708be0..22bd74def 100644 Binary files a/extensions/jake/images/cowboy_hat.png and b/extensions/jake/images/cowboy_hat.png differ diff --git a/extensions/jake/src/main.ts b/extensions/jake/src/main.ts index c4e20edf1..938ca51be 100644 --- a/extensions/jake/src/main.ts +++ b/extensions/jake/src/main.ts @@ -81,7 +81,7 @@ class FolderDetector { } public start(): void { - let pattern = path.join(this._workspaceFolder.uri.fsPath, '{Jakefile,Jakefile.js}'); + let pattern = path.join(this._workspaceFolder.uri.fsPath, '{node_modules,Jakefile,Jakefile.js}'); this.fileWatcher = vscode.workspace.createFileSystemWatcher(pattern); this.fileWatcher.onDidChange(() => this.promise = undefined); this.fileWatcher.onDidCreate(() => this.promise = undefined); diff --git a/extensions/java/.vscodeignore b/extensions/java/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/java/.vscodeignore +++ b/extensions/java/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/java/OSSREADME.json b/extensions/java/OSSREADME.json deleted file mode 100644 index c87e95751..000000000 --- a/extensions/java/OSSREADME.json +++ /dev/null @@ -1,9 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "atom/language-java", - "version": "0.0.0", - "license": "MIT", - "repositoryURL": "https://github.com/atom/language-java", - "description": "The file syntaxes/java.tmLanguage.json was derived from the Atom package https://github.com/atom/language-java which was originally converted from the TextMate bundle https://github.com/textmate/java.tmbundle." - -}] diff --git a/extensions/java/cgmanifest.json b/extensions/java/cgmanifest.json new file mode 100644 index 000000000..cff02de7c --- /dev/null +++ b/extensions/java/cgmanifest.json @@ -0,0 +1,17 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "atom/language-java", + "repositoryUrl": "https://github.com/atom/language-java", + "commitHash": "95ebcd0b15c369666ecc4d1593495466132dd5bf" + } + }, + "license": "MIT", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/java/syntaxes/java.tmLanguage.json b/extensions/java/syntaxes/java.tmLanguage.json index b0ecaec63..2ccdc0733 100644 --- a/extensions/java/syntaxes/java.tmLanguage.json +++ b/extensions/java/syntaxes/java.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/atom/language-java/commit/295af4375e4a5da4a4352fa08a8bb3e17145ec47", + "version": "https://github.com/atom/language-java/commit/95ebcd0b15c369666ecc4d1593495466132dd5bf", "name": "Java", "scopeName": "source.java", "patterns": [ @@ -373,7 +373,7 @@ "include": "#static-initializer" }, { - "include": "#methods" + "include": "#class-fields-and-methods" }, { "include": "#annotations" @@ -389,6 +389,22 @@ } ] }, + "class-fields-and-methods": { + "patterns": [ + { + "begin": "(?=\\=)", + "end": "(?=;)", + "patterns": [ + { + "include": "#code" + } + ] + }, + { + "include": "#methods" + } + ] + }, "code": { "patterns": [ { @@ -442,6 +458,9 @@ { "include": "#variables" }, + { + "include": "#variables-local" + }, { "include": "#objects" }, @@ -479,9 +498,6 @@ "match": "/\\*\\*/", "name": "comment.block.empty.java" }, - { - "include": "text.html.javadoc" - }, { "include": "#comments-inline" } @@ -911,7 +927,7 @@ "include": "#parens" }, { - "include": "#comments-inline" + "include": "#comments" } ] }, @@ -928,6 +944,9 @@ }, { "include": "#parens" + }, + { + "include": "#comments" } ] }, @@ -1496,12 +1515,12 @@ ] }, "variables": { - "begin": "(?x)\n(?=\n (\n \\b(void|boolean|byte|char|short|int|float|long|double)\\b\n |\n (?>(\\w+\\.)*[A-Z]+\\w*) # e.g. `javax.ws.rs.Response`, or `String`\n )\n (\n <[\\w<>,\\.?\\s\\[\\]]*> # e.g. `HashMap`, or `List`\n )?\n (\n (\\[\\])* # int[][]\n )?\n \\s+\n [A-Za-z_$][\\w$]* # At least one identifier after space\n ([\\w\\[\\],$][\\w\\[\\],\\s]*)? # possibly primitive array or additional identifiers\n \\s*(=|;)\n)", - "end": "(?=\\=|;)", + "begin": "(?x)\n(?=\n (\n \\b(void|boolean|byte|char|short|int|float|long|double)\\b\n |\n (?>(\\w+\\.)*[A-Z]+\\w*) # e.g. `javax.ws.rs.Response`, or `String`\n )\n (\n <[\\w<>,\\.?\\s\\[\\]]*> # e.g. `HashMap`, or `List`\n )?\n (\n (\\[\\])* # int[][]\n )?\n \\s+\n [A-Za-z_$][\\w$]* # At least one identifier after space\n ([\\w\\[\\],$][\\w\\[\\],\\s]*)? # possibly primitive array or additional identifiers\n \\s*(=|:|;)\n)", + "end": "(?=\\=|:|;)", "name": "meta.definition.variable.java", "patterns": [ { - "match": "([A-Za-z$_][\\w$]*)(?=\\s*(\\[\\])*\\s*(;|=|,))", + "match": "([A-Za-z$_][\\w$]*)(?=\\s*(\\[\\])*\\s*(;|:|=|,))", "captures": { "1": { "name": "variable.other.definition.java" @@ -1515,6 +1534,28 @@ "include": "#code" } ] + }, + "variables-local": { + "begin": "(?=\\b(var)\\b\\s+[A-Za-z_$][\\w$]*\\s*(=|:|;))", + "end": "(?=\\=|:|;)", + "name": "meta.definition.variable.local.java", + "patterns": [ + { + "match": "\\bvar\\b", + "name": "storage.type.local.java" + }, + { + "match": "([A-Za-z$_][\\w$]*)(?=\\s*(\\[\\])*\\s*(=|:|;))", + "captures": { + "1": { + "name": "variable.other.definition.java" + } + } + }, + { + "include": "#code" + } + ] } } } \ No newline at end of file diff --git a/extensions/javascript/.vscodeignore b/extensions/javascript/.vscodeignore index 80239a4dd..b93dc7566 100644 --- a/extensions/javascript/.vscodeignore +++ b/extensions/javascript/.vscodeignore @@ -1,4 +1,4 @@ test/** src/**/*.ts tsconfig.json -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/javascript/OSSREADME.json b/extensions/javascript/OSSREADME.json deleted file mode 100644 index ad083ab87..000000000 --- a/extensions/javascript/OSSREADME.json +++ /dev/null @@ -1,30 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "Microsoft/TypeScript-TmLanguage", - "version": "0.0.1", - "license": "MIT", - "repositoryURL": "https://github.com/Microsoft/TypeScript-TmLanguage", - "description": "The file syntaxes/JavaScript.tmLanguage.json was derived from TypeScriptReact.tmLanguage in https://github.com/Microsoft/TypeScript-TmLanguage." -}, -{ - "name": "textmate/javascript.tmbundle", - "version": "0.0.0", - "license": "TextMate Bundle License", - "repositoryURL": "https://github.com/textmate/javascript.tmbundle", - "licenseDetail": [ - "Copyright (c) textmate-javascript.tmbundle project authors", - "", - "If not otherwise specified (see below), files in this repository fall under the following license:", - "", - "Permission to copy, use, modify, sell and distribute this", - "software is granted. This software is provided \"as is\" without", - "express or implied warranty, and with no claim as to its", - "suitability for any purpose.", - "", - "An exception is made for files in readable text which contain their own license information,", - "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", - "to the base-name name of the original file, and an extension of txt, html, or similar. For example", - "\"tidy\" is accompanied by \"tidy-license.txt\"." - ] -}] diff --git a/extensions/javascript/cgmanifest.json b/extensions/javascript/cgmanifest.json new file mode 100644 index 000000000..7053443ef --- /dev/null +++ b/extensions/javascript/cgmanifest.json @@ -0,0 +1,45 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "Microsoft/TypeScript-TmLanguage", + "repositoryUrl": "https://github.com/Microsoft/TypeScript-TmLanguage", + "commitHash": "3133e3d914db9a2bb8812119f9273727a305f16b" + } + }, + "license": "MIT", + "version": "0.0.1", + "description": "The file syntaxes/JavaScript.tmLanguage.json was derived from TypeScriptReact.tmLanguage in https://github.com/Microsoft/TypeScript-TmLanguage." + }, + { + "component": { + "type": "git", + "git": { + "name": "textmate/javascript.tmbundle", + "repositoryUrl": "https://github.com/textmate/javascript.tmbundle", + "commitHash": "fccf0af0c95430a42e1bf98f0c7a4723a53283e7" + } + }, + "licenseDetail": [ + "Copyright (c) textmate-javascript.tmbundle project authors", + "", + "If not otherwise specified (see below), files in this repository fall under the following license:", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose.", + "", + "An exception is made for files in readable text which contain their own license information,", + "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", + "to the base-name name of the original file, and an extension of txt, html, or similar. For example", + "\"tidy\" is accompanied by \"tidy-license.txt\"." + ], + "license": "TextMate Bundle License", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index e6a93b338..50fa8fca3 100644 --- a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScript.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/TypeScript-TmLanguage/commit/a34cb117a38ac6f6eae0df88db984780c6b3df1e", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/3133e3d914db9a2bb8812119f9273727a305f16b", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -1153,7 +1153,7 @@ "name": "meta.definition.function.js entity.name.function.js" } }, - "end": "(?=$|^|;)|(?<=\\})", + "end": "(?=;|(?:^\\s*(?:abstract|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))|(?<=\\})", "patterns": [ { "include": "#function-name" @@ -1413,6 +1413,9 @@ }, { "include": "#arrow-return-type" + }, + { + "include": "#possibly-arrow-return-type" } ] }, @@ -1424,8 +1427,11 @@ "name": "storage.type.function.arrow.js" } }, - "end": "(?<=\\}|\\S)(?)|((?!\\{)(?=\\S))", + "end": "((?<=\\}|\\S)(?)|((?!\\{)(?=\\S)))(?!\\/[\\/\\*])", "patterns": [ + { + "include": "#single-line-comment-consuming-line-ending" + }, { "include": "#decl-block" }, @@ -2623,13 +2629,13 @@ ] }, "function-call": { - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "name": "meta.function-call.js", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -3505,7 +3511,7 @@ ] }, "possibly-arrow-return-type": { - "begin": "(?<=\\))\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", + "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", "beginCaptures": { "1": { "name": "meta.arrow.js meta.return.type.arrow.js keyword.operator.type.annotation.js" @@ -3587,6 +3593,14 @@ } }, "patterns": [ + { + "match": "(?)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))))", "captures": { @@ -3937,7 +3948,7 @@ "include": "#typeof-operator" }, { - "begin": "([&|\\*])(?=\\s*\\{)", + "begin": "([&|])(?=\\s*\\{)", "beginCaptures": { "0": { "name": "keyword.operator.type.js" @@ -3951,7 +3962,7 @@ ] }, { - "begin": "[&|\\*]", + "begin": "[&|]", "beginCaptures": { "0": { "name": "keyword.operator.type.js" @@ -4089,7 +4100,7 @@ "patterns": [ { "name": "string.template.js", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.js" diff --git a/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json b/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json index 9c78c7a3c..38aff6651 100644 --- a/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScriptReact.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/TypeScript-TmLanguage/commit/a34cb117a38ac6f6eae0df88db984780c6b3df1e", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/3133e3d914db9a2bb8812119f9273727a305f16b", "name": "JavaScript (with React support)", "scopeName": "source.js.jsx", "patterns": [ @@ -1153,7 +1153,7 @@ "name": "meta.definition.function.js.jsx entity.name.function.js.jsx" } }, - "end": "(?=$|^|;)|(?<=\\})", + "end": "(?=;|(?:^\\s*(?:abstract|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))|(?<=\\})", "patterns": [ { "include": "#function-name" @@ -1413,6 +1413,9 @@ }, { "include": "#arrow-return-type" + }, + { + "include": "#possibly-arrow-return-type" } ] }, @@ -1424,8 +1427,11 @@ "name": "storage.type.function.arrow.js.jsx" } }, - "end": "(?<=\\}|\\S)(?)|((?!\\{)(?=\\S))", + "end": "((?<=\\}|\\S)(?)|((?!\\{)(?=\\S)))(?!\\/[\\/\\*])", "patterns": [ + { + "include": "#single-line-comment-consuming-line-ending" + }, { "include": "#decl-block" }, @@ -2623,13 +2629,13 @@ ] }, "function-call": { - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "name": "meta.function-call.js.jsx", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -3505,7 +3511,7 @@ ] }, "possibly-arrow-return-type": { - "begin": "(?<=\\))\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", + "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", "beginCaptures": { "1": { "name": "meta.arrow.js.jsx meta.return.type.arrow.js.jsx keyword.operator.type.annotation.js.jsx" @@ -3587,6 +3593,14 @@ } }, "patterns": [ + { + "match": "(?)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))))", "captures": { @@ -3937,7 +3948,7 @@ "include": "#typeof-operator" }, { - "begin": "([&|\\*])(?=\\s*\\{)", + "begin": "([&|])(?=\\s*\\{)", "beginCaptures": { "0": { "name": "keyword.operator.type.js.jsx" @@ -3951,7 +3962,7 @@ ] }, { - "begin": "[&|\\*]", + "begin": "[&|]", "beginCaptures": { "0": { "name": "keyword.operator.type.js.jsx" @@ -4089,7 +4100,7 @@ "patterns": [ { "name": "string.template.js.jsx", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.js.jsx" diff --git a/extensions/json-language-features/.vscode/tasks.json b/extensions/json-language-features/.vscode/tasks.json index 9e5593ade..390a93a3a 100644 --- a/extensions/json-language-features/.vscode/tasks.json +++ b/extensions/json-language-features/.vscode/tasks.json @@ -1,30 +1,11 @@ -// Available variables which can be used inside of strings. -// ${workspaceFolder}: the root folder of the team -// ${file}: the current opened file -// ${fileBasename}: the current opened file's basename -// ${fileDirname}: the current opened file's dirname -// ${fileExtname}: the current opened file's extension -// ${cwd}: the current working directory of the spawned process - -// A task runner that calls a custom npm script that compiles the extension. { - "version": "0.1.0", - - // we want to run npm + "version": "2.0.0", "command": "npm", - - // the command is a shell script - "isShellCommand": true, - - // show the output window only if unrecognized errors occur. - "showOutput": "silent", - - // we run the custom script "compile" as defined in package.json + "type": "shell", + "presentation": { + "reveal": "silent" + }, "args": ["run", "compile"], - - // The tsc compiler is started in watching mode - "isWatching": true, - - // use the standard tsc in watch mode problem matcher to find compile problems in the output. + "isBackground": true, "problemMatcher": "$tsc-watch" -} \ No newline at end of file +} diff --git a/extensions/json-language-features/client/src/jsonMain.ts b/extensions/json-language-features/client/src/jsonMain.ts index 8da28460f..759ef0381 100644 --- a/extensions/json-language-features/client/src/jsonMain.ts +++ b/extensions/json-language-features/client/src/jsonMain.ts @@ -8,8 +8,8 @@ import * as fs from 'fs'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { workspace, languages, ExtensionContext, extensions, Uri, LanguageConfiguration } from 'vscode'; -import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification } from 'vscode-languageclient'; +import { workspace, window, languages, commands, ExtensionContext, extensions, Uri, LanguageConfiguration, Diagnostic, StatusBarAlignment, TextEditor } from 'vscode'; +import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification, HandleDiagnosticsSignature } from 'vscode-languageclient'; import TelemetryReporter from 'vscode-extension-telemetry'; import { hash } from './utils/hash'; @@ -22,6 +22,10 @@ namespace SchemaContentChangeNotification { export const type: NotificationType = new NotificationType('json/schemaContent'); } +namespace ForceValidateRequest { + export const type: RequestType = new RequestType('json/validate'); +} + export interface ISchemaAssociations { [pattern: string]: string[]; } @@ -77,6 +81,14 @@ export function activate(context: ExtensionContext) { let documentSelector = ['json', 'jsonc']; + let schemaResolutionErrorStatusBarItem = window.createStatusBarItem(StatusBarAlignment.Right, 0); + schemaResolutionErrorStatusBarItem.command = '_json.retryResolveSchema'; + schemaResolutionErrorStatusBarItem.tooltip = localize('json.schemaResolutionErrorMessage', 'Unable to resolve schema.') + ' ' + localize('json.clickToRetry', 'Click to retry.'); + schemaResolutionErrorStatusBarItem.text = '$(alert)'; + toDispose.push(schemaResolutionErrorStatusBarItem); + + let fileSchemaErrors = new Map(); + // Options to control the language client let clientOptions: LanguageClientOptions = { // Register the server for json documents @@ -89,6 +101,23 @@ export function activate(context: ExtensionContext) { middleware: { workspace: { didChangeConfiguration: () => client.sendNotification(DidChangeConfigurationNotification.type, { settings: getSettings() }) + }, + handleDiagnostics: (uri: Uri, diagnostics: Diagnostic[], next: HandleDiagnosticsSignature) => { + const schemaErrorIndex = diagnostics.findIndex(candidate => candidate.code === /* SchemaResolveError */ 0x300); + + if (schemaErrorIndex === -1) { + fileSchemaErrors.delete(uri.toString()); + return next(uri, diagnostics); + } + + const schemaResolveDiagnostic = diagnostics[schemaErrorIndex]; + fileSchemaErrors.set(uri.toString(), schemaResolveDiagnostic.message); + + if (window.activeTextEditor && window.activeTextEditor.document.uri.toString() === uri.toString()) { + schemaResolutionErrorStatusBarItem.show(); + } + + next(uri, diagnostics); } } }; @@ -121,8 +150,47 @@ export function activate(context: ExtensionContext) { client.sendNotification(SchemaContentChangeNotification.type, uri.toString()); } }; + + let handleActiveEditorChange = (activeEditor?: TextEditor) => { + if (!activeEditor) { + return; + } + + const activeDocUri = activeEditor.document.uri.toString(); + + if (activeDocUri && fileSchemaErrors.has(activeDocUri)) { + schemaResolutionErrorStatusBarItem.show(); + } else { + schemaResolutionErrorStatusBarItem.hide(); + } + }; + toDispose.push(workspace.onDidChangeTextDocument(e => handleContentChange(e.document.uri))); - toDispose.push(workspace.onDidCloseTextDocument(d => handleContentChange(d.uri))); + toDispose.push(workspace.onDidCloseTextDocument(d => { + handleContentChange(d.uri); + fileSchemaErrors.delete(d.uri.toString()); + })); + toDispose.push(window.onDidChangeActiveTextEditor(handleActiveEditorChange)); + + let handleRetryResolveSchemaCommand = () => { + if (window.activeTextEditor) { + schemaResolutionErrorStatusBarItem.text = '$(watch)'; + const activeDocUri = window.activeTextEditor.document.uri.toString(); + client.sendRequest(ForceValidateRequest.type, activeDocUri).then((diagnostics) => { + const schemaErrorIndex = diagnostics.findIndex(candidate => candidate.code === /* SchemaResolveError */ 0x300); + if (schemaErrorIndex !== -1) { + // Show schema resolution errors in status bar only; ref: #51032 + const schemaResolveDiagnostic = diagnostics[schemaErrorIndex]; + fileSchemaErrors.set(activeDocUri, schemaResolveDiagnostic.message); + } else { + schemaResolutionErrorStatusBarItem.hide(); + } + schemaResolutionErrorStatusBarItem.text = '$(alert)'; + }); + } + }; + + toDispose.push(commands.registerCommand('_json.retryResolveSchema', handleRetryResolveSchemaCommand)); client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociation(context)); }); diff --git a/extensions/json-language-features/icons/json.png b/extensions/json-language-features/icons/json.png index 777432542..c68dbac43 100644 Binary files a/extensions/json-language-features/icons/json.png and b/extensions/json-language-features/icons/json.png differ diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index b5df7479a..a1a5204c8 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -15,7 +15,8 @@ ], "main": "./client/out/jsonMain", "scripts": { - "compile": "gulp compile-extension:json-language-features-client && gulp compile-extension:json-language-features-server", + "compile": "gulp compile-extension:json-language-features-client compile-extension:json-language-features-server", + "watch": "gulp watch-extension:json-language-features-client watch-extension:json-language-features-server", "postinstall": "cd server && yarn install", "install-client-next": "yarn add vscode-languageclient@next" }, diff --git a/extensions/json-language-features/package.nls.json b/extensions/json-language-features/package.nls.json index 943de414e..c61e7b70e 100644 --- a/extensions/json-language-features/package.nls.json +++ b/extensions/json-language-features/package.nls.json @@ -9,5 +9,7 @@ "json.format.enable.desc": "Enable/disable default JSON formatter", "json.tracing.desc": "Traces the communication between VS Code and the JSON language server.", "json.colorDecorators.enable.desc": "Enables or disables color decorators", - "json.colorDecorators.enable.deprecationMessage": "The setting `json.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`." -} \ No newline at end of file + "json.colorDecorators.enable.deprecationMessage": "The setting `json.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`.", + "json.schemaResolutionErrorMessage": "Unable to resolve schema.", + "json.clickToRetry": "Click to retry." +} diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index 5e7de8ed7..db0182fa0 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -14,7 +14,7 @@ "dependencies": { "jsonc-parser": "^2.0.2", "request-light": "^0.2.4", - "vscode-json-languageservice": "^3.2.0", + "vscode-json-languageservice": "^3.2.1", "vscode-languageserver": "^5.1.0", "vscode-nls": "^4.0.0", "vscode-uri": "^1.0.6" diff --git a/extensions/json-language-features/server/src/jsonServerMain.ts b/extensions/json-language-features/server/src/jsonServerMain.ts index 6eadf2403..8a8ce6423 100644 --- a/extensions/json-language-features/server/src/jsonServerMain.ts +++ b/extensions/json-language-features/server/src/jsonServerMain.ts @@ -6,7 +6,7 @@ import { createConnection, IConnection, TextDocuments, TextDocument, InitializeParams, InitializeResult, NotificationType, RequestType, - DocumentRangeFormattingRequest, Disposable, ServerCapabilities + DocumentRangeFormattingRequest, Disposable, ServerCapabilities, Diagnostic } from 'vscode-languageserver'; import { xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDescription } from 'request-light'; @@ -34,6 +34,10 @@ namespace SchemaContentChangeNotification { export const type: NotificationType = new NotificationType('json/schemaContent'); } +namespace ForceValidateRequest { + export const type: RequestType = new RequestType('json/validate'); +} + // Create a connection for the server const connection: IConnection = createConnection(); @@ -207,6 +211,21 @@ connection.onNotification(SchemaContentChangeNotification.type, uri => { languageService.resetSchema(uri); }); +// Retry schema validation on all open documents +connection.onRequest(ForceValidateRequest.type, uri => { + return new Promise(resolve => { + const document = documents.get(uri); + if (document) { + updateConfiguration(); + validateTextDocument(document, diagnostics => { + resolve(diagnostics); + }); + } else { + resolve([]); + } + }); +}); + function updateConfiguration() { const languageSettings = { validate: true, @@ -271,10 +290,15 @@ function triggerValidation(textDocument: TextDocument): void { }, validationDelayMs); } -function validateTextDocument(textDocument: TextDocument): void { +function validateTextDocument(textDocument: TextDocument, callback?: (diagnostics: Diagnostic[]) => void): void { + const respond = (diagnostics: Diagnostic[]) => { + connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); + if (callback) { + callback(diagnostics); + } + }; if (textDocument.getText().length === 0) { - // ignore empty documents - connection.sendDiagnostics({ uri: textDocument.uri, diagnostics: [] }); + respond([]); // ignore empty documents return; } const jsonDocument = getJSONDocument(textDocument); @@ -285,8 +309,7 @@ function validateTextDocument(textDocument: TextDocument): void { setTimeout(() => { const currDocument = documents.get(textDocument.uri); if (currDocument && currDocument.version === version) { - // Send the computed diagnostics to VSCode. - connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); + respond(diagnostics); // Send the computed diagnostics to VSCode. } }, 100); }, error => { @@ -405,4 +428,4 @@ connection.onFoldingRanges((params, token) => { }); // Listen on the connection -connection.listen(); \ No newline at end of file +connection.listen(); diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index e4c2fee15..012e34636 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -73,10 +73,10 @@ request-light@^0.2.4: https-proxy-agent "^2.2.1" vscode-nls "^4.0.0" -vscode-json-languageservice@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.2.0.tgz#fe796c2ddbda966d87905442f9636f139e00f341" - integrity sha512-tLAv9/D01fLAvnYnZ1OLy03HSHhVFjaSkUidEjfrwytHrxVDgqXLkHAJg+F6Q3mPYfpnPQvN2jTjiJ1yInuNVg== +vscode-json-languageservice@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.2.1.tgz#991d51128ebd81c5525d0578cabfa5b03e3cba2a" + integrity sha512-ee9MJ70/xR55ywvm0bZsDLhA800HCRE27AYgMNTU14RSg20Y+ngHdQnUt6OmiTXrQDI/7sne6QUOtHIN0hPQYA== dependencies: jsonc-parser "^2.0.2" vscode-languageserver-types "^3.13.0" diff --git a/extensions/json/.vscodeignore b/extensions/json/.vscodeignore index d41e74d16..d42f161c7 100644 --- a/extensions/json/.vscodeignore +++ b/extensions/json/.vscodeignore @@ -1,3 +1,3 @@ build/** test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/json/OSSREADME.json b/extensions/json/OSSREADME.json deleted file mode 100644 index 391402b86..000000000 --- a/extensions/json/OSSREADME.json +++ /dev/null @@ -1,7 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "Microsoft/vscode-JSON.tmLanguage", - "version": "0.0.0", - "license": "MIT", - "repositoryURL": "https://github.com/Microsoft/vscode-JSON.tmLanguage" -}] \ No newline at end of file diff --git a/extensions/json/cgmanifest.json b/extensions/json/cgmanifest.json new file mode 100644 index 000000000..d0b23b7e0 --- /dev/null +++ b/extensions/json/cgmanifest.json @@ -0,0 +1,17 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "Microsoft/vscode-JSON.tmLanguage", + "repositoryUrl": "https://github.com/Microsoft/vscode-JSON.tmLanguage", + "commitHash": "9bd83f1c252b375e957203f21793316203f61f70" + } + }, + "license": "MIT", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/less/.vscodeignore b/extensions/less/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/less/.vscodeignore +++ b/extensions/less/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/less/OSSREADME.json b/extensions/less/OSSREADME.json deleted file mode 100644 index d0dfbef7c..000000000 --- a/extensions/less/OSSREADME.json +++ /dev/null @@ -1,7 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "language-less", - "version": "0.0.0", - "license": "MIT", - "repositoryURL": "https://github.com/atom/language-less" -}] diff --git a/extensions/less/cgmanifest.json b/extensions/less/cgmanifest.json new file mode 100644 index 000000000..0b6d4d471 --- /dev/null +++ b/extensions/less/cgmanifest.json @@ -0,0 +1,17 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "language-less", + "repositoryUrl": "https://github.com/atom/language-less", + "commitHash": "87d4d59e8de6796b506b81a16e1dc1fafc99d30f" + } + }, + "license": "MIT", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/log/.vscodeignore b/extensions/log/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/log/.vscodeignore +++ b/extensions/log/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/log/OSSREADME.json b/extensions/log/OSSREADME.json deleted file mode 100644 index 048224ec0..000000000 --- a/extensions/log/OSSREADME.json +++ /dev/null @@ -1,9 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[ - { - "name": "vscode-logfile-highlighter", - "version": "1.2.0", - "license": "MIT", - "repositoryURL": "https://github.com/emilast/vscode-logfile-highlighter" - } -] \ No newline at end of file diff --git a/extensions/log/cgmanifest.json b/extensions/log/cgmanifest.json new file mode 100644 index 000000000..fc6eb3965 --- /dev/null +++ b/extensions/log/cgmanifest.json @@ -0,0 +1,17 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "vscode-logfile-highlighter", + "repositoryUrl": "https://github.com/emilast/vscode-logfile-highlighter", + "commitHash": "30a19d24b9a0070b0fab5eef45f89c92e7cd71ea" + } + }, + "license": "MIT", + "version": "1.2.0" + } + ], + "version": 1 +} diff --git a/extensions/lua/.vscodeignore b/extensions/lua/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/lua/.vscodeignore +++ b/extensions/lua/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/lua/OSSREADME.json b/extensions/lua/OSSREADME.json deleted file mode 100644 index 792bc1d9c..000000000 --- a/extensions/lua/OSSREADME.json +++ /dev/null @@ -1,22 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "textmate/lua.tmbundle", - "version": "0.0.0", - "license": "TextMate Bundle License", - "repositoryURL": "https://github.com/textmate/lua.tmbundle", - "licenseDetail": [ - "Copyright (c) textmate-lua.tmbundle project authors", - "", - "If not otherwise specified (see below), files in this repository fall under the following license:", - "", - "Permission to copy, use, modify, sell and distribute this", - "software is granted. This software is provided \"as is\" without", - "express or implied warranty, and with no claim as to its", - "suitability for any purpose.", - "", - "An exception is made for files in readable text which contain their own license information,", - "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", - "to the base-name name of the original file, and an extension of txt, html, or similar. For example", - "\"tidy\" is accompanied by \"tidy-license.txt\"." - ] -}] diff --git a/extensions/lua/cgmanifest.json b/extensions/lua/cgmanifest.json new file mode 100644 index 000000000..78d8538bf --- /dev/null +++ b/extensions/lua/cgmanifest.json @@ -0,0 +1,32 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "textmate/lua.tmbundle", + "repositoryUrl": "https://github.com/textmate/lua.tmbundle", + "commitHash": "8ae5641365b28f697121ba1133890e8d81f5b00e" + } + }, + "licenseDetail": [ + "Copyright (c) textmate-lua.tmbundle project authors", + "", + "If not otherwise specified (see below), files in this repository fall under the following license:", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose.", + "", + "An exception is made for files in readable text which contain their own license information,", + "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", + "to the base-name name of the original file, and an extension of txt, html, or similar. For example", + "\"tidy\" is accompanied by \"tidy-license.txt\"." + ], + "license": "TextMate Bundle License", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/make/.vscodeignore b/extensions/make/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/make/.vscodeignore +++ b/extensions/make/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/make/OSSREADME.json b/extensions/make/OSSREADME.json deleted file mode 100644 index 504a7a260..000000000 --- a/extensions/make/OSSREADME.json +++ /dev/null @@ -1,24 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "fadeevab/make.tmbundle", - "version": "0.0.0", - "license": "TextMate Bundle License", - "repositoryURL": "https://github.com/fadeevab/make.tmbundle", - "description": "The file syntaxes/Makefile.json was derived from Makefile.plist in https://github.com/fadeevab/make.tmbundle.", - "licenseDetail": [ - "Copyright (c) textmate-make.tmbundle project authors", - "", - "If not otherwise specified (see below), files in this repository fall under the following license:", - "", - "Permission to copy, use, modify, sell and distribute this", - "software is granted. This software is provided \"as is\" without", - "express or implied warranty, and with no claim as to its", - "suitability for any purpose.", - "", - "An exception is made for files in readable text which contain their own license information,", - "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", - "to the base-name name of the original file, and an extension of txt, html, or similar. For example", - "\"tidy\" is accompanied by \"tidy-license.txt\"." - ] -}] diff --git a/extensions/make/cgmanifest.json b/extensions/make/cgmanifest.json new file mode 100644 index 000000000..6ac84e7b1 --- /dev/null +++ b/extensions/make/cgmanifest.json @@ -0,0 +1,32 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "fadeevab/make.tmbundle", + "repositoryUrl": "https://github.com/fadeevab/make.tmbundle", + "commitHash": "c6b3ae6e4f22e01fc483be5bb34a9682c28f3219" + } + }, + "licenseDetail": [ + "Copyright (c) textmate-make.tmbundle project authors", + "", + "If not otherwise specified (see below), files in this repository fall under the following license:", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose.", + "", + "An exception is made for files in readable text which contain their own license information,", + "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", + "to the base-name name of the original file, and an extension of txt, html, or similar. For example", + "\"tidy\" is accompanied by \"tidy-license.txt\"." + ], + "license": "TextMate Bundle License", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/make/syntaxes/make.tmLanguage.json b/extensions/make/syntaxes/make.tmLanguage.json index acfd8adea..cae70e5ec 100644 --- a/extensions/make/syntaxes/make.tmLanguage.json +++ b/extensions/make/syntaxes/make.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/fadeevab/make.tmbundle/commit/d94d403d6d31623763a4ff86b656886fa699ef60", + "version": "https://github.com/fadeevab/make.tmbundle/commit/c6b3ae6e4f22e01fc483be5bb34a9682c28f3219", "name": "Makefile", "scopeName": "source.makefile", "patterns": [ @@ -17,6 +17,9 @@ { "include": "#variable-assignment" }, + { + "include": "#target" + }, { "include": "#recipe" }, @@ -288,7 +291,7 @@ } ] }, - "recipe": { + "target": { "begin": "^(?!\\t)([^:]*)(:)(?!\\=)", "beginCaptures": { "1": { @@ -344,37 +347,28 @@ "include": "#variables" } ] + } + ] + }, + "recipe": { + "begin": "^\\t", + "end": "[^\\\\]$", + "name": "meta.scope.recipe.makefile", + "patterns": [ + { + "match": "\\\\\\n", + "name": "constant.character.escape.continuation.makefile" }, { - "begin": "^\\t", - "name": "meta.scope.recipe.makefile", - "patterns": [ - { - "captures": { - "0": { - "patterns": [ - { - "match": "\\\\\\n", - "name": "constant.character.escape.continuation.makefile" - }, - { - "include": "#variables" - }, - { - "include": "source.shell" - } - ] - } - }, - "match": ".+\\n?" - } - ], - "while": "^\\t" + "include": "#variables" + }, + { + "include": "source.shell" } ] }, "variable-assignment": { - "begin": "(^[ ]*|\\G\\s*)([^\\s]+)\\s*(=|\\?=|:=|\\+=)", + "begin": "(^[ ]*|\\G\\s*)([^\\s]+)\\s*((?= 40800", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, { "c": ")", "t": "source.makefile string.interpolated.makefile punctuation.definition.variable.makefile", @@ -1430,7 +2200,18 @@ } }, { - "c": "3rdparty.mk", + "c": "ok", + "t": "source.makefile variable.other.makefile", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", "t": "source.makefile", "r": { "dark_plus": "default: #D4D4D4", @@ -1441,19 +2222,19 @@ } }, { - "c": "ifeq", - "t": "source.makefile meta.scope.conditional.makefile keyword.control.ifeq.makefile", + "c": ":=", + "t": "source.makefile punctuation.separator.key-value.makefile", "r": { - "dark_plus": "keyword.control: #C586C0", - "light_plus": "keyword.control: #AF00DB", - "dark_vs": "keyword.control: #569CD6", - "light_vs": "keyword.control: #0000FF", - "hc_black": "keyword.control: #C586C0" + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" } }, { - "c": " (", - "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile", + "c": " ok", + "t": "source.makefile", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1464,7 +2245,7 @@ }, { "c": "$(", - "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "t": "source.makefile string.interpolated.makefile punctuation.definition.variable.makefile", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1474,8 +2255,8 @@ } }, { - "c": "strip", - "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile string.interpolated.makefile meta.scope.function-call.makefile support.function.strip.makefile", + "c": "info", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile support.function.info.makefile", "r": { "dark_plus": "support.function: #DCDCAA", "light_plus": "support.function: #795E26", @@ -1485,8 +2266,8 @@ } }, { - "c": " ", - "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile string.interpolated.makefile meta.scope.function-call.makefile", + "c": " Braces {} in parentheses ({}): ", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1496,8 +2277,8 @@ } }, { - "c": "$(", - "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "c": "${", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile punctuation.definition.variable.makefile", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1507,30 +2288,19 @@ } }, { - "c": "call", - "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile meta.scope.function-call.makefile support.function.call.makefile", - "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "support.function: #DCDCAA" - } - }, - { - "c": " defined,CODIT_DIR", - "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile meta.scope.function-call.makefile", + "c": "ok", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile variable.other.makefile", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" + "hc_black": "variable: #9CDCFE" } }, { - "c": ")", - "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "c": "}", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile punctuation.definition.variable.makefile", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1541,7 +2311,7 @@ }, { "c": ")", - "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "t": "source.makefile string.interpolated.makefile punctuation.definition.variable.makefile", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1551,30 +2321,8 @@ } }, { - "c": ",0)", - "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": " ", - "t": "source.makefile meta.scope.conditional.makefile", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "$(", - "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "c": "${", + "t": "source.makefile string.interpolated.makefile punctuation.definition.variable.makefile", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1585,7 +2333,7 @@ }, { "c": "info", - "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile meta.scope.function-call.makefile support.function.info.makefile", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile support.function.info.makefile", "r": { "dark_plus": "support.function: #DCDCAA", "light_plus": "support.function: #795E26", @@ -1595,8 +2343,8 @@ } }, { - "c": " CODIT_DIR must be set in ", - "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile meta.scope.function-call.makefile", + "c": " Parentheses () in braces {()}: ", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1607,7 +2355,7 @@ }, { "c": "$(", - "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile punctuation.definition.variable.makefile", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1617,8 +2365,8 @@ } }, { - "c": "TOP_DIR", - "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile variable.other.makefile", + "c": "ok", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile variable.other.makefile", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1629,18 +2377,7 @@ }, { "c": ")", - "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile punctuation.definition.variable.makefile", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "3rdparty.mk", - "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile meta.scope.function-call.makefile", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile punctuation.definition.variable.makefile", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1650,8 +2387,8 @@ } }, { - "c": ")", - "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "c": "}", + "t": "source.makefile string.interpolated.makefile punctuation.definition.variable.makefile", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1661,8 +2398,8 @@ } }, { - "c": "endif", - "t": "source.makefile meta.scope.conditional.makefile keyword.control.endif.makefile", + "c": "ifeq", + "t": "source.makefile meta.scope.conditional.makefile keyword.control.ifeq.makefile", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -1672,30 +2409,52 @@ } }, { - "c": "CXXVER_GE480", - "t": "source.makefile variable.other.makefile", + "c": " (\"", + "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "${", + "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "ok", + "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile string.interpolated.makefile variable.other.makefile", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", "hc_black": "variable: #9CDCFE" } }, { - "c": " ", - "t": "source.makefile", + "c": "}", + "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile string.interpolated.makefile punctuation.definition.variable.makefile", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" } }, { - "c": ":=", - "t": "source.makefile punctuation.separator.key-value.makefile", + "c": "\", \"skip\")", + "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1705,8 +2464,8 @@ } }, { - "c": " ", - "t": "source.makefile", + "c": " ", + "t": "source.makefile meta.scope.conditional.makefile", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1717,7 +2476,7 @@ }, { "c": "$(", - "t": "source.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile punctuation.definition.variable.makefile", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1727,19 +2486,19 @@ } }, { - "c": "shell", - "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile support.function.shell.makefile", + "c": "ok", + "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile variable.other.makefile", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", - "hc_black": "support.function: #DCDCAA" + "hc_black": "variable: #9CDCFE" } }, { - "c": " expr `", - "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile", + "c": ")", + "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile punctuation.definition.variable.makefile", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1749,30 +2508,30 @@ } }, { - "c": "$(", - "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "c": ")}", + "t": "source.makefile meta.scope.conditional.makefile", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" } }, { - "c": "CXX", - "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile variable.other.makefile", + "c": " ", + "t": "source.makefile meta.scope.conditional.makefile", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "variable: #9CDCFE" + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" } }, { - "c": ")", - "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "c": "${", + "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile punctuation.definition.variable.makefile", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1782,30 +2541,19 @@ } }, { - "c": " -dumpversion | sed -e 's/\\.\\([0-9][0-9]\\)/\\1/g' -e 's/\\.\\([0-9]\\)/0\\1/g' -e 's/^[0-9]\\{3,4\\}", - "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile", + "c": "ok", + "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile variable.other.makefile", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "$$", - "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile variable.language.makefile", - "r": { - "dark_plus": "variable.language: #569CD6", - "light_plus": "variable.language: #0000FF", - "dark_vs": "variable.language: #569CD6", - "light_vs": "variable.language: #0000FF", "hc_black": "variable: #9CDCFE" } }, { - "c": "/&00/'` \\>= 40800", - "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile", + "c": "}", + "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile punctuation.definition.variable.makefile", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1815,18 +2563,29 @@ } }, { - "c": ")", - "t": "source.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "c": "})", + "t": "source.makefile meta.scope.conditional.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "endif", + "t": "source.makefile meta.scope.conditional.makefile keyword.control.endif.makefile", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" } }, { - "c": "ok", + "c": "result", "t": "source.makefile variable.other.makefile", "r": { "dark_plus": "variable: #9CDCFE", @@ -1848,7 +2607,7 @@ } }, { - "c": ":=", + "c": "!=", "t": "source.makefile punctuation.separator.key-value.makefile", "r": { "dark_plus": "default: #D4D4D4", @@ -1859,7 +2618,7 @@ } }, { - "c": " ok", + "c": " echo \"'", "t": "source.makefile", "r": { "dark_plus": "default: #D4D4D4", @@ -1881,19 +2640,19 @@ } }, { - "c": "info", - "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile support.function.info.makefile", + "c": "ok", + "t": "source.makefile string.interpolated.makefile variable.other.makefile", "r": { - "dark_plus": "support.function: #DCDCAA", - "light_plus": "support.function: #795E26", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", - "hc_black": "support.function: #DCDCAA" + "hc_black": "variable: #9CDCFE" } }, { - "c": " Braces {} in parentheses ({}): ", - "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile", + "c": ")", + "t": "source.makefile string.interpolated.makefile punctuation.definition.variable.makefile", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1903,8 +2662,19 @@ } }, { - "c": "${", - "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "c": "' ", + "t": "source.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$(", + "t": "source.makefile string.interpolated.makefile punctuation.definition.variable.makefile", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1914,19 +2684,19 @@ } }, { - "c": "ok", - "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile variable.other.makefile", + "c": "shell", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile support.function.shell.makefile", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", "dark_vs": "string: #CE9178", "light_vs": "string: #A31515", - "hc_black": "variable: #9CDCFE" + "hc_black": "support.function: #DCDCAA" } }, { - "c": "}", - "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "c": " echo \"from inlined shell\"", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1947,7 +2717,18 @@ } }, { - "c": "${", + "c": "\"", + "t": "source.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$(", "t": "source.makefile string.interpolated.makefile punctuation.definition.variable.makefile", "r": { "dark_plus": "string: #CE9178", @@ -1969,7 +2750,7 @@ } }, { - "c": " Parentheses () in braces {()}: ", + "c": " ", "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile", "r": { "dark_plus": "string: #CE9178", @@ -1991,7 +2772,7 @@ } }, { - "c": "ok", + "c": "result", "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile variable.other.makefile", "r": { "dark_plus": "variable: #9CDCFE", @@ -2013,7 +2794,7 @@ } }, { - "c": "}", + "c": ")", "t": "source.makefile string.interpolated.makefile punctuation.definition.variable.makefile", "r": { "dark_plus": "string: #CE9178", @@ -2024,19 +2805,41 @@ } }, { - "c": "ifeq", - "t": "source.makefile meta.scope.conditional.makefile keyword.control.ifeq.makefile", + "c": "#", + "t": "source.makefile comment.line.number-sign.makefile punctuation.definition.comment.makefile", "r": { - "dark_plus": "keyword.control: #C586C0", - "light_plus": "keyword.control: #AF00DB", - "dark_vs": "keyword.control: #569CD6", - "light_vs": "keyword.control: #0000FF", - "hc_black": "keyword.control: #C586C0" + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" } }, { - "c": " (\"", - "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile", + "c": " Below is a test of variable assignment without any spacing.", + "t": "source.makefile comment.line.number-sign.makefile", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "var", + "t": "source.makefile variable.other.makefile", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "=", + "t": "source.makefile punctuation.separator.key-value.makefile", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2046,41 +2849,30 @@ } }, { - "c": "${", - "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "c": "val", + "t": "source.makefile", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" } }, { - "c": "ok", - "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile string.interpolated.makefile variable.other.makefile", + "c": "var", + "t": "source.makefile variable.other.makefile", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", "hc_black": "variable: #9CDCFE" } }, { - "c": "}", - "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile string.interpolated.makefile punctuation.definition.variable.makefile", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "\", \"skip\")", - "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile", + "c": "?=", + "t": "source.makefile punctuation.separator.key-value.makefile", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2090,8 +2882,8 @@ } }, { - "c": " ", - "t": "source.makefile meta.scope.conditional.makefile", + "c": "val", + "t": "source.makefile", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2101,41 +2893,30 @@ } }, { - "c": "$(", - "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile punctuation.definition.variable.makefile", - "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" - } - }, - { - "c": "ok", - "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile variable.other.makefile", + "c": "var", + "t": "source.makefile variable.other.makefile", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", "hc_black": "variable: #9CDCFE" } }, { - "c": ")", - "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "c": ":=", + "t": "source.makefile punctuation.separator.key-value.makefile", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" } }, { - "c": ")}", - "t": "source.makefile meta.scope.conditional.makefile", + "c": "123", + "t": "source.makefile", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2145,8 +2926,19 @@ } }, { - "c": " ", - "t": "source.makefile meta.scope.conditional.makefile", + "c": "var", + "t": "source.makefile variable.other.makefile", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "!=", + "t": "source.makefile punctuation.separator.key-value.makefile", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2156,41 +2948,41 @@ } }, { - "c": "${", - "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "c": "echo val", + "t": "source.makefile", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" } }, { - "c": "ok", - "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile variable.other.makefile", + "c": "var", + "t": "source.makefile variable.other.makefile", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", "hc_black": "variable: #9CDCFE" } }, { - "c": "}", - "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "c": ":=", + "t": "source.makefile punctuation.separator.key-value.makefile", "r": { - "dark_plus": "string: #CE9178", - "light_plus": "string: #A31515", - "dark_vs": "string: #CE9178", - "light_vs": "string: #A31515", - "hc_black": "string: #CE9178" + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" } }, { - "c": "})", - "t": "source.makefile meta.scope.conditional.makefile", + "c": "val ", + "t": "source.makefile", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2200,14 +2992,25 @@ } }, { - "c": "endif", - "t": "source.makefile meta.scope.conditional.makefile keyword.control.endif.makefile", + "c": "\\", + "t": "source.makefile constant.character.escape.continuation.makefile", "r": { - "dark_plus": "keyword.control: #C586C0", - "light_plus": "keyword.control: #AF00DB", - "dark_vs": "keyword.control: #569CD6", - "light_vs": "keyword.control: #0000FF", - "hc_black": "keyword.control: #C586C0" + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #FF0000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "constant.character: #569CD6" + } + }, + { + "c": "notvar=butval", + "t": "source.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" } } ] \ No newline at end of file diff --git a/extensions/markdown-basics/.vscodeignore b/extensions/markdown-basics/.vscodeignore index 67b755fdc..89fb2149d 100644 --- a/extensions/markdown-basics/.vscodeignore +++ b/extensions/markdown-basics/.vscodeignore @@ -1,4 +1,4 @@ test/** src/** tsconfig.json -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/markdown-basics/OSSREADME.json b/extensions/markdown-basics/OSSREADME.json deleted file mode 100644 index c34486f8a..000000000 --- a/extensions/markdown-basics/OSSREADME.json +++ /dev/null @@ -1,23 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[ -{ - "name": "textmate/markdown.tmbundle", - "version": "0.0.0", - "license": "TextMate Bundle License", - "repositoryURL": "https://github.com/textmate/markdown.tmbundle", - "licenseDetail": [ - "Copyright (c) markdown.tmbundle authors", - "", - "If not otherwise specified (see below), files in this repository fall under the following license:", - "", - "Permission to copy, use, modify, sell and distribute this", - "software is granted. This software is provided \"as is\" without", - "express or implied warranty, and with no claim as to its", - "suitability for any purpose.", - "", - "An exception is made for files in readable text which contain their own license information,", - "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", - "to the base-name name of the original file, and an extension of txt, html, or similar. For example", - "\"tidy\" is accompanied by \"tidy-license.txt\"." - ] -}] diff --git a/extensions/markdown-basics/cgmanifest.json b/extensions/markdown-basics/cgmanifest.json new file mode 100644 index 000000000..5e7930de0 --- /dev/null +++ b/extensions/markdown-basics/cgmanifest.json @@ -0,0 +1,32 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "textmate/markdown.tmbundle", + "repositoryUrl": "https://github.com/textmate/markdown.tmbundle", + "commitHash": "11cf764606cb2cde54badb5d0e5a0758a8871c4b" + } + }, + "licenseDetail": [ + "Copyright (c) markdown.tmbundle authors", + "", + "If not otherwise specified (see below), files in this repository fall under the following license:", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose.", + "", + "An exception is made for files in readable text which contain their own license information,", + "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", + "to the base-name name of the original file, and an extension of txt, html, or similar. For example", + "\"tidy\" is accompanied by \"tidy-license.txt\"." + ], + "license": "TextMate Bundle License", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/markdown-basics/snippets/markdown.json b/extensions/markdown-basics/snippets/markdown.json index 4a5152c18..6ee831ae4 100644 --- a/extensions/markdown-basics/snippets/markdown.json +++ b/extensions/markdown-basics/snippets/markdown.json @@ -23,14 +23,14 @@ "prefix": "fenced codeblock", "body": [ "```${1:language}", - "$0", + "${TM_SELECTED_TEXT}$0", "```" ], "description": "Insert fenced code block" }, "Insert heading": { "prefix": "heading", - "body": "# ${1:text}", + "body": "# ${1:${TM_SELECTED_TEXT}}", "description": "Insert heading" }, "Insert unordered list": { @@ -60,12 +60,12 @@ }, "Insert link": { "prefix": "link", - "body": "[${1:text}](https://${2:link})$0", + "body": "[${TM_SELECTED_TEXT:${1:text}}](https://${2:link})$0", "description": "Insert link" }, "Insert image": { "prefix": "image", - "body": "![${1:alt}](https://${2:link})$0", + "body": "![${TM_SELECTED_TEXT:${1:alt}}](https://${2:link})$0", "description": "Insert image" } } diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index c74fb3ba7..d206f39db 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/ee6a11d0bbbe8ef6bcf8e664f446c00fb14212f2", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/47e0c947d0a8d13c07e06a086a5c0c9bbffc6cf6", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -120,6 +120,9 @@ { "include": "#fenced_code_block_json" }, + { + "include": "#fenced_code_block_jsonc" + }, { "include": "#fenced_code_block_less" }, @@ -1196,6 +1199,39 @@ } ] }, + "fenced_code_block_jsonc": { + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(jsonc)(\\s+[^`~]*)?$)", + "name": "markup.fenced_code.block.markdown", + "end": "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$", + "beginCaptures": { + "3": { + "name": "punctuation.definition.markdown" + }, + "5": { + "name": "fenced_code.block.language" + }, + "6": { + "name": "fenced_code.block.language.attributes" + } + }, + "endCaptures": { + "3": { + "name": "punctuation.definition.markdown" + } + }, + "patterns": [ + { + "begin": "(^|\\G)(\\s*)(.*)", + "while": "(^|\\G)(?!\\s*([`~]{3,})\\s*$)", + "contentName": "meta.embedded.block.jsonc", + "patterns": [ + { + "include": "source.json.comments" + } + ] + } + ] + }, "fenced_code_block_less": { "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(less)(\\s+[^`~]*)?$)", "name": "markup.fenced_code.block.markdown", @@ -1984,7 +2020,7 @@ "name": "punctuation.definition.string.end.markdown" } }, - "match": "(?x)\n \\s* # Leading whitespace\n (\\[)([\\w ]+?)(\\])(:) # Reference name\n [ \\t]* # Optional whitespace\n (?) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in quotes…\n | ((\").+?(\")) # or in parens.\n )? # Title is optional\n \\s* # Optional whitespace\n $\n", + "match": "(?x)\n \\s* # Leading whitespace\n (\\[)([^]]+?)(\\])(:) # Reference name\n [ \\t]* # Optional whitespace\n (?) # The url\n [ \\t]* # Optional whitespace\n (?:\n ((\\().+?(\\))) # Match title in quotes…\n | ((\").+?(\")) # or in parens.\n )? # Title is optional\n \\s* # Optional whitespace\n $\n", "name": "meta.link.reference.def.markdown" }, "list_paragraph": { @@ -2079,7 +2115,7 @@ "include": "source.yaml" } ], - "end": "(^|\\G)-{3}|\\.{3}\\s*$\"" + "end": "(^|\\G)-{3}|\\.{3}\\s*$" }, "inline": { "patterns": [ diff --git a/extensions/markdown-language-features/.vscodeignore b/extensions/markdown-language-features/.vscodeignore index 28cda32ac..30d948fbc 100644 --- a/extensions/markdown-language-features/.vscodeignore +++ b/extensions/markdown-language-features/.vscodeignore @@ -4,7 +4,7 @@ tsconfig.json out/test/** out/** extension.webpack.config.js -OSSREADME.json +cgmanifest.json yarn.lock preview-src/** webpack.config.js diff --git a/extensions/markdown-language-features/OSSREADME.json b/extensions/markdown-language-features/OSSREADME.json deleted file mode 100644 index 33c460e00..000000000 --- a/extensions/markdown-language-features/OSSREADME.json +++ /dev/null @@ -1,38 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[ -{ - "name": "chriskempson/tomorrow-theme", - "version": "0.0.0", - "license": "MIT", - "repositoryURL": "https://github.com/chriskempson/tomorrow-theme", - "licenseDetail": [ - "Copyright (C) 2013 Chris Kempson", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,", "and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." - ] -}, -{ - "name": "textmate/markdown.tmbundle", - "version": "0.0.0", - "license": "TextMate Bundle License", - "repositoryURL": "https://github.com/textmate/markdown.tmbundle", - "licenseDetail": [ - "Copyright (c) markdown.tmbundle authors", - "", - "If not otherwise specified (see below), files in this repository fall under the following license:", - "", - "Permission to copy, use, modify, sell and distribute this", - "software is granted. This software is provided \"as is\" without", - "express or implied warranty, and with no claim as to its", - "suitability for any purpose.", - "", - "An exception is made for files in readable text which contain their own license information,", - "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", - "to the base-name name of the original file, and an extension of txt, html, or similar. For example", - "\"tidy\" is accompanied by \"tidy-license.txt\"." - ] -}] diff --git a/extensions/markdown-language-features/cgmanifest.json b/extensions/markdown-language-features/cgmanifest.json new file mode 100644 index 000000000..89c68532e --- /dev/null +++ b/extensions/markdown-language-features/cgmanifest.json @@ -0,0 +1,55 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "chriskempson/tomorrow-theme", + "repositoryUrl": "https://github.com/chriskempson/tomorrow-theme", + "commitHash": "0e0d35ac303f99b8aa182091ebeaee81cf2183a0" + } + }, + "licenseDetail": [ + "Copyright (C) 2013 Chris Kempson", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ], + "license": "MIT", + "version": "0.0.0" + }, + { + "component": { + "type": "git", + "git": { + "name": "textmate/markdown.tmbundle", + "repositoryUrl": "https://github.com/textmate/markdown.tmbundle", + "commitHash": "11cf764606cb2cde54badb5d0e5a0758a8871c4b" + } + }, + "licenseDetail": [ + "Copyright (c) markdown.tmbundle authors", + "", + "If not otherwise specified (see below), files in this repository fall under the following license:", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose.", + "", + "An exception is made for files in readable text which contain their own license information,", + "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", + "to the base-name name of the original file, and an extension of txt, html, or similar. For example", + "\"tidy\" is accompanied by \"tidy-license.txt\"." + ], + "license": "TextMate Bundle License", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/markdown-language-features/icon.png b/extensions/markdown-language-features/icon.png index 48e0f035e..cb3f50758 100644 Binary files a/extensions/markdown-language-features/icon.png and b/extensions/markdown-language-features/icon.png differ diff --git a/extensions/markdown-language-features/media/index.js b/extensions/markdown-language-features/media/index.js index 99e630835..25df5db9f 100644 --- a/extensions/markdown-language-features/media/index.js +++ b/extensions/markdown-language-features/media/index.js @@ -825,11 +825,11 @@ const getCodeLineElements = (() => { let elements; return () => { if (!elements) { - elements = Array.prototype.map.call(document.getElementsByClassName('code-line'), (element) => { + elements = ([{ element: document.body, line: 0 }]).concat(Array.prototype.map.call(document.getElementsByClassName('code-line'), (element) => { const line = +element.getAttribute('data-line'); return { element, line }; }) - .filter((x) => !isNaN(x.line)); + .filter((x) => !isNaN(x.line))); } return elements; }; @@ -887,22 +887,30 @@ 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 && settings_1.getSettings().scrollPreviewWithEditor) { - 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 { - scrollTo = previousTop; - } - window.scroll(0, Math.max(1, window.scrollY + scrollTo)); + 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 { + scrollTo = previousTop; } + window.scroll(window.scrollX, Math.max(1, window.scrollY + scrollTo)); } exports.scrollToRevealSourceLine = scrollToRevealSourceLine; function getEditorLineNumberForPageOffset(offset) { @@ -970,4 +978,4 @@ exports.getSettings = getSettings; /***/ }) /******/ }); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL2xvZGFzaC50aHJvdHRsZS9pbmRleC5qcyIsIndlYnBhY2s6Ly8vKHdlYnBhY2spL2J1aWxkaW4vZ2xvYmFsLmpzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2FjdGl2ZUxpbmVNYXJrZXIudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvZXZlbnRzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2luZGV4LnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL21lc3NhZ2luZy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zY3JvbGwtc3luYy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zZXR0aW5ncy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQUs7QUFDTDtBQUNBOztBQUVBO0FBQ0E7QUFDQSx5REFBaUQsY0FBYztBQUMvRDs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxtQ0FBMkIsMEJBQTBCLEVBQUU7QUFDdkQseUNBQWlDLGVBQWU7QUFDaEQ7QUFDQTtBQUNBOztBQUVBO0FBQ0EsOERBQXNELCtEQUErRDs7QUFFckg7QUFDQTs7O0FBR0E7QUFDQTs7Ozs7Ozs7Ozs7O0FDbkVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsYUFBYSxPQUFPO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLFNBQVM7QUFDcEIsV0FBVyxPQUFPO0FBQ2xCLFdBQVcsT0FBTyxZQUFZO0FBQzlCLFdBQVcsUUFBUTtBQUNuQjtBQUNBLFdBQVcsT0FBTztBQUNsQjtBQUNBLFdBQVcsUUFBUTtBQUNuQjtBQUNBLGFBQWEsU0FBUztBQUN0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBLDhDQUE4QyxrQkFBa0I7QUFDaEU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLFNBQVM7QUFDcEIsV0FBVyxPQUFPO0FBQ2xCLFdBQVcsT0FBTyxZQUFZO0FBQzlCLFdBQVcsUUFBUTtBQUNuQjtBQUNBLFdBQVcsUUFBUTtBQUNuQjtBQUNBLGFBQWEsU0FBUztBQUN0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxtREFBbUQsb0JBQW9CO0FBQ3ZFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLEVBQUU7QUFDYixhQUFhLFFBQVE7QUFDckI7QUFDQTtBQUNBLGdCQUFnQjtBQUNoQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVcsRUFBRTtBQUNiLGFBQWEsUUFBUTtBQUNyQjtBQUNBO0FBQ0Esb0JBQW9CO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLEVBQUU7QUFDYixhQUFhLFFBQVE7QUFDckI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsV0FBVyxFQUFFO0FBQ2IsYUFBYSxPQUFPO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOzs7Ozs7Ozs7Ozs7O0FDdGJBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0EsQ0FBQztBQUNEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsNENBQTRDOztBQUU1Qzs7Ozs7Ozs7Ozs7Ozs7O0FDbkJBOzs7Z0dBR2dHO0FBQ2hHLCtGQUF5RDtBQUV6RDtJQUdDLDhCQUE4QixDQUFDLElBQVk7UUFDMUMsTUFBTSxFQUFFLFFBQVEsRUFBRSxHQUFHLHNDQUF3QixDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3BELElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxJQUFJLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUM1QyxDQUFDO0lBRUQsT0FBTyxDQUFDLE1BQStCO1FBQ3RDLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDekMsSUFBSSxDQUFDLGtCQUFrQixDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ2hDLElBQUksQ0FBQyxRQUFRLEdBQUcsTUFBTSxDQUFDO0lBQ3hCLENBQUM7SUFFRCxvQkFBb0IsQ0FBQyxPQUFnQztRQUNwRCxFQUFFLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7WUFDZCxNQUFNLENBQUM7UUFDUixDQUFDO1FBQ0QsT0FBTyxDQUFDLFNBQVMsR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyx1QkFBdUIsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUM1RSxDQUFDO0lBRUQsa0JBQWtCLENBQUMsT0FBZ0M7UUFDbEQsRUFBRSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1lBQ2QsTUFBTSxDQUFDO1FBQ1IsQ0FBQztRQUNELE9BQU8sQ0FBQyxTQUFTLElBQUksbUJBQW1CLENBQUM7SUFDMUMsQ0FBQztDQUNEO0FBM0JELDRDQTJCQzs7Ozs7Ozs7Ozs7Ozs7QUNqQ0Q7OztnR0FHZ0c7O0FBRWhHLDRCQUFtQyxDQUFhO0lBQy9DLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxVQUFVLEtBQUssU0FBUyxJQUFJLFFBQVEsQ0FBQyxVQUFVLEtBQUssZUFBZSxDQUFDLENBQUMsQ0FBQztRQUNsRixRQUFRLENBQUMsZ0JBQWdCLENBQUMsa0JBQWtCLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDbEQsQ0FBQztJQUFDLElBQUksQ0FBQyxDQUFDO1FBQ1AsQ0FBQyxFQUFFLENBQUM7SUFDTCxDQUFDO0FBQ0YsQ0FBQztBQU5ELGdEQU1DOzs7Ozs7Ozs7Ozs7OztBQ1hEOzs7Z0dBR2dHOztBQUVoRyw4R0FBc0Q7QUFDdEQsZ0ZBQThDO0FBQzlDLHlGQUFvRDtBQUNwRCwrRkFBMkY7QUFDM0Ysc0ZBQWtEO0FBQ2xELHVHQUE2QztBQUk3QyxJQUFJLGNBQWMsR0FBRyxJQUFJLENBQUM7QUFDMUIsTUFBTSxNQUFNLEdBQUcsSUFBSSxtQ0FBZ0IsRUFBRSxDQUFDO0FBQ3RDLE1BQU0sUUFBUSxHQUFHLHNCQUFXLEVBQUUsQ0FBQztBQUUvQixNQUFNLE1BQU0sR0FBRyxnQkFBZ0IsRUFBRSxDQUFDO0FBRWxDLG9CQUFvQjtBQUNwQixNQUFNLEtBQUssR0FBRyxrQkFBTyxDQUFDLFlBQVksQ0FBQyxDQUFDO0FBQ3BDLE1BQU0sQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUM7QUFFdkIsTUFBTSxTQUFTLEdBQUcsaUNBQXFCLENBQUMsTUFBTSxDQUFDLENBQUM7QUFFaEQsTUFBTSxDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUMsU0FBUyxDQUFDLENBQUM7QUFDdkMsTUFBTSxDQUFDLG1CQUFtQixDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsQ0FBQztBQUVoRCxNQUFNLENBQUMsTUFBTSxHQUFHLEdBQUcsRUFBRTtJQUNwQixnQkFBZ0IsRUFBRSxDQUFDO0FBQ3BCLENBQUMsQ0FBQztBQUVGLDJCQUFrQixDQUFDLEdBQUcsRUFBRTtJQUN2QixFQUFFLENBQUMsQ0FBQyxRQUFRLENBQUMsdUJBQXVCLENBQUMsQ0FBQyxDQUFDO1FBQ3RDLFVBQVUsQ0FBQyxHQUFHLEVBQUU7WUFDZixNQUFNLFdBQVcsR0FBRyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUM7WUFDbkMsRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUN6QixjQUFjLEdBQUcsSUFBSSxDQUFDO2dCQUN0QixzQ0FBd0IsQ0FBQyxXQUFXLENBQUMsQ0FBQztZQUN2QyxDQUFDO1FBQ0YsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztBQUNGLENBQUMsQ0FBQyxDQUFDO0FBRUgsTUFBTSxZQUFZLEdBQUcsQ0FBQyxHQUFHLEVBQUU7SUFDMUIsTUFBTSxRQUFRLEdBQUcsUUFBUSxDQUFDLENBQUMsSUFBWSxFQUFFLEVBQUU7UUFDMUMsY0FBYyxHQUFHLElBQUksQ0FBQztRQUN0QixzQ0FBd0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNoQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFFUCxNQUFNLENBQUMsQ0FBQyxJQUFZLEVBQUUsUUFBYSxFQUFFLEVBQUU7UUFDdEMsRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ2xCLFFBQVEsQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO1lBQ3JCLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNoQixDQUFDO0lBQ0YsQ0FBQyxDQUFDO0FBQ0gsQ0FBQyxDQUFDLEVBQUUsQ0FBQztBQUVMLElBQUksZ0JBQWdCLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRTtJQUNwQyxNQUFNLFNBQVMsR0FBb0QsRUFBRSxDQUFDO0lBQ3RFLElBQUksTUFBTSxHQUFHLFFBQVEsQ0FBQyxvQkFBb0IsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNsRCxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1FBQ1osSUFBSSxDQUFDLENBQUM7UUFDTixHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDcEMsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBRXRCLEVBQUUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDdkMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDakMsQ0FBQztZQUVELFNBQVMsQ0FBQyxJQUFJLENBQUM7Z0JBQ2QsRUFBRSxFQUFFLEdBQUcsQ0FBQyxFQUFFO2dCQUNWLE1BQU0sRUFBRSxHQUFHLENBQUMsTUFBTTtnQkFDbEIsS0FBSyxFQUFFLEdBQUcsQ0FBQyxLQUFLO2FBQ2hCLENBQUMsQ0FBQztRQUNKLENBQUM7UUFFRCxTQUFTLENBQUMsV0FBVyxDQUFDLGlCQUFpQixFQUFFLFNBQVMsQ0FBQyxDQUFDO0lBQ3JELENBQUM7QUFDRixDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7QUFFUCxNQUFNLENBQUMsZ0JBQWdCLENBQUMsUUFBUSxFQUFFLEdBQUcsRUFBRTtJQUN0QyxjQUFjLEdBQUcsSUFBSSxDQUFDO0lBQ3RCLGdCQUFnQixFQUFFLENBQUM7QUFDcEIsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDO0FBRVQsTUFBTSxDQUFDLGdCQUFnQixDQUFDLFNBQVMsRUFBRSxLQUFLLENBQUMsRUFBRTtJQUMxQyxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sS0FBSyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztRQUMzQyxNQUFNLENBQUM7SUFDUixDQUFDO0lBRUQsTUFBTSxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ3pCLEtBQUssZ0NBQWdDO1lBQ3BDLE1BQU0sQ0FBQyw4QkFBOEIsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3ZELEtBQUssQ0FBQztRQUVQLEtBQUssWUFBWTtZQUNoQixZQUFZLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDLENBQUM7WUFDeEMsS0FBSyxDQUFDO0lBQ1IsQ0FBQztBQUNGLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQztBQUVWLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxVQUFVLEVBQUUsS0FBSyxDQUFDLEVBQUU7SUFDN0MsRUFBRSxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsMkJBQTJCLENBQUMsQ0FBQyxDQUFDO1FBQzNDLE1BQU0sQ0FBQztJQUNSLENBQUM7SUFFRCx5QkFBeUI7SUFDekIsR0FBRyxDQUFDLENBQUMsSUFBSSxJQUFJLEdBQUcsS0FBSyxDQUFDLE1BQXFCLEVBQUUsSUFBSSxFQUFFLElBQUksR0FBRyxJQUFJLENBQUMsVUFBeUIsRUFBRSxDQUFDO1FBQzFGLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQztZQUMxQixNQUFNLENBQUM7UUFDUixDQUFDO0lBQ0YsQ0FBQztJQUVELE1BQU0sTUFBTSxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUM7SUFDM0IsTUFBTSxJQUFJLEdBQUcsOENBQWdDLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDdEQsRUFBRSxDQUFDLENBQUMsT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUM5QyxTQUFTLENBQUMsV0FBVyxDQUFDLFVBQVUsRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUMvRCxDQUFDO0FBQ0YsQ0FBQyxDQUFDLENBQUM7QUFFSCxRQUFRLENBQUMsZ0JBQWdCLENBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQyxFQUFFO0lBQzFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztRQUNaLE1BQU0sQ0FBQztJQUNSLENBQUM7SUFFRCxJQUFJLElBQUksR0FBUSxLQUFLLENBQUMsTUFBTSxDQUFDO0lBQzdCLE9BQU8sSUFBSSxFQUFFLENBQUM7UUFDYixFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxPQUFPLEtBQUssR0FBRyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ3ZELEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDL0MsS0FBSyxDQUFDO1lBQ1AsQ0FBQztZQUNELEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLGtCQUFrQixDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUNqRixNQUFNLENBQUMsSUFBSSxFQUFFLFFBQVEsQ0FBQyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLGdDQUFnQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDNUYsU0FBUyxDQUFDLFdBQVcsQ0FBQyxXQUFXLEVBQUUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQztnQkFDdkQsS0FBSyxDQUFDLGNBQWMsRUFBRSxDQUFDO2dCQUN2QixLQUFLLENBQUMsZUFBZSxFQUFFLENBQUM7Z0JBQ3hCLEtBQUssQ0FBQztZQUNQLENBQUM7WUFDRCxLQUFLLENBQUM7UUFDUCxDQUFDO1FBQ0QsSUFBSSxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUM7SUFDeEIsQ0FBQztBQUNGLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQztBQUVULEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDLENBQUM7SUFDdEMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLFFBQVEsRUFBRSxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQy9DLEVBQUUsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUM7WUFDcEIsY0FBYyxHQUFHLEtBQUssQ0FBQztRQUN4QixDQUFDO1FBQUMsSUFBSSxDQUFDLENBQUM7WUFDUCxNQUFNLElBQUksR0FBRyw4Q0FBZ0MsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDOUQsRUFBRSxDQUFDLENBQUMsT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDOUMsU0FBUyxDQUFDLFdBQVcsQ0FBQyxZQUFZLEVBQUUsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1lBQy9DLENBQUM7UUFDRixDQUFDO0lBQ0YsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUM7QUFDVCxDQUFDOzs7Ozs7Ozs7Ozs7OztBQzdKRDs7O2dHQUdnRzs7QUFFaEcsc0ZBQXlDO0FBUzVCLDZCQUFxQixHQUFHLENBQUMsTUFBVyxFQUFFLEVBQUU7SUFDcEQsTUFBTSxDQUFDLElBQUk7UUFDVixXQUFXLENBQUMsSUFBWSxFQUFFLElBQVk7WUFDckMsTUFBTSxDQUFDLFdBQVcsQ0FBQztnQkFDbEIsSUFBSTtnQkFDSixNQUFNLEVBQUUsc0JBQVcsRUFBRSxDQUFDLE1BQU07Z0JBQzVCLElBQUk7YUFDSixDQUFDLENBQUM7UUFDSixDQUFDO0tBQ0QsQ0FBQztBQUNILENBQUMsQ0FBQzs7Ozs7Ozs7Ozs7Ozs7QUN4QkY7OztnR0FHZ0c7O0FBRWhHLHNGQUF5QztBQUd6QyxlQUFlLEdBQVcsRUFBRSxHQUFXLEVBQUUsS0FBYTtJQUNyRCxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQztBQUM1QyxDQUFDO0FBRUQsbUJBQW1CLElBQVk7SUFDOUIsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsc0JBQVcsRUFBRSxDQUFDLFNBQVMsR0FBRyxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUM7QUFDcEQsQ0FBQztBQVFELE1BQU0sbUJBQW1CLEdBQUcsQ0FBQyxHQUFHLEVBQUU7SUFDakMsSUFBSSxRQUEyQixDQUFDO0lBQ2hDLE1BQU0sQ0FBQyxHQUFHLEVBQUU7UUFDWCxFQUFFLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7WUFDZixRQUFRLEdBQUcsS0FBSyxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUNsQyxRQUFRLENBQUMsc0JBQXNCLENBQUMsV0FBVyxDQUFDLEVBQzVDLENBQUMsT0FBWSxFQUFFLEVBQUU7Z0JBQ2hCLE1BQU0sSUFBSSxHQUFHLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxXQUFXLENBQUMsQ0FBQztnQkFDaEQsTUFBTSxDQUFDLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxDQUFDO1lBQzFCLENBQUMsQ0FBQztpQkFDRCxNQUFNLENBQUMsQ0FBQyxDQUFNLEVBQUUsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ3RDLENBQUM7UUFDRCxNQUFNLENBQUMsUUFBUSxDQUFDO0lBQ2pCLENBQUMsQ0FBQztBQUNILENBQUMsQ0FBQyxFQUFFLENBQUM7QUFFTDs7Ozs7R0FLRztBQUNILGtDQUF5QyxVQUFrQjtJQUMxRCxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQzFDLE1BQU0sS0FBSyxHQUFHLG1CQUFtQixFQUFFLENBQUM7SUFDcEMsSUFBSSxRQUFRLEdBQUcsS0FBSyxDQUFDLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQztJQUNoQyxHQUFHLENBQUMsQ0FBQyxNQUFNLEtBQUssSUFBSSxLQUFLLENBQUMsQ0FBQyxDQUFDO1FBQzNCLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxJQUFJLEtBQUssVUFBVSxDQUFDLENBQUMsQ0FBQztZQUMvQixNQUFNLENBQUMsRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsQ0FBQztRQUM3QyxDQUFDO1FBQ0QsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxJQUFJLEdBQUcsVUFBVSxDQUFDLENBQUMsQ0FBQztZQUNsQyxNQUFNLENBQUMsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxDQUFDO1FBQ2xDLENBQUM7UUFDRCxRQUFRLEdBQUcsS0FBSyxDQUFDO0lBQ2xCLENBQUM7SUFDRCxNQUFNLENBQUMsRUFBRSxRQUFRLEVBQUUsQ0FBQztBQUNyQixDQUFDO0FBZEQsNERBY0M7QUFFRDs7R0FFRztBQUNILHFDQUE0QyxNQUFjO0lBQ3pELE1BQU0sS0FBSyxHQUFHLG1CQUFtQixFQUFFLENBQUM7SUFDcEMsTUFBTSxRQUFRLEdBQUcsTUFBTSxHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUM7SUFDekMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDWixJQUFJLEVBQUUsR0FBRyxLQUFLLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQztJQUMxQixPQUFPLEVBQUUsR0FBRyxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUM7UUFDcEIsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsR0FBRyxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztRQUN0QyxNQUFNLE1BQU0sR0FBRyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsT0FBTyxDQUFDLHFCQUFxQixFQUFFLENBQUM7UUFDMUQsRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLEdBQUcsR0FBRyxNQUFNLENBQUMsTUFBTSxJQUFJLFFBQVEsQ0FBQyxDQUFDLENBQUM7WUFDNUMsRUFBRSxHQUFHLEdBQUcsQ0FBQztRQUNWLENBQUM7UUFDRCxJQUFJLENBQUMsQ0FBQztZQUNMLEVBQUUsR0FBRyxHQUFHLENBQUM7UUFDVixDQUFDO0lBQ0YsQ0FBQztJQUNELE1BQU0sU0FBUyxHQUFHLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUM1QixNQUFNLFFBQVEsR0FBRyxTQUFTLENBQUMsT0FBTyxDQUFDLHFCQUFxQixFQUFFLENBQUM7SUFDM0QsRUFBRSxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsSUFBSSxRQUFRLENBQUMsR0FBRyxHQUFHLFFBQVEsQ0FBQyxDQUFDLENBQUM7UUFDeEMsTUFBTSxTQUFTLEdBQUcsS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQzVCLE1BQU0sQ0FBQyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxDQUFDO0lBQ2pELENBQUM7SUFDRCxNQUFNLENBQUMsRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLENBQUM7QUFDaEMsQ0FBQztBQXRCRCxrRUFzQkM7QUFFRDs7R0FFRztBQUNILGtDQUF5QyxJQUFZO0lBQ3BELE1BQU0sRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLEdBQUcsd0JBQXdCLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDMUQsRUFBRSxDQUFDLENBQUMsUUFBUSxJQUFJLHNCQUFXLEVBQUUsQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDLENBQUM7UUFDdkQsSUFBSSxRQUFRLEdBQUcsQ0FBQyxDQUFDO1FBQ2pCLE1BQU0sSUFBSSxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQztRQUN0RCxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDO1FBQzdCLEVBQUUsQ0FBQyxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsSUFBSSxLQUFLLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ3pDLDhEQUE4RDtZQUM5RCxNQUFNLGVBQWUsR0FBRyxDQUFDLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxHQUFHLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUM3RSxNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLHFCQUFxQixFQUFFLENBQUMsR0FBRyxHQUFHLFdBQVcsQ0FBQztZQUM3RSxRQUFRLEdBQUcsV0FBVyxHQUFHLGVBQWUsR0FBRyxhQUFhLENBQUM7UUFDMUQsQ0FBQztRQUNELElBQUksQ0FBQyxDQUFDO1lBQ0wsUUFBUSxHQUFHLFdBQVcsQ0FBQztRQUN4QixDQUFDO1FBQ0QsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsTUFBTSxDQUFDLE9BQU8sR0FBRyxRQUFRLENBQUMsQ0FBQyxDQUFDO0lBQzFELENBQUM7QUFDRixDQUFDO0FBakJELDREQWlCQztBQUVELDBDQUFpRCxNQUFjO0lBQzlELE1BQU0sRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLEdBQUcsMkJBQTJCLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDL0QsRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztRQUNkLE1BQU0sY0FBYyxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQztRQUNoRSxNQUFNLGtCQUFrQixHQUFHLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQyxPQUFPLEdBQUcsY0FBYyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQzFFLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7WUFDVixNQUFNLHVCQUF1QixHQUFHLGtCQUFrQixHQUFHLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxxQkFBcUIsRUFBRSxDQUFDLEdBQUcsR0FBRyxjQUFjLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDckgsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLElBQUksR0FBRyx1QkFBdUIsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLEdBQUcsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ25GLE1BQU0sQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDeEIsQ0FBQztRQUNELElBQUksQ0FBQyxDQUFDO1lBQ0wsTUFBTSxxQkFBcUIsR0FBRyxrQkFBa0IsR0FBRyxDQUFDLGNBQWMsQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUMzRSxNQUFNLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxHQUFHLHFCQUFxQixDQUFDO1lBQ25ELE1BQU0sQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDeEIsQ0FBQztJQUNGLENBQUM7SUFDRCxNQUFNLENBQUMsSUFBSSxDQUFDO0FBQ2IsQ0FBQztBQWpCRCw0RUFpQkM7Ozs7Ozs7Ozs7Ozs7O0FDOUhEOzs7Z0dBR2dHOztBQVloRyxJQUFJLGNBQWMsR0FBZ0MsU0FBUyxDQUFDO0FBRTVELGlCQUF3QixHQUFXO0lBQ2xDLE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxjQUFjLENBQUMsOEJBQThCLENBQUMsQ0FBQztJQUN4RSxFQUFFLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1FBQ2IsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUN2QyxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ1YsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDekIsQ0FBQztJQUNGLENBQUM7SUFFRCxNQUFNLElBQUksS0FBSyxDQUFDLDJCQUEyQixHQUFHLEVBQUUsQ0FBQyxDQUFDO0FBQ25ELENBQUM7QUFWRCwwQkFVQztBQUVEO0lBQ0MsRUFBRSxDQUFDLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQztRQUNwQixNQUFNLENBQUMsY0FBYyxDQUFDO0lBQ3ZCLENBQUM7SUFFRCxjQUFjLEdBQUcsT0FBTyxDQUFDLGVBQWUsQ0FBQyxDQUFDO0lBQzFDLEVBQUUsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUM7UUFDcEIsTUFBTSxDQUFDLGNBQWMsQ0FBQztJQUN2QixDQUFDO0lBRUQsTUFBTSxJQUFJLEtBQUssQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDO0FBQzVDLENBQUM7QUFYRCxrQ0FXQyIsImZpbGUiOiJpbmRleC5qcyIsInNvdXJjZXNDb250ZW50IjpbIiBcdC8vIFRoZSBtb2R1bGUgY2FjaGVcbiBcdHZhciBpbnN0YWxsZWRNb2R1bGVzID0ge307XG5cbiBcdC8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG4gXHRmdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cbiBcdFx0Ly8gQ2hlY2sgaWYgbW9kdWxlIGlzIGluIGNhY2hlXG4gXHRcdGlmKGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdKSB7XG4gXHRcdFx0cmV0dXJuIGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdLmV4cG9ydHM7XG4gXHRcdH1cbiBcdFx0Ly8gQ3JlYXRlIGEgbmV3IG1vZHVsZSAoYW5kIHB1dCBpdCBpbnRvIHRoZSBjYWNoZSlcbiBcdFx0dmFyIG1vZHVsZSA9IGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdID0ge1xuIFx0XHRcdGk6IG1vZHVsZUlkLFxuIFx0XHRcdGw6IGZhbHNlLFxuIFx0XHRcdGV4cG9ydHM6IHt9XG4gXHRcdH07XG5cbiBcdFx0Ly8gRXhlY3V0ZSB0aGUgbW9kdWxlIGZ1bmN0aW9uXG4gXHRcdG1vZHVsZXNbbW9kdWxlSWRdLmNhbGwobW9kdWxlLmV4cG9ydHMsIG1vZHVsZSwgbW9kdWxlLmV4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pO1xuXG4gXHRcdC8vIEZsYWcgdGhlIG1vZHVsZSBhcyBsb2FkZWRcbiBcdFx0bW9kdWxlLmwgPSB0cnVlO1xuXG4gXHRcdC8vIFJldHVybiB0aGUgZXhwb3J0cyBvZiB0aGUgbW9kdWxlXG4gXHRcdHJldHVybiBtb2R1bGUuZXhwb3J0cztcbiBcdH1cblxuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZXMgb2JqZWN0IChfX3dlYnBhY2tfbW9kdWxlc19fKVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5tID0gbW9kdWxlcztcblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGUgY2FjaGVcbiBcdF9fd2VicGFja19yZXF1aXJlX18uYyA9IGluc3RhbGxlZE1vZHVsZXM7XG5cbiBcdC8vIGRlZmluZSBnZXR0ZXIgZnVuY3Rpb24gZm9yIGhhcm1vbnkgZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kID0gZnVuY3Rpb24oZXhwb3J0cywgbmFtZSwgZ2V0dGVyKSB7XG4gXHRcdGlmKCFfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZXhwb3J0cywgbmFtZSkpIHtcbiBcdFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgbmFtZSwge1xuIFx0XHRcdFx0Y29uZmlndXJhYmxlOiBmYWxzZSxcbiBcdFx0XHRcdGVudW1lcmFibGU6IHRydWUsXG4gXHRcdFx0XHRnZXQ6IGdldHRlclxuIFx0XHRcdH0pO1xuIFx0XHR9XG4gXHR9O1xuXG4gXHQvLyBkZWZpbmUgX19lc01vZHVsZSBvbiBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIgPSBmdW5jdGlvbihleHBvcnRzKSB7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gXCIuL3ByZXZpZXctc3JjL2luZGV4LnRzXCIpO1xuIiwiLyoqXG4gKiBsb2Rhc2ggKEN1c3RvbSBCdWlsZCkgPGh0dHBzOi8vbG9kYXNoLmNvbS8+XG4gKiBCdWlsZDogYGxvZGFzaCBtb2R1bGFyaXplIGV4cG9ydHM9XCJucG1cIiAtbyAuL2BcbiAqIENvcHlyaWdodCBqUXVlcnkgRm91bmRhdGlvbiBhbmQgb3RoZXIgY29udHJpYnV0b3JzIDxodHRwczovL2pxdWVyeS5vcmcvPlxuICogUmVsZWFzZWQgdW5kZXIgTUlUIGxpY2Vuc2UgPGh0dHBzOi8vbG9kYXNoLmNvbS9saWNlbnNlPlxuICogQmFzZWQgb24gVW5kZXJzY29yZS5qcyAxLjguMyA8aHR0cDovL3VuZGVyc2NvcmVqcy5vcmcvTElDRU5TRT5cbiAqIENvcHlyaWdodCBKZXJlbXkgQXNoa2VuYXMsIERvY3VtZW50Q2xvdWQgYW5kIEludmVzdGlnYXRpdmUgUmVwb3J0ZXJzICYgRWRpdG9yc1xuICovXG5cbi8qKiBVc2VkIGFzIHRoZSBgVHlwZUVycm9yYCBtZXNzYWdlIGZvciBcIkZ1bmN0aW9uc1wiIG1ldGhvZHMuICovXG52YXIgRlVOQ19FUlJPUl9URVhUID0gJ0V4cGVjdGVkIGEgZnVuY3Rpb24nO1xuXG4vKiogVXNlZCBhcyByZWZlcmVuY2VzIGZvciB2YXJpb3VzIGBOdW1iZXJgIGNvbnN0YW50cy4gKi9cbnZhciBOQU4gPSAwIC8gMDtcblxuLyoqIGBPYmplY3QjdG9TdHJpbmdgIHJlc3VsdCByZWZlcmVuY2VzLiAqL1xudmFyIHN5bWJvbFRhZyA9ICdbb2JqZWN0IFN5bWJvbF0nO1xuXG4vKiogVXNlZCB0byBtYXRjaCBsZWFkaW5nIGFuZCB0cmFpbGluZyB3aGl0ZXNwYWNlLiAqL1xudmFyIHJlVHJpbSA9IC9eXFxzK3xcXHMrJC9nO1xuXG4vKiogVXNlZCB0byBkZXRlY3QgYmFkIHNpZ25lZCBoZXhhZGVjaW1hbCBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNCYWRIZXggPSAvXlstK10weFswLTlhLWZdKyQvaTtcblxuLyoqIFVzZWQgdG8gZGV0ZWN0IGJpbmFyeSBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNCaW5hcnkgPSAvXjBiWzAxXSskL2k7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBvY3RhbCBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNPY3RhbCA9IC9eMG9bMC03XSskL2k7XG5cbi8qKiBCdWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcyB3aXRob3V0IGEgZGVwZW5kZW5jeSBvbiBgcm9vdGAuICovXG52YXIgZnJlZVBhcnNlSW50ID0gcGFyc2VJbnQ7XG5cbi8qKiBEZXRlY3QgZnJlZSB2YXJpYWJsZSBgZ2xvYmFsYCBmcm9tIE5vZGUuanMuICovXG52YXIgZnJlZUdsb2JhbCA9IHR5cGVvZiBnbG9iYWwgPT0gJ29iamVjdCcgJiYgZ2xvYmFsICYmIGdsb2JhbC5PYmplY3QgPT09IE9iamVjdCAmJiBnbG9iYWw7XG5cbi8qKiBEZXRlY3QgZnJlZSB2YXJpYWJsZSBgc2VsZmAuICovXG52YXIgZnJlZVNlbGYgPSB0eXBlb2Ygc2VsZiA9PSAnb2JqZWN0JyAmJiBzZWxmICYmIHNlbGYuT2JqZWN0ID09PSBPYmplY3QgJiYgc2VsZjtcblxuLyoqIFVzZWQgYXMgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvYmplY3QuICovXG52YXIgcm9vdCA9IGZyZWVHbG9iYWwgfHwgZnJlZVNlbGYgfHwgRnVuY3Rpb24oJ3JldHVybiB0aGlzJykoKTtcblxuLyoqIFVzZWQgZm9yIGJ1aWx0LWluIG1ldGhvZCByZWZlcmVuY2VzLiAqL1xudmFyIG9iamVjdFByb3RvID0gT2JqZWN0LnByb3RvdHlwZTtcblxuLyoqXG4gKiBVc2VkIHRvIHJlc29sdmUgdGhlXG4gKiBbYHRvU3RyaW5nVGFnYF0oaHR0cDovL2VjbWEtaW50ZXJuYXRpb25hbC5vcmcvZWNtYS0yNjIvNy4wLyNzZWMtb2JqZWN0LnByb3RvdHlwZS50b3N0cmluZylcbiAqIG9mIHZhbHVlcy5cbiAqL1xudmFyIG9iamVjdFRvU3RyaW5nID0gb2JqZWN0UHJvdG8udG9TdHJpbmc7XG5cbi8qIEJ1aWx0LWluIG1ldGhvZCByZWZlcmVuY2VzIGZvciB0aG9zZSB3aXRoIHRoZSBzYW1lIG5hbWUgYXMgb3RoZXIgYGxvZGFzaGAgbWV0aG9kcy4gKi9cbnZhciBuYXRpdmVNYXggPSBNYXRoLm1heCxcbiAgICBuYXRpdmVNaW4gPSBNYXRoLm1pbjtcblxuLyoqXG4gKiBHZXRzIHRoZSB0aW1lc3RhbXAgb2YgdGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdGhhdCBoYXZlIGVsYXBzZWQgc2luY2VcbiAqIHRoZSBVbml4IGVwb2NoICgxIEphbnVhcnkgMTk3MCAwMDowMDowMCBVVEMpLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMi40LjBcbiAqIEBjYXRlZ29yeSBEYXRlXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBSZXR1cm5zIHRoZSB0aW1lc3RhbXAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uZGVmZXIoZnVuY3Rpb24oc3RhbXApIHtcbiAqICAgY29uc29sZS5sb2coXy5ub3coKSAtIHN0YW1wKTtcbiAqIH0sIF8ubm93KCkpO1xuICogLy8gPT4gTG9ncyB0aGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyBpdCB0b29rIGZvciB0aGUgZGVmZXJyZWQgaW52b2NhdGlvbi5cbiAqL1xudmFyIG5vdyA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gcm9vdC5EYXRlLm5vdygpO1xufTtcblxuLyoqXG4gKiBDcmVhdGVzIGEgZGVib3VuY2VkIGZ1bmN0aW9uIHRoYXQgZGVsYXlzIGludm9raW5nIGBmdW5jYCB1bnRpbCBhZnRlciBgd2FpdGBcbiAqIG1pbGxpc2Vjb25kcyBoYXZlIGVsYXBzZWQgc2luY2UgdGhlIGxhc3QgdGltZSB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uIHdhc1xuICogaW52b2tlZC4gVGhlIGRlYm91bmNlZCBmdW5jdGlvbiBjb21lcyB3aXRoIGEgYGNhbmNlbGAgbWV0aG9kIHRvIGNhbmNlbFxuICogZGVsYXllZCBgZnVuY2AgaW52b2NhdGlvbnMgYW5kIGEgYGZsdXNoYCBtZXRob2QgdG8gaW1tZWRpYXRlbHkgaW52b2tlIHRoZW0uXG4gKiBQcm92aWRlIGBvcHRpb25zYCB0byBpbmRpY2F0ZSB3aGV0aGVyIGBmdW5jYCBzaG91bGQgYmUgaW52b2tlZCBvbiB0aGVcbiAqIGxlYWRpbmcgYW5kL29yIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIGB3YWl0YCB0aW1lb3V0LiBUaGUgYGZ1bmNgIGlzIGludm9rZWRcbiAqIHdpdGggdGhlIGxhc3QgYXJndW1lbnRzIHByb3ZpZGVkIHRvIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb24uIFN1YnNlcXVlbnRcbiAqIGNhbGxzIHRvIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb24gcmV0dXJuIHRoZSByZXN1bHQgb2YgdGhlIGxhc3QgYGZ1bmNgXG4gKiBpbnZvY2F0aW9uLlxuICpcbiAqICoqTm90ZToqKiBJZiBgbGVhZGluZ2AgYW5kIGB0cmFpbGluZ2Agb3B0aW9ucyBhcmUgYHRydWVgLCBgZnVuY2AgaXNcbiAqIGludm9rZWQgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQgb25seSBpZiB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uXG4gKiBpcyBpbnZva2VkIG1vcmUgdGhhbiBvbmNlIGR1cmluZyB0aGUgYHdhaXRgIHRpbWVvdXQuXG4gKlxuICogSWYgYHdhaXRgIGlzIGAwYCBhbmQgYGxlYWRpbmdgIGlzIGBmYWxzZWAsIGBmdW5jYCBpbnZvY2F0aW9uIGlzIGRlZmVycmVkXG4gKiB1bnRpbCB0byB0aGUgbmV4dCB0aWNrLCBzaW1pbGFyIHRvIGBzZXRUaW1lb3V0YCB3aXRoIGEgdGltZW91dCBvZiBgMGAuXG4gKlxuICogU2VlIFtEYXZpZCBDb3JiYWNobydzIGFydGljbGVdKGh0dHBzOi8vY3NzLXRyaWNrcy5jb20vZGVib3VuY2luZy10aHJvdHRsaW5nLWV4cGxhaW5lZC1leGFtcGxlcy8pXG4gKiBmb3IgZGV0YWlscyBvdmVyIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGBfLmRlYm91bmNlYCBhbmQgYF8udGhyb3R0bGVgLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBGdW5jdGlvblxuICogQHBhcmFtIHtGdW5jdGlvbn0gZnVuYyBUaGUgZnVuY3Rpb24gdG8gZGVib3VuY2UuXG4gKiBAcGFyYW0ge251bWJlcn0gW3dhaXQ9MF0gVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gZGVsYXkuXG4gKiBAcGFyYW0ge09iamVjdH0gW29wdGlvbnM9e31dIFRoZSBvcHRpb25zIG9iamVjdC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMubGVhZGluZz1mYWxzZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSBsZWFkaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcGFyYW0ge251bWJlcn0gW29wdGlvbnMubWF4V2FpdF1cbiAqICBUaGUgbWF4aW11bSB0aW1lIGBmdW5jYCBpcyBhbGxvd2VkIHRvIGJlIGRlbGF5ZWQgYmVmb3JlIGl0J3MgaW52b2tlZC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMudHJhaWxpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgZGVib3VuY2VkIGZ1bmN0aW9uLlxuICogQGV4YW1wbGVcbiAqXG4gKiAvLyBBdm9pZCBjb3N0bHkgY2FsY3VsYXRpb25zIHdoaWxlIHRoZSB3aW5kb3cgc2l6ZSBpcyBpbiBmbHV4LlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3Jlc2l6ZScsIF8uZGVib3VuY2UoY2FsY3VsYXRlTGF5b3V0LCAxNTApKTtcbiAqXG4gKiAvLyBJbnZva2UgYHNlbmRNYWlsYCB3aGVuIGNsaWNrZWQsIGRlYm91bmNpbmcgc3Vic2VxdWVudCBjYWxscy5cbiAqIGpRdWVyeShlbGVtZW50KS5vbignY2xpY2snLCBfLmRlYm91bmNlKHNlbmRNYWlsLCAzMDAsIHtcbiAqICAgJ2xlYWRpbmcnOiB0cnVlLFxuICogICAndHJhaWxpbmcnOiBmYWxzZVxuICogfSkpO1xuICpcbiAqIC8vIEVuc3VyZSBgYmF0Y2hMb2dgIGlzIGludm9rZWQgb25jZSBhZnRlciAxIHNlY29uZCBvZiBkZWJvdW5jZWQgY2FsbHMuXG4gKiB2YXIgZGVib3VuY2VkID0gXy5kZWJvdW5jZShiYXRjaExvZywgMjUwLCB7ICdtYXhXYWl0JzogMTAwMCB9KTtcbiAqIHZhciBzb3VyY2UgPSBuZXcgRXZlbnRTb3VyY2UoJy9zdHJlYW0nKTtcbiAqIGpRdWVyeShzb3VyY2UpLm9uKCdtZXNzYWdlJywgZGVib3VuY2VkKTtcbiAqXG4gKiAvLyBDYW5jZWwgdGhlIHRyYWlsaW5nIGRlYm91bmNlZCBpbnZvY2F0aW9uLlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3BvcHN0YXRlJywgZGVib3VuY2VkLmNhbmNlbCk7XG4gKi9cbmZ1bmN0aW9uIGRlYm91bmNlKGZ1bmMsIHdhaXQsIG9wdGlvbnMpIHtcbiAgdmFyIGxhc3RBcmdzLFxuICAgICAgbGFzdFRoaXMsXG4gICAgICBtYXhXYWl0LFxuICAgICAgcmVzdWx0LFxuICAgICAgdGltZXJJZCxcbiAgICAgIGxhc3RDYWxsVGltZSxcbiAgICAgIGxhc3RJbnZva2VUaW1lID0gMCxcbiAgICAgIGxlYWRpbmcgPSBmYWxzZSxcbiAgICAgIG1heGluZyA9IGZhbHNlLFxuICAgICAgdHJhaWxpbmcgPSB0cnVlO1xuXG4gIGlmICh0eXBlb2YgZnVuYyAhPSAnZnVuY3Rpb24nKSB7XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcihGVU5DX0VSUk9SX1RFWFQpO1xuICB9XG4gIHdhaXQgPSB0b051bWJlcih3YWl0KSB8fCAwO1xuICBpZiAoaXNPYmplY3Qob3B0aW9ucykpIHtcbiAgICBsZWFkaW5nID0gISFvcHRpb25zLmxlYWRpbmc7XG4gICAgbWF4aW5nID0gJ21heFdhaXQnIGluIG9wdGlvbnM7XG4gICAgbWF4V2FpdCA9IG1heGluZyA/IG5hdGl2ZU1heCh0b051bWJlcihvcHRpb25zLm1heFdhaXQpIHx8IDAsIHdhaXQpIDogbWF4V2FpdDtcbiAgICB0cmFpbGluZyA9ICd0cmFpbGluZycgaW4gb3B0aW9ucyA/ICEhb3B0aW9ucy50cmFpbGluZyA6IHRyYWlsaW5nO1xuICB9XG5cbiAgZnVuY3Rpb24gaW52b2tlRnVuYyh0aW1lKSB7XG4gICAgdmFyIGFyZ3MgPSBsYXN0QXJncyxcbiAgICAgICAgdGhpc0FyZyA9IGxhc3RUaGlzO1xuXG4gICAgbGFzdEFyZ3MgPSBsYXN0VGhpcyA9IHVuZGVmaW5lZDtcbiAgICBsYXN0SW52b2tlVGltZSA9IHRpbWU7XG4gICAgcmVzdWx0ID0gZnVuYy5hcHBseSh0aGlzQXJnLCBhcmdzKTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gbGVhZGluZ0VkZ2UodGltZSkge1xuICAgIC8vIFJlc2V0IGFueSBgbWF4V2FpdGAgdGltZXIuXG4gICAgbGFzdEludm9rZVRpbWUgPSB0aW1lO1xuICAgIC8vIFN0YXJ0IHRoZSB0aW1lciBmb3IgdGhlIHRyYWlsaW5nIGVkZ2UuXG4gICAgdGltZXJJZCA9IHNldFRpbWVvdXQodGltZXJFeHBpcmVkLCB3YWl0KTtcbiAgICAvLyBJbnZva2UgdGhlIGxlYWRpbmcgZWRnZS5cbiAgICByZXR1cm4gbGVhZGluZyA/IGludm9rZUZ1bmModGltZSkgOiByZXN1bHQ7XG4gIH1cblxuICBmdW5jdGlvbiByZW1haW5pbmdXYWl0KHRpbWUpIHtcbiAgICB2YXIgdGltZVNpbmNlTGFzdENhbGwgPSB0aW1lIC0gbGFzdENhbGxUaW1lLFxuICAgICAgICB0aW1lU2luY2VMYXN0SW52b2tlID0gdGltZSAtIGxhc3RJbnZva2VUaW1lLFxuICAgICAgICByZXN1bHQgPSB3YWl0IC0gdGltZVNpbmNlTGFzdENhbGw7XG5cbiAgICByZXR1cm4gbWF4aW5nID8gbmF0aXZlTWluKHJlc3VsdCwgbWF4V2FpdCAtIHRpbWVTaW5jZUxhc3RJbnZva2UpIDogcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gc2hvdWxkSW52b2tlKHRpbWUpIHtcbiAgICB2YXIgdGltZVNpbmNlTGFzdENhbGwgPSB0aW1lIC0gbGFzdENhbGxUaW1lLFxuICAgICAgICB0aW1lU2luY2VMYXN0SW52b2tlID0gdGltZSAtIGxhc3RJbnZva2VUaW1lO1xuXG4gICAgLy8gRWl0aGVyIHRoaXMgaXMgdGhlIGZpcnN0IGNhbGwsIGFjdGl2aXR5IGhhcyBzdG9wcGVkIGFuZCB3ZSdyZSBhdCB0aGVcbiAgICAvLyB0cmFpbGluZyBlZGdlLCB0aGUgc3lzdGVtIHRpbWUgaGFzIGdvbmUgYmFja3dhcmRzIGFuZCB3ZSdyZSB0cmVhdGluZ1xuICAgIC8vIGl0IGFzIHRoZSB0cmFpbGluZyBlZGdlLCBvciB3ZSd2ZSBoaXQgdGhlIGBtYXhXYWl0YCBsaW1pdC5cbiAgICByZXR1cm4gKGxhc3RDYWxsVGltZSA9PT0gdW5kZWZpbmVkIHx8ICh0aW1lU2luY2VMYXN0Q2FsbCA+PSB3YWl0KSB8fFxuICAgICAgKHRpbWVTaW5jZUxhc3RDYWxsIDwgMCkgfHwgKG1heGluZyAmJiB0aW1lU2luY2VMYXN0SW52b2tlID49IG1heFdhaXQpKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHRpbWVyRXhwaXJlZCgpIHtcbiAgICB2YXIgdGltZSA9IG5vdygpO1xuICAgIGlmIChzaG91bGRJbnZva2UodGltZSkpIHtcbiAgICAgIHJldHVybiB0cmFpbGluZ0VkZ2UodGltZSk7XG4gICAgfVxuICAgIC8vIFJlc3RhcnQgdGhlIHRpbWVyLlxuICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgcmVtYWluaW5nV2FpdCh0aW1lKSk7XG4gIH1cblxuICBmdW5jdGlvbiB0cmFpbGluZ0VkZ2UodGltZSkge1xuICAgIHRpbWVySWQgPSB1bmRlZmluZWQ7XG5cbiAgICAvLyBPbmx5IGludm9rZSBpZiB3ZSBoYXZlIGBsYXN0QXJnc2Agd2hpY2ggbWVhbnMgYGZ1bmNgIGhhcyBiZWVuXG4gICAgLy8gZGVib3VuY2VkIGF0IGxlYXN0IG9uY2UuXG4gICAgaWYgKHRyYWlsaW5nICYmIGxhc3RBcmdzKSB7XG4gICAgICByZXR1cm4gaW52b2tlRnVuYyh0aW1lKTtcbiAgICB9XG4gICAgbGFzdEFyZ3MgPSBsYXN0VGhpcyA9IHVuZGVmaW5lZDtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gY2FuY2VsKCkge1xuICAgIGlmICh0aW1lcklkICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGNsZWFyVGltZW91dCh0aW1lcklkKTtcbiAgICB9XG4gICAgbGFzdEludm9rZVRpbWUgPSAwO1xuICAgIGxhc3RBcmdzID0gbGFzdENhbGxUaW1lID0gbGFzdFRoaXMgPSB0aW1lcklkID0gdW5kZWZpbmVkO1xuICB9XG5cbiAgZnVuY3Rpb24gZmx1c2goKSB7XG4gICAgcmV0dXJuIHRpbWVySWQgPT09IHVuZGVmaW5lZCA/IHJlc3VsdCA6IHRyYWlsaW5nRWRnZShub3coKSk7XG4gIH1cblxuICBmdW5jdGlvbiBkZWJvdW5jZWQoKSB7XG4gICAgdmFyIHRpbWUgPSBub3coKSxcbiAgICAgICAgaXNJbnZva2luZyA9IHNob3VsZEludm9rZSh0aW1lKTtcblxuICAgIGxhc3RBcmdzID0gYXJndW1lbnRzO1xuICAgIGxhc3RUaGlzID0gdGhpcztcbiAgICBsYXN0Q2FsbFRpbWUgPSB0aW1lO1xuXG4gICAgaWYgKGlzSW52b2tpbmcpIHtcbiAgICAgIGlmICh0aW1lcklkID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuIGxlYWRpbmdFZGdlKGxhc3RDYWxsVGltZSk7XG4gICAgICB9XG4gICAgICBpZiAobWF4aW5nKSB7XG4gICAgICAgIC8vIEhhbmRsZSBpbnZvY2F0aW9ucyBpbiBhIHRpZ2h0IGxvb3AuXG4gICAgICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgICAgIHJldHVybiBpbnZva2VGdW5jKGxhc3RDYWxsVGltZSk7XG4gICAgICB9XG4gICAgfVxuICAgIGlmICh0aW1lcklkID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbiAgZGVib3VuY2VkLmNhbmNlbCA9IGNhbmNlbDtcbiAgZGVib3VuY2VkLmZsdXNoID0gZmx1c2g7XG4gIHJldHVybiBkZWJvdW5jZWQ7XG59XG5cbi8qKlxuICogQ3JlYXRlcyBhIHRocm90dGxlZCBmdW5jdGlvbiB0aGF0IG9ubHkgaW52b2tlcyBgZnVuY2AgYXQgbW9zdCBvbmNlIHBlclxuICogZXZlcnkgYHdhaXRgIG1pbGxpc2Vjb25kcy4gVGhlIHRocm90dGxlZCBmdW5jdGlvbiBjb21lcyB3aXRoIGEgYGNhbmNlbGBcbiAqIG1ldGhvZCB0byBjYW5jZWwgZGVsYXllZCBgZnVuY2AgaW52b2NhdGlvbnMgYW5kIGEgYGZsdXNoYCBtZXRob2QgdG9cbiAqIGltbWVkaWF0ZWx5IGludm9rZSB0aGVtLiBQcm92aWRlIGBvcHRpb25zYCB0byBpbmRpY2F0ZSB3aGV0aGVyIGBmdW5jYFxuICogc2hvdWxkIGJlIGludm9rZWQgb24gdGhlIGxlYWRpbmcgYW5kL29yIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIGB3YWl0YFxuICogdGltZW91dC4gVGhlIGBmdW5jYCBpcyBpbnZva2VkIHdpdGggdGhlIGxhc3QgYXJndW1lbnRzIHByb3ZpZGVkIHRvIHRoZVxuICogdGhyb3R0bGVkIGZ1bmN0aW9uLiBTdWJzZXF1ZW50IGNhbGxzIHRvIHRoZSB0aHJvdHRsZWQgZnVuY3Rpb24gcmV0dXJuIHRoZVxuICogcmVzdWx0IG9mIHRoZSBsYXN0IGBmdW5jYCBpbnZvY2F0aW9uLlxuICpcbiAqICoqTm90ZToqKiBJZiBgbGVhZGluZ2AgYW5kIGB0cmFpbGluZ2Agb3B0aW9ucyBhcmUgYHRydWVgLCBgZnVuY2AgaXNcbiAqIGludm9rZWQgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQgb25seSBpZiB0aGUgdGhyb3R0bGVkIGZ1bmN0aW9uXG4gKiBpcyBpbnZva2VkIG1vcmUgdGhhbiBvbmNlIGR1cmluZyB0aGUgYHdhaXRgIHRpbWVvdXQuXG4gKlxuICogSWYgYHdhaXRgIGlzIGAwYCBhbmQgYGxlYWRpbmdgIGlzIGBmYWxzZWAsIGBmdW5jYCBpbnZvY2F0aW9uIGlzIGRlZmVycmVkXG4gKiB1bnRpbCB0byB0aGUgbmV4dCB0aWNrLCBzaW1pbGFyIHRvIGBzZXRUaW1lb3V0YCB3aXRoIGEgdGltZW91dCBvZiBgMGAuXG4gKlxuICogU2VlIFtEYXZpZCBDb3JiYWNobydzIGFydGljbGVdKGh0dHBzOi8vY3NzLXRyaWNrcy5jb20vZGVib3VuY2luZy10aHJvdHRsaW5nLWV4cGxhaW5lZC1leGFtcGxlcy8pXG4gKiBmb3IgZGV0YWlscyBvdmVyIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGBfLnRocm90dGxlYCBhbmQgYF8uZGVib3VuY2VgLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBGdW5jdGlvblxuICogQHBhcmFtIHtGdW5jdGlvbn0gZnVuYyBUaGUgZnVuY3Rpb24gdG8gdGhyb3R0bGUuXG4gKiBAcGFyYW0ge251bWJlcn0gW3dhaXQ9MF0gVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gdGhyb3R0bGUgaW52b2NhdGlvbnMgdG8uXG4gKiBAcGFyYW0ge09iamVjdH0gW29wdGlvbnM9e31dIFRoZSBvcHRpb25zIG9iamVjdC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMubGVhZGluZz10cnVlXVxuICogIFNwZWNpZnkgaW52b2tpbmcgb24gdGhlIGxlYWRpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMudHJhaWxpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgdGhyb3R0bGVkIGZ1bmN0aW9uLlxuICogQGV4YW1wbGVcbiAqXG4gKiAvLyBBdm9pZCBleGNlc3NpdmVseSB1cGRhdGluZyB0aGUgcG9zaXRpb24gd2hpbGUgc2Nyb2xsaW5nLlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3Njcm9sbCcsIF8udGhyb3R0bGUodXBkYXRlUG9zaXRpb24sIDEwMCkpO1xuICpcbiAqIC8vIEludm9rZSBgcmVuZXdUb2tlbmAgd2hlbiB0aGUgY2xpY2sgZXZlbnQgaXMgZmlyZWQsIGJ1dCBub3QgbW9yZSB0aGFuIG9uY2UgZXZlcnkgNSBtaW51dGVzLlxuICogdmFyIHRocm90dGxlZCA9IF8udGhyb3R0bGUocmVuZXdUb2tlbiwgMzAwMDAwLCB7ICd0cmFpbGluZyc6IGZhbHNlIH0pO1xuICogalF1ZXJ5KGVsZW1lbnQpLm9uKCdjbGljaycsIHRocm90dGxlZCk7XG4gKlxuICogLy8gQ2FuY2VsIHRoZSB0cmFpbGluZyB0aHJvdHRsZWQgaW52b2NhdGlvbi5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdwb3BzdGF0ZScsIHRocm90dGxlZC5jYW5jZWwpO1xuICovXG5mdW5jdGlvbiB0aHJvdHRsZShmdW5jLCB3YWl0LCBvcHRpb25zKSB7XG4gIHZhciBsZWFkaW5nID0gdHJ1ZSxcbiAgICAgIHRyYWlsaW5nID0gdHJ1ZTtcblxuICBpZiAodHlwZW9mIGZ1bmMgIT0gJ2Z1bmN0aW9uJykge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoRlVOQ19FUlJPUl9URVhUKTtcbiAgfVxuICBpZiAoaXNPYmplY3Qob3B0aW9ucykpIHtcbiAgICBsZWFkaW5nID0gJ2xlYWRpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMubGVhZGluZyA6IGxlYWRpbmc7XG4gICAgdHJhaWxpbmcgPSAndHJhaWxpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMudHJhaWxpbmcgOiB0cmFpbGluZztcbiAgfVxuICByZXR1cm4gZGVib3VuY2UoZnVuYywgd2FpdCwge1xuICAgICdsZWFkaW5nJzogbGVhZGluZyxcbiAgICAnbWF4V2FpdCc6IHdhaXQsXG4gICAgJ3RyYWlsaW5nJzogdHJhaWxpbmdcbiAgfSk7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgdGhlXG4gKiBbbGFuZ3VhZ2UgdHlwZV0oaHR0cDovL3d3dy5lY21hLWludGVybmF0aW9uYWwub3JnL2VjbWEtMjYyLzcuMC8jc2VjLWVjbWFzY3JpcHQtbGFuZ3VhZ2UtdHlwZXMpXG4gKiBvZiBgT2JqZWN0YC4gKGUuZy4gYXJyYXlzLCBmdW5jdGlvbnMsIG9iamVjdHMsIHJlZ2V4ZXMsIGBuZXcgTnVtYmVyKDApYCwgYW5kIGBuZXcgU3RyaW5nKCcnKWApXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSAwLjEuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgYW4gb2JqZWN0LCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNPYmplY3Qoe30pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3QoWzEsIDIsIDNdKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0KF8ubm9vcCk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdChudWxsKTtcbiAqIC8vID0+IGZhbHNlXG4gKi9cbmZ1bmN0aW9uIGlzT2JqZWN0KHZhbHVlKSB7XG4gIHZhciB0eXBlID0gdHlwZW9mIHZhbHVlO1xuICByZXR1cm4gISF2YWx1ZSAmJiAodHlwZSA9PSAnb2JqZWN0JyB8fCB0eXBlID09ICdmdW5jdGlvbicpO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIG9iamVjdC1saWtlLiBBIHZhbHVlIGlzIG9iamVjdC1saWtlIGlmIGl0J3Mgbm90IGBudWxsYFxuICogYW5kIGhhcyBhIGB0eXBlb2ZgIHJlc3VsdCBvZiBcIm9iamVjdFwiLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgNC4wLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIG9iamVjdC1saWtlLCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKHt9KTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShbMSwgMiwgM10pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKF8ubm9vcCk7XG4gKiAvLyA9PiBmYWxzZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKG51bGwpO1xuICogLy8gPT4gZmFsc2VcbiAqL1xuZnVuY3Rpb24gaXNPYmplY3RMaWtlKHZhbHVlKSB7XG4gIHJldHVybiAhIXZhbHVlICYmIHR5cGVvZiB2YWx1ZSA9PSAnb2JqZWN0Jztcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyBjbGFzc2lmaWVkIGFzIGEgYFN5bWJvbGAgcHJpbWl0aXZlIG9yIG9iamVjdC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBhIHN5bWJvbCwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzU3ltYm9sKFN5bWJvbC5pdGVyYXRvcik7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc1N5bWJvbCgnYWJjJyk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc1N5bWJvbCh2YWx1ZSkge1xuICByZXR1cm4gdHlwZW9mIHZhbHVlID09ICdzeW1ib2wnIHx8XG4gICAgKGlzT2JqZWN0TGlrZSh2YWx1ZSkgJiYgb2JqZWN0VG9TdHJpbmcuY2FsbCh2YWx1ZSkgPT0gc3ltYm9sVGFnKTtcbn1cblxuLyoqXG4gKiBDb252ZXJ0cyBgdmFsdWVgIHRvIGEgbnVtYmVyLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgNC4wLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBwcm9jZXNzLlxuICogQHJldHVybnMge251bWJlcn0gUmV0dXJucyB0aGUgbnVtYmVyLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLnRvTnVtYmVyKDMuMik7XG4gKiAvLyA9PiAzLjJcbiAqXG4gKiBfLnRvTnVtYmVyKE51bWJlci5NSU5fVkFMVUUpO1xuICogLy8gPT4gNWUtMzI0XG4gKlxuICogXy50b051bWJlcihJbmZpbml0eSk7XG4gKiAvLyA9PiBJbmZpbml0eVxuICpcbiAqIF8udG9OdW1iZXIoJzMuMicpO1xuICogLy8gPT4gMy4yXG4gKi9cbmZ1bmN0aW9uIHRvTnVtYmVyKHZhbHVlKSB7XG4gIGlmICh0eXBlb2YgdmFsdWUgPT0gJ251bWJlcicpIHtcbiAgICByZXR1cm4gdmFsdWU7XG4gIH1cbiAgaWYgKGlzU3ltYm9sKHZhbHVlKSkge1xuICAgIHJldHVybiBOQU47XG4gIH1cbiAgaWYgKGlzT2JqZWN0KHZhbHVlKSkge1xuICAgIHZhciBvdGhlciA9IHR5cGVvZiB2YWx1ZS52YWx1ZU9mID09ICdmdW5jdGlvbicgPyB2YWx1ZS52YWx1ZU9mKCkgOiB2YWx1ZTtcbiAgICB2YWx1ZSA9IGlzT2JqZWN0KG90aGVyKSA/IChvdGhlciArICcnKSA6IG90aGVyO1xuICB9XG4gIGlmICh0eXBlb2YgdmFsdWUgIT0gJ3N0cmluZycpIHtcbiAgICByZXR1cm4gdmFsdWUgPT09IDAgPyB2YWx1ZSA6ICt2YWx1ZTtcbiAgfVxuICB2YWx1ZSA9IHZhbHVlLnJlcGxhY2UocmVUcmltLCAnJyk7XG4gIHZhciBpc0JpbmFyeSA9IHJlSXNCaW5hcnkudGVzdCh2YWx1ZSk7XG4gIHJldHVybiAoaXNCaW5hcnkgfHwgcmVJc09jdGFsLnRlc3QodmFsdWUpKVxuICAgID8gZnJlZVBhcnNlSW50KHZhbHVlLnNsaWNlKDIpLCBpc0JpbmFyeSA/IDIgOiA4KVxuICAgIDogKHJlSXNCYWRIZXgudGVzdCh2YWx1ZSkgPyBOQU4gOiArdmFsdWUpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHRocm90dGxlO1xuIiwidmFyIGc7XHJcblxyXG4vLyBUaGlzIHdvcmtzIGluIG5vbi1zdHJpY3QgbW9kZVxyXG5nID0gKGZ1bmN0aW9uKCkge1xyXG5cdHJldHVybiB0aGlzO1xyXG59KSgpO1xyXG5cclxudHJ5IHtcclxuXHQvLyBUaGlzIHdvcmtzIGlmIGV2YWwgaXMgYWxsb3dlZCAoc2VlIENTUClcclxuXHRnID0gZyB8fCBGdW5jdGlvbihcInJldHVybiB0aGlzXCIpKCkgfHwgKDEsIGV2YWwpKFwidGhpc1wiKTtcclxufSBjYXRjaCAoZSkge1xyXG5cdC8vIFRoaXMgd29ya3MgaWYgdGhlIHdpbmRvdyByZWZlcmVuY2UgaXMgYXZhaWxhYmxlXHJcblx0aWYgKHR5cGVvZiB3aW5kb3cgPT09IFwib2JqZWN0XCIpIGcgPSB3aW5kb3c7XHJcbn1cclxuXHJcbi8vIGcgY2FuIHN0aWxsIGJlIHVuZGVmaW5lZCwgYnV0IG5vdGhpbmcgdG8gZG8gYWJvdXQgaXQuLi5cclxuLy8gV2UgcmV0dXJuIHVuZGVmaW5lZCwgaW5zdGVhZCBvZiBub3RoaW5nIGhlcmUsIHNvIGl0J3NcclxuLy8gZWFzaWVyIHRvIGhhbmRsZSB0aGlzIGNhc2UuIGlmKCFnbG9iYWwpIHsgLi4ufVxyXG5cclxubW9kdWxlLmV4cG9ydHMgPSBnO1xyXG4iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbmltcG9ydCB7IGdldEVsZW1lbnRzRm9yU291cmNlTGluZSB9IGZyb20gJy4vc2Nyb2xsLXN5bmMnO1xuXG5leHBvcnQgY2xhc3MgQWN0aXZlTGluZU1hcmtlciB7XG5cdHByaXZhdGUgX2N1cnJlbnQ6IGFueTtcblxuXHRvbkRpZENoYW5nZVRleHRFZGl0b3JTZWxlY3Rpb24obGluZTogbnVtYmVyKSB7XG5cdFx0Y29uc3QgeyBwcmV2aW91cyB9ID0gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKGxpbmUpO1xuXHRcdHRoaXMuX3VwZGF0ZShwcmV2aW91cyAmJiBwcmV2aW91cy5lbGVtZW50KTtcblx0fVxuXG5cdF91cGRhdGUoYmVmb3JlOiBIVE1MRWxlbWVudCB8IHVuZGVmaW5lZCkge1xuXHRcdHRoaXMuX3VubWFya0FjdGl2ZUVsZW1lbnQodGhpcy5fY3VycmVudCk7XG5cdFx0dGhpcy5fbWFya0FjdGl2ZUVsZW1lbnQoYmVmb3JlKTtcblx0XHR0aGlzLl9jdXJyZW50ID0gYmVmb3JlO1xuXHR9XG5cblx0X3VubWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudDogSFRNTEVsZW1lbnQgfCB1bmRlZmluZWQpIHtcblx0XHRpZiAoIWVsZW1lbnQpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0ZWxlbWVudC5jbGFzc05hbWUgPSBlbGVtZW50LmNsYXNzTmFtZS5yZXBsYWNlKC9cXGJjb2RlLWFjdGl2ZS1saW5lXFxiL2csICcnKTtcblx0fVxuXG5cdF9tYXJrQWN0aXZlRWxlbWVudChlbGVtZW50OiBIVE1MRWxlbWVudCB8IHVuZGVmaW5lZCkge1xuXHRcdGlmICghZWxlbWVudCkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHRlbGVtZW50LmNsYXNzTmFtZSArPSAnIGNvZGUtYWN0aXZlLWxpbmUnO1xuXHR9XG59IiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmV4cG9ydCBmdW5jdGlvbiBvbmNlRG9jdW1lbnRMb2FkZWQoZjogKCkgPT4gdm9pZCkge1xuXHRpZiAoZG9jdW1lbnQucmVhZHlTdGF0ZSA9PT0gJ2xvYWRpbmcnIHx8IGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICd1bmluaXRpYWxpemVkJykge1xuXHRcdGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCBmKTtcblx0fSBlbHNlIHtcblx0XHRmKCk7XG5cdH1cbn0iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuaW1wb3J0IHsgQWN0aXZlTGluZU1hcmtlciB9IGZyb20gJy4vYWN0aXZlTGluZU1hcmtlcic7XG5pbXBvcnQgeyBvbmNlRG9jdW1lbnRMb2FkZWQgfSBmcm9tICcuL2V2ZW50cyc7XG5pbXBvcnQgeyBjcmVhdGVQb3N0ZXJGb3JWc0NvZGUgfSBmcm9tICcuL21lc3NhZ2luZyc7XG5pbXBvcnQgeyBnZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCwgc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lIH0gZnJvbSAnLi9zY3JvbGwtc3luYyc7XG5pbXBvcnQgeyBnZXRTZXR0aW5ncywgZ2V0RGF0YSB9IGZyb20gJy4vc2V0dGluZ3MnO1xuaW1wb3J0IHRocm90dGxlID0gcmVxdWlyZSgnbG9kYXNoLnRocm90dGxlJyk7XG5cbmRlY2xhcmUgdmFyIGFjcXVpcmVWc0NvZGVBcGk6IGFueTtcblxudmFyIHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcbmNvbnN0IG1hcmtlciA9IG5ldyBBY3RpdmVMaW5lTWFya2VyKCk7XG5jb25zdCBzZXR0aW5ncyA9IGdldFNldHRpbmdzKCk7XG5cbmNvbnN0IHZzY29kZSA9IGFjcXVpcmVWc0NvZGVBcGkoKTtcblxuLy8gU2V0IFZTIENvZGUgc3RhdGVcbmNvbnN0IHN0YXRlID0gZ2V0RGF0YSgnZGF0YS1zdGF0ZScpO1xudnNjb2RlLnNldFN0YXRlKHN0YXRlKTtcblxuY29uc3QgbWVzc2FnaW5nID0gY3JlYXRlUG9zdGVyRm9yVnNDb2RlKHZzY29kZSk7XG5cbndpbmRvdy5jc3BBbGVydGVyLnNldFBvc3RlcihtZXNzYWdpbmcpO1xud2luZG93LnN0eWxlTG9hZGluZ01vbml0b3Iuc2V0UG9zdGVyKG1lc3NhZ2luZyk7XG5cbndpbmRvdy5vbmxvYWQgPSAoKSA9PiB7XG5cdHVwZGF0ZUltYWdlU2l6ZXMoKTtcbn07XG5cbm9uY2VEb2N1bWVudExvYWRlZCgoKSA9PiB7XG5cdGlmIChzZXR0aW5ncy5zY3JvbGxQcmV2aWV3V2l0aEVkaXRvcikge1xuXHRcdHNldFRpbWVvdXQoKCkgPT4ge1xuXHRcdFx0Y29uc3QgaW5pdGlhbExpbmUgPSArc2V0dGluZ3MubGluZTtcblx0XHRcdGlmICghaXNOYU4oaW5pdGlhbExpbmUpKSB7XG5cdFx0XHRcdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0XHRcdFx0c2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGluaXRpYWxMaW5lKTtcblx0XHRcdH1cblx0XHR9LCAwKTtcblx0fVxufSk7XG5cbmNvbnN0IG9uVXBkYXRlVmlldyA9ICgoKSA9PiB7XG5cdGNvbnN0IGRvU2Nyb2xsID0gdGhyb3R0bGUoKGxpbmU6IG51bWJlcikgPT4ge1xuXHRcdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0XHRzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUobGluZSk7XG5cdH0sIDUwKTtcblxuXHRyZXR1cm4gKGxpbmU6IG51bWJlciwgc2V0dGluZ3M6IGFueSkgPT4ge1xuXHRcdGlmICghaXNOYU4obGluZSkpIHtcblx0XHRcdHNldHRpbmdzLmxpbmUgPSBsaW5lO1xuXHRcdFx0ZG9TY3JvbGwobGluZSk7XG5cdFx0fVxuXHR9O1xufSkoKTtcblxubGV0IHVwZGF0ZUltYWdlU2l6ZXMgPSB0aHJvdHRsZSgoKSA9PiB7XG5cdGNvbnN0IGltYWdlSW5mbzogeyBpZDogc3RyaW5nLCBoZWlnaHQ6IG51bWJlciwgd2lkdGg6IG51bWJlciB9W10gPSBbXTtcblx0bGV0IGltYWdlcyA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdpbWcnKTtcblx0aWYgKGltYWdlcykge1xuXHRcdGxldCBpO1xuXHRcdGZvciAoaSA9IDA7IGkgPCBpbWFnZXMubGVuZ3RoOyBpKyspIHtcblx0XHRcdGNvbnN0IGltZyA9IGltYWdlc1tpXTtcblxuXHRcdFx0aWYgKGltZy5jbGFzc0xpc3QuY29udGFpbnMoJ2xvYWRpbmcnKSkge1xuXHRcdFx0XHRpbWcuY2xhc3NMaXN0LnJlbW92ZSgnbG9hZGluZycpO1xuXHRcdFx0fVxuXG5cdFx0XHRpbWFnZUluZm8ucHVzaCh7XG5cdFx0XHRcdGlkOiBpbWcuaWQsXG5cdFx0XHRcdGhlaWdodDogaW1nLmhlaWdodCxcblx0XHRcdFx0d2lkdGg6IGltZy53aWR0aFxuXHRcdFx0fSk7XG5cdFx0fVxuXG5cdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdjYWNoZUltYWdlU2l6ZXMnLCBpbWFnZUluZm8pO1xuXHR9XG59LCA1MCk7XG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCAoKSA9PiB7XG5cdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0dXBkYXRlSW1hZ2VTaXplcygpO1xufSwgdHJ1ZSk7XG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgZXZlbnQgPT4ge1xuXHRpZiAoZXZlbnQuZGF0YS5zb3VyY2UgIT09IHNldHRpbmdzLnNvdXJjZSkge1xuXHRcdHJldHVybjtcblx0fVxuXG5cdHN3aXRjaCAoZXZlbnQuZGF0YS50eXBlKSB7XG5cdFx0Y2FzZSAnb25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uJzpcblx0XHRcdG1hcmtlci5vbkRpZENoYW5nZVRleHRFZGl0b3JTZWxlY3Rpb24oZXZlbnQuZGF0YS5saW5lKTtcblx0XHRcdGJyZWFrO1xuXG5cdFx0Y2FzZSAndXBkYXRlVmlldyc6XG5cdFx0XHRvblVwZGF0ZVZpZXcoZXZlbnQuZGF0YS5saW5lLCBzZXR0aW5ncyk7XG5cdFx0XHRicmVhaztcblx0fVxufSwgZmFsc2UpO1xuXG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdkYmxjbGljaycsIGV2ZW50ID0+IHtcblx0aWYgKCFzZXR0aW5ncy5kb3VibGVDbGlja1RvU3dpdGNoVG9FZGl0b3IpIHtcblx0XHRyZXR1cm47XG5cdH1cblxuXHQvLyBJZ25vcmUgY2xpY2tzIG9uIGxpbmtzXG5cdGZvciAobGV0IG5vZGUgPSBldmVudC50YXJnZXQgYXMgSFRNTEVsZW1lbnQ7IG5vZGU7IG5vZGUgPSBub2RlLnBhcmVudE5vZGUgYXMgSFRNTEVsZW1lbnQpIHtcblx0XHRpZiAobm9kZS50YWdOYW1lID09PSAnQScpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdH1cblxuXHRjb25zdCBvZmZzZXQgPSBldmVudC5wYWdlWTtcblx0Y29uc3QgbGluZSA9IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldCk7XG5cdGlmICh0eXBlb2YgbGluZSA9PT0gJ251bWJlcicgJiYgIWlzTmFOKGxpbmUpKSB7XG5cdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdkaWRDbGljaycsIHsgbGluZTogTWF0aC5mbG9vcihsaW5lKSB9KTtcblx0fVxufSk7XG5cbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgZXZlbnQgPT4ge1xuXHRpZiAoIWV2ZW50KSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0bGV0IG5vZGU6IGFueSA9IGV2ZW50LnRhcmdldDtcblx0d2hpbGUgKG5vZGUpIHtcblx0XHRpZiAobm9kZS50YWdOYW1lICYmIG5vZGUudGFnTmFtZSA9PT0gJ0EnICYmIG5vZGUuaHJlZikge1xuXHRcdFx0aWYgKG5vZGUuZ2V0QXR0cmlidXRlKCdocmVmJykuc3RhcnRzV2l0aCgnIycpKSB7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdFx0aWYgKG5vZGUuaHJlZi5zdGFydHNXaXRoKCdmaWxlOi8vJykgfHwgbm9kZS5ocmVmLnN0YXJ0c1dpdGgoJ3ZzY29kZS1yZXNvdXJjZTonKSkge1xuXHRcdFx0XHRjb25zdCBbcGF0aCwgZnJhZ21lbnRdID0gbm9kZS5ocmVmLnJlcGxhY2UoL14oZmlsZTpcXC9cXC98dnNjb2RlLXJlc291cmNlOikvaSwgJycpLnNwbGl0KCcjJyk7XG5cdFx0XHRcdG1lc3NhZ2luZy5wb3N0TWVzc2FnZSgnY2xpY2tMaW5rJywgeyBwYXRoLCBmcmFnbWVudCB9KTtcblx0XHRcdFx0ZXZlbnQucHJldmVudERlZmF1bHQoKTtcblx0XHRcdFx0ZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdFx0YnJlYWs7XG5cdFx0fVxuXHRcdG5vZGUgPSBub2RlLnBhcmVudE5vZGU7XG5cdH1cbn0sIHRydWUpO1xuXG5pZiAoc2V0dGluZ3Muc2Nyb2xsRWRpdG9yV2l0aFByZXZpZXcpIHtcblx0d2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Njcm9sbCcsIHRocm90dGxlKCgpID0+IHtcblx0XHRpZiAoc2Nyb2xsRGlzYWJsZWQpIHtcblx0XHRcdHNjcm9sbERpc2FibGVkID0gZmFsc2U7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGNvbnN0IGxpbmUgPSBnZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCh3aW5kb3cuc2Nyb2xsWSk7XG5cdFx0XHRpZiAodHlwZW9mIGxpbmUgPT09ICdudW1iZXInICYmICFpc05hTihsaW5lKSkge1xuXHRcdFx0XHRtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ3JldmVhbExpbmUnLCB7IGxpbmUgfSk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LCA1MCkpO1xufSIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5pbXBvcnQgeyBnZXRTZXR0aW5ncyB9IGZyb20gJy4vc2V0dGluZ3MnO1xuXG5leHBvcnQgaW50ZXJmYWNlIE1lc3NhZ2VQb3N0ZXIge1xuXHQvKipcblx0ICogUG9zdCBhIG1lc3NhZ2UgdG8gdGhlIG1hcmtkb3duIGV4dGVuc2lvblxuXHQgKi9cblx0cG9zdE1lc3NhZ2UodHlwZTogc3RyaW5nLCBib2R5OiBvYmplY3QpOiB2b2lkO1xufVxuXG5leHBvcnQgY29uc3QgY3JlYXRlUG9zdGVyRm9yVnNDb2RlID0gKHZzY29kZTogYW55KSA9PiB7XG5cdHJldHVybiBuZXcgY2xhc3MgaW1wbGVtZW50cyBNZXNzYWdlUG9zdGVyIHtcblx0XHRwb3N0TWVzc2FnZSh0eXBlOiBzdHJpbmcsIGJvZHk6IG9iamVjdCk6IHZvaWQge1xuXHRcdFx0dnNjb2RlLnBvc3RNZXNzYWdlKHtcblx0XHRcdFx0dHlwZSxcblx0XHRcdFx0c291cmNlOiBnZXRTZXR0aW5ncygpLnNvdXJjZSxcblx0XHRcdFx0Ym9keVxuXHRcdFx0fSk7XG5cdFx0fVxuXHR9O1xufTtcblxuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmltcG9ydCB7IGdldFNldHRpbmdzIH0gZnJvbSAnLi9zZXR0aW5ncyc7XG5cblxuZnVuY3Rpb24gY2xhbXAobWluOiBudW1iZXIsIG1heDogbnVtYmVyLCB2YWx1ZTogbnVtYmVyKSB7XG5cdHJldHVybiBNYXRoLm1pbihtYXgsIE1hdGgubWF4KG1pbiwgdmFsdWUpKTtcbn1cblxuZnVuY3Rpb24gY2xhbXBMaW5lKGxpbmU6IG51bWJlcikge1xuXHRyZXR1cm4gY2xhbXAoMCwgZ2V0U2V0dGluZ3MoKS5saW5lQ291bnQgLSAxLCBsaW5lKTtcbn1cblxuXG5leHBvcnQgaW50ZXJmYWNlIENvZGVMaW5lRWxlbWVudCB7XG5cdGVsZW1lbnQ6IEhUTUxFbGVtZW50O1xuXHRsaW5lOiBudW1iZXI7XG59XG5cbmNvbnN0IGdldENvZGVMaW5lRWxlbWVudHMgPSAoKCkgPT4ge1xuXHRsZXQgZWxlbWVudHM6IENvZGVMaW5lRWxlbWVudFtdO1xuXHRyZXR1cm4gKCkgPT4ge1xuXHRcdGlmICghZWxlbWVudHMpIHtcblx0XHRcdGVsZW1lbnRzID0gQXJyYXkucHJvdG90eXBlLm1hcC5jYWxsKFxuXHRcdFx0XHRkb2N1bWVudC5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKCdjb2RlLWxpbmUnKSxcblx0XHRcdFx0KGVsZW1lbnQ6IGFueSkgPT4ge1xuXHRcdFx0XHRcdGNvbnN0IGxpbmUgPSArZWxlbWVudC5nZXRBdHRyaWJ1dGUoJ2RhdGEtbGluZScpO1xuXHRcdFx0XHRcdHJldHVybiB7IGVsZW1lbnQsIGxpbmUgfTtcblx0XHRcdFx0fSlcblx0XHRcdFx0LmZpbHRlcigoeDogYW55KSA9PiAhaXNOYU4oeC5saW5lKSk7XG5cdFx0fVxuXHRcdHJldHVybiBlbGVtZW50cztcblx0fTtcbn0pKCk7XG5cbi8qKlxuICogRmluZCB0aGUgaHRtbCBlbGVtZW50cyB0aGF0IG1hcCB0byBhIHNwZWNpZmljIHRhcmdldCBsaW5lIGluIHRoZSBlZGl0b3IuXG4gKlxuICogSWYgYW4gZXhhY3QgbWF0Y2gsIHJldHVybnMgYSBzaW5nbGUgZWxlbWVudC4gSWYgdGhlIGxpbmUgaXMgYmV0d2VlbiBlbGVtZW50cyxcbiAqIHJldHVybnMgdGhlIGVsZW1lbnQgcHJpb3IgdG8gYW5kIHRoZSBlbGVtZW50IGFmdGVyIHRoZSBnaXZlbiBsaW5lLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKHRhcmdldExpbmU6IG51bWJlcik6IHsgcHJldmlvdXM6IENvZGVMaW5lRWxlbWVudDsgbmV4dD86IENvZGVMaW5lRWxlbWVudDsgfSB7XG5cdGNvbnN0IGxpbmVOdW1iZXIgPSBNYXRoLmZsb29yKHRhcmdldExpbmUpO1xuXHRjb25zdCBsaW5lcyA9IGdldENvZGVMaW5lRWxlbWVudHMoKTtcblx0bGV0IHByZXZpb3VzID0gbGluZXNbMF0gfHwgbnVsbDtcblx0Zm9yIChjb25zdCBlbnRyeSBvZiBsaW5lcykge1xuXHRcdGlmIChlbnRyeS5saW5lID09PSBsaW5lTnVtYmVyKSB7XG5cdFx0XHRyZXR1cm4geyBwcmV2aW91czogZW50cnksIG5leHQ6IHVuZGVmaW5lZCB9O1xuXHRcdH1cblx0XHRlbHNlIGlmIChlbnRyeS5saW5lID4gbGluZU51bWJlcikge1xuXHRcdFx0cmV0dXJuIHsgcHJldmlvdXMsIG5leHQ6IGVudHJ5IH07XG5cdFx0fVxuXHRcdHByZXZpb3VzID0gZW50cnk7XG5cdH1cblx0cmV0dXJuIHsgcHJldmlvdXMgfTtcbn1cblxuLyoqXG4gKiBGaW5kIHRoZSBodG1sIGVsZW1lbnRzIHRoYXQgYXJlIGF0IGEgc3BlY2lmaWMgcGl4ZWwgb2Zmc2V0IG9uIHRoZSBwYWdlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0TGluZUVsZW1lbnRzQXRQYWdlT2Zmc2V0KG9mZnNldDogbnVtYmVyKTogeyBwcmV2aW91czogQ29kZUxpbmVFbGVtZW50OyBuZXh0PzogQ29kZUxpbmVFbGVtZW50OyB9IHtcblx0Y29uc3QgbGluZXMgPSBnZXRDb2RlTGluZUVsZW1lbnRzKCk7XG5cdGNvbnN0IHBvc2l0aW9uID0gb2Zmc2V0IC0gd2luZG93LnNjcm9sbFk7XG5cdGxldCBsbyA9IC0xO1xuXHRsZXQgaGkgPSBsaW5lcy5sZW5ndGggLSAxO1xuXHR3aGlsZSAobG8gKyAxIDwgaGkpIHtcblx0XHRjb25zdCBtaWQgPSBNYXRoLmZsb29yKChsbyArIGhpKSAvIDIpO1xuXHRcdGNvbnN0IGJvdW5kcyA9IGxpbmVzW21pZF0uZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblx0XHRpZiAoYm91bmRzLnRvcCArIGJvdW5kcy5oZWlnaHQgPj0gcG9zaXRpb24pIHtcblx0XHRcdGhpID0gbWlkO1xuXHRcdH1cblx0XHRlbHNlIHtcblx0XHRcdGxvID0gbWlkO1xuXHRcdH1cblx0fVxuXHRjb25zdCBoaUVsZW1lbnQgPSBsaW5lc1toaV07XG5cdGNvbnN0IGhpQm91bmRzID0gaGlFbGVtZW50LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG5cdGlmIChoaSA+PSAxICYmIGhpQm91bmRzLnRvcCA+IHBvc2l0aW9uKSB7XG5cdFx0Y29uc3QgbG9FbGVtZW50ID0gbGluZXNbbG9dO1xuXHRcdHJldHVybiB7IHByZXZpb3VzOiBsb0VsZW1lbnQsIG5leHQ6IGhpRWxlbWVudCB9O1xuXHR9XG5cdHJldHVybiB7IHByZXZpb3VzOiBoaUVsZW1lbnQgfTtcbn1cblxuLyoqXG4gKiBBdHRlbXB0IHRvIHJldmVhbCB0aGUgZWxlbWVudCBmb3IgYSBzb3VyY2UgbGluZSBpbiB0aGUgZWRpdG9yLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGxpbmU6IG51bWJlcikge1xuXHRjb25zdCB7IHByZXZpb3VzLCBuZXh0IH0gPSBnZXRFbGVtZW50c0ZvclNvdXJjZUxpbmUobGluZSk7XG5cdGlmIChwcmV2aW91cyAmJiBnZXRTZXR0aW5ncygpLnNjcm9sbFByZXZpZXdXaXRoRWRpdG9yKSB7XG5cdFx0bGV0IHNjcm9sbFRvID0gMDtcblx0XHRjb25zdCByZWN0ID0gcHJldmlvdXMuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblx0XHRjb25zdCBwcmV2aW91c1RvcCA9IHJlY3QudG9wO1xuXHRcdGlmIChuZXh0ICYmIG5leHQubGluZSAhPT0gcHJldmlvdXMubGluZSkge1xuXHRcdFx0Ly8gQmV0d2VlbiB0d28gZWxlbWVudHMuIEdvIHRvIHBlcmNlbnRhZ2Ugb2Zmc2V0IGJldHdlZW4gdGhlbS5cblx0XHRcdGNvbnN0IGJldHdlZW5Qcm9ncmVzcyA9IChsaW5lIC0gcHJldmlvdXMubGluZSkgLyAobmV4dC5saW5lIC0gcHJldmlvdXMubGluZSk7XG5cdFx0XHRjb25zdCBlbGVtZW50T2Zmc2V0ID0gbmV4dC5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLnRvcCAtIHByZXZpb3VzVG9wO1xuXHRcdFx0c2Nyb2xsVG8gPSBwcmV2aW91c1RvcCArIGJldHdlZW5Qcm9ncmVzcyAqIGVsZW1lbnRPZmZzZXQ7XG5cdFx0fVxuXHRcdGVsc2Uge1xuXHRcdFx0c2Nyb2xsVG8gPSBwcmV2aW91c1RvcDtcblx0XHR9XG5cdFx0d2luZG93LnNjcm9sbCgwLCBNYXRoLm1heCgxLCB3aW5kb3cuc2Nyb2xsWSArIHNjcm9sbFRvKSk7XG5cdH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldDogbnVtYmVyKSB7XG5cdGNvbnN0IHsgcHJldmlvdXMsIG5leHQgfSA9IGdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldChvZmZzZXQpO1xuXHRpZiAocHJldmlvdXMpIHtcblx0XHRjb25zdCBwcmV2aW91c0JvdW5kcyA9IHByZXZpb3VzLmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG5cdFx0Y29uc3Qgb2Zmc2V0RnJvbVByZXZpb3VzID0gKG9mZnNldCAtIHdpbmRvdy5zY3JvbGxZIC0gcHJldmlvdXNCb3VuZHMudG9wKTtcblx0XHRpZiAobmV4dCkge1xuXHRcdFx0Y29uc3QgcHJvZ3Jlc3NCZXR3ZWVuRWxlbWVudHMgPSBvZmZzZXRGcm9tUHJldmlvdXMgLyAobmV4dC5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLnRvcCAtIHByZXZpb3VzQm91bmRzLnRvcCk7XG5cdFx0XHRjb25zdCBsaW5lID0gcHJldmlvdXMubGluZSArIHByb2dyZXNzQmV0d2VlbkVsZW1lbnRzICogKG5leHQubGluZSAtIHByZXZpb3VzLmxpbmUpO1xuXHRcdFx0cmV0dXJuIGNsYW1wTGluZShsaW5lKTtcblx0XHR9XG5cdFx0ZWxzZSB7XG5cdFx0XHRjb25zdCBwcm9ncmVzc1dpdGhpbkVsZW1lbnQgPSBvZmZzZXRGcm9tUHJldmlvdXMgLyAocHJldmlvdXNCb3VuZHMuaGVpZ2h0KTtcblx0XHRcdGNvbnN0IGxpbmUgPSBwcmV2aW91cy5saW5lICsgcHJvZ3Jlc3NXaXRoaW5FbGVtZW50O1xuXHRcdFx0cmV0dXJuIGNsYW1wTGluZShsaW5lKTtcblx0XHR9XG5cdH1cblx0cmV0dXJuIG51bGw7XG59XG4iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuZXhwb3J0IGludGVyZmFjZSBQcmV2aWV3U2V0dGluZ3Mge1xuXHRzb3VyY2U6IHN0cmluZztcblx0bGluZTogbnVtYmVyO1xuXHRsaW5lQ291bnQ6IG51bWJlcjtcblx0c2Nyb2xsUHJldmlld1dpdGhFZGl0b3I/OiBib29sZWFuO1xuXHRzY3JvbGxFZGl0b3JXaXRoUHJldmlldzogYm9vbGVhbjtcblx0ZGlzYWJsZVNlY3VyaXR5V2FybmluZ3M6IGJvb2xlYW47XG5cdGRvdWJsZUNsaWNrVG9Td2l0Y2hUb0VkaXRvcjogYm9vbGVhbjtcbn1cblxubGV0IGNhY2hlZFNldHRpbmdzOiBQcmV2aWV3U2V0dGluZ3MgfCB1bmRlZmluZWQgPSB1bmRlZmluZWQ7XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXREYXRhKGtleTogc3RyaW5nKTogUHJldmlld1NldHRpbmdzIHtcblx0Y29uc3QgZWxlbWVudCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd2c2NvZGUtbWFya2Rvd24tcHJldmlldy1kYXRhJyk7XG5cdGlmIChlbGVtZW50KSB7XG5cdFx0Y29uc3QgZGF0YSA9IGVsZW1lbnQuZ2V0QXR0cmlidXRlKGtleSk7XG5cdFx0aWYgKGRhdGEpIHtcblx0XHRcdHJldHVybiBKU09OLnBhcnNlKGRhdGEpO1xuXHRcdH1cblx0fVxuXG5cdHRocm93IG5ldyBFcnJvcihgQ291bGQgbm90IGxvYWQgZGF0YSBmb3IgJHtrZXl9YCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRTZXR0aW5ncygpOiBQcmV2aWV3U2V0dGluZ3Mge1xuXHRpZiAoY2FjaGVkU2V0dGluZ3MpIHtcblx0XHRyZXR1cm4gY2FjaGVkU2V0dGluZ3M7XG5cdH1cblxuXHRjYWNoZWRTZXR0aW5ncyA9IGdldERhdGEoJ2RhdGEtc2V0dGluZ3MnKTtcblx0aWYgKGNhY2hlZFNldHRpbmdzKSB7XG5cdFx0cmV0dXJuIGNhY2hlZFNldHRpbmdzO1xuXHR9XG5cblx0dGhyb3cgbmV3IEVycm9yKCdDb3VsZCBub3QgbG9hZCBzZXR0aW5ncycpO1xufVxuIl0sInNvdXJjZVJvb3QiOiIifQ== \ No newline at end of file +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL2xvZGFzaC50aHJvdHRsZS9pbmRleC5qcyIsIndlYnBhY2s6Ly8vKHdlYnBhY2spL2J1aWxkaW4vZ2xvYmFsLmpzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2FjdGl2ZUxpbmVNYXJrZXIudHMiLCJ3ZWJwYWNrOi8vLy4vcHJldmlldy1zcmMvZXZlbnRzLnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL2luZGV4LnRzIiwid2VicGFjazovLy8uL3ByZXZpZXctc3JjL21lc3NhZ2luZy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zY3JvbGwtc3luYy50cyIsIndlYnBhY2s6Ly8vLi9wcmV2aWV3LXNyYy9zZXR0aW5ncy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGFBQUs7QUFDTDtBQUNBOztBQUVBO0FBQ0E7QUFDQSx5REFBaUQsY0FBYztBQUMvRDs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxtQ0FBMkIsMEJBQTBCLEVBQUU7QUFDdkQseUNBQWlDLGVBQWU7QUFDaEQ7QUFDQTtBQUNBOztBQUVBO0FBQ0EsOERBQXNELCtEQUErRDs7QUFFckg7QUFDQTs7O0FBR0E7QUFDQTs7Ozs7Ozs7Ozs7O0FDbkVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsYUFBYSxPQUFPO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLFNBQVM7QUFDcEIsV0FBVyxPQUFPO0FBQ2xCLFdBQVcsT0FBTyxZQUFZO0FBQzlCLFdBQVcsUUFBUTtBQUNuQjtBQUNBLFdBQVcsT0FBTztBQUNsQjtBQUNBLFdBQVcsUUFBUTtBQUNuQjtBQUNBLGFBQWEsU0FBUztBQUN0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBLDhDQUE4QyxrQkFBa0I7QUFDaEU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLFNBQVM7QUFDcEIsV0FBVyxPQUFPO0FBQ2xCLFdBQVcsT0FBTyxZQUFZO0FBQzlCLFdBQVcsUUFBUTtBQUNuQjtBQUNBLFdBQVcsUUFBUTtBQUNuQjtBQUNBLGFBQWEsU0FBUztBQUN0QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxtREFBbUQsb0JBQW9CO0FBQ3ZFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLEVBQUU7QUFDYixhQUFhLFFBQVE7QUFDckI7QUFDQTtBQUNBLGdCQUFnQjtBQUNoQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVcsRUFBRTtBQUNiLGFBQWEsUUFBUTtBQUNyQjtBQUNBO0FBQ0Esb0JBQW9CO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLEVBQUU7QUFDYixhQUFhLFFBQVE7QUFDckI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsV0FBVyxFQUFFO0FBQ2IsYUFBYSxPQUFPO0FBQ3BCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOzs7Ozs7Ozs7Ozs7O0FDdGJBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0EsQ0FBQztBQUNEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsNENBQTRDOztBQUU1Qzs7Ozs7Ozs7Ozs7Ozs7O0FDbkJBOzs7Z0dBR2dHO0FBQ2hHLCtGQUF5RDtBQUV6RDtJQUdDLDhCQUE4QixDQUFDLElBQVk7UUFDMUMsTUFBTSxFQUFFLFFBQVEsRUFBRSxHQUFHLHNDQUF3QixDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3BELElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxJQUFJLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUM1QyxDQUFDO0lBRUQsT0FBTyxDQUFDLE1BQStCO1FBQ3RDLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDekMsSUFBSSxDQUFDLGtCQUFrQixDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ2hDLElBQUksQ0FBQyxRQUFRLEdBQUcsTUFBTSxDQUFDO0lBQ3hCLENBQUM7SUFFRCxvQkFBb0IsQ0FBQyxPQUFnQztRQUNwRCxFQUFFLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7WUFDZCxNQUFNLENBQUM7UUFDUixDQUFDO1FBQ0QsT0FBTyxDQUFDLFNBQVMsR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyx1QkFBdUIsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUM1RSxDQUFDO0lBRUQsa0JBQWtCLENBQUMsT0FBZ0M7UUFDbEQsRUFBRSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1lBQ2QsTUFBTSxDQUFDO1FBQ1IsQ0FBQztRQUNELE9BQU8sQ0FBQyxTQUFTLElBQUksbUJBQW1CLENBQUM7SUFDMUMsQ0FBQztDQUNEO0FBM0JELDRDQTJCQzs7Ozs7Ozs7Ozs7Ozs7QUNqQ0Q7OztnR0FHZ0c7O0FBRWhHLDRCQUFtQyxDQUFhO0lBQy9DLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxVQUFVLEtBQUssU0FBUyxJQUFJLFFBQVEsQ0FBQyxVQUFVLEtBQUssZUFBZSxDQUFDLENBQUMsQ0FBQztRQUNsRixRQUFRLENBQUMsZ0JBQWdCLENBQUMsa0JBQWtCLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDbEQsQ0FBQztJQUFDLElBQUksQ0FBQyxDQUFDO1FBQ1AsQ0FBQyxFQUFFLENBQUM7SUFDTCxDQUFDO0FBQ0YsQ0FBQztBQU5ELGdEQU1DOzs7Ozs7Ozs7Ozs7OztBQ1hEOzs7Z0dBR2dHOztBQUVoRyw4R0FBc0Q7QUFDdEQsZ0ZBQThDO0FBQzlDLHlGQUFvRDtBQUNwRCwrRkFBMkY7QUFDM0Ysc0ZBQWtEO0FBQ2xELHVHQUE2QztBQUk3QyxJQUFJLGNBQWMsR0FBRyxJQUFJLENBQUM7QUFDMUIsTUFBTSxNQUFNLEdBQUcsSUFBSSxtQ0FBZ0IsRUFBRSxDQUFDO0FBQ3RDLE1BQU0sUUFBUSxHQUFHLHNCQUFXLEVBQUUsQ0FBQztBQUUvQixNQUFNLE1BQU0sR0FBRyxnQkFBZ0IsRUFBRSxDQUFDO0FBRWxDLG9CQUFvQjtBQUNwQixNQUFNLEtBQUssR0FBRyxrQkFBTyxDQUFDLFlBQVksQ0FBQyxDQUFDO0FBQ3BDLE1BQU0sQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUM7QUFFdkIsTUFBTSxTQUFTLEdBQUcsaUNBQXFCLENBQUMsTUFBTSxDQUFDLENBQUM7QUFFaEQsTUFBTSxDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUMsU0FBUyxDQUFDLENBQUM7QUFDdkMsTUFBTSxDQUFDLG1CQUFtQixDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsQ0FBQztBQUVoRCxNQUFNLENBQUMsTUFBTSxHQUFHLEdBQUcsRUFBRTtJQUNwQixnQkFBZ0IsRUFBRSxDQUFDO0FBQ3BCLENBQUMsQ0FBQztBQUVGLDJCQUFrQixDQUFDLEdBQUcsRUFBRTtJQUN2QixFQUFFLENBQUMsQ0FBQyxRQUFRLENBQUMsdUJBQXVCLENBQUMsQ0FBQyxDQUFDO1FBQ3RDLFVBQVUsQ0FBQyxHQUFHLEVBQUU7WUFDZixNQUFNLFdBQVcsR0FBRyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUM7WUFDbkMsRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUN6QixjQUFjLEdBQUcsSUFBSSxDQUFDO2dCQUN0QixzQ0FBd0IsQ0FBQyxXQUFXLENBQUMsQ0FBQztZQUN2QyxDQUFDO1FBQ0YsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ1AsQ0FBQztBQUNGLENBQUMsQ0FBQyxDQUFDO0FBRUgsTUFBTSxZQUFZLEdBQUcsQ0FBQyxHQUFHLEVBQUU7SUFDMUIsTUFBTSxRQUFRLEdBQUcsUUFBUSxDQUFDLENBQUMsSUFBWSxFQUFFLEVBQUU7UUFDMUMsY0FBYyxHQUFHLElBQUksQ0FBQztRQUN0QixzQ0FBd0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNoQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFFUCxNQUFNLENBQUMsQ0FBQyxJQUFZLEVBQUUsUUFBYSxFQUFFLEVBQUU7UUFDdEMsRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ2xCLFFBQVEsQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO1lBQ3JCLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNoQixDQUFDO0lBQ0YsQ0FBQyxDQUFDO0FBQ0gsQ0FBQyxDQUFDLEVBQUUsQ0FBQztBQUVMLElBQUksZ0JBQWdCLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRTtJQUNwQyxNQUFNLFNBQVMsR0FBb0QsRUFBRSxDQUFDO0lBQ3RFLElBQUksTUFBTSxHQUFHLFFBQVEsQ0FBQyxvQkFBb0IsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNsRCxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1FBQ1osSUFBSSxDQUFDLENBQUM7UUFDTixHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDcEMsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBRXRCLEVBQUUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDdkMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDakMsQ0FBQztZQUVELFNBQVMsQ0FBQyxJQUFJLENBQUM7Z0JBQ2QsRUFBRSxFQUFFLEdBQUcsQ0FBQyxFQUFFO2dCQUNWLE1BQU0sRUFBRSxHQUFHLENBQUMsTUFBTTtnQkFDbEIsS0FBSyxFQUFFLEdBQUcsQ0FBQyxLQUFLO2FBQ2hCLENBQUMsQ0FBQztRQUNKLENBQUM7UUFFRCxTQUFTLENBQUMsV0FBVyxDQUFDLGlCQUFpQixFQUFFLFNBQVMsQ0FBQyxDQUFDO0lBQ3JELENBQUM7QUFDRixDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7QUFFUCxNQUFNLENBQUMsZ0JBQWdCLENBQUMsUUFBUSxFQUFFLEdBQUcsRUFBRTtJQUN0QyxjQUFjLEdBQUcsSUFBSSxDQUFDO0lBQ3RCLGdCQUFnQixFQUFFLENBQUM7QUFDcEIsQ0FBQyxFQUFFLElBQUksQ0FBQyxDQUFDO0FBRVQsTUFBTSxDQUFDLGdCQUFnQixDQUFDLFNBQVMsRUFBRSxLQUFLLENBQUMsRUFBRTtJQUMxQyxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sS0FBSyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztRQUMzQyxNQUFNLENBQUM7SUFDUixDQUFDO0lBRUQsTUFBTSxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ3pCLEtBQUssZ0NBQWdDO1lBQ3BDLE1BQU0sQ0FBQyw4QkFBOEIsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3ZELEtBQUssQ0FBQztRQUVQLEtBQUssWUFBWTtZQUNoQixZQUFZLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDLENBQUM7WUFDeEMsS0FBSyxDQUFDO0lBQ1IsQ0FBQztBQUNGLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQztBQUVWLFFBQVEsQ0FBQyxnQkFBZ0IsQ0FBQyxVQUFVLEVBQUUsS0FBSyxDQUFDLEVBQUU7SUFDN0MsRUFBRSxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsMkJBQTJCLENBQUMsQ0FBQyxDQUFDO1FBQzNDLE1BQU0sQ0FBQztJQUNSLENBQUM7SUFFRCx5QkFBeUI7SUFDekIsR0FBRyxDQUFDLENBQUMsSUFBSSxJQUFJLEdBQUcsS0FBSyxDQUFDLE1BQXFCLEVBQUUsSUFBSSxFQUFFLElBQUksR0FBRyxJQUFJLENBQUMsVUFBeUIsRUFBRSxDQUFDO1FBQzFGLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQztZQUMxQixNQUFNLENBQUM7UUFDUixDQUFDO0lBQ0YsQ0FBQztJQUVELE1BQU0sTUFBTSxHQUFHLEtBQUssQ0FBQyxLQUFLLENBQUM7SUFDM0IsTUFBTSxJQUFJLEdBQUcsOENBQWdDLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDdEQsRUFBRSxDQUFDLENBQUMsT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUM5QyxTQUFTLENBQUMsV0FBVyxDQUFDLFVBQVUsRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUMvRCxDQUFDO0FBQ0YsQ0FBQyxDQUFDLENBQUM7QUFFSCxRQUFRLENBQUMsZ0JBQWdCLENBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQyxFQUFFO0lBQzFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztRQUNaLE1BQU0sQ0FBQztJQUNSLENBQUM7SUFFRCxJQUFJLElBQUksR0FBUSxLQUFLLENBQUMsTUFBTSxDQUFDO0lBQzdCLE9BQU8sSUFBSSxFQUFFLENBQUM7UUFDYixFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxPQUFPLEtBQUssR0FBRyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ3ZELEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDL0MsS0FBSyxDQUFDO1lBQ1AsQ0FBQztZQUNELEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLGtCQUFrQixDQUFDLENBQUMsQ0FBQyxDQUFDO2dCQUNqRixNQUFNLENBQUMsSUFBSSxFQUFFLFFBQVEsQ0FBQyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLGdDQUFnQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztnQkFDNUYsU0FBUyxDQUFDLFdBQVcsQ0FBQyxXQUFXLEVBQUUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQztnQkFDdkQsS0FBSyxDQUFDLGNBQWMsRUFBRSxDQUFDO2dCQUN2QixLQUFLLENBQUMsZUFBZSxFQUFFLENBQUM7Z0JBQ3hCLEtBQUssQ0FBQztZQUNQLENBQUM7WUFDRCxLQUFLLENBQUM7UUFDUCxDQUFDO1FBQ0QsSUFBSSxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUM7SUFDeEIsQ0FBQztBQUNGLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQztBQUVULEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDLENBQUM7SUFDdEMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLFFBQVEsRUFBRSxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQy9DLEVBQUUsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUM7WUFDcEIsY0FBYyxHQUFHLEtBQUssQ0FBQztRQUN4QixDQUFDO1FBQUMsSUFBSSxDQUFDLENBQUM7WUFDUCxNQUFNLElBQUksR0FBRyw4Q0FBZ0MsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDOUQsRUFBRSxDQUFDLENBQUMsT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDOUMsU0FBUyxDQUFDLFdBQVcsQ0FBQyxZQUFZLEVBQUUsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1lBQy9DLENBQUM7UUFDRixDQUFDO0lBQ0YsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUM7QUFDVCxDQUFDOzs7Ozs7Ozs7Ozs7OztBQzdKRDs7O2dHQUdnRzs7QUFFaEcsc0ZBQXlDO0FBUzVCLDZCQUFxQixHQUFHLENBQUMsTUFBVyxFQUFFLEVBQUU7SUFDcEQsTUFBTSxDQUFDLElBQUk7UUFDVixXQUFXLENBQUMsSUFBWSxFQUFFLElBQVk7WUFDckMsTUFBTSxDQUFDLFdBQVcsQ0FBQztnQkFDbEIsSUFBSTtnQkFDSixNQUFNLEVBQUUsc0JBQVcsRUFBRSxDQUFDLE1BQU07Z0JBQzVCLElBQUk7YUFDSixDQUFDLENBQUM7UUFDSixDQUFDO0tBQ0QsQ0FBQztBQUNILENBQUMsQ0FBQzs7Ozs7Ozs7Ozs7Ozs7QUN4QkY7OztnR0FHZ0c7O0FBRWhHLHNGQUF5QztBQUd6QyxlQUFlLEdBQVcsRUFBRSxHQUFXLEVBQUUsS0FBYTtJQUNyRCxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQztBQUM1QyxDQUFDO0FBRUQsbUJBQW1CLElBQVk7SUFDOUIsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsc0JBQVcsRUFBRSxDQUFDLFNBQVMsR0FBRyxDQUFDLEVBQUUsSUFBSSxDQUFDLENBQUM7QUFDcEQsQ0FBQztBQVFELE1BQU0sbUJBQW1CLEdBQUcsQ0FBQyxHQUFHLEVBQUU7SUFDakMsSUFBSSxRQUEyQixDQUFDO0lBQ2hDLE1BQU0sQ0FBQyxHQUFHLEVBQUU7UUFDWCxFQUFFLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7WUFDZixRQUFRLEdBQUcsQ0FBQyxDQUFDLEVBQUUsT0FBTyxFQUFFLFFBQVEsQ0FBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUNqRixRQUFRLENBQUMsc0JBQXNCLENBQUMsV0FBVyxDQUFDLEVBQzVDLENBQUMsT0FBWSxFQUFFLEVBQUU7Z0JBQ2hCLE1BQU0sSUFBSSxHQUFHLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxXQUFXLENBQUMsQ0FBQztnQkFDaEQsTUFBTSxDQUFDLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxDQUFDO1lBQzFCLENBQUMsQ0FBQztpQkFDRCxNQUFNLENBQUMsQ0FBQyxDQUFNLEVBQUUsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDdkMsQ0FBQztRQUNELE1BQU0sQ0FBQyxRQUFRLENBQUM7SUFDakIsQ0FBQyxDQUFDO0FBQ0gsQ0FBQyxDQUFDLEVBQUUsQ0FBQztBQUVMOzs7OztHQUtHO0FBQ0gsa0NBQXlDLFVBQWtCO0lBQzFELE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDMUMsTUFBTSxLQUFLLEdBQUcsbUJBQW1CLEVBQUUsQ0FBQztJQUNwQyxJQUFJLFFBQVEsR0FBRyxLQUFLLENBQUMsQ0FBQyxDQUFDLElBQUksSUFBSSxDQUFDO0lBQ2hDLEdBQUcsQ0FBQyxDQUFDLE1BQU0sS0FBSyxJQUFJLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFDM0IsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksS0FBSyxVQUFVLENBQUMsQ0FBQyxDQUFDO1lBQy9CLE1BQU0sQ0FBQyxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxDQUFDO1FBQzdDLENBQUM7UUFBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUksR0FBRyxVQUFVLENBQUMsQ0FBQyxDQUFDO1lBQ3BDLE1BQU0sQ0FBQyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLENBQUM7UUFDbEMsQ0FBQztRQUNELFFBQVEsR0FBRyxLQUFLLENBQUM7SUFDbEIsQ0FBQztJQUNELE1BQU0sQ0FBQyxFQUFFLFFBQVEsRUFBRSxDQUFDO0FBQ3JCLENBQUM7QUFiRCw0REFhQztBQUVEOztHQUVHO0FBQ0gscUNBQTRDLE1BQWM7SUFDekQsTUFBTSxLQUFLLEdBQUcsbUJBQW1CLEVBQUUsQ0FBQztJQUNwQyxNQUFNLFFBQVEsR0FBRyxNQUFNLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQztJQUN6QyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUNaLElBQUksRUFBRSxHQUFHLEtBQUssQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO0lBQzFCLE9BQU8sRUFBRSxHQUFHLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQztRQUNwQixNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxHQUFHLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ3RDLE1BQU0sTUFBTSxHQUFHLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQztRQUMxRCxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsR0FBRyxHQUFHLE1BQU0sQ0FBQyxNQUFNLElBQUksUUFBUSxDQUFDLENBQUMsQ0FBQztZQUM1QyxFQUFFLEdBQUcsR0FBRyxDQUFDO1FBQ1YsQ0FBQztRQUNELElBQUksQ0FBQyxDQUFDO1lBQ0wsRUFBRSxHQUFHLEdBQUcsQ0FBQztRQUNWLENBQUM7SUFDRixDQUFDO0lBQ0QsTUFBTSxTQUFTLEdBQUcsS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQzVCLE1BQU0sUUFBUSxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQztJQUMzRCxFQUFFLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxJQUFJLFFBQVEsQ0FBQyxHQUFHLEdBQUcsUUFBUSxDQUFDLENBQUMsQ0FBQztRQUN4QyxNQUFNLFNBQVMsR0FBRyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDNUIsTUFBTSxDQUFDLEVBQUUsUUFBUSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLENBQUM7SUFDakQsQ0FBQztJQUNELE1BQU0sQ0FBQyxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsQ0FBQztBQUNoQyxDQUFDO0FBdEJELGtFQXNCQztBQUVEOztHQUVHO0FBQ0gsa0NBQXlDLElBQVk7SUFDcEQsRUFBRSxDQUFDLENBQUMsQ0FBQyxzQkFBVyxFQUFFLENBQUMsdUJBQXVCLENBQUMsQ0FBQyxDQUFDO1FBQzVDLE1BQU0sQ0FBQztJQUNSLENBQUM7SUFFRCxFQUFFLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNmLE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQztRQUNqQyxNQUFNLENBQUM7SUFDUixDQUFDO0lBRUQsTUFBTSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsR0FBRyx3QkFBd0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUMxRCxFQUFFLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7UUFDZixNQUFNLENBQUM7SUFDUixDQUFDO0lBQ0QsSUFBSSxRQUFRLEdBQUcsQ0FBQyxDQUFDO0lBQ2pCLE1BQU0sSUFBSSxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQztJQUN0RCxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDO0lBQzdCLEVBQUUsQ0FBQyxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsSUFBSSxLQUFLLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBQ3pDLDhEQUE4RDtRQUM5RCxNQUFNLGVBQWUsR0FBRyxDQUFDLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxHQUFHLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM3RSxNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLHFCQUFxQixFQUFFLENBQUMsR0FBRyxHQUFHLFdBQVcsQ0FBQztRQUM3RSxRQUFRLEdBQUcsV0FBVyxHQUFHLGVBQWUsR0FBRyxhQUFhLENBQUM7SUFDMUQsQ0FBQztJQUFDLElBQUksQ0FBQyxDQUFDO1FBQ1AsUUFBUSxHQUFHLFdBQVcsQ0FBQztJQUN4QixDQUFDO0lBQ0QsTUFBTSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxPQUFPLEdBQUcsUUFBUSxDQUFDLENBQUMsQ0FBQztBQUN2RSxDQUFDO0FBMUJELDREQTBCQztBQUVELDBDQUFpRCxNQUFjO0lBQzlELE1BQU0sRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLEdBQUcsMkJBQTJCLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDL0QsRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztRQUNkLE1BQU0sY0FBYyxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMscUJBQXFCLEVBQUUsQ0FBQztRQUNoRSxNQUFNLGtCQUFrQixHQUFHLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQyxPQUFPLEdBQUcsY0FBYyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQzFFLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7WUFDVixNQUFNLHVCQUF1QixHQUFHLGtCQUFrQixHQUFHLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxxQkFBcUIsRUFBRSxDQUFDLEdBQUcsR0FBRyxjQUFjLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDckgsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLElBQUksR0FBRyx1QkFBdUIsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLEdBQUcsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ25GLE1BQU0sQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDeEIsQ0FBQztRQUNELElBQUksQ0FBQyxDQUFDO1lBQ0wsTUFBTSxxQkFBcUIsR0FBRyxrQkFBa0IsR0FBRyxDQUFDLGNBQWMsQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUMzRSxNQUFNLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxHQUFHLHFCQUFxQixDQUFDO1lBQ25ELE1BQU0sQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDeEIsQ0FBQztJQUNGLENBQUM7SUFDRCxNQUFNLENBQUMsSUFBSSxDQUFDO0FBQ2IsQ0FBQztBQWpCRCw0RUFpQkM7Ozs7Ozs7Ozs7Ozs7O0FDdElEOzs7Z0dBR2dHOztBQVloRyxJQUFJLGNBQWMsR0FBZ0MsU0FBUyxDQUFDO0FBRTVELGlCQUF3QixHQUFXO0lBQ2xDLE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxjQUFjLENBQUMsOEJBQThCLENBQUMsQ0FBQztJQUN4RSxFQUFFLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1FBQ2IsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUN2QyxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ1YsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDekIsQ0FBQztJQUNGLENBQUM7SUFFRCxNQUFNLElBQUksS0FBSyxDQUFDLDJCQUEyQixHQUFHLEVBQUUsQ0FBQyxDQUFDO0FBQ25ELENBQUM7QUFWRCwwQkFVQztBQUVEO0lBQ0MsRUFBRSxDQUFDLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQztRQUNwQixNQUFNLENBQUMsY0FBYyxDQUFDO0lBQ3ZCLENBQUM7SUFFRCxjQUFjLEdBQUcsT0FBTyxDQUFDLGVBQWUsQ0FBQyxDQUFDO0lBQzFDLEVBQUUsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUM7UUFDcEIsTUFBTSxDQUFDLGNBQWMsQ0FBQztJQUN2QixDQUFDO0lBRUQsTUFBTSxJQUFJLEtBQUssQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDO0FBQzVDLENBQUM7QUFYRCxrQ0FXQyIsImZpbGUiOiJpbmRleC5qcyIsInNvdXJjZXNDb250ZW50IjpbIiBcdC8vIFRoZSBtb2R1bGUgY2FjaGVcbiBcdHZhciBpbnN0YWxsZWRNb2R1bGVzID0ge307XG5cbiBcdC8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG4gXHRmdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cbiBcdFx0Ly8gQ2hlY2sgaWYgbW9kdWxlIGlzIGluIGNhY2hlXG4gXHRcdGlmKGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdKSB7XG4gXHRcdFx0cmV0dXJuIGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdLmV4cG9ydHM7XG4gXHRcdH1cbiBcdFx0Ly8gQ3JlYXRlIGEgbmV3IG1vZHVsZSAoYW5kIHB1dCBpdCBpbnRvIHRoZSBjYWNoZSlcbiBcdFx0dmFyIG1vZHVsZSA9IGluc3RhbGxlZE1vZHVsZXNbbW9kdWxlSWRdID0ge1xuIFx0XHRcdGk6IG1vZHVsZUlkLFxuIFx0XHRcdGw6IGZhbHNlLFxuIFx0XHRcdGV4cG9ydHM6IHt9XG4gXHRcdH07XG5cbiBcdFx0Ly8gRXhlY3V0ZSB0aGUgbW9kdWxlIGZ1bmN0aW9uXG4gXHRcdG1vZHVsZXNbbW9kdWxlSWRdLmNhbGwobW9kdWxlLmV4cG9ydHMsIG1vZHVsZSwgbW9kdWxlLmV4cG9ydHMsIF9fd2VicGFja19yZXF1aXJlX18pO1xuXG4gXHRcdC8vIEZsYWcgdGhlIG1vZHVsZSBhcyBsb2FkZWRcbiBcdFx0bW9kdWxlLmwgPSB0cnVlO1xuXG4gXHRcdC8vIFJldHVybiB0aGUgZXhwb3J0cyBvZiB0aGUgbW9kdWxlXG4gXHRcdHJldHVybiBtb2R1bGUuZXhwb3J0cztcbiBcdH1cblxuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZXMgb2JqZWN0IChfX3dlYnBhY2tfbW9kdWxlc19fKVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5tID0gbW9kdWxlcztcblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGUgY2FjaGVcbiBcdF9fd2VicGFja19yZXF1aXJlX18uYyA9IGluc3RhbGxlZE1vZHVsZXM7XG5cbiBcdC8vIGRlZmluZSBnZXR0ZXIgZnVuY3Rpb24gZm9yIGhhcm1vbnkgZXhwb3J0c1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kID0gZnVuY3Rpb24oZXhwb3J0cywgbmFtZSwgZ2V0dGVyKSB7XG4gXHRcdGlmKCFfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZXhwb3J0cywgbmFtZSkpIHtcbiBcdFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgbmFtZSwge1xuIFx0XHRcdFx0Y29uZmlndXJhYmxlOiBmYWxzZSxcbiBcdFx0XHRcdGVudW1lcmFibGU6IHRydWUsXG4gXHRcdFx0XHRnZXQ6IGdldHRlclxuIFx0XHRcdH0pO1xuIFx0XHR9XG4gXHR9O1xuXG4gXHQvLyBkZWZpbmUgX19lc01vZHVsZSBvbiBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIgPSBmdW5jdGlvbihleHBvcnRzKSB7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gXCIuL3ByZXZpZXctc3JjL2luZGV4LnRzXCIpO1xuIiwiLyoqXG4gKiBsb2Rhc2ggKEN1c3RvbSBCdWlsZCkgPGh0dHBzOi8vbG9kYXNoLmNvbS8+XG4gKiBCdWlsZDogYGxvZGFzaCBtb2R1bGFyaXplIGV4cG9ydHM9XCJucG1cIiAtbyAuL2BcbiAqIENvcHlyaWdodCBqUXVlcnkgRm91bmRhdGlvbiBhbmQgb3RoZXIgY29udHJpYnV0b3JzIDxodHRwczovL2pxdWVyeS5vcmcvPlxuICogUmVsZWFzZWQgdW5kZXIgTUlUIGxpY2Vuc2UgPGh0dHBzOi8vbG9kYXNoLmNvbS9saWNlbnNlPlxuICogQmFzZWQgb24gVW5kZXJzY29yZS5qcyAxLjguMyA8aHR0cDovL3VuZGVyc2NvcmVqcy5vcmcvTElDRU5TRT5cbiAqIENvcHlyaWdodCBKZXJlbXkgQXNoa2VuYXMsIERvY3VtZW50Q2xvdWQgYW5kIEludmVzdGlnYXRpdmUgUmVwb3J0ZXJzICYgRWRpdG9yc1xuICovXG5cbi8qKiBVc2VkIGFzIHRoZSBgVHlwZUVycm9yYCBtZXNzYWdlIGZvciBcIkZ1bmN0aW9uc1wiIG1ldGhvZHMuICovXG52YXIgRlVOQ19FUlJPUl9URVhUID0gJ0V4cGVjdGVkIGEgZnVuY3Rpb24nO1xuXG4vKiogVXNlZCBhcyByZWZlcmVuY2VzIGZvciB2YXJpb3VzIGBOdW1iZXJgIGNvbnN0YW50cy4gKi9cbnZhciBOQU4gPSAwIC8gMDtcblxuLyoqIGBPYmplY3QjdG9TdHJpbmdgIHJlc3VsdCByZWZlcmVuY2VzLiAqL1xudmFyIHN5bWJvbFRhZyA9ICdbb2JqZWN0IFN5bWJvbF0nO1xuXG4vKiogVXNlZCB0byBtYXRjaCBsZWFkaW5nIGFuZCB0cmFpbGluZyB3aGl0ZXNwYWNlLiAqL1xudmFyIHJlVHJpbSA9IC9eXFxzK3xcXHMrJC9nO1xuXG4vKiogVXNlZCB0byBkZXRlY3QgYmFkIHNpZ25lZCBoZXhhZGVjaW1hbCBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNCYWRIZXggPSAvXlstK10weFswLTlhLWZdKyQvaTtcblxuLyoqIFVzZWQgdG8gZGV0ZWN0IGJpbmFyeSBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNCaW5hcnkgPSAvXjBiWzAxXSskL2k7XG5cbi8qKiBVc2VkIHRvIGRldGVjdCBvY3RhbCBzdHJpbmcgdmFsdWVzLiAqL1xudmFyIHJlSXNPY3RhbCA9IC9eMG9bMC03XSskL2k7XG5cbi8qKiBCdWlsdC1pbiBtZXRob2QgcmVmZXJlbmNlcyB3aXRob3V0IGEgZGVwZW5kZW5jeSBvbiBgcm9vdGAuICovXG52YXIgZnJlZVBhcnNlSW50ID0gcGFyc2VJbnQ7XG5cbi8qKiBEZXRlY3QgZnJlZSB2YXJpYWJsZSBgZ2xvYmFsYCBmcm9tIE5vZGUuanMuICovXG52YXIgZnJlZUdsb2JhbCA9IHR5cGVvZiBnbG9iYWwgPT0gJ29iamVjdCcgJiYgZ2xvYmFsICYmIGdsb2JhbC5PYmplY3QgPT09IE9iamVjdCAmJiBnbG9iYWw7XG5cbi8qKiBEZXRlY3QgZnJlZSB2YXJpYWJsZSBgc2VsZmAuICovXG52YXIgZnJlZVNlbGYgPSB0eXBlb2Ygc2VsZiA9PSAnb2JqZWN0JyAmJiBzZWxmICYmIHNlbGYuT2JqZWN0ID09PSBPYmplY3QgJiYgc2VsZjtcblxuLyoqIFVzZWQgYXMgYSByZWZlcmVuY2UgdG8gdGhlIGdsb2JhbCBvYmplY3QuICovXG52YXIgcm9vdCA9IGZyZWVHbG9iYWwgfHwgZnJlZVNlbGYgfHwgRnVuY3Rpb24oJ3JldHVybiB0aGlzJykoKTtcblxuLyoqIFVzZWQgZm9yIGJ1aWx0LWluIG1ldGhvZCByZWZlcmVuY2VzLiAqL1xudmFyIG9iamVjdFByb3RvID0gT2JqZWN0LnByb3RvdHlwZTtcblxuLyoqXG4gKiBVc2VkIHRvIHJlc29sdmUgdGhlXG4gKiBbYHRvU3RyaW5nVGFnYF0oaHR0cDovL2VjbWEtaW50ZXJuYXRpb25hbC5vcmcvZWNtYS0yNjIvNy4wLyNzZWMtb2JqZWN0LnByb3RvdHlwZS50b3N0cmluZylcbiAqIG9mIHZhbHVlcy5cbiAqL1xudmFyIG9iamVjdFRvU3RyaW5nID0gb2JqZWN0UHJvdG8udG9TdHJpbmc7XG5cbi8qIEJ1aWx0LWluIG1ldGhvZCByZWZlcmVuY2VzIGZvciB0aG9zZSB3aXRoIHRoZSBzYW1lIG5hbWUgYXMgb3RoZXIgYGxvZGFzaGAgbWV0aG9kcy4gKi9cbnZhciBuYXRpdmVNYXggPSBNYXRoLm1heCxcbiAgICBuYXRpdmVNaW4gPSBNYXRoLm1pbjtcblxuLyoqXG4gKiBHZXRzIHRoZSB0aW1lc3RhbXAgb2YgdGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdGhhdCBoYXZlIGVsYXBzZWQgc2luY2VcbiAqIHRoZSBVbml4IGVwb2NoICgxIEphbnVhcnkgMTk3MCAwMDowMDowMCBVVEMpLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMi40LjBcbiAqIEBjYXRlZ29yeSBEYXRlXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBSZXR1cm5zIHRoZSB0aW1lc3RhbXAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uZGVmZXIoZnVuY3Rpb24oc3RhbXApIHtcbiAqICAgY29uc29sZS5sb2coXy5ub3coKSAtIHN0YW1wKTtcbiAqIH0sIF8ubm93KCkpO1xuICogLy8gPT4gTG9ncyB0aGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyBpdCB0b29rIGZvciB0aGUgZGVmZXJyZWQgaW52b2NhdGlvbi5cbiAqL1xudmFyIG5vdyA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gcm9vdC5EYXRlLm5vdygpO1xufTtcblxuLyoqXG4gKiBDcmVhdGVzIGEgZGVib3VuY2VkIGZ1bmN0aW9uIHRoYXQgZGVsYXlzIGludm9raW5nIGBmdW5jYCB1bnRpbCBhZnRlciBgd2FpdGBcbiAqIG1pbGxpc2Vjb25kcyBoYXZlIGVsYXBzZWQgc2luY2UgdGhlIGxhc3QgdGltZSB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uIHdhc1xuICogaW52b2tlZC4gVGhlIGRlYm91bmNlZCBmdW5jdGlvbiBjb21lcyB3aXRoIGEgYGNhbmNlbGAgbWV0aG9kIHRvIGNhbmNlbFxuICogZGVsYXllZCBgZnVuY2AgaW52b2NhdGlvbnMgYW5kIGEgYGZsdXNoYCBtZXRob2QgdG8gaW1tZWRpYXRlbHkgaW52b2tlIHRoZW0uXG4gKiBQcm92aWRlIGBvcHRpb25zYCB0byBpbmRpY2F0ZSB3aGV0aGVyIGBmdW5jYCBzaG91bGQgYmUgaW52b2tlZCBvbiB0aGVcbiAqIGxlYWRpbmcgYW5kL29yIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIGB3YWl0YCB0aW1lb3V0LiBUaGUgYGZ1bmNgIGlzIGludm9rZWRcbiAqIHdpdGggdGhlIGxhc3QgYXJndW1lbnRzIHByb3ZpZGVkIHRvIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb24uIFN1YnNlcXVlbnRcbiAqIGNhbGxzIHRvIHRoZSBkZWJvdW5jZWQgZnVuY3Rpb24gcmV0dXJuIHRoZSByZXN1bHQgb2YgdGhlIGxhc3QgYGZ1bmNgXG4gKiBpbnZvY2F0aW9uLlxuICpcbiAqICoqTm90ZToqKiBJZiBgbGVhZGluZ2AgYW5kIGB0cmFpbGluZ2Agb3B0aW9ucyBhcmUgYHRydWVgLCBgZnVuY2AgaXNcbiAqIGludm9rZWQgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQgb25seSBpZiB0aGUgZGVib3VuY2VkIGZ1bmN0aW9uXG4gKiBpcyBpbnZva2VkIG1vcmUgdGhhbiBvbmNlIGR1cmluZyB0aGUgYHdhaXRgIHRpbWVvdXQuXG4gKlxuICogSWYgYHdhaXRgIGlzIGAwYCBhbmQgYGxlYWRpbmdgIGlzIGBmYWxzZWAsIGBmdW5jYCBpbnZvY2F0aW9uIGlzIGRlZmVycmVkXG4gKiB1bnRpbCB0byB0aGUgbmV4dCB0aWNrLCBzaW1pbGFyIHRvIGBzZXRUaW1lb3V0YCB3aXRoIGEgdGltZW91dCBvZiBgMGAuXG4gKlxuICogU2VlIFtEYXZpZCBDb3JiYWNobydzIGFydGljbGVdKGh0dHBzOi8vY3NzLXRyaWNrcy5jb20vZGVib3VuY2luZy10aHJvdHRsaW5nLWV4cGxhaW5lZC1leGFtcGxlcy8pXG4gKiBmb3IgZGV0YWlscyBvdmVyIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGBfLmRlYm91bmNlYCBhbmQgYF8udGhyb3R0bGVgLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBGdW5jdGlvblxuICogQHBhcmFtIHtGdW5jdGlvbn0gZnVuYyBUaGUgZnVuY3Rpb24gdG8gZGVib3VuY2UuXG4gKiBAcGFyYW0ge251bWJlcn0gW3dhaXQ9MF0gVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gZGVsYXkuXG4gKiBAcGFyYW0ge09iamVjdH0gW29wdGlvbnM9e31dIFRoZSBvcHRpb25zIG9iamVjdC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMubGVhZGluZz1mYWxzZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSBsZWFkaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQuXG4gKiBAcGFyYW0ge251bWJlcn0gW29wdGlvbnMubWF4V2FpdF1cbiAqICBUaGUgbWF4aW11bSB0aW1lIGBmdW5jYCBpcyBhbGxvd2VkIHRvIGJlIGRlbGF5ZWQgYmVmb3JlIGl0J3MgaW52b2tlZC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMudHJhaWxpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgZGVib3VuY2VkIGZ1bmN0aW9uLlxuICogQGV4YW1wbGVcbiAqXG4gKiAvLyBBdm9pZCBjb3N0bHkgY2FsY3VsYXRpb25zIHdoaWxlIHRoZSB3aW5kb3cgc2l6ZSBpcyBpbiBmbHV4LlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3Jlc2l6ZScsIF8uZGVib3VuY2UoY2FsY3VsYXRlTGF5b3V0LCAxNTApKTtcbiAqXG4gKiAvLyBJbnZva2UgYHNlbmRNYWlsYCB3aGVuIGNsaWNrZWQsIGRlYm91bmNpbmcgc3Vic2VxdWVudCBjYWxscy5cbiAqIGpRdWVyeShlbGVtZW50KS5vbignY2xpY2snLCBfLmRlYm91bmNlKHNlbmRNYWlsLCAzMDAsIHtcbiAqICAgJ2xlYWRpbmcnOiB0cnVlLFxuICogICAndHJhaWxpbmcnOiBmYWxzZVxuICogfSkpO1xuICpcbiAqIC8vIEVuc3VyZSBgYmF0Y2hMb2dgIGlzIGludm9rZWQgb25jZSBhZnRlciAxIHNlY29uZCBvZiBkZWJvdW5jZWQgY2FsbHMuXG4gKiB2YXIgZGVib3VuY2VkID0gXy5kZWJvdW5jZShiYXRjaExvZywgMjUwLCB7ICdtYXhXYWl0JzogMTAwMCB9KTtcbiAqIHZhciBzb3VyY2UgPSBuZXcgRXZlbnRTb3VyY2UoJy9zdHJlYW0nKTtcbiAqIGpRdWVyeShzb3VyY2UpLm9uKCdtZXNzYWdlJywgZGVib3VuY2VkKTtcbiAqXG4gKiAvLyBDYW5jZWwgdGhlIHRyYWlsaW5nIGRlYm91bmNlZCBpbnZvY2F0aW9uLlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3BvcHN0YXRlJywgZGVib3VuY2VkLmNhbmNlbCk7XG4gKi9cbmZ1bmN0aW9uIGRlYm91bmNlKGZ1bmMsIHdhaXQsIG9wdGlvbnMpIHtcbiAgdmFyIGxhc3RBcmdzLFxuICAgICAgbGFzdFRoaXMsXG4gICAgICBtYXhXYWl0LFxuICAgICAgcmVzdWx0LFxuICAgICAgdGltZXJJZCxcbiAgICAgIGxhc3RDYWxsVGltZSxcbiAgICAgIGxhc3RJbnZva2VUaW1lID0gMCxcbiAgICAgIGxlYWRpbmcgPSBmYWxzZSxcbiAgICAgIG1heGluZyA9IGZhbHNlLFxuICAgICAgdHJhaWxpbmcgPSB0cnVlO1xuXG4gIGlmICh0eXBlb2YgZnVuYyAhPSAnZnVuY3Rpb24nKSB7XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcihGVU5DX0VSUk9SX1RFWFQpO1xuICB9XG4gIHdhaXQgPSB0b051bWJlcih3YWl0KSB8fCAwO1xuICBpZiAoaXNPYmplY3Qob3B0aW9ucykpIHtcbiAgICBsZWFkaW5nID0gISFvcHRpb25zLmxlYWRpbmc7XG4gICAgbWF4aW5nID0gJ21heFdhaXQnIGluIG9wdGlvbnM7XG4gICAgbWF4V2FpdCA9IG1heGluZyA/IG5hdGl2ZU1heCh0b051bWJlcihvcHRpb25zLm1heFdhaXQpIHx8IDAsIHdhaXQpIDogbWF4V2FpdDtcbiAgICB0cmFpbGluZyA9ICd0cmFpbGluZycgaW4gb3B0aW9ucyA/ICEhb3B0aW9ucy50cmFpbGluZyA6IHRyYWlsaW5nO1xuICB9XG5cbiAgZnVuY3Rpb24gaW52b2tlRnVuYyh0aW1lKSB7XG4gICAgdmFyIGFyZ3MgPSBsYXN0QXJncyxcbiAgICAgICAgdGhpc0FyZyA9IGxhc3RUaGlzO1xuXG4gICAgbGFzdEFyZ3MgPSBsYXN0VGhpcyA9IHVuZGVmaW5lZDtcbiAgICBsYXN0SW52b2tlVGltZSA9IHRpbWU7XG4gICAgcmVzdWx0ID0gZnVuYy5hcHBseSh0aGlzQXJnLCBhcmdzKTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gbGVhZGluZ0VkZ2UodGltZSkge1xuICAgIC8vIFJlc2V0IGFueSBgbWF4V2FpdGAgdGltZXIuXG4gICAgbGFzdEludm9rZVRpbWUgPSB0aW1lO1xuICAgIC8vIFN0YXJ0IHRoZSB0aW1lciBmb3IgdGhlIHRyYWlsaW5nIGVkZ2UuXG4gICAgdGltZXJJZCA9IHNldFRpbWVvdXQodGltZXJFeHBpcmVkLCB3YWl0KTtcbiAgICAvLyBJbnZva2UgdGhlIGxlYWRpbmcgZWRnZS5cbiAgICByZXR1cm4gbGVhZGluZyA/IGludm9rZUZ1bmModGltZSkgOiByZXN1bHQ7XG4gIH1cblxuICBmdW5jdGlvbiByZW1haW5pbmdXYWl0KHRpbWUpIHtcbiAgICB2YXIgdGltZVNpbmNlTGFzdENhbGwgPSB0aW1lIC0gbGFzdENhbGxUaW1lLFxuICAgICAgICB0aW1lU2luY2VMYXN0SW52b2tlID0gdGltZSAtIGxhc3RJbnZva2VUaW1lLFxuICAgICAgICByZXN1bHQgPSB3YWl0IC0gdGltZVNpbmNlTGFzdENhbGw7XG5cbiAgICByZXR1cm4gbWF4aW5nID8gbmF0aXZlTWluKHJlc3VsdCwgbWF4V2FpdCAtIHRpbWVTaW5jZUxhc3RJbnZva2UpIDogcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gc2hvdWxkSW52b2tlKHRpbWUpIHtcbiAgICB2YXIgdGltZVNpbmNlTGFzdENhbGwgPSB0aW1lIC0gbGFzdENhbGxUaW1lLFxuICAgICAgICB0aW1lU2luY2VMYXN0SW52b2tlID0gdGltZSAtIGxhc3RJbnZva2VUaW1lO1xuXG4gICAgLy8gRWl0aGVyIHRoaXMgaXMgdGhlIGZpcnN0IGNhbGwsIGFjdGl2aXR5IGhhcyBzdG9wcGVkIGFuZCB3ZSdyZSBhdCB0aGVcbiAgICAvLyB0cmFpbGluZyBlZGdlLCB0aGUgc3lzdGVtIHRpbWUgaGFzIGdvbmUgYmFja3dhcmRzIGFuZCB3ZSdyZSB0cmVhdGluZ1xuICAgIC8vIGl0IGFzIHRoZSB0cmFpbGluZyBlZGdlLCBvciB3ZSd2ZSBoaXQgdGhlIGBtYXhXYWl0YCBsaW1pdC5cbiAgICByZXR1cm4gKGxhc3RDYWxsVGltZSA9PT0gdW5kZWZpbmVkIHx8ICh0aW1lU2luY2VMYXN0Q2FsbCA+PSB3YWl0KSB8fFxuICAgICAgKHRpbWVTaW5jZUxhc3RDYWxsIDwgMCkgfHwgKG1heGluZyAmJiB0aW1lU2luY2VMYXN0SW52b2tlID49IG1heFdhaXQpKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHRpbWVyRXhwaXJlZCgpIHtcbiAgICB2YXIgdGltZSA9IG5vdygpO1xuICAgIGlmIChzaG91bGRJbnZva2UodGltZSkpIHtcbiAgICAgIHJldHVybiB0cmFpbGluZ0VkZ2UodGltZSk7XG4gICAgfVxuICAgIC8vIFJlc3RhcnQgdGhlIHRpbWVyLlxuICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgcmVtYWluaW5nV2FpdCh0aW1lKSk7XG4gIH1cblxuICBmdW5jdGlvbiB0cmFpbGluZ0VkZ2UodGltZSkge1xuICAgIHRpbWVySWQgPSB1bmRlZmluZWQ7XG5cbiAgICAvLyBPbmx5IGludm9rZSBpZiB3ZSBoYXZlIGBsYXN0QXJnc2Agd2hpY2ggbWVhbnMgYGZ1bmNgIGhhcyBiZWVuXG4gICAgLy8gZGVib3VuY2VkIGF0IGxlYXN0IG9uY2UuXG4gICAgaWYgKHRyYWlsaW5nICYmIGxhc3RBcmdzKSB7XG4gICAgICByZXR1cm4gaW52b2tlRnVuYyh0aW1lKTtcbiAgICB9XG4gICAgbGFzdEFyZ3MgPSBsYXN0VGhpcyA9IHVuZGVmaW5lZDtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgZnVuY3Rpb24gY2FuY2VsKCkge1xuICAgIGlmICh0aW1lcklkICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGNsZWFyVGltZW91dCh0aW1lcklkKTtcbiAgICB9XG4gICAgbGFzdEludm9rZVRpbWUgPSAwO1xuICAgIGxhc3RBcmdzID0gbGFzdENhbGxUaW1lID0gbGFzdFRoaXMgPSB0aW1lcklkID0gdW5kZWZpbmVkO1xuICB9XG5cbiAgZnVuY3Rpb24gZmx1c2goKSB7XG4gICAgcmV0dXJuIHRpbWVySWQgPT09IHVuZGVmaW5lZCA/IHJlc3VsdCA6IHRyYWlsaW5nRWRnZShub3coKSk7XG4gIH1cblxuICBmdW5jdGlvbiBkZWJvdW5jZWQoKSB7XG4gICAgdmFyIHRpbWUgPSBub3coKSxcbiAgICAgICAgaXNJbnZva2luZyA9IHNob3VsZEludm9rZSh0aW1lKTtcblxuICAgIGxhc3RBcmdzID0gYXJndW1lbnRzO1xuICAgIGxhc3RUaGlzID0gdGhpcztcbiAgICBsYXN0Q2FsbFRpbWUgPSB0aW1lO1xuXG4gICAgaWYgKGlzSW52b2tpbmcpIHtcbiAgICAgIGlmICh0aW1lcklkID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcmV0dXJuIGxlYWRpbmdFZGdlKGxhc3RDYWxsVGltZSk7XG4gICAgICB9XG4gICAgICBpZiAobWF4aW5nKSB7XG4gICAgICAgIC8vIEhhbmRsZSBpbnZvY2F0aW9ucyBpbiBhIHRpZ2h0IGxvb3AuXG4gICAgICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgICAgIHJldHVybiBpbnZva2VGdW5jKGxhc3RDYWxsVGltZSk7XG4gICAgICB9XG4gICAgfVxuICAgIGlmICh0aW1lcklkID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHRpbWVySWQgPSBzZXRUaW1lb3V0KHRpbWVyRXhwaXJlZCwgd2FpdCk7XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbiAgZGVib3VuY2VkLmNhbmNlbCA9IGNhbmNlbDtcbiAgZGVib3VuY2VkLmZsdXNoID0gZmx1c2g7XG4gIHJldHVybiBkZWJvdW5jZWQ7XG59XG5cbi8qKlxuICogQ3JlYXRlcyBhIHRocm90dGxlZCBmdW5jdGlvbiB0aGF0IG9ubHkgaW52b2tlcyBgZnVuY2AgYXQgbW9zdCBvbmNlIHBlclxuICogZXZlcnkgYHdhaXRgIG1pbGxpc2Vjb25kcy4gVGhlIHRocm90dGxlZCBmdW5jdGlvbiBjb21lcyB3aXRoIGEgYGNhbmNlbGBcbiAqIG1ldGhvZCB0byBjYW5jZWwgZGVsYXllZCBgZnVuY2AgaW52b2NhdGlvbnMgYW5kIGEgYGZsdXNoYCBtZXRob2QgdG9cbiAqIGltbWVkaWF0ZWx5IGludm9rZSB0aGVtLiBQcm92aWRlIGBvcHRpb25zYCB0byBpbmRpY2F0ZSB3aGV0aGVyIGBmdW5jYFxuICogc2hvdWxkIGJlIGludm9rZWQgb24gdGhlIGxlYWRpbmcgYW5kL29yIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIGB3YWl0YFxuICogdGltZW91dC4gVGhlIGBmdW5jYCBpcyBpbnZva2VkIHdpdGggdGhlIGxhc3QgYXJndW1lbnRzIHByb3ZpZGVkIHRvIHRoZVxuICogdGhyb3R0bGVkIGZ1bmN0aW9uLiBTdWJzZXF1ZW50IGNhbGxzIHRvIHRoZSB0aHJvdHRsZWQgZnVuY3Rpb24gcmV0dXJuIHRoZVxuICogcmVzdWx0IG9mIHRoZSBsYXN0IGBmdW5jYCBpbnZvY2F0aW9uLlxuICpcbiAqICoqTm90ZToqKiBJZiBgbGVhZGluZ2AgYW5kIGB0cmFpbGluZ2Agb3B0aW9ucyBhcmUgYHRydWVgLCBgZnVuY2AgaXNcbiAqIGludm9rZWQgb24gdGhlIHRyYWlsaW5nIGVkZ2Ugb2YgdGhlIHRpbWVvdXQgb25seSBpZiB0aGUgdGhyb3R0bGVkIGZ1bmN0aW9uXG4gKiBpcyBpbnZva2VkIG1vcmUgdGhhbiBvbmNlIGR1cmluZyB0aGUgYHdhaXRgIHRpbWVvdXQuXG4gKlxuICogSWYgYHdhaXRgIGlzIGAwYCBhbmQgYGxlYWRpbmdgIGlzIGBmYWxzZWAsIGBmdW5jYCBpbnZvY2F0aW9uIGlzIGRlZmVycmVkXG4gKiB1bnRpbCB0byB0aGUgbmV4dCB0aWNrLCBzaW1pbGFyIHRvIGBzZXRUaW1lb3V0YCB3aXRoIGEgdGltZW91dCBvZiBgMGAuXG4gKlxuICogU2VlIFtEYXZpZCBDb3JiYWNobydzIGFydGljbGVdKGh0dHBzOi8vY3NzLXRyaWNrcy5jb20vZGVib3VuY2luZy10aHJvdHRsaW5nLWV4cGxhaW5lZC1leGFtcGxlcy8pXG4gKiBmb3IgZGV0YWlscyBvdmVyIHRoZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIGBfLnRocm90dGxlYCBhbmQgYF8uZGVib3VuY2VgLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgMC4xLjBcbiAqIEBjYXRlZ29yeSBGdW5jdGlvblxuICogQHBhcmFtIHtGdW5jdGlvbn0gZnVuYyBUaGUgZnVuY3Rpb24gdG8gdGhyb3R0bGUuXG4gKiBAcGFyYW0ge251bWJlcn0gW3dhaXQ9MF0gVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gdGhyb3R0bGUgaW52b2NhdGlvbnMgdG8uXG4gKiBAcGFyYW0ge09iamVjdH0gW29wdGlvbnM9e31dIFRoZSBvcHRpb25zIG9iamVjdC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMubGVhZGluZz10cnVlXVxuICogIFNwZWNpZnkgaW52b2tpbmcgb24gdGhlIGxlYWRpbmcgZWRnZSBvZiB0aGUgdGltZW91dC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMudHJhaWxpbmc9dHJ1ZV1cbiAqICBTcGVjaWZ5IGludm9raW5nIG9uIHRoZSB0cmFpbGluZyBlZGdlIG9mIHRoZSB0aW1lb3V0LlxuICogQHJldHVybnMge0Z1bmN0aW9ufSBSZXR1cm5zIHRoZSBuZXcgdGhyb3R0bGVkIGZ1bmN0aW9uLlxuICogQGV4YW1wbGVcbiAqXG4gKiAvLyBBdm9pZCBleGNlc3NpdmVseSB1cGRhdGluZyB0aGUgcG9zaXRpb24gd2hpbGUgc2Nyb2xsaW5nLlxuICogalF1ZXJ5KHdpbmRvdykub24oJ3Njcm9sbCcsIF8udGhyb3R0bGUodXBkYXRlUG9zaXRpb24sIDEwMCkpO1xuICpcbiAqIC8vIEludm9rZSBgcmVuZXdUb2tlbmAgd2hlbiB0aGUgY2xpY2sgZXZlbnQgaXMgZmlyZWQsIGJ1dCBub3QgbW9yZSB0aGFuIG9uY2UgZXZlcnkgNSBtaW51dGVzLlxuICogdmFyIHRocm90dGxlZCA9IF8udGhyb3R0bGUocmVuZXdUb2tlbiwgMzAwMDAwLCB7ICd0cmFpbGluZyc6IGZhbHNlIH0pO1xuICogalF1ZXJ5KGVsZW1lbnQpLm9uKCdjbGljaycsIHRocm90dGxlZCk7XG4gKlxuICogLy8gQ2FuY2VsIHRoZSB0cmFpbGluZyB0aHJvdHRsZWQgaW52b2NhdGlvbi5cbiAqIGpRdWVyeSh3aW5kb3cpLm9uKCdwb3BzdGF0ZScsIHRocm90dGxlZC5jYW5jZWwpO1xuICovXG5mdW5jdGlvbiB0aHJvdHRsZShmdW5jLCB3YWl0LCBvcHRpb25zKSB7XG4gIHZhciBsZWFkaW5nID0gdHJ1ZSxcbiAgICAgIHRyYWlsaW5nID0gdHJ1ZTtcblxuICBpZiAodHlwZW9mIGZ1bmMgIT0gJ2Z1bmN0aW9uJykge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoRlVOQ19FUlJPUl9URVhUKTtcbiAgfVxuICBpZiAoaXNPYmplY3Qob3B0aW9ucykpIHtcbiAgICBsZWFkaW5nID0gJ2xlYWRpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMubGVhZGluZyA6IGxlYWRpbmc7XG4gICAgdHJhaWxpbmcgPSAndHJhaWxpbmcnIGluIG9wdGlvbnMgPyAhIW9wdGlvbnMudHJhaWxpbmcgOiB0cmFpbGluZztcbiAgfVxuICByZXR1cm4gZGVib3VuY2UoZnVuYywgd2FpdCwge1xuICAgICdsZWFkaW5nJzogbGVhZGluZyxcbiAgICAnbWF4V2FpdCc6IHdhaXQsXG4gICAgJ3RyYWlsaW5nJzogdHJhaWxpbmdcbiAgfSk7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIGB2YWx1ZWAgaXMgdGhlXG4gKiBbbGFuZ3VhZ2UgdHlwZV0oaHR0cDovL3d3dy5lY21hLWludGVybmF0aW9uYWwub3JnL2VjbWEtMjYyLzcuMC8jc2VjLWVjbWFzY3JpcHQtbGFuZ3VhZ2UtdHlwZXMpXG4gKiBvZiBgT2JqZWN0YC4gKGUuZy4gYXJyYXlzLCBmdW5jdGlvbnMsIG9iamVjdHMsIHJlZ2V4ZXMsIGBuZXcgTnVtYmVyKDApYCwgYW5kIGBuZXcgU3RyaW5nKCcnKWApXG4gKlxuICogQHN0YXRpY1xuICogQG1lbWJlck9mIF9cbiAqIEBzaW5jZSAwLjEuMFxuICogQGNhdGVnb3J5IExhbmdcbiAqIEBwYXJhbSB7Kn0gdmFsdWUgVGhlIHZhbHVlIHRvIGNoZWNrLlxuICogQHJldHVybnMge2Jvb2xlYW59IFJldHVybnMgYHRydWVgIGlmIGB2YWx1ZWAgaXMgYW4gb2JqZWN0LCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNPYmplY3Qoe30pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3QoWzEsIDIsIDNdKTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0KF8ubm9vcCk7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc09iamVjdChudWxsKTtcbiAqIC8vID0+IGZhbHNlXG4gKi9cbmZ1bmN0aW9uIGlzT2JqZWN0KHZhbHVlKSB7XG4gIHZhciB0eXBlID0gdHlwZW9mIHZhbHVlO1xuICByZXR1cm4gISF2YWx1ZSAmJiAodHlwZSA9PSAnb2JqZWN0JyB8fCB0eXBlID09ICdmdW5jdGlvbicpO1xufVxuXG4vKipcbiAqIENoZWNrcyBpZiBgdmFsdWVgIGlzIG9iamVjdC1saWtlLiBBIHZhbHVlIGlzIG9iamVjdC1saWtlIGlmIGl0J3Mgbm90IGBudWxsYFxuICogYW5kIGhhcyBhIGB0eXBlb2ZgIHJlc3VsdCBvZiBcIm9iamVjdFwiLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgNC4wLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBjaGVjay5cbiAqIEByZXR1cm5zIHtib29sZWFufSBSZXR1cm5zIGB0cnVlYCBpZiBgdmFsdWVgIGlzIG9iamVjdC1saWtlLCBlbHNlIGBmYWxzZWAuXG4gKiBAZXhhbXBsZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKHt9KTtcbiAqIC8vID0+IHRydWVcbiAqXG4gKiBfLmlzT2JqZWN0TGlrZShbMSwgMiwgM10pO1xuICogLy8gPT4gdHJ1ZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKF8ubm9vcCk7XG4gKiAvLyA9PiBmYWxzZVxuICpcbiAqIF8uaXNPYmplY3RMaWtlKG51bGwpO1xuICogLy8gPT4gZmFsc2VcbiAqL1xuZnVuY3Rpb24gaXNPYmplY3RMaWtlKHZhbHVlKSB7XG4gIHJldHVybiAhIXZhbHVlICYmIHR5cGVvZiB2YWx1ZSA9PSAnb2JqZWN0Jztcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYHZhbHVlYCBpcyBjbGFzc2lmaWVkIGFzIGEgYFN5bWJvbGAgcHJpbWl0aXZlIG9yIG9iamVjdC5cbiAqXG4gKiBAc3RhdGljXG4gKiBAbWVtYmVyT2YgX1xuICogQHNpbmNlIDQuMC4wXG4gKiBAY2F0ZWdvcnkgTGFuZ1xuICogQHBhcmFtIHsqfSB2YWx1ZSBUaGUgdmFsdWUgdG8gY2hlY2suXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gUmV0dXJucyBgdHJ1ZWAgaWYgYHZhbHVlYCBpcyBhIHN5bWJvbCwgZWxzZSBgZmFsc2VgLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLmlzU3ltYm9sKFN5bWJvbC5pdGVyYXRvcik7XG4gKiAvLyA9PiB0cnVlXG4gKlxuICogXy5pc1N5bWJvbCgnYWJjJyk7XG4gKiAvLyA9PiBmYWxzZVxuICovXG5mdW5jdGlvbiBpc1N5bWJvbCh2YWx1ZSkge1xuICByZXR1cm4gdHlwZW9mIHZhbHVlID09ICdzeW1ib2wnIHx8XG4gICAgKGlzT2JqZWN0TGlrZSh2YWx1ZSkgJiYgb2JqZWN0VG9TdHJpbmcuY2FsbCh2YWx1ZSkgPT0gc3ltYm9sVGFnKTtcbn1cblxuLyoqXG4gKiBDb252ZXJ0cyBgdmFsdWVgIHRvIGEgbnVtYmVyLlxuICpcbiAqIEBzdGF0aWNcbiAqIEBtZW1iZXJPZiBfXG4gKiBAc2luY2UgNC4wLjBcbiAqIEBjYXRlZ29yeSBMYW5nXG4gKiBAcGFyYW0geyp9IHZhbHVlIFRoZSB2YWx1ZSB0byBwcm9jZXNzLlxuICogQHJldHVybnMge251bWJlcn0gUmV0dXJucyB0aGUgbnVtYmVyLlxuICogQGV4YW1wbGVcbiAqXG4gKiBfLnRvTnVtYmVyKDMuMik7XG4gKiAvLyA9PiAzLjJcbiAqXG4gKiBfLnRvTnVtYmVyKE51bWJlci5NSU5fVkFMVUUpO1xuICogLy8gPT4gNWUtMzI0XG4gKlxuICogXy50b051bWJlcihJbmZpbml0eSk7XG4gKiAvLyA9PiBJbmZpbml0eVxuICpcbiAqIF8udG9OdW1iZXIoJzMuMicpO1xuICogLy8gPT4gMy4yXG4gKi9cbmZ1bmN0aW9uIHRvTnVtYmVyKHZhbHVlKSB7XG4gIGlmICh0eXBlb2YgdmFsdWUgPT0gJ251bWJlcicpIHtcbiAgICByZXR1cm4gdmFsdWU7XG4gIH1cbiAgaWYgKGlzU3ltYm9sKHZhbHVlKSkge1xuICAgIHJldHVybiBOQU47XG4gIH1cbiAgaWYgKGlzT2JqZWN0KHZhbHVlKSkge1xuICAgIHZhciBvdGhlciA9IHR5cGVvZiB2YWx1ZS52YWx1ZU9mID09ICdmdW5jdGlvbicgPyB2YWx1ZS52YWx1ZU9mKCkgOiB2YWx1ZTtcbiAgICB2YWx1ZSA9IGlzT2JqZWN0KG90aGVyKSA/IChvdGhlciArICcnKSA6IG90aGVyO1xuICB9XG4gIGlmICh0eXBlb2YgdmFsdWUgIT0gJ3N0cmluZycpIHtcbiAgICByZXR1cm4gdmFsdWUgPT09IDAgPyB2YWx1ZSA6ICt2YWx1ZTtcbiAgfVxuICB2YWx1ZSA9IHZhbHVlLnJlcGxhY2UocmVUcmltLCAnJyk7XG4gIHZhciBpc0JpbmFyeSA9IHJlSXNCaW5hcnkudGVzdCh2YWx1ZSk7XG4gIHJldHVybiAoaXNCaW5hcnkgfHwgcmVJc09jdGFsLnRlc3QodmFsdWUpKVxuICAgID8gZnJlZVBhcnNlSW50KHZhbHVlLnNsaWNlKDIpLCBpc0JpbmFyeSA/IDIgOiA4KVxuICAgIDogKHJlSXNCYWRIZXgudGVzdCh2YWx1ZSkgPyBOQU4gOiArdmFsdWUpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHRocm90dGxlO1xuIiwidmFyIGc7XHJcblxyXG4vLyBUaGlzIHdvcmtzIGluIG5vbi1zdHJpY3QgbW9kZVxyXG5nID0gKGZ1bmN0aW9uKCkge1xyXG5cdHJldHVybiB0aGlzO1xyXG59KSgpO1xyXG5cclxudHJ5IHtcclxuXHQvLyBUaGlzIHdvcmtzIGlmIGV2YWwgaXMgYWxsb3dlZCAoc2VlIENTUClcclxuXHRnID0gZyB8fCBGdW5jdGlvbihcInJldHVybiB0aGlzXCIpKCkgfHwgKDEsIGV2YWwpKFwidGhpc1wiKTtcclxufSBjYXRjaCAoZSkge1xyXG5cdC8vIFRoaXMgd29ya3MgaWYgdGhlIHdpbmRvdyByZWZlcmVuY2UgaXMgYXZhaWxhYmxlXHJcblx0aWYgKHR5cGVvZiB3aW5kb3cgPT09IFwib2JqZWN0XCIpIGcgPSB3aW5kb3c7XHJcbn1cclxuXHJcbi8vIGcgY2FuIHN0aWxsIGJlIHVuZGVmaW5lZCwgYnV0IG5vdGhpbmcgdG8gZG8gYWJvdXQgaXQuLi5cclxuLy8gV2UgcmV0dXJuIHVuZGVmaW5lZCwgaW5zdGVhZCBvZiBub3RoaW5nIGhlcmUsIHNvIGl0J3NcclxuLy8gZWFzaWVyIHRvIGhhbmRsZSB0aGlzIGNhc2UuIGlmKCFnbG9iYWwpIHsgLi4ufVxyXG5cclxubW9kdWxlLmV4cG9ydHMgPSBnO1xyXG4iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbmltcG9ydCB7IGdldEVsZW1lbnRzRm9yU291cmNlTGluZSB9IGZyb20gJy4vc2Nyb2xsLXN5bmMnO1xuXG5leHBvcnQgY2xhc3MgQWN0aXZlTGluZU1hcmtlciB7XG5cdHByaXZhdGUgX2N1cnJlbnQ6IGFueTtcblxuXHRvbkRpZENoYW5nZVRleHRFZGl0b3JTZWxlY3Rpb24obGluZTogbnVtYmVyKSB7XG5cdFx0Y29uc3QgeyBwcmV2aW91cyB9ID0gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKGxpbmUpO1xuXHRcdHRoaXMuX3VwZGF0ZShwcmV2aW91cyAmJiBwcmV2aW91cy5lbGVtZW50KTtcblx0fVxuXG5cdF91cGRhdGUoYmVmb3JlOiBIVE1MRWxlbWVudCB8IHVuZGVmaW5lZCkge1xuXHRcdHRoaXMuX3VubWFya0FjdGl2ZUVsZW1lbnQodGhpcy5fY3VycmVudCk7XG5cdFx0dGhpcy5fbWFya0FjdGl2ZUVsZW1lbnQoYmVmb3JlKTtcblx0XHR0aGlzLl9jdXJyZW50ID0gYmVmb3JlO1xuXHR9XG5cblx0X3VubWFya0FjdGl2ZUVsZW1lbnQoZWxlbWVudDogSFRNTEVsZW1lbnQgfCB1bmRlZmluZWQpIHtcblx0XHRpZiAoIWVsZW1lbnQpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0ZWxlbWVudC5jbGFzc05hbWUgPSBlbGVtZW50LmNsYXNzTmFtZS5yZXBsYWNlKC9cXGJjb2RlLWFjdGl2ZS1saW5lXFxiL2csICcnKTtcblx0fVxuXG5cdF9tYXJrQWN0aXZlRWxlbWVudChlbGVtZW50OiBIVE1MRWxlbWVudCB8IHVuZGVmaW5lZCkge1xuXHRcdGlmICghZWxlbWVudCkge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHRlbGVtZW50LmNsYXNzTmFtZSArPSAnIGNvZGUtYWN0aXZlLWxpbmUnO1xuXHR9XG59IiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmV4cG9ydCBmdW5jdGlvbiBvbmNlRG9jdW1lbnRMb2FkZWQoZjogKCkgPT4gdm9pZCkge1xuXHRpZiAoZG9jdW1lbnQucmVhZHlTdGF0ZSA9PT0gJ2xvYWRpbmcnIHx8IGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICd1bmluaXRpYWxpemVkJykge1xuXHRcdGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCBmKTtcblx0fSBlbHNlIHtcblx0XHRmKCk7XG5cdH1cbn0iLCIvKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICogIENvcHlyaWdodCAoYykgTWljcm9zb2Z0IENvcnBvcmF0aW9uLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIExpY2Vuc2UudHh0IGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXG4gKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cblxuaW1wb3J0IHsgQWN0aXZlTGluZU1hcmtlciB9IGZyb20gJy4vYWN0aXZlTGluZU1hcmtlcic7XG5pbXBvcnQgeyBvbmNlRG9jdW1lbnRMb2FkZWQgfSBmcm9tICcuL2V2ZW50cyc7XG5pbXBvcnQgeyBjcmVhdGVQb3N0ZXJGb3JWc0NvZGUgfSBmcm9tICcuL21lc3NhZ2luZyc7XG5pbXBvcnQgeyBnZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCwgc2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lIH0gZnJvbSAnLi9zY3JvbGwtc3luYyc7XG5pbXBvcnQgeyBnZXRTZXR0aW5ncywgZ2V0RGF0YSB9IGZyb20gJy4vc2V0dGluZ3MnO1xuaW1wb3J0IHRocm90dGxlID0gcmVxdWlyZSgnbG9kYXNoLnRocm90dGxlJyk7XG5cbmRlY2xhcmUgdmFyIGFjcXVpcmVWc0NvZGVBcGk6IGFueTtcblxudmFyIHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcbmNvbnN0IG1hcmtlciA9IG5ldyBBY3RpdmVMaW5lTWFya2VyKCk7XG5jb25zdCBzZXR0aW5ncyA9IGdldFNldHRpbmdzKCk7XG5cbmNvbnN0IHZzY29kZSA9IGFjcXVpcmVWc0NvZGVBcGkoKTtcblxuLy8gU2V0IFZTIENvZGUgc3RhdGVcbmNvbnN0IHN0YXRlID0gZ2V0RGF0YSgnZGF0YS1zdGF0ZScpO1xudnNjb2RlLnNldFN0YXRlKHN0YXRlKTtcblxuY29uc3QgbWVzc2FnaW5nID0gY3JlYXRlUG9zdGVyRm9yVnNDb2RlKHZzY29kZSk7XG5cbndpbmRvdy5jc3BBbGVydGVyLnNldFBvc3RlcihtZXNzYWdpbmcpO1xud2luZG93LnN0eWxlTG9hZGluZ01vbml0b3Iuc2V0UG9zdGVyKG1lc3NhZ2luZyk7XG5cbndpbmRvdy5vbmxvYWQgPSAoKSA9PiB7XG5cdHVwZGF0ZUltYWdlU2l6ZXMoKTtcbn07XG5cbm9uY2VEb2N1bWVudExvYWRlZCgoKSA9PiB7XG5cdGlmIChzZXR0aW5ncy5zY3JvbGxQcmV2aWV3V2l0aEVkaXRvcikge1xuXHRcdHNldFRpbWVvdXQoKCkgPT4ge1xuXHRcdFx0Y29uc3QgaW5pdGlhbExpbmUgPSArc2V0dGluZ3MubGluZTtcblx0XHRcdGlmICghaXNOYU4oaW5pdGlhbExpbmUpKSB7XG5cdFx0XHRcdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0XHRcdFx0c2Nyb2xsVG9SZXZlYWxTb3VyY2VMaW5lKGluaXRpYWxMaW5lKTtcblx0XHRcdH1cblx0XHR9LCAwKTtcblx0fVxufSk7XG5cbmNvbnN0IG9uVXBkYXRlVmlldyA9ICgoKSA9PiB7XG5cdGNvbnN0IGRvU2Nyb2xsID0gdGhyb3R0bGUoKGxpbmU6IG51bWJlcikgPT4ge1xuXHRcdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0XHRzY3JvbGxUb1JldmVhbFNvdXJjZUxpbmUobGluZSk7XG5cdH0sIDUwKTtcblxuXHRyZXR1cm4gKGxpbmU6IG51bWJlciwgc2V0dGluZ3M6IGFueSkgPT4ge1xuXHRcdGlmICghaXNOYU4obGluZSkpIHtcblx0XHRcdHNldHRpbmdzLmxpbmUgPSBsaW5lO1xuXHRcdFx0ZG9TY3JvbGwobGluZSk7XG5cdFx0fVxuXHR9O1xufSkoKTtcblxubGV0IHVwZGF0ZUltYWdlU2l6ZXMgPSB0aHJvdHRsZSgoKSA9PiB7XG5cdGNvbnN0IGltYWdlSW5mbzogeyBpZDogc3RyaW5nLCBoZWlnaHQ6IG51bWJlciwgd2lkdGg6IG51bWJlciB9W10gPSBbXTtcblx0bGV0IGltYWdlcyA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdpbWcnKTtcblx0aWYgKGltYWdlcykge1xuXHRcdGxldCBpO1xuXHRcdGZvciAoaSA9IDA7IGkgPCBpbWFnZXMubGVuZ3RoOyBpKyspIHtcblx0XHRcdGNvbnN0IGltZyA9IGltYWdlc1tpXTtcblxuXHRcdFx0aWYgKGltZy5jbGFzc0xpc3QuY29udGFpbnMoJ2xvYWRpbmcnKSkge1xuXHRcdFx0XHRpbWcuY2xhc3NMaXN0LnJlbW92ZSgnbG9hZGluZycpO1xuXHRcdFx0fVxuXG5cdFx0XHRpbWFnZUluZm8ucHVzaCh7XG5cdFx0XHRcdGlkOiBpbWcuaWQsXG5cdFx0XHRcdGhlaWdodDogaW1nLmhlaWdodCxcblx0XHRcdFx0d2lkdGg6IGltZy53aWR0aFxuXHRcdFx0fSk7XG5cdFx0fVxuXG5cdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdjYWNoZUltYWdlU2l6ZXMnLCBpbWFnZUluZm8pO1xuXHR9XG59LCA1MCk7XG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCAoKSA9PiB7XG5cdHNjcm9sbERpc2FibGVkID0gdHJ1ZTtcblx0dXBkYXRlSW1hZ2VTaXplcygpO1xufSwgdHJ1ZSk7XG5cbndpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgZXZlbnQgPT4ge1xuXHRpZiAoZXZlbnQuZGF0YS5zb3VyY2UgIT09IHNldHRpbmdzLnNvdXJjZSkge1xuXHRcdHJldHVybjtcblx0fVxuXG5cdHN3aXRjaCAoZXZlbnQuZGF0YS50eXBlKSB7XG5cdFx0Y2FzZSAnb25EaWRDaGFuZ2VUZXh0RWRpdG9yU2VsZWN0aW9uJzpcblx0XHRcdG1hcmtlci5vbkRpZENoYW5nZVRleHRFZGl0b3JTZWxlY3Rpb24oZXZlbnQuZGF0YS5saW5lKTtcblx0XHRcdGJyZWFrO1xuXG5cdFx0Y2FzZSAndXBkYXRlVmlldyc6XG5cdFx0XHRvblVwZGF0ZVZpZXcoZXZlbnQuZGF0YS5saW5lLCBzZXR0aW5ncyk7XG5cdFx0XHRicmVhaztcblx0fVxufSwgZmFsc2UpO1xuXG5kb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdkYmxjbGljaycsIGV2ZW50ID0+IHtcblx0aWYgKCFzZXR0aW5ncy5kb3VibGVDbGlja1RvU3dpdGNoVG9FZGl0b3IpIHtcblx0XHRyZXR1cm47XG5cdH1cblxuXHQvLyBJZ25vcmUgY2xpY2tzIG9uIGxpbmtzXG5cdGZvciAobGV0IG5vZGUgPSBldmVudC50YXJnZXQgYXMgSFRNTEVsZW1lbnQ7IG5vZGU7IG5vZGUgPSBub2RlLnBhcmVudE5vZGUgYXMgSFRNTEVsZW1lbnQpIHtcblx0XHRpZiAobm9kZS50YWdOYW1lID09PSAnQScpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdH1cblxuXHRjb25zdCBvZmZzZXQgPSBldmVudC5wYWdlWTtcblx0Y29uc3QgbGluZSA9IGdldEVkaXRvckxpbmVOdW1iZXJGb3JQYWdlT2Zmc2V0KG9mZnNldCk7XG5cdGlmICh0eXBlb2YgbGluZSA9PT0gJ251bWJlcicgJiYgIWlzTmFOKGxpbmUpKSB7XG5cdFx0bWVzc2FnaW5nLnBvc3RNZXNzYWdlKCdkaWRDbGljaycsIHsgbGluZTogTWF0aC5mbG9vcihsaW5lKSB9KTtcblx0fVxufSk7XG5cbmRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgZXZlbnQgPT4ge1xuXHRpZiAoIWV2ZW50KSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0bGV0IG5vZGU6IGFueSA9IGV2ZW50LnRhcmdldDtcblx0d2hpbGUgKG5vZGUpIHtcblx0XHRpZiAobm9kZS50YWdOYW1lICYmIG5vZGUudGFnTmFtZSA9PT0gJ0EnICYmIG5vZGUuaHJlZikge1xuXHRcdFx0aWYgKG5vZGUuZ2V0QXR0cmlidXRlKCdocmVmJykuc3RhcnRzV2l0aCgnIycpKSB7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdFx0aWYgKG5vZGUuaHJlZi5zdGFydHNXaXRoKCdmaWxlOi8vJykgfHwgbm9kZS5ocmVmLnN0YXJ0c1dpdGgoJ3ZzY29kZS1yZXNvdXJjZTonKSkge1xuXHRcdFx0XHRjb25zdCBbcGF0aCwgZnJhZ21lbnRdID0gbm9kZS5ocmVmLnJlcGxhY2UoL14oZmlsZTpcXC9cXC98dnNjb2RlLXJlc291cmNlOikvaSwgJycpLnNwbGl0KCcjJyk7XG5cdFx0XHRcdG1lc3NhZ2luZy5wb3N0TWVzc2FnZSgnY2xpY2tMaW5rJywgeyBwYXRoLCBmcmFnbWVudCB9KTtcblx0XHRcdFx0ZXZlbnQucHJldmVudERlZmF1bHQoKTtcblx0XHRcdFx0ZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdFx0YnJlYWs7XG5cdFx0fVxuXHRcdG5vZGUgPSBub2RlLnBhcmVudE5vZGU7XG5cdH1cbn0sIHRydWUpO1xuXG5pZiAoc2V0dGluZ3Muc2Nyb2xsRWRpdG9yV2l0aFByZXZpZXcpIHtcblx0d2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Njcm9sbCcsIHRocm90dGxlKCgpID0+IHtcblx0XHRpZiAoc2Nyb2xsRGlzYWJsZWQpIHtcblx0XHRcdHNjcm9sbERpc2FibGVkID0gZmFsc2U7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGNvbnN0IGxpbmUgPSBnZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldCh3aW5kb3cuc2Nyb2xsWSk7XG5cdFx0XHRpZiAodHlwZW9mIGxpbmUgPT09ICdudW1iZXInICYmICFpc05hTihsaW5lKSkge1xuXHRcdFx0XHRtZXNzYWdpbmcucG9zdE1lc3NhZ2UoJ3JldmVhbExpbmUnLCB7IGxpbmUgfSk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9LCA1MCkpO1xufSIsIi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4gKiAgQ29weXJpZ2h0IChjKSBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4gKiAgTGljZW5zZWQgdW5kZXIgdGhlIE1JVCBMaWNlbnNlLiBTZWUgTGljZW5zZS50eHQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cbiAqLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qL1xuXG5pbXBvcnQgeyBnZXRTZXR0aW5ncyB9IGZyb20gJy4vc2V0dGluZ3MnO1xuXG5leHBvcnQgaW50ZXJmYWNlIE1lc3NhZ2VQb3N0ZXIge1xuXHQvKipcblx0ICogUG9zdCBhIG1lc3NhZ2UgdG8gdGhlIG1hcmtkb3duIGV4dGVuc2lvblxuXHQgKi9cblx0cG9zdE1lc3NhZ2UodHlwZTogc3RyaW5nLCBib2R5OiBvYmplY3QpOiB2b2lkO1xufVxuXG5leHBvcnQgY29uc3QgY3JlYXRlUG9zdGVyRm9yVnNDb2RlID0gKHZzY29kZTogYW55KSA9PiB7XG5cdHJldHVybiBuZXcgY2xhc3MgaW1wbGVtZW50cyBNZXNzYWdlUG9zdGVyIHtcblx0XHRwb3N0TWVzc2FnZSh0eXBlOiBzdHJpbmcsIGJvZHk6IG9iamVjdCk6IHZvaWQge1xuXHRcdFx0dnNjb2RlLnBvc3RNZXNzYWdlKHtcblx0XHRcdFx0dHlwZSxcblx0XHRcdFx0c291cmNlOiBnZXRTZXR0aW5ncygpLnNvdXJjZSxcblx0XHRcdFx0Ym9keVxuXHRcdFx0fSk7XG5cdFx0fVxuXHR9O1xufTtcblxuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmltcG9ydCB7IGdldFNldHRpbmdzIH0gZnJvbSAnLi9zZXR0aW5ncyc7XG5cblxuZnVuY3Rpb24gY2xhbXAobWluOiBudW1iZXIsIG1heDogbnVtYmVyLCB2YWx1ZTogbnVtYmVyKSB7XG5cdHJldHVybiBNYXRoLm1pbihtYXgsIE1hdGgubWF4KG1pbiwgdmFsdWUpKTtcbn1cblxuZnVuY3Rpb24gY2xhbXBMaW5lKGxpbmU6IG51bWJlcikge1xuXHRyZXR1cm4gY2xhbXAoMCwgZ2V0U2V0dGluZ3MoKS5saW5lQ291bnQgLSAxLCBsaW5lKTtcbn1cblxuXG5leHBvcnQgaW50ZXJmYWNlIENvZGVMaW5lRWxlbWVudCB7XG5cdGVsZW1lbnQ6IEhUTUxFbGVtZW50O1xuXHRsaW5lOiBudW1iZXI7XG59XG5cbmNvbnN0IGdldENvZGVMaW5lRWxlbWVudHMgPSAoKCkgPT4ge1xuXHRsZXQgZWxlbWVudHM6IENvZGVMaW5lRWxlbWVudFtdO1xuXHRyZXR1cm4gKCkgPT4ge1xuXHRcdGlmICghZWxlbWVudHMpIHtcblx0XHRcdGVsZW1lbnRzID0gKFt7IGVsZW1lbnQ6IGRvY3VtZW50LmJvZHksIGxpbmU6IDAgfV0pLmNvbmNhdChBcnJheS5wcm90b3R5cGUubWFwLmNhbGwoXG5cdFx0XHRcdGRvY3VtZW50LmdldEVsZW1lbnRzQnlDbGFzc05hbWUoJ2NvZGUtbGluZScpLFxuXHRcdFx0XHQoZWxlbWVudDogYW55KSA9PiB7XG5cdFx0XHRcdFx0Y29uc3QgbGluZSA9ICtlbGVtZW50LmdldEF0dHJpYnV0ZSgnZGF0YS1saW5lJyk7XG5cdFx0XHRcdFx0cmV0dXJuIHsgZWxlbWVudCwgbGluZSB9O1xuXHRcdFx0XHR9KVxuXHRcdFx0XHQuZmlsdGVyKCh4OiBhbnkpID0+ICFpc05hTih4LmxpbmUpKSk7XG5cdFx0fVxuXHRcdHJldHVybiBlbGVtZW50cztcblx0fTtcbn0pKCk7XG5cbi8qKlxuICogRmluZCB0aGUgaHRtbCBlbGVtZW50cyB0aGF0IG1hcCB0byBhIHNwZWNpZmljIHRhcmdldCBsaW5lIGluIHRoZSBlZGl0b3IuXG4gKlxuICogSWYgYW4gZXhhY3QgbWF0Y2gsIHJldHVybnMgYSBzaW5nbGUgZWxlbWVudC4gSWYgdGhlIGxpbmUgaXMgYmV0d2VlbiBlbGVtZW50cyxcbiAqIHJldHVybnMgdGhlIGVsZW1lbnQgcHJpb3IgdG8gYW5kIHRoZSBlbGVtZW50IGFmdGVyIHRoZSBnaXZlbiBsaW5lLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0RWxlbWVudHNGb3JTb3VyY2VMaW5lKHRhcmdldExpbmU6IG51bWJlcik6IHsgcHJldmlvdXM6IENvZGVMaW5lRWxlbWVudDsgbmV4dD86IENvZGVMaW5lRWxlbWVudDsgfSB7XG5cdGNvbnN0IGxpbmVOdW1iZXIgPSBNYXRoLmZsb29yKHRhcmdldExpbmUpO1xuXHRjb25zdCBsaW5lcyA9IGdldENvZGVMaW5lRWxlbWVudHMoKTtcblx0bGV0IHByZXZpb3VzID0gbGluZXNbMF0gfHwgbnVsbDtcblx0Zm9yIChjb25zdCBlbnRyeSBvZiBsaW5lcykge1xuXHRcdGlmIChlbnRyeS5saW5lID09PSBsaW5lTnVtYmVyKSB7XG5cdFx0XHRyZXR1cm4geyBwcmV2aW91czogZW50cnksIG5leHQ6IHVuZGVmaW5lZCB9O1xuXHRcdH0gZWxzZSBpZiAoZW50cnkubGluZSA+IGxpbmVOdW1iZXIpIHtcblx0XHRcdHJldHVybiB7IHByZXZpb3VzLCBuZXh0OiBlbnRyeSB9O1xuXHRcdH1cblx0XHRwcmV2aW91cyA9IGVudHJ5O1xuXHR9XG5cdHJldHVybiB7IHByZXZpb3VzIH07XG59XG5cbi8qKlxuICogRmluZCB0aGUgaHRtbCBlbGVtZW50cyB0aGF0IGFyZSBhdCBhIHNwZWNpZmljIHBpeGVsIG9mZnNldCBvbiB0aGUgcGFnZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldExpbmVFbGVtZW50c0F0UGFnZU9mZnNldChvZmZzZXQ6IG51bWJlcik6IHsgcHJldmlvdXM6IENvZGVMaW5lRWxlbWVudDsgbmV4dD86IENvZGVMaW5lRWxlbWVudDsgfSB7XG5cdGNvbnN0IGxpbmVzID0gZ2V0Q29kZUxpbmVFbGVtZW50cygpO1xuXHRjb25zdCBwb3NpdGlvbiA9IG9mZnNldCAtIHdpbmRvdy5zY3JvbGxZO1xuXHRsZXQgbG8gPSAtMTtcblx0bGV0IGhpID0gbGluZXMubGVuZ3RoIC0gMTtcblx0d2hpbGUgKGxvICsgMSA8IGhpKSB7XG5cdFx0Y29uc3QgbWlkID0gTWF0aC5mbG9vcigobG8gKyBoaSkgLyAyKTtcblx0XHRjb25zdCBib3VuZHMgPSBsaW5lc1ttaWRdLmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG5cdFx0aWYgKGJvdW5kcy50b3AgKyBib3VuZHMuaGVpZ2h0ID49IHBvc2l0aW9uKSB7XG5cdFx0XHRoaSA9IG1pZDtcblx0XHR9XG5cdFx0ZWxzZSB7XG5cdFx0XHRsbyA9IG1pZDtcblx0XHR9XG5cdH1cblx0Y29uc3QgaGlFbGVtZW50ID0gbGluZXNbaGldO1xuXHRjb25zdCBoaUJvdW5kcyA9IGhpRWxlbWVudC5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuXHRpZiAoaGkgPj0gMSAmJiBoaUJvdW5kcy50b3AgPiBwb3NpdGlvbikge1xuXHRcdGNvbnN0IGxvRWxlbWVudCA9IGxpbmVzW2xvXTtcblx0XHRyZXR1cm4geyBwcmV2aW91czogbG9FbGVtZW50LCBuZXh0OiBoaUVsZW1lbnQgfTtcblx0fVxuXHRyZXR1cm4geyBwcmV2aW91czogaGlFbGVtZW50IH07XG59XG5cbi8qKlxuICogQXR0ZW1wdCB0byByZXZlYWwgdGhlIGVsZW1lbnQgZm9yIGEgc291cmNlIGxpbmUgaW4gdGhlIGVkaXRvci5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNjcm9sbFRvUmV2ZWFsU291cmNlTGluZShsaW5lOiBudW1iZXIpIHtcblx0aWYgKCFnZXRTZXR0aW5ncygpLnNjcm9sbFByZXZpZXdXaXRoRWRpdG9yKSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0aWYgKGxpbmUgPD0gMCkge1xuXHRcdHdpbmRvdy5zY3JvbGwod2luZG93LnNjcm9sbFgsIDApO1xuXHRcdHJldHVybjtcblx0fVxuXG5cdGNvbnN0IHsgcHJldmlvdXMsIG5leHQgfSA9IGdldEVsZW1lbnRzRm9yU291cmNlTGluZShsaW5lKTtcblx0aWYgKCFwcmV2aW91cykge1xuXHRcdHJldHVybjtcblx0fVxuXHRsZXQgc2Nyb2xsVG8gPSAwO1xuXHRjb25zdCByZWN0ID0gcHJldmlvdXMuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblx0Y29uc3QgcHJldmlvdXNUb3AgPSByZWN0LnRvcDtcblx0aWYgKG5leHQgJiYgbmV4dC5saW5lICE9PSBwcmV2aW91cy5saW5lKSB7XG5cdFx0Ly8gQmV0d2VlbiB0d28gZWxlbWVudHMuIEdvIHRvIHBlcmNlbnRhZ2Ugb2Zmc2V0IGJldHdlZW4gdGhlbS5cblx0XHRjb25zdCBiZXR3ZWVuUHJvZ3Jlc3MgPSAobGluZSAtIHByZXZpb3VzLmxpbmUpIC8gKG5leHQubGluZSAtIHByZXZpb3VzLmxpbmUpO1xuXHRcdGNvbnN0IGVsZW1lbnRPZmZzZXQgPSBuZXh0LmVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkudG9wIC0gcHJldmlvdXNUb3A7XG5cdFx0c2Nyb2xsVG8gPSBwcmV2aW91c1RvcCArIGJldHdlZW5Qcm9ncmVzcyAqIGVsZW1lbnRPZmZzZXQ7XG5cdH0gZWxzZSB7XG5cdFx0c2Nyb2xsVG8gPSBwcmV2aW91c1RvcDtcblx0fVxuXHR3aW5kb3cuc2Nyb2xsKHdpbmRvdy5zY3JvbGxYLCBNYXRoLm1heCgxLCB3aW5kb3cuc2Nyb2xsWSArIHNjcm9sbFRvKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRFZGl0b3JMaW5lTnVtYmVyRm9yUGFnZU9mZnNldChvZmZzZXQ6IG51bWJlcikge1xuXHRjb25zdCB7IHByZXZpb3VzLCBuZXh0IH0gPSBnZXRMaW5lRWxlbWVudHNBdFBhZ2VPZmZzZXQob2Zmc2V0KTtcblx0aWYgKHByZXZpb3VzKSB7XG5cdFx0Y29uc3QgcHJldmlvdXNCb3VuZHMgPSBwcmV2aW91cy5lbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuXHRcdGNvbnN0IG9mZnNldEZyb21QcmV2aW91cyA9IChvZmZzZXQgLSB3aW5kb3cuc2Nyb2xsWSAtIHByZXZpb3VzQm91bmRzLnRvcCk7XG5cdFx0aWYgKG5leHQpIHtcblx0XHRcdGNvbnN0IHByb2dyZXNzQmV0d2VlbkVsZW1lbnRzID0gb2Zmc2V0RnJvbVByZXZpb3VzIC8gKG5leHQuZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKS50b3AgLSBwcmV2aW91c0JvdW5kcy50b3ApO1xuXHRcdFx0Y29uc3QgbGluZSA9IHByZXZpb3VzLmxpbmUgKyBwcm9ncmVzc0JldHdlZW5FbGVtZW50cyAqIChuZXh0LmxpbmUgLSBwcmV2aW91cy5saW5lKTtcblx0XHRcdHJldHVybiBjbGFtcExpbmUobGluZSk7XG5cdFx0fVxuXHRcdGVsc2Uge1xuXHRcdFx0Y29uc3QgcHJvZ3Jlc3NXaXRoaW5FbGVtZW50ID0gb2Zmc2V0RnJvbVByZXZpb3VzIC8gKHByZXZpb3VzQm91bmRzLmhlaWdodCk7XG5cdFx0XHRjb25zdCBsaW5lID0gcHJldmlvdXMubGluZSArIHByb2dyZXNzV2l0aGluRWxlbWVudDtcblx0XHRcdHJldHVybiBjbGFtcExpbmUobGluZSk7XG5cdFx0fVxuXHR9XG5cdHJldHVybiBudWxsO1xufVxuIiwiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAqICBDb3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqICBMaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSBMaWNlbnNlLnR4dCBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxuICotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovXG5cbmV4cG9ydCBpbnRlcmZhY2UgUHJldmlld1NldHRpbmdzIHtcblx0c291cmNlOiBzdHJpbmc7XG5cdGxpbmU6IG51bWJlcjtcblx0bGluZUNvdW50OiBudW1iZXI7XG5cdHNjcm9sbFByZXZpZXdXaXRoRWRpdG9yPzogYm9vbGVhbjtcblx0c2Nyb2xsRWRpdG9yV2l0aFByZXZpZXc6IGJvb2xlYW47XG5cdGRpc2FibGVTZWN1cml0eVdhcm5pbmdzOiBib29sZWFuO1xuXHRkb3VibGVDbGlja1RvU3dpdGNoVG9FZGl0b3I6IGJvb2xlYW47XG59XG5cbmxldCBjYWNoZWRTZXR0aW5nczogUHJldmlld1NldHRpbmdzIHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RGF0YShrZXk6IHN0cmluZyk6IFByZXZpZXdTZXR0aW5ncyB7XG5cdGNvbnN0IGVsZW1lbnQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndnNjb2RlLW1hcmtkb3duLXByZXZpZXctZGF0YScpO1xuXHRpZiAoZWxlbWVudCkge1xuXHRcdGNvbnN0IGRhdGEgPSBlbGVtZW50LmdldEF0dHJpYnV0ZShrZXkpO1xuXHRcdGlmIChkYXRhKSB7XG5cdFx0XHRyZXR1cm4gSlNPTi5wYXJzZShkYXRhKTtcblx0XHR9XG5cdH1cblxuXHR0aHJvdyBuZXcgRXJyb3IoYENvdWxkIG5vdCBsb2FkIGRhdGEgZm9yICR7a2V5fWApO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2V0dGluZ3MoKTogUHJldmlld1NldHRpbmdzIHtcblx0aWYgKGNhY2hlZFNldHRpbmdzKSB7XG5cdFx0cmV0dXJuIGNhY2hlZFNldHRpbmdzO1xuXHR9XG5cblx0Y2FjaGVkU2V0dGluZ3MgPSBnZXREYXRhKCdkYXRhLXNldHRpbmdzJyk7XG5cdGlmIChjYWNoZWRTZXR0aW5ncykge1xuXHRcdHJldHVybiBjYWNoZWRTZXR0aW5ncztcblx0fVxuXG5cdHRocm93IG5ldyBFcnJvcignQ291bGQgbm90IGxvYWQgc2V0dGluZ3MnKTtcbn1cbiJdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file diff --git a/extensions/markdown-language-features/preview-src/scroll-sync.ts b/extensions/markdown-language-features/preview-src/scroll-sync.ts index dacc14a86..3bd2939e0 100644 --- a/extensions/markdown-language-features/preview-src/scroll-sync.ts +++ b/extensions/markdown-language-features/preview-src/scroll-sync.ts @@ -24,13 +24,13 @@ const getCodeLineElements = (() => { let elements: CodeLineElement[]; return () => { if (!elements) { - elements = Array.prototype.map.call( + elements = ([{ element: document.body, line: 0 }]).concat(Array.prototype.map.call( document.getElementsByClassName('code-line'), (element: any) => { const line = +element.getAttribute('data-line'); return { element, line }; }) - .filter((x: any) => !isNaN(x.line)); + .filter((x: any) => !isNaN(x.line))); } return elements; }; @@ -49,8 +49,7 @@ export function getElementsForSourceLine(targetLine: number): { previous: CodeLi for (const entry of lines) { if (entry.line === lineNumber) { return { previous: entry, next: undefined }; - } - else if (entry.line > lineNumber) { + } else if (entry.line > lineNumber) { return { previous, next: entry }; } previous = entry; @@ -89,22 +88,31 @@ export function getLineElementsAtPageOffset(offset: number): { previous: CodeLin * Attempt to reveal the element for a source line in the editor. */ export function scrollToRevealSourceLine(line: number) { + if (!getSettings().scrollPreviewWithEditor) { + return; + } + + if (line <= 0) { + window.scroll(window.scrollX, 0); + return; + } + const { previous, next } = getElementsForSourceLine(line); - if (previous && getSettings().scrollPreviewWithEditor) { - 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 { - scrollTo = previousTop; - } - window.scroll(0, Math.max(1, window.scrollY + scrollTo)); + 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 { + scrollTo = previousTop; } + window.scroll(window.scrollX, Math.max(1, window.scrollY + scrollTo)); } export function getEditorLineNumberForPageOffset(offset: number) { diff --git a/extensions/markdown-language-features/src/features/foldingProvider.ts b/extensions/markdown-language-features/src/features/foldingProvider.ts index 2de47bd78..594baaadb 100644 --- a/extensions/markdown-language-features/src/features/foldingProvider.ts +++ b/extensions/markdown-language-features/src/features/foldingProvider.ts @@ -53,7 +53,7 @@ export default class MarkdownFoldingProvider implements vscode.FoldingRangeProvi this.getRegions(document), this.getHeaderFoldingRanges(document), this.getBlockFoldingRanges(document)]); - return [].concat.apply([], foldables).slice(0, rangeLimit); + return ([] as vscode.FoldingRange[]).concat.apply([], foldables).slice(0, rangeLimit); } private async getHeaderFoldingRanges(document: vscode.TextDocument) { diff --git a/extensions/markdown-language-features/src/util/links.ts b/extensions/markdown-language-features/src/util/links.ts index e1fc274d0..1afcbff83 100644 --- a/extensions/markdown-language-features/src/util/links.ts +++ b/extensions/markdown-language-features/src/util/links.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; -const knownSchemes = ['http:', 'https:', 'file:', 'mailto:']; +const knownSchemes = ['http:', 'https:', 'file:', 'mailto:', 'data:', 'vscode-resource:']; export function getUriForLinkWithKnownExternalScheme( link: string, diff --git a/extensions/merge-conflict/resources/icons/merge-conflict.png b/extensions/merge-conflict/resources/icons/merge-conflict.png index ec6930bcf..3cf8d7b80 100644 Binary files a/extensions/merge-conflict/resources/icons/merge-conflict.png and b/extensions/merge-conflict/resources/icons/merge-conflict.png differ diff --git a/extensions/npm/images/npm_icon.png b/extensions/npm/images/npm_icon.png index f7f18b560..9fb9cf100 100644 Binary files a/extensions/npm/images/npm_icon.png and b/extensions/npm/images/npm_icon.png differ diff --git a/extensions/npm/src/main.ts b/extensions/npm/src/main.ts index bc40b996a..5903d6ebb 100644 --- a/extensions/npm/src/main.ts +++ b/extensions/npm/src/main.ts @@ -11,9 +11,11 @@ import { invalidateTasksCache, NpmTaskProvider } from './tasks'; import { invalidateHoverScriptsCache, NpmScriptHoverProvider } from './scriptHover'; import { runSelectedScript } from './commands'; +let treeDataProvider: NpmScriptsTreeDataProvider | undefined; + export async function activate(context: vscode.ExtensionContext): Promise { registerTaskProvider(context); - const treeDataProvider = registerExplorer(context); + treeDataProvider = registerExplorer(context); registerHoverProvider(context); configureHttpRequest(); @@ -46,6 +48,9 @@ function registerTaskProvider(context: vscode.ExtensionContext): vscode.Disposab function invalidateScriptCaches() { invalidateHoverScriptsCache(); invalidateTasksCache(); + if (treeDataProvider) { + treeDataProvider.refresh(); + } } if (vscode.workspace.workspaceFolders) { diff --git a/extensions/npm/src/npmView.ts b/extensions/npm/src/npmView.ts index 2a7fd2956..9bcd272a9 100644 --- a/extensions/npm/src/npmView.ts +++ b/extensions/npm/src/npmView.ts @@ -108,6 +108,13 @@ class NpmScript extends TreeItem { dark: context.asAbsolutePath(path.join('resources', 'dark', 'script.svg')) }; } + + let uri = getPackageJsonUriFromTask(task); + getScripts(uri!).then(scripts => { + if (scripts && scripts[task.definition['script']]) { + this.tooltip = scripts[task.definition['script']]; + } + }); } getFolder(): WorkspaceFolder { diff --git a/extensions/objective-c/.vscodeignore b/extensions/objective-c/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/objective-c/.vscodeignore +++ b/extensions/objective-c/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/objective-c/OSSREADME.json b/extensions/objective-c/OSSREADME.json deleted file mode 100644 index a67e4bcec..000000000 --- a/extensions/objective-c/OSSREADME.json +++ /dev/null @@ -1,8 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "atom/language-objective-c", - "version": "0.0.0", - "license": "MIT", - "repositoryURL": "https://github.com/atom/language-objective-c", - "description": "The files syntaxes/objective-c.tmLanguage.json and syntaxes/objective-c++.tmLanguage.json were derived from the Atom package https://github.com/atom/language-objective-c which was originally converted from the TextMate bundle https://github.com/textmate/objective-c.tmbundle." -}] diff --git a/extensions/objective-c/cgmanifest.json b/extensions/objective-c/cgmanifest.json new file mode 100644 index 000000000..58d211c8f --- /dev/null +++ b/extensions/objective-c/cgmanifest.json @@ -0,0 +1,18 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "atom/language-objective-c", + "repositoryUrl": "https://github.com/atom/language-objective-c", + "commitHash": "0727e04544f3414c1c339cf15a39a05ea3938cb4" + } + }, + "license": "MIT", + "description": "The files syntaxes/objective-c.tmLanguage.json and syntaxes/objective-c++.tmLanguage.json were derived from the Atom package https://github.com/atom/language-objective-c which was originally converted from the TextMate bundle https://github.com/textmate/objective-c.tmbundle.", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/package.json b/extensions/package.json index 10a06e899..af0de2f61 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "3.1.3" + "typescript": "3.2.0-rc" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/perl/.vscodeignore b/extensions/perl/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/perl/.vscodeignore +++ b/extensions/perl/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/perl/OSSREADME.json b/extensions/perl/OSSREADME.json deleted file mode 100644 index f7bae4f23..000000000 --- a/extensions/perl/OSSREADME.json +++ /dev/null @@ -1,23 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "textmate/perl.tmbundle", - "version": "0.0.0", - "license": "TextMate Bundle License", - "repositoryURL": "https://github.com/textmate/perl.tmbundle", - "description": "The files syntaxes/perl.tmLanguage.json and syntaxes/perl6.tmLanguage.json were derived from Perl.plist and Perl 6.tmLanguage from https://github.com/textmate/perl.tmbundle.", - "licenseDetail": [ - "Copyright (c) textmate-perl.tmbundle project authors", - "", - "If not otherwise specified (see below), files in this repository fall under the following license:", - "", - "Permission to copy, use, modify, sell and distribute this", - "software is granted. This software is provided \"as is\" without", - "express or implied warranty, and with no claim as to its", - "suitability for any purpose.", - "", - "An exception is made for files in readable text which contain their own license information,", - "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", - "to the base-name name of the original file, and an extension of txt, html, or similar. For example", - "\"tidy\" is accompanied by \"tidy-license.txt\"." - ] -}] diff --git a/extensions/perl/cgmanifest.json b/extensions/perl/cgmanifest.json new file mode 100644 index 000000000..6338921e7 --- /dev/null +++ b/extensions/perl/cgmanifest.json @@ -0,0 +1,33 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "textmate/perl.tmbundle", + "repositoryUrl": "https://github.com/textmate/perl.tmbundle", + "commitHash": "80826abe75250286c2a1a07958e50e8551d3f50c" + } + }, + "licenseDetail": [ + "Copyright (c) textmate-perl.tmbundle project authors", + "", + "If not otherwise specified (see below), files in this repository fall under the following license:", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose.", + "", + "An exception is made for files in readable text which contain their own license information,", + "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added", + "to the base-name name of the original file, and an extension of txt, html, or similar. For example", + "\"tidy\" is accompanied by \"tidy-license.txt\"." + ], + "license": "TextMate Bundle License", + "description": "The files syntaxes/perl.tmLanguage.json and syntaxes/perl6.tmLanguage.json were derived from Perl.plist and Perl 6.tmLanguage from https://github.com/textmate/perl.tmbundle.", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/php-language-features/icons/logo.png b/extensions/php-language-features/icons/logo.png index 80a219c56..332e417a8 100644 Binary files a/extensions/php-language-features/icons/logo.png and b/extensions/php-language-features/icons/logo.png differ diff --git a/extensions/php/.vscode/tasks.json b/extensions/php/.vscode/tasks.json index 9e5593ade..390a93a3a 100644 --- a/extensions/php/.vscode/tasks.json +++ b/extensions/php/.vscode/tasks.json @@ -1,30 +1,11 @@ -// Available variables which can be used inside of strings. -// ${workspaceFolder}: the root folder of the team -// ${file}: the current opened file -// ${fileBasename}: the current opened file's basename -// ${fileDirname}: the current opened file's dirname -// ${fileExtname}: the current opened file's extension -// ${cwd}: the current working directory of the spawned process - -// A task runner that calls a custom npm script that compiles the extension. { - "version": "0.1.0", - - // we want to run npm + "version": "2.0.0", "command": "npm", - - // the command is a shell script - "isShellCommand": true, - - // show the output window only if unrecognized errors occur. - "showOutput": "silent", - - // we run the custom script "compile" as defined in package.json + "type": "shell", + "presentation": { + "reveal": "silent" + }, "args": ["run", "compile"], - - // The tsc compiler is started in watching mode - "isWatching": true, - - // use the standard tsc in watch mode problem matcher to find compile problems in the output. + "isBackground": true, "problemMatcher": "$tsc-watch" -} \ No newline at end of file +} diff --git a/extensions/php/.vscodeignore b/extensions/php/.vscodeignore index a377c213f..98efc0327 100644 --- a/extensions/php/.vscodeignore +++ b/extensions/php/.vscodeignore @@ -3,4 +3,4 @@ build/** out/test/** src/** tsconfig.json -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/php/OSSREADME.json b/extensions/php/OSSREADME.json deleted file mode 100644 index 132d1967f..000000000 --- a/extensions/php/OSSREADME.json +++ /dev/null @@ -1,8 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "language-php", - "version": "0.0.0", - "license": "MIT", - "repositoryURL": "https://github.com/atom/language-php", - "description": "The files snippets/php.json & syntaxes/php.tmLanguage.json were derived from the Atom package https://atom.io/packages/language-php which was originally converted from the PHP TextMate bundle https://github.com/textmate/php.tmbundle." -}] diff --git a/extensions/php/cgmanifest.json b/extensions/php/cgmanifest.json new file mode 100644 index 000000000..63002fc56 --- /dev/null +++ b/extensions/php/cgmanifest.json @@ -0,0 +1,17 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "language-php", + "repositoryUrl": "https://github.com/atom/language-php", + "commitHash": "b6c5e83016b52311cdc622c2579462861ee91587" + } + }, + "license": "MIT", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/php/syntaxes/html.tmLanguage.json b/extensions/php/syntaxes/html.tmLanguage.json index 59db289a2..809b11205 100644 --- a/extensions/php/syntaxes/html.tmLanguage.json +++ b/extensions/php/syntaxes/html.tmLanguage.json @@ -4,10 +4,17 @@ "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/atom/language-php/commit/72bfa9592e689fdcb70562ff7d882ad5308e79f7", + "version": "https://github.com/atom/language-php/commit/b6c5e83016b52311cdc622c2579462861ee91587", "name": "PHP", "scopeName": "text.html.php", "injections": { + "text.html.php - (meta.embedded | meta.tag), L:((text.html.php meta.tag) - (meta.embedded.block.php | meta.embedded.line.php)), L:(source.js.embedded.html - (meta.embedded.block.php | meta.embedded.line.php))": { + "patterns": [ + { + "include": "#php-tag" + } + ] + }, "L:source.php string.quoted.single.sql.php source.sql.embedded.php": { "patterns": [ { @@ -108,13 +115,6 @@ "include": "source.php#interpolation_double_quoted" } ] - }, - "text.html.php - (meta.embedded | meta.tag), L:text.html.php meta.tag, L:text.html.php source.js": { - "patterns": [ - { - "include": "#php-tag" - } - ] } }, "patterns": [ diff --git a/extensions/php/test/colorize-results/issue-28354_php.json b/extensions/php/test/colorize-results/issue-28354_php.json index 12e439430..329c9d6f9 100644 --- a/extensions/php/test/colorize-results/issue-28354_php.json +++ b/extensions/php/test/colorize-results/issue-28354_php.json @@ -66,19 +66,41 @@ } }, { - "c": "", + "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js", "r": { - "dark_plus": "meta.embedded: #D4D4D4", - "light_plus": "meta.embedded: #000000", + "dark_plus": "meta.object-literal.key: #9CDCFE", + "light_plus": "meta.object-literal.key: #001080", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "meta.embedded: #FFFFFF" + "hc_black": "meta.object-literal.key: #9CDCFE" } }, { - "c": " ", - "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php source.php", + "c": " ...", + "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js", "r": { - "dark_plus": "meta.embedded: #D4D4D4", - "light_plus": "meta.embedded: #000000", + "dark_plus": "meta.object-literal.key: #9CDCFE", + "light_plus": "meta.object-literal.key: #001080", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "meta.embedded: #FFFFFF" - } - }, - { - "c": "?", - "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php punctuation.section.embedded.end.php source.php", - "r": { - "dark_plus": "punctuation.section.embedded.end.php: #569CD6", - "light_plus": "punctuation.section.embedded.end.php: #800000", - "dark_vs": "punctuation.section.embedded.end.php: #569CD6", - "light_vs": "punctuation.section.embedded.end.php: #800000", - "hc_black": "punctuation.section.embedded: #569CD6" + "hc_black": "meta.object-literal.key: #9CDCFE" } }, { - "c": ">", - "t": "text.html.php meta.embedded.block.html source.js meta.embedded.block.php punctuation.section.embedded.end.php", + "c": "", + "t": "text.html.php meta.embedded.block.html source.js meta.objectliteral.js meta.object.member.js meta.object-literal.key.js", "r": { - "dark_plus": "punctuation.section.embedded.end.php: #569CD6", - "light_plus": "punctuation.section.embedded.end.php: #800000", - "dark_vs": "punctuation.section.embedded.end.php: #569CD6", - "light_vs": "punctuation.section.embedded.end.php: #800000", - "hc_black": "punctuation.section.embedded: #569CD6" - } - }, - { - "c": " ", - "t": "text.html.php meta.embedded.block.html source.js", - "r": { - "dark_plus": "meta.embedded: #D4D4D4", - "light_plus": "meta.embedded: #000000", + "dark_plus": "meta.object-literal.key: #9CDCFE", + "light_plus": "meta.object-literal.key: #001080", "dark_vs": "meta.embedded: #D4D4D4", "light_vs": "meta.embedded: #000000", - "hc_black": "meta.embedded: #FFFFFF" - } - }, - { - "c": "...", - "t": "text.html.php meta.embedded.block.html source.js keyword.operator.spread.js", - "r": { - "dark_plus": "keyword.operator: #D4D4D4", - "light_plus": "keyword.operator: #000000", - "dark_vs": "keyword.operator: #D4D4D4", - "light_vs": "keyword.operator: #000000", - "hc_black": "keyword.operator: #D4D4D4" - } - }, - { - "c": "<", - "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js", - "r": { - "dark_plus": "punctuation.definition.tag: #808080", - "light_plus": "punctuation.definition.tag: #800000", - "dark_vs": "punctuation.definition.tag: #808080", - "light_vs": "punctuation.definition.tag: #800000", - "hc_black": "punctuation.definition.tag: #808080" - } - }, - { - "c": "/", - "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html", - "r": { - "dark_plus": "punctuation.definition.tag: #808080", - "light_plus": "punctuation.definition.tag: #800000", - "dark_vs": "punctuation.definition.tag: #808080", - "light_vs": "punctuation.definition.tag: #800000", - "hc_black": "punctuation.definition.tag: #808080" - } - }, - { - "c": "script", - "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.end.html entity.name.tag.html", - "r": { - "dark_plus": "entity.name.tag: #569CD6", - "light_plus": "entity.name.tag: #800000", - "dark_vs": "entity.name.tag: #569CD6", - "light_vs": "entity.name.tag: #800000", - "hc_black": "entity.name.tag: #569CD6" - } - }, - { - "c": ">", - "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.end.html", - "r": { - "dark_plus": "punctuation.definition.tag: #808080", - "light_plus": "punctuation.definition.tag: #800000", - "dark_vs": "punctuation.definition.tag: #808080", - "light_vs": "punctuation.definition.tag: #800000", - "hc_black": "punctuation.definition.tag: #808080" + "hc_black": "meta.object-literal.key: #9CDCFE" } } ] \ No newline at end of file diff --git a/extensions/powershell/.vscodeignore b/extensions/powershell/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/powershell/.vscodeignore +++ b/extensions/powershell/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/powershell/OSSREADME.json b/extensions/powershell/OSSREADME.json deleted file mode 100644 index b6eb8cf04..000000000 --- a/extensions/powershell/OSSREADME.json +++ /dev/null @@ -1,7 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "PowerShell/EditorSyntax", - "version": "0.0.0", - "license": "MIT", - "repositoryURL": "https://github.com/powershell/editorsyntax" -}] diff --git a/extensions/powershell/cgmanifest.json b/extensions/powershell/cgmanifest.json new file mode 100644 index 000000000..9c01a619c --- /dev/null +++ b/extensions/powershell/cgmanifest.json @@ -0,0 +1,17 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "PowerShell/EditorSyntax", + "repositoryUrl": "https://github.com/powershell/editorsyntax", + "commitHash": "12b7d7257eb493e45a9af0af9094ec0c2a996712" + } + }, + "license": "MIT", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/pug/.vscodeignore b/extensions/pug/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/pug/.vscodeignore +++ b/extensions/pug/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/pug/OSSREADME.json b/extensions/pug/OSSREADME.json deleted file mode 100644 index 37b45142c..000000000 --- a/extensions/pug/OSSREADME.json +++ /dev/null @@ -1,8 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "davidrios/pug-tmbundle", - "version": "0.0.0", - "license": "MIT", - "repositoryURL": "https://github.com/davidrios/pug-tmbundle", - "description": "The file syntaxes/pug.tmLanguage.json was derived from Syntaxes/Pug.JSON-tmLanguage in https://github.com/davidrios/pug-tmbundle." -}] diff --git a/extensions/pug/cgmanifest.json b/extensions/pug/cgmanifest.json new file mode 100644 index 000000000..cdfc2ded9 --- /dev/null +++ b/extensions/pug/cgmanifest.json @@ -0,0 +1,18 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "davidrios/pug-tmbundle", + "repositoryUrl": "https://github.com/davidrios/pug-tmbundle", + "commitHash": "e67e895f6fb64932aa122e471000fa55d826bff6" + } + }, + "license": "MIT", + "description": "The file syntaxes/pug.tmLanguage.json was derived from Syntaxes/Pug.JSON-tmLanguage in https://github.com/davidrios/pug-tmbundle.", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/python/.vscodeignore b/extensions/python/.vscodeignore index 67b755fdc..89fb2149d 100644 --- a/extensions/python/.vscodeignore +++ b/extensions/python/.vscodeignore @@ -1,4 +1,4 @@ test/** src/** tsconfig.json -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/python/OSSREADME.json b/extensions/python/OSSREADME.json deleted file mode 100644 index cf4449588..000000000 --- a/extensions/python/OSSREADME.json +++ /dev/null @@ -1,9 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[ - { - "name": "MagicStack/MagicPython", - "version": "0.0.0", - "license": "MIT", - "repositoryURL": "https://github.com/MagicStack/MagicPython" - } -] \ No newline at end of file diff --git a/extensions/python/cgmanifest.json b/extensions/python/cgmanifest.json new file mode 100644 index 000000000..4f5dbadab --- /dev/null +++ b/extensions/python/cgmanifest.json @@ -0,0 +1,17 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "MagicStack/MagicPython", + "repositoryUrl": "https://github.com/MagicStack/MagicPython", + "commitHash": "8ff35b3e5fcde471fae62a57ea1ae1c7cd34c9fc" + } + }, + "license": "MIT", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/r/.vscodeignore b/extensions/r/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/r/.vscodeignore +++ b/extensions/r/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/r/OSSREADME.json b/extensions/r/OSSREADME.json deleted file mode 100644 index bd7a0cab2..000000000 --- a/extensions/r/OSSREADME.json +++ /dev/null @@ -1,7 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "Ikuyadeu/vscode-R", - "version": "0.0.0", - "license": "MIT", - "repositoryURL": "https://github.com/Ikuyadeu/vscode-R" -}] diff --git a/extensions/r/cgmanifest.json b/extensions/r/cgmanifest.json new file mode 100644 index 000000000..f520a2df9 --- /dev/null +++ b/extensions/r/cgmanifest.json @@ -0,0 +1,17 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "Ikuyadeu/vscode-R", + "repositoryUrl": "https://github.com/Ikuyadeu/vscode-R", + "commitHash": "1cd3d42a6b2e54276ef2d71fe33bb3fefb1d6cff" + } + }, + "license": "MIT", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/razor/.vscodeignore b/extensions/razor/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/razor/.vscodeignore +++ b/extensions/razor/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/razor/OSSREADME.json b/extensions/razor/OSSREADME.json deleted file mode 100644 index 383a46dd4..000000000 --- a/extensions/razor/OSSREADME.json +++ /dev/null @@ -1,7 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "demyte/language-cshtml", - "version": "0.0.0", - "license": "MIT", - "repositoryURL": "https://github.com/demyte/language-cshtml" -}] diff --git a/extensions/razor/cgmanifest.json b/extensions/razor/cgmanifest.json new file mode 100644 index 000000000..532a4c401 --- /dev/null +++ b/extensions/razor/cgmanifest.json @@ -0,0 +1,17 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "demyte/language-cshtml", + "repositoryUrl": "https://github.com/demyte/language-cshtml", + "commitHash": "e6e54d5a86a28cc1e44609a32aaa10a244cd3f81" + } + }, + "license": "MIT", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/ruby/.vscodeignore b/extensions/ruby/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/ruby/.vscodeignore +++ b/extensions/ruby/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/ruby/OSSREADME.json b/extensions/ruby/OSSREADME.json deleted file mode 100644 index b4af78a03..000000000 --- a/extensions/ruby/OSSREADME.json +++ /dev/null @@ -1,22 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[{ - "name": "textmate/ruby.tmbundle", - "version": "0.0.0", - "license": "TextMate Bundle License", - "repositoryURL": "https://github.com/textmate/ruby.tmbundle", - "licenseDetail": [ - "Copyright (c) textmate-ruby.tmbundle project authors", - "", - "If not otherwise specified (see below), files in this folder fall under the following license: ", - "", - "Permission to copy, use, modify, sell and distribute this", - "software is granted. This software is provided \"as is\" without", - "express or implied warranty, and with no claim as to its", - "suitability for any purpose.", - "", - "An exception is made for files in readable text which contain their own license information, ", - "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added ", - "to the base-name name of the original file, and an extension of txt, html, or similar. For example ", - "\"tidy\" is accompanied by \"tidy-license.txt\"." - ] -}] diff --git a/extensions/ruby/cgmanifest.json b/extensions/ruby/cgmanifest.json new file mode 100644 index 000000000..dd4b29e51 --- /dev/null +++ b/extensions/ruby/cgmanifest.json @@ -0,0 +1,32 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "textmate/ruby.tmbundle", + "repositoryUrl": "https://github.com/textmate/ruby.tmbundle", + "commitHash": "74713556df10fbc7b1f9e99013ab1e34cd836f56" + } + }, + "licenseDetail": [ + "Copyright (c) textmate-ruby.tmbundle project authors", + "", + "If not otherwise specified (see below), files in this folder fall under the following license: ", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose.", + "", + "An exception is made for files in readable text which contain their own license information, ", + "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added ", + "to the base-name name of the original file, and an extension of txt, html, or similar. For example ", + "\"tidy\" is accompanied by \"tidy-license.txt\"." + ], + "license": "TextMate Bundle License", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/rust/.vscodeignore b/extensions/rust/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/rust/.vscodeignore +++ b/extensions/rust/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/rust/OSSREADME.json b/extensions/rust/OSSREADME.json deleted file mode 100644 index 3b68ecda9..000000000 --- a/extensions/rust/OSSREADME.json +++ /dev/null @@ -1,9 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "language-rust", - "version": "0.4.9", - "license": "MIT", - "repositoryURL": "https://github.com/zargony/atom-language-rust", - "description": "The files syntaxes/rust.tmLanguage.json was derived from the Atom package https://atom.io/packages/language-rust." -}] diff --git a/extensions/rust/cgmanifest.json b/extensions/rust/cgmanifest.json new file mode 100644 index 000000000..640d650cf --- /dev/null +++ b/extensions/rust/cgmanifest.json @@ -0,0 +1,18 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "language-rust", + "repositoryUrl": "https://github.com/zargony/atom-language-rust", + "commitHash": "179f449a69182cae4fcdf644d59d842b7e445f89" + } + }, + "license": "MIT", + "description": "The files syntaxes/rust.tmLanguage.json was derived from the Atom package https://atom.io/packages/language-rust.", + "version": "0.4.9" + } + ], + "version": 1 +} diff --git a/extensions/scss/.vscodeignore b/extensions/scss/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/scss/.vscodeignore +++ b/extensions/scss/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/scss/OSSREADME.json b/extensions/scss/OSSREADME.json deleted file mode 100644 index b2d456ed5..000000000 --- a/extensions/scss/OSSREADME.json +++ /dev/null @@ -1,10 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[ - { - "name": "atom/language-sass", - "version": "0.52.0", - "license": "MIT", - "repositoryURL": "https://github.com/atom/language-sass", - "description": "The file syntaxes/scss.json was derived from the Atom package https://github.com/atom/language-sass which was originally converted from the TextMate bundle https://github.com/alexsancho/SASS.tmbundle." - } -] \ No newline at end of file diff --git a/extensions/scss/cgmanifest.json b/extensions/scss/cgmanifest.json new file mode 100644 index 000000000..05499284d --- /dev/null +++ b/extensions/scss/cgmanifest.json @@ -0,0 +1,18 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "atom/language-sass", + "repositoryUrl": "https://github.com/atom/language-sass", + "commitHash": "303bbf0c250fe380b9e57375598cfd916110758b" + } + }, + "license": "MIT", + "description": "The file syntaxes/scss.json was derived from the Atom package https://github.com/atom/language-sass which was originally converted from the TextMate bundle https://github.com/alexsancho/SASS.tmbundle.", + "version": "0.52.0" + } + ], + "version": 1 +} diff --git a/extensions/shaderlab/.vscodeignore b/extensions/shaderlab/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/shaderlab/.vscodeignore +++ b/extensions/shaderlab/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/shaderlab/OSSREADME.json b/extensions/shaderlab/OSSREADME.json deleted file mode 100644 index 5acab3453..000000000 --- a/extensions/shaderlab/OSSREADME.json +++ /dev/null @@ -1,8 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "shaders-tmLanguage", - "version": "0.1.0", - "license": "MIT", - "repositoryURL": "https://github.com/tgjones/shaders-tmLanguage" -}] diff --git a/extensions/shaderlab/cgmanifest.json b/extensions/shaderlab/cgmanifest.json new file mode 100644 index 000000000..3558286bc --- /dev/null +++ b/extensions/shaderlab/cgmanifest.json @@ -0,0 +1,17 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "shaders-tmLanguage", + "repositoryUrl": "https://github.com/tgjones/shaders-tmLanguage", + "commitHash": "c72c8b39380ba5a86c58ceed053b5d965ebf38b3" + } + }, + "license": "MIT", + "version": "0.1.0" + } + ], + "version": 1 +} diff --git a/extensions/shellscript/.vscodeignore b/extensions/shellscript/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/shellscript/.vscodeignore +++ b/extensions/shellscript/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/shellscript/OSSREADME.json b/extensions/shellscript/OSSREADME.json deleted file mode 100644 index 3776c2af3..000000000 --- a/extensions/shellscript/OSSREADME.json +++ /dev/null @@ -1,9 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "atom/language-shellscript", - "version": "0.0.0", - "license": "MIT", - "repositoryURL": "https://github.com/atom/language-shellscript", - "description": "The file syntaxes/shell-unix-bash.tmLanguage.json was derived from the Atom package https://github.com/atom/language-shellscript which was originally converted from the TextMate bundle https://github.com/textmate/shellscript.tmbundle." -}] diff --git a/extensions/shellscript/cgmanifest.json b/extensions/shellscript/cgmanifest.json new file mode 100644 index 000000000..0eee972fc --- /dev/null +++ b/extensions/shellscript/cgmanifest.json @@ -0,0 +1,18 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "atom/language-shellscript", + "repositoryUrl": "https://github.com/atom/language-shellscript", + "commitHash": "4c3711edbe8eac6f501976893976b1ac6a043d50" + } + }, + "license": "MIT", + "description": "The file syntaxes/shell-unix-bash.tmLanguage.json was derived from the Atom package https://github.com/atom/language-shellscript which was originally converted from the TextMate bundle https://github.com/textmate/shellscript.tmbundle.", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/shellscript/package.json b/extensions/shellscript/package.json index 446e98b07..b3c234638 100644 --- a/extensions/shellscript/package.json +++ b/extensions/shellscript/package.json @@ -11,10 +11,10 @@ "contributes": { "languages": [{ "id": "shellscript", - "aliases": ["Shell Script", "shellscript", "bash", "sh", "zsh"], - "extensions": [".sh", ".bash", ".bashrc", ".bash_aliases", ".bash_profile", ".bash_login", ".ebuild", ".install", ".profile", ".bash_logout", ".zsh", ".zshrc", ".zprofile", ".zlogin", ".zlogout", ".zshenv", ".zsh-theme"], + "aliases": ["Shell Script", "shellscript", "bash", "sh", "zsh", "ksh"], + "extensions": [".sh", ".bash", ".bashrc", ".bash_aliases", ".bash_profile", ".bash_login", ".ebuild", ".install", ".profile", ".bash_logout", ".zsh", ".zshrc", ".zprofile", ".zlogin", ".zlogout", ".zshenv", ".zsh-theme", ".ksh"], "filenames": ["PKGBUILD"], - "firstLine": "^#!.*\\b(bash|zsh|sh|tcsh).*|^#\\s*-\\*-[^*]*mode:\\s*shell-script[^*]*-\\*-", + "firstLine": "^#!.*\\b(bash|zsh|sh|tcsh|ksh).*|^#\\s*-\\*-[^*]*mode:\\s*shell-script[^*]*-\\*-", "configuration": "./language-configuration.json", "mimetypes": ["text/x-shellscript"] }], diff --git a/extensions/sql/.vscodeignore b/extensions/sql/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/sql/.vscodeignore +++ b/extensions/sql/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/sql/OSSREADME.json b/extensions/sql/OSSREADME.json deleted file mode 100644 index 0fc48ab93..000000000 --- a/extensions/sql/OSSREADME.json +++ /dev/null @@ -1,8 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "Microsoft/vscode-mssql", - "version": "0.0.0", - "license": "MIT", - "repositoryURL": "https://github.com/Microsoft/vscode-mssql" -}] diff --git a/extensions/sql/cgmanifest.json b/extensions/sql/cgmanifest.json new file mode 100644 index 000000000..a40abdb6d --- /dev/null +++ b/extensions/sql/cgmanifest.json @@ -0,0 +1,17 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "Microsoft/vscode-mssql", + "repositoryUrl": "https://github.com/Microsoft/vscode-mssql", + "commitHash": "68d4b740b6a9e12592a32f1c0c8a0dd987f19da8" + } + }, + "license": "MIT", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/swift/.vscodeignore b/extensions/swift/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/swift/.vscodeignore +++ b/extensions/swift/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/swift/OSSREADME.json b/extensions/swift/OSSREADME.json deleted file mode 100644 index 335e66618..000000000 --- a/extensions/swift/OSSREADME.json +++ /dev/null @@ -1,61 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "vscode-swift", - "license": "MIT", - "licenseDetail": [ - "The MIT License (MIT)", - "", - "Copyright (c) 2015 David Owens II", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all", - "copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." - ], - "version": "0.0.1", - "repositoryURL": "https://github.com/owensd/vscode-swift", - "description": "The files in this folder are based on https://github.com/owensd/vscode-swift." -}, -{ - "name": "freebroccolo/atom-language-swift", - "version": "0.0.0", - "license": "MIT", - "repositoryURL": "https://github.com/freebroccolo/atom-language-swift", - "licenseDetail": [ - "The MIT License (MIT)", - "", - "Copyright (c) 2014 Darin Morrison", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all", - "copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." - ] -} - -] diff --git a/extensions/swift/cgmanifest.json b/extensions/swift/cgmanifest.json new file mode 100644 index 000000000..a3f7977cb --- /dev/null +++ b/extensions/swift/cgmanifest.json @@ -0,0 +1,74 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "vscode-swift", + "repositoryUrl": "https://github.com/owensd/vscode-swift", + "commitHash": "cec27af6662e3799120b208e64483efdfe5521f5" + } + }, + "licenseDetail": [ + "The MIT License (MIT)", + "", + "Copyright (c) 2015 David Owens II", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ], + "license": "MIT", + "description": "The files in this folder are based on https://github.com/owensd/vscode-swift.", + "version": "0.0.1" + }, + { + "component": { + "type": "git", + "git": { + "name": "freebroccolo/atom-language-swift", + "repositoryUrl": "https://github.com/freebroccolo/atom-language-swift", + "commitHash": "d8f4ed1f79cbb4d5d215e7a68fa8cc6b618e16e0" + } + }, + "licenseDetail": [ + "The MIT License (MIT)", + "", + "Copyright (c) 2014 Darin Morrison", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ], + "license": "MIT", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/theme-abyss/.vscodeignore b/extensions/theme-abyss/.vscodeignore index eb66319db..a0100f8d7 100644 --- a/extensions/theme-abyss/.vscodeignore +++ b/extensions/theme-abyss/.vscodeignore @@ -1 +1 @@ -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/theme-abyss/OSSREADME.json b/extensions/theme-abyss/OSSREADME.json deleted file mode 100644 index ddb630bfd..000000000 --- a/extensions/theme-abyss/OSSREADME.json +++ /dev/null @@ -1,8 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "Colorsublime-Themes", - "version": "0.1.0", - "repositoryURL": "https://github.com/Colorsublime/Colorsublime-Themes", - "description": "The themes in this folders are copied from colorsublime.com. <<>>" -}] diff --git a/extensions/theme-abyss/cgmanifest.json b/extensions/theme-abyss/cgmanifest.json new file mode 100644 index 000000000..f3bd1db12 --- /dev/null +++ b/extensions/theme-abyss/cgmanifest.json @@ -0,0 +1,17 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "Colorsublime-Themes", + "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", + "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + } + }, + "description": "The themes in this folders are copied from colorsublime.com. <<>>", + "version": "0.1.0" + } + ], + "version": 1 +} diff --git a/extensions/theme-defaults/themes/light_defaults.json b/extensions/theme-defaults/themes/light_defaults.json index dc53cbfb1..91c5fb1d9 100644 --- a/extensions/theme-defaults/themes/light_defaults.json +++ b/extensions/theme-defaults/themes/light_defaults.json @@ -7,7 +7,7 @@ "editor.inactiveSelectionBackground": "#E5EBF1", "editorIndentGuide.background": "#D3D3D3", "editorIndentGuide.activeBackground": "#939393", - "editor.selectionHighlightBackground": "#ADD6FF4D", + "editor.selectionHighlightBackground": "#ADD6FF80", "editorSuggestWidget.background": "#F3F3F3", "activityBarBadge.background": "#007ACC", "sideBarTitle.foreground": "#6F6F6F", diff --git a/extensions/theme-kimbie-dark/.vscodeignore b/extensions/theme-kimbie-dark/.vscodeignore index eb66319db..a0100f8d7 100644 --- a/extensions/theme-kimbie-dark/.vscodeignore +++ b/extensions/theme-kimbie-dark/.vscodeignore @@ -1 +1 @@ -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/theme-kimbie-dark/OSSREADME.json b/extensions/theme-kimbie-dark/OSSREADME.json deleted file mode 100644 index ddb630bfd..000000000 --- a/extensions/theme-kimbie-dark/OSSREADME.json +++ /dev/null @@ -1,8 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "Colorsublime-Themes", - "version": "0.1.0", - "repositoryURL": "https://github.com/Colorsublime/Colorsublime-Themes", - "description": "The themes in this folders are copied from colorsublime.com. <<>>" -}] diff --git a/extensions/theme-kimbie-dark/cgmanifest.json b/extensions/theme-kimbie-dark/cgmanifest.json new file mode 100644 index 000000000..6c169cecb --- /dev/null +++ b/extensions/theme-kimbie-dark/cgmanifest.json @@ -0,0 +1,16 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "Colorsublime-Themes", + "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", + "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + } + }, + "version": "0.1.0" + } + ], + "version": 1 +} diff --git a/extensions/theme-monokai-dimmed/.vscodeignore b/extensions/theme-monokai-dimmed/.vscodeignore index eb66319db..a0100f8d7 100644 --- a/extensions/theme-monokai-dimmed/.vscodeignore +++ b/extensions/theme-monokai-dimmed/.vscodeignore @@ -1 +1 @@ -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/theme-monokai-dimmed/OSSREADME.json b/extensions/theme-monokai-dimmed/OSSREADME.json deleted file mode 100644 index ddb630bfd..000000000 --- a/extensions/theme-monokai-dimmed/OSSREADME.json +++ /dev/null @@ -1,8 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "Colorsublime-Themes", - "version": "0.1.0", - "repositoryURL": "https://github.com/Colorsublime/Colorsublime-Themes", - "description": "The themes in this folders are copied from colorsublime.com. <<>>" -}] diff --git a/extensions/theme-monokai-dimmed/cgmanifest.json b/extensions/theme-monokai-dimmed/cgmanifest.json new file mode 100644 index 000000000..6c169cecb --- /dev/null +++ b/extensions/theme-monokai-dimmed/cgmanifest.json @@ -0,0 +1,16 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "Colorsublime-Themes", + "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", + "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + } + }, + "version": "0.1.0" + } + ], + "version": 1 +} diff --git a/extensions/theme-monokai/.vscodeignore b/extensions/theme-monokai/.vscodeignore index eb66319db..a0100f8d7 100644 --- a/extensions/theme-monokai/.vscodeignore +++ b/extensions/theme-monokai/.vscodeignore @@ -1 +1 @@ -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/theme-monokai/OSSREADME.json b/extensions/theme-monokai/OSSREADME.json deleted file mode 100644 index ddb630bfd..000000000 --- a/extensions/theme-monokai/OSSREADME.json +++ /dev/null @@ -1,8 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "Colorsublime-Themes", - "version": "0.1.0", - "repositoryURL": "https://github.com/Colorsublime/Colorsublime-Themes", - "description": "The themes in this folders are copied from colorsublime.com. <<>>" -}] diff --git a/extensions/theme-monokai/cgmanifest.json b/extensions/theme-monokai/cgmanifest.json new file mode 100644 index 000000000..6c169cecb --- /dev/null +++ b/extensions/theme-monokai/cgmanifest.json @@ -0,0 +1,16 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "Colorsublime-Themes", + "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", + "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + } + }, + "version": "0.1.0" + } + ], + "version": 1 +} diff --git a/extensions/theme-quietlight/.vscodeignore b/extensions/theme-quietlight/.vscodeignore index eb66319db..a0100f8d7 100644 --- a/extensions/theme-quietlight/.vscodeignore +++ b/extensions/theme-quietlight/.vscodeignore @@ -1 +1 @@ -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/theme-quietlight/OSSREADME.json b/extensions/theme-quietlight/OSSREADME.json deleted file mode 100644 index ddb630bfd..000000000 --- a/extensions/theme-quietlight/OSSREADME.json +++ /dev/null @@ -1,8 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "Colorsublime-Themes", - "version": "0.1.0", - "repositoryURL": "https://github.com/Colorsublime/Colorsublime-Themes", - "description": "The themes in this folders are copied from colorsublime.com. <<>>" -}] diff --git a/extensions/theme-quietlight/cgmanifest.json b/extensions/theme-quietlight/cgmanifest.json new file mode 100644 index 000000000..6c169cecb --- /dev/null +++ b/extensions/theme-quietlight/cgmanifest.json @@ -0,0 +1,16 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "Colorsublime-Themes", + "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", + "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + } + }, + "version": "0.1.0" + } + ], + "version": 1 +} diff --git a/extensions/theme-red/.vscodeignore b/extensions/theme-red/.vscodeignore index eb66319db..a0100f8d7 100644 --- a/extensions/theme-red/.vscodeignore +++ b/extensions/theme-red/.vscodeignore @@ -1 +1 @@ -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/theme-red/OSSREADME.json b/extensions/theme-red/OSSREADME.json deleted file mode 100644 index ddb630bfd..000000000 --- a/extensions/theme-red/OSSREADME.json +++ /dev/null @@ -1,8 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "Colorsublime-Themes", - "version": "0.1.0", - "repositoryURL": "https://github.com/Colorsublime/Colorsublime-Themes", - "description": "The themes in this folders are copied from colorsublime.com. <<>>" -}] diff --git a/extensions/theme-red/cgmanifest.json b/extensions/theme-red/cgmanifest.json new file mode 100644 index 000000000..6c169cecb --- /dev/null +++ b/extensions/theme-red/cgmanifest.json @@ -0,0 +1,16 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "Colorsublime-Themes", + "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", + "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + } + }, + "version": "0.1.0" + } + ], + "version": 1 +} diff --git a/extensions/theme-seti/.vscodeignore b/extensions/theme-seti/.vscodeignore index 02a2d5ede..d9011becf 100644 --- a/extensions/theme-seti/.vscodeignore +++ b/extensions/theme-seti/.vscodeignore @@ -1,2 +1,2 @@ build/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/theme-seti/OSSREADME.json b/extensions/theme-seti/OSSREADME.json deleted file mode 100644 index 7601d594b..000000000 --- a/extensions/theme-seti/OSSREADME.json +++ /dev/null @@ -1,8 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "seti-ui", - "version": "0.1.0", - "repositoryURL": "https://github.com/jesseweed/seti-ui", - "description": "The file ./icons/seti.woff has been copied from https://github.com/jesseweed/seti-ui/blob/master/styles/_fonts/seti/seti.woff" -}] diff --git a/extensions/theme-seti/build/update-icon-theme.js b/extensions/theme-seti/build/update-icon-theme.js index 318ba18d0..c875a47c1 100644 --- a/extensions/theme-seti/build/update-icon-theme.js +++ b/extensions/theme-seti/build/update-icon-theme.js @@ -33,7 +33,7 @@ let nonBuiltInLanguages = { // { fileNames, extensions } "stylus": { extensions: ['styl'] }, "vala": { extensions: ['vala'] }, "todo": { fileNames: ['todo'] } -} +}; function getCommitSha(repoId, repoPath) { let commitInfo = 'https://api.github.com/repos/' + repoId + '/commits?path=' + repoPath; diff --git a/extensions/theme-seti/cgmanifest.json b/extensions/theme-seti/cgmanifest.json new file mode 100644 index 000000000..98fcf55e0 --- /dev/null +++ b/extensions/theme-seti/cgmanifest.json @@ -0,0 +1,16 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "seti-ui", + "repositoryUrl": "https://github.com/jesseweed/seti-ui", + "commitHash": "0b576faae405d3cd8df6ac1a397f287aa6d8b3fe" + } + }, + "version": "0.1.0" + } + ], + "version": 1 +} diff --git a/extensions/theme-seti/icons/seti-circular-128x128.png b/extensions/theme-seti/icons/seti-circular-128x128.png index 37c689939..fbe533bba 100644 Binary files a/extensions/theme-seti/icons/seti-circular-128x128.png and b/extensions/theme-seti/icons/seti-circular-128x128.png differ diff --git a/extensions/theme-solarized-dark/.vscodeignore b/extensions/theme-solarized-dark/.vscodeignore index eb66319db..a0100f8d7 100644 --- a/extensions/theme-solarized-dark/.vscodeignore +++ b/extensions/theme-solarized-dark/.vscodeignore @@ -1 +1 @@ -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/theme-solarized-dark/OSSREADME.json b/extensions/theme-solarized-dark/OSSREADME.json deleted file mode 100644 index ddb630bfd..000000000 --- a/extensions/theme-solarized-dark/OSSREADME.json +++ /dev/null @@ -1,8 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "Colorsublime-Themes", - "version": "0.1.0", - "repositoryURL": "https://github.com/Colorsublime/Colorsublime-Themes", - "description": "The themes in this folders are copied from colorsublime.com. <<>>" -}] diff --git a/extensions/theme-solarized-dark/cgmanifest.json b/extensions/theme-solarized-dark/cgmanifest.json new file mode 100644 index 000000000..6c169cecb --- /dev/null +++ b/extensions/theme-solarized-dark/cgmanifest.json @@ -0,0 +1,16 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "Colorsublime-Themes", + "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", + "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + } + }, + "version": "0.1.0" + } + ], + "version": 1 +} diff --git a/extensions/theme-solarized-light/.vscodeignore b/extensions/theme-solarized-light/.vscodeignore index eb66319db..a0100f8d7 100644 --- a/extensions/theme-solarized-light/.vscodeignore +++ b/extensions/theme-solarized-light/.vscodeignore @@ -1 +1 @@ -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/theme-solarized-light/OSSREADME.json b/extensions/theme-solarized-light/OSSREADME.json deleted file mode 100644 index ddb630bfd..000000000 --- a/extensions/theme-solarized-light/OSSREADME.json +++ /dev/null @@ -1,8 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "Colorsublime-Themes", - "version": "0.1.0", - "repositoryURL": "https://github.com/Colorsublime/Colorsublime-Themes", - "description": "The themes in this folders are copied from colorsublime.com. <<>>" -}] diff --git a/extensions/theme-solarized-light/cgmanifest.json b/extensions/theme-solarized-light/cgmanifest.json new file mode 100644 index 000000000..6c169cecb --- /dev/null +++ b/extensions/theme-solarized-light/cgmanifest.json @@ -0,0 +1,16 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "Colorsublime-Themes", + "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", + "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + } + }, + "version": "0.1.0" + } + ], + "version": 1 +} diff --git a/extensions/theme-tomorrow-night-blue/.vscodeignore b/extensions/theme-tomorrow-night-blue/.vscodeignore index eb66319db..a0100f8d7 100644 --- a/extensions/theme-tomorrow-night-blue/.vscodeignore +++ b/extensions/theme-tomorrow-night-blue/.vscodeignore @@ -1 +1 @@ -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/theme-tomorrow-night-blue/OSSREADME.json b/extensions/theme-tomorrow-night-blue/OSSREADME.json deleted file mode 100644 index ddb630bfd..000000000 --- a/extensions/theme-tomorrow-night-blue/OSSREADME.json +++ /dev/null @@ -1,8 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "Colorsublime-Themes", - "version": "0.1.0", - "repositoryURL": "https://github.com/Colorsublime/Colorsublime-Themes", - "description": "The themes in this folders are copied from colorsublime.com. <<>>" -}] diff --git a/extensions/theme-tomorrow-night-blue/cgmanifest.json b/extensions/theme-tomorrow-night-blue/cgmanifest.json new file mode 100644 index 000000000..6c169cecb --- /dev/null +++ b/extensions/theme-tomorrow-night-blue/cgmanifest.json @@ -0,0 +1,16 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "Colorsublime-Themes", + "repositoryUrl": "https://github.com/Colorsublime/Colorsublime-Themes", + "commitHash": "d7401929c97c5fe78319b424f575ddce3f05da81" + } + }, + "version": "0.1.0" + } + ], + "version": 1 +} diff --git a/extensions/typescript-basics/.vscodeignore b/extensions/typescript-basics/.vscodeignore index d6f32d256..06c7b11c5 100644 --- a/extensions/typescript-basics/.vscodeignore +++ b/extensions/typescript-basics/.vscodeignore @@ -2,4 +2,4 @@ build/** src/** test/** tsconfig.json -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/typescript-basics/OSSREADME.json b/extensions/typescript-basics/OSSREADME.json deleted file mode 100644 index 160da003c..000000000 --- a/extensions/typescript-basics/OSSREADME.json +++ /dev/null @@ -1,7 +0,0 @@ -[{ - "name": "TypeScript-TmLanguage", - "version": "0.1.8", - "license": "MIT", - "repositoryURL": "https://github.com/Microsoft/TypeScript-TmLanguage", - "description": "The files syntaxes/TypeScript.tmLanguage.json and syntaxes/TypeScriptReact.tmLanguage.json were derived from TypeScript.tmLanguage and TypeScriptReact.tmLanguage in https://github.com/Microsoft/TypeScript-TmLanguage." -}] diff --git a/extensions/typescript-basics/cgmanifest.json b/extensions/typescript-basics/cgmanifest.json new file mode 100644 index 000000000..d6220176f --- /dev/null +++ b/extensions/typescript-basics/cgmanifest.json @@ -0,0 +1,18 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "TypeScript-TmLanguage", + "repositoryUrl": "https://github.com/Microsoft/TypeScript-TmLanguage", + "commitHash": "3133e3d914db9a2bb8812119f9273727a305f16b" + } + }, + "license": "MIT", + "description": "The files syntaxes/TypeScript.tmLanguage.json and syntaxes/TypeScriptReact.tmLanguage.json were derived from TypeScript.tmLanguage and TypeScriptReact.tmLanguage in https://github.com/Microsoft/TypeScript-TmLanguage.", + "version": "0.1.8" + } + ], + "version": 1 +} diff --git a/extensions/typescript-basics/language-configuration.json b/extensions/typescript-basics/language-configuration.json index b41053843..ee4e295af 100644 --- a/extensions/typescript-basics/language-configuration.json +++ b/extensions/typescript-basics/language-configuration.json @@ -23,7 +23,8 @@ ["(", ")"], ["'", "'"], ["\"", "\""], - ["`", "`"] + ["`", "`"], + ["<", ">"], ], "autoCloseBefore": ";:.,=}])>` \n\t", "folding": { diff --git a/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json index 428fbe65f..70f6d36f8 100644 --- a/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json +++ b/extensions/typescript-basics/syntaxes/TypeScript.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/TypeScript-TmLanguage/commit/a34cb117a38ac6f6eae0df88db984780c6b3df1e", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/3133e3d914db9a2bb8812119f9273727a305f16b", "name": "TypeScript", "scopeName": "source.ts", "patterns": [ @@ -1150,7 +1150,7 @@ "name": "meta.definition.function.ts entity.name.function.ts" } }, - "end": "(?=$|^|;)|(?<=\\})", + "end": "(?=;|(?:^\\s*(?:abstract|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))|(?<=\\})", "patterns": [ { "include": "#function-name" @@ -1410,6 +1410,9 @@ }, { "include": "#arrow-return-type" + }, + { + "include": "#possibly-arrow-return-type" } ] }, @@ -1421,8 +1424,11 @@ "name": "storage.type.function.arrow.ts" } }, - "end": "(?<=\\}|\\S)(?)|((?!\\{)(?=\\S))", + "end": "((?<=\\}|\\S)(?)|((?!\\{)(?=\\S)))(?!\\/[\\/\\*])", "patterns": [ + { + "include": "#single-line-comment-consuming-line-ending" + }, { "include": "#decl-block" }, @@ -2620,13 +2626,13 @@ ] }, "function-call": { - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "name": "meta.function-call.ts", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -3539,7 +3545,7 @@ ] }, "possibly-arrow-return-type": { - "begin": "(?<=\\))\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", + "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", "beginCaptures": { "1": { "name": "meta.arrow.ts meta.return.type.arrow.ts keyword.operator.type.annotation.ts" @@ -3621,6 +3627,14 @@ } }, "patterns": [ + { + "match": "(?)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$))))", "captures": { @@ -3971,7 +3982,7 @@ "include": "#typeof-operator" }, { - "begin": "([&|\\*])(?=\\s*\\{)", + "begin": "([&|])(?=\\s*\\{)", "beginCaptures": { "0": { "name": "keyword.operator.type.ts" @@ -3985,7 +3996,7 @@ ] }, { - "begin": "[&|\\*]", + "begin": "[&|]", "beginCaptures": { "0": { "name": "keyword.operator.type.ts" @@ -4123,7 +4134,7 @@ "patterns": [ { "name": "string.template.ts", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.ts" diff --git a/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json index a12c5b2eb..6d39e0862 100644 --- a/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json +++ b/extensions/typescript-basics/syntaxes/TypeScriptReact.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/TypeScript-TmLanguage/commit/a34cb117a38ac6f6eae0df88db984780c6b3df1e", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/3133e3d914db9a2bb8812119f9273727a305f16b", "name": "TypeScriptReact", "scopeName": "source.tsx", "patterns": [ @@ -1153,7 +1153,7 @@ "name": "meta.definition.function.tsx entity.name.function.tsx" } }, - "end": "(?=$|^|;)|(?<=\\})", + "end": "(?=;|(?:^\\s*(?:abstract|async|class|const|declare|enum|export|function|import|interface|let|module|namespace|return|type|var)\\b))|(?<=\\})", "patterns": [ { "include": "#function-name" @@ -1413,6 +1413,9 @@ }, { "include": "#arrow-return-type" + }, + { + "include": "#possibly-arrow-return-type" } ] }, @@ -1424,8 +1427,11 @@ "name": "storage.type.function.arrow.tsx" } }, - "end": "(?<=\\}|\\S)(?)|((?!\\{)(?=\\S))", + "end": "((?<=\\}|\\S)(?)|((?!\\{)(?=\\S)))(?!\\/[\\/\\*])", "patterns": [ + { + "include": "#single-line-comment-consuming-line-ending" + }, { "include": "#decl-block" }, @@ -2623,13 +2629,13 @@ ] }, "function-call": { - "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", - "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?<=\\))(?!(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*)\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "name": "meta.function-call.tsx", "begin": "(?=(([_$[:alpha:]][_$[:alnum:]]*\\s*\\??\\.\\s*)*|(\\??\\.\\s*)?)([_$[:alpha:]][_$[:alnum:]]*))", - "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", + "end": "(?=\\s*(\\?\\.\\s*)?(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)?\\()", "patterns": [ { "include": "#literal" @@ -3505,7 +3511,7 @@ ] }, "possibly-arrow-return-type": { - "begin": "(?<=\\))\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", + "begin": "(?<=\\)|^)\\s*(:)(?=\\s*([^<>\\(\\)\\{\\}]|\\<[^<>]+\\>|\\([^\\(\\)]+\\)|\\{[^\\{\\}]+\\})+\\s*=>)", "beginCaptures": { "1": { "name": "meta.arrow.tsx meta.return.type.arrow.tsx keyword.operator.type.annotation.tsx" @@ -3587,6 +3593,14 @@ } }, "patterns": [ + { + "match": "(?)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))))", "captures": { @@ -3937,7 +3948,7 @@ "include": "#typeof-operator" }, { - "begin": "([&|\\*])(?=\\s*\\{)", + "begin": "([&|])(?=\\s*\\{)", "beginCaptures": { "0": { "name": "keyword.operator.type.tsx" @@ -3951,7 +3962,7 @@ ] }, { - "begin": "[&|\\*]", + "begin": "[&|]", "beginCaptures": { "0": { "name": "keyword.operator.type.tsx" @@ -4089,7 +4100,7 @@ "patterns": [ { "name": "string.template.tsx", - "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(\\*(?=\\s*[,>]))|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=(<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>|\\<\\s*(((keyof|infer)\\s+)|(([_$[:alpha:]][_$[:alnum:]]*|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\'[^\\']*\\')|(\\\"[^\\\"]*\\\")|(\\`[^\\`]*\\`))(?=\\s*([\\<\\>\\,\\.\\[]|=>|&(?!&)|\\|(?!\\|)))))([^<>\\(]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(?<==)\\>)*(?!=)\\>)*(?!=)>\\s*)`)", "beginCaptures": { "1": { "name": "entity.name.function.tagged-template.tsx" diff --git a/extensions/typescript-language-features/.vscodeignore b/extensions/typescript-language-features/.vscodeignore index 7b229b61f..1edbc2a7b 100644 --- a/extensions/typescript-language-features/.vscodeignore +++ b/extensions/typescript-language-features/.vscodeignore @@ -4,5 +4,5 @@ test/** out/** tsconfig.json extension.webpack.config.js -OSSREADME.json -yarn.lock \ No newline at end of file +cgmanifest.json +yarn.lock diff --git a/extensions/typescript-language-features/OSSREADME.json b/extensions/typescript-language-features/OSSREADME.json deleted file mode 100644 index 692c0592a..000000000 --- a/extensions/typescript-language-features/OSSREADME.json +++ /dev/null @@ -1,106 +0,0 @@ -[ - { - "name": "TypeScript-TmLanguage", - "version": "0.1.8", - "license": "MIT", - "repositoryURL": "https://github.com/Microsoft/TypeScript-TmLanguage", - "description": "The files syntaxes/TypeScript.tmLanguage.json and syntaxes/TypeScriptReact.tmLanguage.json were derived from TypeScript.tmLanguage and TypeScriptReact.tmLanguage in https://github.com/Microsoft/TypeScript-TmLanguage." - }, - { - "name": "definitelytyped", - "license": "MIT", - "repositoryURL": "https://github.com/DefinitelyTyped/DefinitelyTyped", - "description": "Typings files that are downloaded by TypeScript. These typings power IntelliSense for JavaScript and TypeScript." - }, - { - "name": "Unicode", - "license": "UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE", - "description": "These files are included by TypeScript.Ø", - "licenseDetail": [ - "Unicode Data Files include all data files under the directories", - "http://www.unicode.org/Public/, http://www.unicode.org/reports/,", - "http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and", - "http://www.unicode.org/utility/trac/browser/.", - "", - "Unicode Data Files do not include PDF online code charts under the", - "directory http://www.unicode.org/Public/.", - "", - "Software includes any source code published in the Unicode Standard", - "or under the directories", - "http://www.unicode.org/Public/, http://www.unicode.org/reports/,", - "http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and", - "http://www.unicode.org/utility/trac/browser/.", - "", - "NOTICE TO USER: Carefully read the following legal agreement.", - "BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S", - "DATA FILES (\"DATA FILES\"), AND/OR SOFTWARE (\"SOFTWARE\"),", - "YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE", - "TERMS AND CONDITIONS OF THIS AGREEMENT.", - "IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE", - "THE DATA FILES OR SOFTWARE.", - "", - "COPYRIGHT AND PERMISSION NOTICE", - "", - "Copyright (c) 1991-2017 Unicode, Inc. All rights reserved.", - "Distributed under the Terms of Use in http://www.unicode.org/copyright.html.", - "", - "Permission is hereby granted, free of charge, to any person obtaining", - "a copy of the Unicode data files and any associated documentation", - "(the \"Data Files\") or Unicode software and any associated documentation", - "(the \"Software\") to deal in the Data Files or Software", - "without restriction, including without limitation the rights to use,", - "copy, modify, merge, publish, distribute, and/or sell copies of", - "the Data Files or Software, and to permit persons to whom the Data Files", - "or Software are furnished to do so, provided that either", - "(a) this copyright and permission notice appear with all copies", - "of the Data Files or Software, or", - "(b) this copyright and permission notice appear in associated", - "Documentation.", - "", - "THE DATA FILES AND SOFTWARE ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF", - "ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE", - "WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND", - "NONINFRINGEMENT OF THIRD PARTY RIGHTS.", - "IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS", - "NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL", - "DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,", - "DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER", - "TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR", - "PERFORMANCE OF THE DATA FILES OR SOFTWARE.", - "", - "Except as contained in this notice, the name of a copyright holder", - "shall not be used in advertising or otherwise to promote the sale,", - "use or other dealings in these Data Files or Software without prior", - "written authorization of the copyright holder." - ] - }, - { - "name": "Document Object Model", - "license": "W3C License", - "description": "These files", - "licenseDetail": [ - "W3C License", - "This work is being provided by the copyright holders under the following license.", - "By obtaining and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions.", - "Permission to copy, modify, and distribute this work, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following ", - "on ALL copies of the work or portions thereof, including modifications:", - "* The full text of this NOTICE in a location viewable to users of the redistributed or derivative work.", - "* Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, the W3C Software and Document Short Notice should be included.", - "* Notice of any changes or modifications, through a copyright statement on the new code or document such as \"This software or document includes material copied from or derived ", - "from Document Object Model. Copyright © 2015 W3C® (MIT, ERCIM, Keio, Beihang).\" ", - "Disclaimers", - "THIS WORK IS PROVIDED \"AS IS", - " AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR ", - "FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.", - "COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT.", - "The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the work without specific, written prior permission. ", - "Title to copyright in this work will at all times remain with copyright holders." - ] - }, - { - "name": "Web Background Synchronization", - "license": "Apache2", - "repositoryURL": "https://github.com/WICG/BackgroundSync", - "description": "TypeScript includes files related to this specification" - } -] \ No newline at end of file diff --git a/extensions/typescript-language-features/cgmanifest.json b/extensions/typescript-language-features/cgmanifest.json new file mode 100644 index 000000000..b0a573534 --- /dev/null +++ b/extensions/typescript-language-features/cgmanifest.json @@ -0,0 +1,134 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "TypeScript-TmLanguage", + "repositoryUrl": "https://github.com/Microsoft/TypeScript-TmLanguage", + "commitHash": "3133e3d914db9a2bb8812119f9273727a305f16b" + } + }, + "license": "MIT", + "version": "0.1.8" + }, + { + "component": { + "type": "git", + "git": { + "name": "definitelytyped", + "repositoryUrl": "https://github.com/DefinitelyTyped/DefinitelyTyped", + "commitHash": "69e3ac6bec3008271f76bbfa7cf69aa9198c4ff0" + } + }, + "license": "MIT" + }, + { + "component": { + "type": "other", + "other": { + "name": "Unicode", + "downloadUrl": "http://www.unicode.org/" + } + }, + "licenseDetail": [ + "Unicode Data Files include all data files under the directories", + "http://www.unicode.org/Public/, http://www.unicode.org/reports/,", + "http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and", + "http://www.unicode.org/utility/trac/browser/.", + "", + "Unicode Data Files do not include PDF online code charts under the", + "directory http://www.unicode.org/Public/.", + "", + "Software includes any source code published in the Unicode Standard", + "or under the directories", + "http://www.unicode.org/Public/, http://www.unicode.org/reports/,", + "http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and", + "http://www.unicode.org/utility/trac/browser/.", + "", + "NOTICE TO USER: Carefully read the following legal agreement.", + "BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S", + "DATA FILES (\"DATA FILES\"), AND/OR SOFTWARE (\"SOFTWARE\"),", + "YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE", + "TERMS AND CONDITIONS OF THIS AGREEMENT.", + "IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE", + "THE DATA FILES OR SOFTWARE.", + "", + "COPYRIGHT AND PERMISSION NOTICE", + "", + "Copyright (c) 1991-2017 Unicode, Inc. All rights reserved.", + "Distributed under the Terms of Use in http://www.unicode.org/copyright.html.", + "", + "Permission is hereby granted, free of charge, to any person obtaining", + "a copy of the Unicode data files and any associated documentation", + "(the \"Data Files\") or Unicode software and any associated documentation", + "(the \"Software\") to deal in the Data Files or Software", + "without restriction, including without limitation the rights to use,", + "copy, modify, merge, publish, distribute, and/or sell copies of", + "the Data Files or Software, and to permit persons to whom the Data Files", + "or Software are furnished to do so, provided that either", + "(a) this copyright and permission notice appear with all copies", + "of the Data Files or Software, or", + "(b) this copyright and permission notice appear in associated", + "Documentation.", + "", + "THE DATA FILES AND SOFTWARE ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF", + "ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE", + "WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND", + "NONINFRINGEMENT OF THIRD PARTY RIGHTS.", + "IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS", + "NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL", + "DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,", + "DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER", + "TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR", + "PERFORMANCE OF THE DATA FILES OR SOFTWARE.", + "", + "Except as contained in this notice, the name of a copyright holder", + "shall not be used in advertising or otherwise to promote the sale,", + "use or other dealings in these Data Files or Software without prior", + "written authorization of the copyright holder." + ], + "license": "UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE" + }, + { + "component": { + "type": "other", + "other": { + "name": "Document Object Model", + "downloadUrl": "https://www.w3.org/DOM/" + } + }, + "licenseDetail": [ + "W3C License", + "This work is being provided by the copyright holders under the following license.", + "By obtaining and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions.", + "Permission to copy, modify, and distribute this work, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following ", + "on ALL copies of the work or portions thereof, including modifications:", + "* The full text of this NOTICE in a location viewable to users of the redistributed or derivative work.", + "* Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, the W3C Software and Document Short Notice should be included.", + "* Notice of any changes or modifications, through a copyright statement on the new code or document such as \"This software or document includes material copied from or derived ", + "from Document Object Model. Copyright © 2015 W3C® (MIT, ERCIM, Keio, Beihang).\" ", + "Disclaimers", + "THIS WORK IS PROVIDED \"AS IS", + " AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR ", + "FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.", + "COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT.", + "The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the work without specific, written prior permission. ", + "Title to copyright in this work will at all times remain with copyright holders." + ], + "license": "W3C License" + }, + { + "component": { + "type": "git", + "git": { + "name": "Web Background Synchronization", + "repositoryUrl": "https://github.com/WICG/BackgroundSync", + "commitHash": "10778afe95b5d46c99f7a77565328b7108091510" + } + }, + "license": "Apache2" + } + ], + "version": 1 +} diff --git a/extensions/typescript-language-features/icon.png b/extensions/typescript-language-features/icon.png index c2e186fdc..b57f88660 100644 Binary files a/extensions/typescript-language-features/icon.png and b/extensions/typescript-language-features/icon.png differ diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 048c8d669..92160253e 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -42,6 +42,7 @@ "onCommand:typescript.goToProjectConfig", "onCommand:typescript.openTsServerLog", "onCommand:workbench.action.tasks.runTask", + "onCommand:_typescript.configurePlugin", "onLanguage:jsonc" ], "main": "./out/extension", diff --git a/extensions/typescript-language-features/src/api.ts b/extensions/typescript-language-features/src/api.ts new file mode 100644 index 000000000..6afc92166 --- /dev/null +++ b/extensions/typescript-language-features/src/api.ts @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { PluginManager } from './utils/plugins'; + +class ApiV0 { + public constructor( + public readonly onCompletionAccepted: vscode.Event, + private readonly _pluginManager: PluginManager, + ) { } + + configurePlugin(pluginId: string, configuration: {}): void { + this._pluginManager.setConfiguration(pluginId, configuration); + } +} + +export interface Api { + getAPI(version: 0): ApiV0 | undefined; +} + +export function getExtensionApi( + onCompletionAccepted: vscode.Event, + pluginManager: PluginManager, +): Api { + return { + getAPI(version) { + if (version === 0) { + return new ApiV0(onCompletionAccepted, pluginManager); + } + return undefined; + } + }; +} \ No newline at end of file diff --git a/extensions/typescript-language-features/src/commands/configurePlugin.ts b/extensions/typescript-language-features/src/commands/configurePlugin.ts new file mode 100644 index 000000000..8af85d8b9 --- /dev/null +++ b/extensions/typescript-language-features/src/commands/configurePlugin.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 { Command } from '../utils/commandManager'; +import { PluginManager } from '../utils/plugins'; + +export class ConfigurePluginCommand implements Command { + public readonly id = '_typescript.configurePlugin'; + + public constructor( + private readonly pluginManager: PluginManager, + ) { } + + public execute(pluginId: string, configuration: any) { + this.pluginManager.setConfiguration(pluginId, configuration); + } +} diff --git a/extensions/typescript-language-features/src/commands.ts b/extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts similarity index 67% rename from extensions/typescript-language-features/src/commands.ts rename to extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts index 2c40c512e..61729eafa 100644 --- a/extensions/typescript-language-features/src/commands.ts +++ b/extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts @@ -5,76 +5,14 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import TypeScriptServiceClientHost from './typeScriptServiceClientHost'; -import { Command } from './utils/commandManager'; -import { Lazy } from './utils/lazy'; -import { isImplicitProjectConfigFile, openOrCreateConfigFile } from './utils/tsconfig'; -import { nulToken } from './utils/cancellation'; - +import TypeScriptServiceClientHost from '../typeScriptServiceClientHost'; +import { nulToken } from '../utils/cancellation'; +import { Command } from '../utils/commandManager'; +import { Lazy } from '../utils/lazy'; +import { isImplicitProjectConfigFile, openOrCreateConfigFile } from '../utils/tsconfig'; const localize = nls.loadMessageBundle(); - -export class ReloadTypeScriptProjectsCommand implements Command { - public readonly id = 'typescript.reloadProjects'; - - public constructor( - private readonly lazyClientHost: Lazy - ) { } - - public execute() { - this.lazyClientHost.value.reloadProjects(); - } -} - -export class ReloadJavaScriptProjectsCommand implements Command { - public readonly id = 'javascript.reloadProjects'; - - public constructor( - private readonly lazyClientHost: Lazy - ) { } - - public execute() { - this.lazyClientHost.value.reloadProjects(); - } -} - -export class SelectTypeScriptVersionCommand implements Command { - public readonly id = 'typescript.selectTypeScriptVersion'; - - public constructor( - private readonly lazyClientHost: Lazy - ) { } - - public execute() { - this.lazyClientHost.value.serviceClient.onVersionStatusClicked(); - } -} - -export class OpenTsServerLogCommand implements Command { - public readonly id = 'typescript.openTsServerLog'; - - public constructor( - private readonly lazyClientHost: Lazy - ) { } - - public execute() { - this.lazyClientHost.value.serviceClient.openTsServerLogFile(); - } -} - -export class RestartTsServerCommand implements Command { - public readonly id = 'typescript.restartTsServer'; - - public constructor( - private readonly lazyClientHost: Lazy - ) { } - - public execute() { - this.lazyClientHost.value.serviceClient.restartTsServer(); - } -} - export class TypeScriptGoToProjectConfigCommand implements Command { public readonly id = 'typescript.goToProjectConfig'; @@ -130,7 +68,7 @@ async function goToProjectConfig( return; } - let res: protocol.ProjectInfoResponse | undefined = undefined; + let res: protocol.ProjectInfoResponse | undefined; try { res = await client.execute('projectInfo', { file, needFileNameList: false }, nulToken); } catch { @@ -151,7 +89,7 @@ async function goToProjectConfig( enum ProjectConfigAction { None, CreateConfig, - LearnMore + LearnMore, } interface ProjectConfigMessageItem extends vscode.MessageItem { @@ -166,7 +104,7 @@ async function goToProjectConfig( title: isTypeScriptProject ? localize('typescript.configureTsconfigQuickPick', 'Configure tsconfig.json') : localize('typescript.configureJsconfigQuickPick', 'Configure jsconfig.json'), - id: ProjectConfigAction.CreateConfig + id: ProjectConfigAction.CreateConfig, }); switch (selected && selected.id) { diff --git a/extensions/typescript-language-features/src/commands/index.ts b/extensions/typescript-language-features/src/commands/index.ts new file mode 100644 index 000000000..f22b3e448 --- /dev/null +++ b/extensions/typescript-language-features/src/commands/index.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import TypeScriptServiceClientHost from '../typeScriptServiceClientHost'; +import { CommandManager } from '../utils/commandManager'; +import { Lazy } from '../utils/lazy'; +import { PluginManager } from '../utils/plugins'; +import { ConfigurePluginCommand } from './configurePlugin'; +import { JavaScriptGoToProjectConfigCommand, TypeScriptGoToProjectConfigCommand } from './goToProjectConfiguration'; +import { OpenTsServerLogCommand } from './openTsServerLog'; +import { ReloadJavaScriptProjectsCommand, ReloadTypeScriptProjectsCommand } from './reloadProject'; +import { RestartTsServerCommand } from './restartTsServer'; +import { SelectTypeScriptVersionCommand } from './selectTypeScriptVersion'; + +export function registerCommands( + commandManager: CommandManager, + lazyClientHost: Lazy, + pluginManager: PluginManager +) { + commandManager.register(new ReloadTypeScriptProjectsCommand(lazyClientHost)); + commandManager.register(new ReloadJavaScriptProjectsCommand(lazyClientHost)); + commandManager.register(new SelectTypeScriptVersionCommand(lazyClientHost)); + commandManager.register(new OpenTsServerLogCommand(lazyClientHost)); + commandManager.register(new RestartTsServerCommand(lazyClientHost)); + commandManager.register(new TypeScriptGoToProjectConfigCommand(lazyClientHost)); + commandManager.register(new JavaScriptGoToProjectConfigCommand(lazyClientHost)); + commandManager.register(new ConfigurePluginCommand(pluginManager)); +} \ No newline at end of file diff --git a/extensions/typescript-language-features/src/commands/openTsServerLog.ts b/extensions/typescript-language-features/src/commands/openTsServerLog.ts new file mode 100644 index 000000000..be47a985c --- /dev/null +++ b/extensions/typescript-language-features/src/commands/openTsServerLog.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import TypeScriptServiceClientHost from '../typeScriptServiceClientHost'; +import { Command } from '../utils/commandManager'; +import { Lazy } from '../utils/lazy'; + +export class OpenTsServerLogCommand implements Command { + public readonly id = 'typescript.openTsServerLog'; + + public constructor( + private readonly lazyClientHost: Lazy + ) { } + + public execute() { + this.lazyClientHost.value.serviceClient.openTsServerLogFile(); + } +} diff --git a/extensions/typescript-language-features/src/commands/reloadProject.ts b/extensions/typescript-language-features/src/commands/reloadProject.ts new file mode 100644 index 000000000..fbba68eb9 --- /dev/null +++ b/extensions/typescript-language-features/src/commands/reloadProject.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import TypeScriptServiceClientHost from '../typeScriptServiceClientHost'; +import { Command } from '../utils/commandManager'; +import { Lazy } from '../utils/lazy'; + +export class ReloadTypeScriptProjectsCommand implements Command { + public readonly id = 'typescript.reloadProjects'; + + public constructor( + private readonly lazyClientHost: Lazy + ) { } + + public execute() { + this.lazyClientHost.value.reloadProjects(); + } +} + +export class ReloadJavaScriptProjectsCommand implements Command { + public readonly id = 'javascript.reloadProjects'; + + public constructor( + private readonly lazyClientHost: Lazy + ) { } + + public execute() { + this.lazyClientHost.value.reloadProjects(); + } +} diff --git a/extensions/typescript-language-features/src/commands/restartTsServer.ts b/extensions/typescript-language-features/src/commands/restartTsServer.ts new file mode 100644 index 000000000..a357224d3 --- /dev/null +++ b/extensions/typescript-language-features/src/commands/restartTsServer.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import TypeScriptServiceClientHost from '../typeScriptServiceClientHost'; +import { Command } from '../utils/commandManager'; +import { Lazy } from '../utils/lazy'; + +export class RestartTsServerCommand implements Command { + public readonly id = 'typescript.restartTsServer'; + + public constructor( + private readonly lazyClientHost: Lazy + ) { } + + public execute() { + this.lazyClientHost.value.serviceClient.restartTsServer(); + } +} diff --git a/extensions/typescript-language-features/src/commands/selectTypeScriptVersion.ts b/extensions/typescript-language-features/src/commands/selectTypeScriptVersion.ts new file mode 100644 index 000000000..dc771df8b --- /dev/null +++ b/extensions/typescript-language-features/src/commands/selectTypeScriptVersion.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import TypeScriptServiceClientHost from '../typeScriptServiceClientHost'; +import { Command } from '../utils/commandManager'; +import { Lazy } from '../utils/lazy'; + +export class SelectTypeScriptVersionCommand implements Command { + public readonly id = 'typescript.selectTypeScriptVersion'; + + public constructor( + private readonly lazyClientHost: Lazy + ) { } + + public execute() { + this.lazyClientHost.value.serviceClient.onVersionStatusClicked(); + } +} diff --git a/extensions/typescript-language-features/src/extension.ts b/extensions/typescript-language-features/src/extension.ts index 48415ef1f..782aa9810 100644 --- a/extensions/typescript-language-features/src/extension.ts +++ b/extensions/typescript-language-features/src/extension.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as commands from './commands'; +import { Api, getExtensionApi } from './api'; +import { registerCommands } from './commands/index'; import { LanguageConfigurationManager } from './features/languageConfiguration'; import TypeScriptTaskProviderManager from './features/task'; import TypeScriptServiceClientHost from './typeScriptServiceClientHost'; @@ -15,22 +16,27 @@ import { standardLanguageDescriptions } from './utils/languageDescription'; import { lazy, Lazy } from './utils/lazy'; import LogDirectoryProvider from './utils/logDirectoryProvider'; import ManagedFileContextManager from './utils/managedFileContext'; -import { getContributedTypeScriptServerPlugins, TypeScriptServerPlugin } from './utils/plugins'; +import { PluginManager } from './utils/plugins'; import * as ProjectStatus from './utils/projectStatus'; import { Surveyor } from './utils/surveyor'; - export function activate( context: vscode.ExtensionContext -): void { - const plugins = getContributedTypeScriptServerPlugins(); +): Api { + const pluginManager = new PluginManager(); + context.subscriptions.push(pluginManager); const commandManager = new CommandManager(); context.subscriptions.push(commandManager); - const lazyClientHost = createLazyClientHost(context, plugins, commandManager); + const onCompletionAccepted = new vscode.EventEmitter(); + context.subscriptions.push(onCompletionAccepted); + + const lazyClientHost = createLazyClientHost(context, pluginManager, commandManager, item => { + onCompletionAccepted.fire(item); + }); - registerCommands(commandManager, lazyClientHost); + registerCommands(commandManager, lazyClientHost, pluginManager); context.subscriptions.push(new TypeScriptTaskProviderManager(lazyClientHost.map(x => x.serviceClient))); context.subscriptions.push(new LanguageConfigurationManager()); @@ -38,36 +44,16 @@ export function activate( context.subscriptions.push(module.register()); }); - const supportedLanguage = flatten([ - ...standardLanguageDescriptions.map(x => x.modeIds), - ...plugins.map(x => x.languages) - ]); - function didOpenTextDocument(textDocument: vscode.TextDocument): boolean { - if (isSupportedDocument(supportedLanguage, textDocument)) { - openListener.dispose(); - // Force activation - // tslint:disable-next-line:no-unused-expression - void lazyClientHost.value; + context.subscriptions.push(lazilyActivateClient(lazyClientHost, pluginManager)); - context.subscriptions.push(new ManagedFileContextManager(resource => { - return lazyClientHost.value.serviceClient.toPath(resource); - })); - return true; - } - return false; - } - const openListener = vscode.workspace.onDidOpenTextDocument(didOpenTextDocument, undefined, context.subscriptions); - for (const textDocument of vscode.workspace.textDocuments) { - if (didOpenTextDocument(textDocument)) { - break; - } - } + return getExtensionApi(onCompletionAccepted.event, pluginManager); } function createLazyClientHost( context: vscode.ExtensionContext, - plugins: TypeScriptServerPlugin[], - commandManager: CommandManager + pluginManager: PluginManager, + commandManager: CommandManager, + onCompletionAccepted: (item: vscode.CompletionItem) => void, ): Lazy { return lazy(() => { const logDirectoryProvider = new LogDirectoryProvider(context); @@ -75,16 +61,14 @@ function createLazyClientHost( const clientHost = new TypeScriptServiceClientHost( standardLanguageDescriptions, context.workspaceState, - plugins, + pluginManager, commandManager, - logDirectoryProvider); + logDirectoryProvider, + onCompletionAccepted); context.subscriptions.push(clientHost); - const surveyor = new Surveyor(context.globalState); - context.subscriptions.push(clientHost.serviceClient.onSurveyReady(e => surveyor.surveyReady(e.surveyId))); - - + context.subscriptions.push(new Surveyor(context.globalState, clientHost.serviceClient)); clientHost.serviceClient.onReady(() => { context.subscriptions.push( @@ -97,17 +81,43 @@ function createLazyClientHost( }); } -function registerCommands( - commandManager: CommandManager, - lazyClientHost: Lazy +function lazilyActivateClient( + lazyClientHost: Lazy, + pluginManager: PluginManager, ) { - commandManager.register(new commands.ReloadTypeScriptProjectsCommand(lazyClientHost)); - commandManager.register(new commands.ReloadJavaScriptProjectsCommand(lazyClientHost)); - commandManager.register(new commands.SelectTypeScriptVersionCommand(lazyClientHost)); - commandManager.register(new commands.OpenTsServerLogCommand(lazyClientHost)); - commandManager.register(new commands.RestartTsServerCommand(lazyClientHost)); - commandManager.register(new commands.TypeScriptGoToProjectConfigCommand(lazyClientHost)); - commandManager.register(new commands.JavaScriptGoToProjectConfigCommand(lazyClientHost)); + const disposables: vscode.Disposable[] = []; + + const supportedLanguage = flatten([ + ...standardLanguageDescriptions.map(x => x.modeIds), + ...pluginManager.plugins.map(x => x.languages) + ]); + + let hasActivated = false; + const maybeActivate = (textDocument: vscode.TextDocument): boolean => { + if (!hasActivated && isSupportedDocument(supportedLanguage, textDocument)) { + hasActivated = true; + // Force activation + // tslint:disable-next-line:no-unused-expression + void lazyClientHost.value; + + disposables.push(new ManagedFileContextManager(resource => { + return lazyClientHost.value.serviceClient.toPath(resource); + })); + return true; + } + return false; + }; + + const didActivate = vscode.workspace.textDocuments.some(maybeActivate); + if (!didActivate) { + const openListener = vscode.workspace.onDidOpenTextDocument(doc => { + if (maybeActivate(doc)) { + openListener.dispose(); + } + }, undefined, disposables); + } + + return vscode.Disposable.from(...disposables); } function isSupportedDocument( diff --git a/extensions/typescript-language-features/src/features/baseCodeLensProvider.ts b/extensions/typescript-language-features/src/features/baseCodeLensProvider.ts index 6ed458083..ba25a71bc 100644 --- a/extensions/typescript-language-features/src/features/baseCodeLensProvider.ts +++ b/extensions/typescript-language-features/src/features/baseCodeLensProvider.ts @@ -9,7 +9,6 @@ import { ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; import { escapeRegExp } from '../utils/regexp'; import * as typeConverters from '../utils/typeConverters'; - export class ReferencesCodeLens extends vscode.CodeLens { constructor( public document: vscode.Uri, @@ -20,14 +19,14 @@ export class ReferencesCodeLens extends vscode.CodeLens { } } -export class CachedNavTreeResponse { - private response?: Promise>; +export class CachedResponse { + private response?: Promise>; private version: number = -1; private document: string = ''; public execute( document: vscode.TextDocument, - f: () => Promise> + f: () => Promise> ) { if (this.matches(document)) { return this.response; @@ -42,8 +41,8 @@ export class CachedNavTreeResponse { private update( document: vscode.TextDocument, - response: Promise> - ): Promise> { + response: Promise> + ): Promise> { this.response = response; this.version = document.version; this.document = document.uri.toString(); @@ -56,7 +55,7 @@ export abstract class TypeScriptBaseCodeLensProvider implements vscode.CodeLensP public constructor( protected client: ITypeScriptServiceClient, - private cachedResponse: CachedNavTreeResponse + private cachedResponse: CachedResponse ) { } public get onDidChangeCodeLenses(): vscode.Event { @@ -105,31 +104,31 @@ export abstract class TypeScriptBaseCodeLensProvider implements vscode.CodeLensP (item.childItems || []).forEach(child => this.walkNavTree(document, child, item, results)); } - protected getSymbolRange(document: vscode.TextDocument, item: Proto.NavigationTree): vscode.Range | null { - if (!item) { - return null; - } +} - // TS 3.0+ provides a span for just the symbol - if (item.nameSpan) { - return typeConverters.Range.fromTextSpan((item as any).nameSpan); - } +export function getSymbolRange( + document: vscode.TextDocument, + item: Proto.NavigationTree +): vscode.Range | null { + // TS 3.0+ provides a span for just the symbol + if (item.nameSpan) { + return typeConverters.Range.fromTextSpan(item.nameSpan); + } - // In older versions, we have to calculate this manually. See #23924 - const span = item.spans && item.spans[0]; - if (!span) { - return null; - } + // In older versions, we have to calculate this manually. See #23924 + const span = item.spans && item.spans[0]; + if (!span) { + return null; + } - const range = typeConverters.Range.fromTextSpan(span); - const text = document.getText(range); + const range = typeConverters.Range.fromTextSpan(span); + const text = document.getText(range); - const identifierMatch = new RegExp(`^(.*?(\\b|\\W))${escapeRegExp(item.text || '')}(\\b|\\W)`, 'gm'); - const match = identifierMatch.exec(text); - const prefixLength = match ? match.index + match[1].length : 0; - const startOffset = document.offsetAt(new vscode.Position(range.start.line, range.start.character)) + prefixLength; - return new vscode.Range( - document.positionAt(startOffset), - document.positionAt(startOffset + item.text.length)); - } + const identifierMatch = new RegExp(`^(.*?(\\b|\\W))${escapeRegExp(item.text || '')}(\\b|\\W)`, 'gm'); + const match = identifierMatch.exec(text); + const prefixLength = match ? match.index + match[1].length : 0; + const startOffset = document.offsetAt(new vscode.Position(range.start.line, range.start.character)) + prefixLength; + return new vscode.Range( + document.positionAt(startOffset), + document.positionAt(startOffset + item.text.length)); } diff --git a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts index d31b83970..f84f9d31a 100644 --- a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts @@ -55,7 +55,7 @@ class SyncedBuffer { } if (this.client.apiVersion.gte(API.v240)) { - const tsPluginsForDocument = this.client.plugins + const tsPluginsForDocument = this.client.pluginManager.plugins .filter(x => x.languages.indexOf(this.document.languageId) >= 0); if (tsPluginsForDocument.length) { diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/features/completions.ts index 01f598b5b..bc1a27d63 100644 --- a/extensions/typescript-language-features/src/features/completions.ts +++ b/extensions/typescript-language-features/src/features/completions.ts @@ -21,7 +21,6 @@ import FileConfigurationManager from './fileConfigurationManager'; const localize = nls.loadMessageBundle(); - interface CommitCharactersSettings { readonly isNewIdentifierLocation: boolean; readonly isInValidCommitCharacterContext: boolean; @@ -37,7 +36,8 @@ class MyCompletionItem extends vscode.CompletionItem { line: string, public readonly tsEntry: Proto.CompletionEntry, useCodeSnippetsOnMethodSuggest: boolean, - public readonly commitCharactersSettings: CommitCharactersSettings + public readonly commitCharactersSettings: CommitCharactersSettings, + public readonly metadata: any | undefined, ) { super(tsEntry.name, MyCompletionItem.convertKind(tsEntry.kind)); @@ -76,15 +76,36 @@ class MyCompletionItem extends vscode.CompletionItem { } } - if (tsEntry.kindModifiers && tsEntry.kindModifiers.match(/\boptional\b/)) { - if (!this.insertText) { - this.insertText = this.label; + if (tsEntry.kindModifiers) { + const kindModifiers = new Set(tsEntry.kindModifiers.split(/\s+/g)); + + if (kindModifiers.has(PConst.KindModifiers.optional)) { + if (!this.insertText) { + this.insertText = this.label; + } + + if (!this.filterText) { + this.filterText = this.label; + } + this.label += '?'; } - if (!this.filterText) { - this.filterText = this.label; + if (kindModifiers.has(PConst.KindModifiers.color)) { + this.kind = vscode.CompletionItemKind.Color; + } + + if (tsEntry.kind === PConst.Kind.script) { + for (const extModifier of PConst.KindModifiers.fileExtensionKindModifiers) { + if (kindModifiers.has(extModifier)) { + if (tsEntry.name.toLowerCase().endsWith(extModifier)) { + this.detail = tsEntry.name; + } else { + this.detail = tsEntry.name + extModifier; + } + break; + } + } } - this.label += '?'; } this.resolveRange(line); } @@ -191,6 +212,30 @@ class MyCompletionItem extends vscode.CompletionItem { } } +class CompositeCommand implements Command { + public static readonly ID = '_typescript.composite'; + public readonly id = CompositeCommand.ID; + + public execute(...commands: vscode.Command[]) { + for (const command of commands) { + vscode.commands.executeCommand(command.command, ...(command.arguments || [])); + } + } +} + +class CompletionAcceptedCommand implements Command { + public static readonly ID = '_typescript.onCompletionAccepted'; + public readonly id = CompletionAcceptedCommand.ID; + + public constructor( + private readonly onCompletionAccepted: (item: vscode.CompletionItem) => void, + ) { } + + public execute(item: vscode.CompletionItem) { + this.onCompletionAccepted(item); + } +} + class ApplyCompletionCodeActionCommand implements Command { public static readonly ID = '_typescript.applyCompletionCodeAction'; public readonly id = ApplyCompletionCodeActionCommand.ID; @@ -270,9 +315,12 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider private readonly modeId: string, private readonly typingsStatus: TypingsStatus, private readonly fileConfigurationManager: FileConfigurationManager, - commandManager: CommandManager + commandManager: CommandManager, + onCompletionAccepted: (item: vscode.CompletionItem) => void ) { commandManager.register(new ApplyCompletionCodeActionCommand(this.client)); + commandManager.register(new CompositeCommand()); + commandManager.register(new CompletionAcceptedCommand(onCompletionAccepted)); } public async provideCompletionItems( @@ -280,9 +328,9 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext - ): Promise { + ): Promise { if (this.typingsStatus.isAcquiringTypings) { - return Promise.reject({ + return Promise.reject({ label: localize( { key: 'acquiringTypingsLabel', comment: ['Typings refers to the *.d.ts typings files that power our IntelliSense. It should not be localized'] }, 'Acquiring typings...'), @@ -310,35 +358,41 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider ...typeConverters.Position.toFileLocationRequestArgs(file, position), includeExternalModuleExports: completionConfiguration.autoImportSuggestions, includeInsertTextCompletions: true, - triggerCharacter: this.getTsTriggerCharacter(context) + triggerCharacter: this.getTsTriggerCharacter(context), }; let isNewIdentifierLocation = true; - let msg: ReadonlyArray | undefined = undefined; + let isIncomplete = false; + let entries: ReadonlyArray; + let metadata: any | undefined; if (this.client.apiVersion.gte(API.v300)) { const response = await this.client.interuptGetErr(() => this.client.execute('completionInfo', args, token)); if (response.type !== 'response' || !response.body) { return null; } isNewIdentifierLocation = response.body.isNewIdentifierLocation; - msg = response.body.entries; + isIncomplete = (response as any).metadata && (response as any).metadata.isIncomplete; + entries = response.body.entries; + metadata = response.metadata; } else { const response = await this.client.interuptGetErr(() => this.client.execute('completions', args, token)); if (response.type !== 'response' || !response.body) { return null; } - msg = response.body; + entries = response.body; + metadata = response.metadata; } const isInValidCommitCharacterContext = this.isInValidCommitCharacterContext(document, position); - return msg + const items = entries .filter(entry => !shouldExcludeCompletionEntry(entry, completionConfiguration)) .map(entry => new MyCompletionItem(position, document, line.text, entry, completionConfiguration.useCodeSnippetsOnMethodSuggest, { isNewIdentifierLocation, isInValidCommitCharacterContext, enableCallCompletions: !completionConfiguration.useCodeSnippetsOnMethodSuggest - })); + }, metadata)); + return new vscode.CompletionList(items, isIncomplete); } private getTsTriggerCharacter(context: vscode.CompletionContext): Proto.CompletionsTriggerCharacter | undefined { @@ -372,30 +426,46 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider ] }; - let details: Proto.CompletionEntryDetails[] | undefined; const response = await this.client.execute('completionEntryDetails', args, token); - if (response.type !== 'response') { + if (response.type !== 'response' || !response.body) { return item; } - const { body } = response; - details = body; - if (!details || !details.length || !details[0]) { - return item; + const detail = response.body[0]; + + if (!item.detail && detail.displayParts.length) { + item.detail = Previewer.plain(detail.displayParts); } - const detail = details[0]; - item.detail = detail.displayParts.length ? Previewer.plain(detail.displayParts) : undefined; item.documentation = this.getDocumentation(detail, item); - const { command, additionalTextEdits } = this.getCodeActions(detail, filepath); - item.command = command; - item.additionalTextEdits = additionalTextEdits; + const codeAction = this.getCodeActions(detail, filepath); + const commands: vscode.Command[] = [{ + command: CompletionAcceptedCommand.ID, + title: '', + arguments: [item] + }]; + if (codeAction.command) { + commands.push(codeAction.command); + } + item.additionalTextEdits = codeAction.additionalTextEdits; if (detail && item.useCodeSnippet) { const shouldCompleteFunction = await this.isValidFunctionCompletionContext(filepath, item.position, token); if (shouldCompleteFunction) { item.insertText = this.snippetForFunctionCall(item, detail); - item.command = { title: 'triggerParameterHints', command: 'editor.action.triggerParameterHints' }; + commands.push({ title: 'triggerParameterHints', command: 'editor.action.triggerParameterHints' }); + } + } + + if (commands.length) { + if (commands.length === 1) { + item.command = commands[0]; + } else { + item.command = { + command: CompositeCommand.ID, + title: '', + arguments: commands + }; } } @@ -456,14 +526,16 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider document: vscode.TextDocument, position: vscode.Position ): boolean { - // TODO: Workaround for https://github.com/Microsoft/TypeScript/issues/13456 - // Only enable dot completions when previous character is an identifier. - // Prevents incorrectly completing while typing spread operators. - if (position.character > 1) { - const preText = document.getText(new vscode.Range( - position.line, 0, - position.line, position.character)); - return preText.match(/(^|[a-z_$\(\)\[\]\{\}]|[^.]\.)\s*$/ig) !== null; + if (this.client.apiVersion.lt(API.v320)) { + // Workaround for https://github.com/Microsoft/TypeScript/issues/27742 + // Only enable dot completions when previous character not a dot preceeded by whitespace. + // Prevents incorrectly completing while typing spread operators. + if (position.character > 1) { + const preText = document.getText(new vscode.Range( + position.line, 0, + position.line, position.character)); + return preText.match(/(\s|^)\.$/ig) === null; + } } return true; @@ -628,9 +700,10 @@ export function register( typingsStatus: TypingsStatus, fileConfigurationManager: FileConfigurationManager, commandManager: CommandManager, + onCompletionAccepted: (item: vscode.CompletionItem) => void ) { return new ConfigurationDependentRegistration(modeId, 'suggest.enabled', () => vscode.languages.registerCompletionItemProvider(selector, - new TypeScriptCompletionItemProvider(client, modeId, typingsStatus, fileConfigurationManager, commandManager), + new TypeScriptCompletionItemProvider(client, modeId, typingsStatus, fileConfigurationManager, commandManager, onCompletionAccepted), ...TypeScriptCompletionItemProvider.triggerCharacters)); } diff --git a/extensions/typescript-language-features/src/features/definitions.ts b/extensions/typescript-language-features/src/features/definitions.ts index 94ad4593e..7f085ef93 100644 --- a/extensions/typescript-language-features/src/features/definitions.ts +++ b/extensions/typescript-language-features/src/features/definitions.ts @@ -37,11 +37,11 @@ export default class TypeScriptDefinitionProvider extends DefinitionProviderBase return response.body.definitions .map(location => { const target = typeConverters.Location.fromTextSpan(this.client.toResource(location.file), location); - return { + return { originSelectionRange: span, targetRange: target.range, targetUri: target.uri, - }; + } as vscode.DefinitionLink; }); } diff --git a/extensions/typescript-language-features/src/features/diagnostics.ts b/extensions/typescript-language-features/src/features/diagnostics.ts index a37cd310c..4dc7a5272 100644 --- a/extensions/typescript-language-features/src/features/diagnostics.ts +++ b/extensions/typescript-language-features/src/features/diagnostics.ts @@ -10,7 +10,7 @@ import { DiagnosticLanguage, allDiagnosticLangauges } from '../utils/languageDes export const enum DiagnosticKind { Syntax, Semantic, - Suggestion + Suggestion, } class FileDiagnostics { @@ -97,7 +97,7 @@ class DiagnosticSettings { public setValidate(language: DiagnosticLanguage, value: boolean): boolean { return this.update(language, settings => ({ validate: value, - enableSuggestions: settings.enableSuggestions + enableSuggestions: settings.enableSuggestions, })); } diff --git a/extensions/typescript-language-features/src/features/documentHighlight.ts b/extensions/typescript-language-features/src/features/documentHighlight.ts index 15a94df2c..b81ead9e0 100644 --- a/extensions/typescript-language-features/src/features/documentHighlight.ts +++ b/extensions/typescript-language-features/src/features/documentHighlight.ts @@ -8,8 +8,6 @@ import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import * as typeConverters from '../utils/typeConverters'; - - class TypeScriptDocumentHighlightProvider implements vscode.DocumentHighlightProvider { public constructor( private readonly client: ITypeScriptServiceClient @@ -26,21 +24,21 @@ class TypeScriptDocumentHighlightProvider implements vscode.DocumentHighlightPro } const args = typeConverters.Position.toFileLocationRequestArgs(file, position); - const response = await this.client.execute('occurrences', args, token); + const response = await this.client.execute('references', args, token); if (response.type !== 'response' || !response.body) { return []; } - return response.body - .filter(x => !x.isInString) - .map(documentHighlightFromOccurance); + return response.body.refs + .filter(ref => ref.file === file) + .map(documentHighlightFromReference); } } -function documentHighlightFromOccurance(occurrence: Proto.OccurrencesResponseItem): vscode.DocumentHighlight { +function documentHighlightFromReference(reference: Proto.ReferencesResponseItem): vscode.DocumentHighlight { return new vscode.DocumentHighlight( - typeConverters.Range.fromTextSpan(occurrence), - occurrence.isWriteAccess ? vscode.DocumentHighlightKind.Write : vscode.DocumentHighlightKind.Read); + typeConverters.Range.fromTextSpan(reference), + reference.isWriteAccess ? vscode.DocumentHighlightKind.Write : vscode.DocumentHighlightKind.Read); } export function register( diff --git a/extensions/typescript-language-features/src/features/documentSymbol.ts b/extensions/typescript-language-features/src/features/documentSymbol.ts index a0222683f..cc66470ee 100644 --- a/extensions/typescript-language-features/src/features/documentSymbol.ts +++ b/extensions/typescript-language-features/src/features/documentSymbol.ts @@ -99,5 +99,5 @@ export function register( client: ITypeScriptServiceClient, ) { return vscode.languages.registerDocumentSymbolProvider(selector, - new TypeScriptDocumentSymbolProvider(client)); + new TypeScriptDocumentSymbolProvider(client), { label: 'TypeScript' }); } diff --git a/extensions/typescript-language-features/src/features/fileConfigurationManager.ts b/extensions/typescript-language-features/src/features/fileConfigurationManager.ts index 043a6009a..64c0324cf 100644 --- a/extensions/typescript-language-features/src/features/fileConfigurationManager.ts +++ b/extensions/typescript-language-features/src/features/fileConfigurationManager.ts @@ -41,7 +41,7 @@ export default class FileConfigurationManager { public constructor( private readonly client: ITypeScriptServiceClient ) { - this.onDidCloseTextDocumentSub = vscode.workspace.onDidCloseTextDocument((textDocument) => { + this.onDidCloseTextDocumentSub = vscode.workspace.onDidCloseTextDocument(textDocument => { // When a document gets closed delete the cached formatting options. // This is necessary since the tsserver now closed a project when its // last file in it closes which drops the stored formatting options diff --git a/extensions/typescript-language-features/src/features/implementationsCodeLens.ts b/extensions/typescript-language-features/src/features/implementationsCodeLens.ts index 32559e09e..1d352dc4f 100644 --- a/extensions/typescript-language-features/src/features/implementationsCodeLens.ts +++ b/extensions/typescript-language-features/src/features/implementationsCodeLens.ts @@ -10,7 +10,7 @@ import * as PConst from '../protocol.const'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { ConfigurationDependentRegistration, VersionDependentRegistration } from '../utils/dependentRegistration'; -import { CachedNavTreeResponse, ReferencesCodeLens, TypeScriptBaseCodeLensProvider } from './baseCodeLensProvider'; +import { CachedResponse, ReferencesCodeLens, TypeScriptBaseCodeLensProvider, getSymbolRange } from './baseCodeLensProvider'; const localize = nls.loadMessageBundle(); export default class TypeScriptImplementationsCodeLensProvider extends TypeScriptBaseCodeLensProvider { @@ -58,7 +58,7 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip ): vscode.Range | null { switch (item.kind) { case PConst.Kind.interface: - return super.getSymbolRange(document, item); + return getSymbolRange(document, item); case PConst.Kind.class: case PConst.Kind.memberFunction: @@ -66,7 +66,7 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip case PConst.Kind.memberGetAccessor: case PConst.Kind.memberSetAccessor: if (item.kindModifiers.match(/\babstract\b/g)) { - return super.getSymbolRange(document, item); + return getSymbolRange(document, item); } break; } @@ -78,7 +78,7 @@ export function register( selector: vscode.DocumentSelector, modeId: string, client: ITypeScriptServiceClient, - cachedResponse: CachedNavTreeResponse, + cachedResponse: CachedResponse, ) { return new VersionDependentRegistration(client, API.v220, () => new ConfigurationDependentRegistration(modeId, 'implementationsCodeLens.enabled', () => { diff --git a/extensions/typescript-language-features/src/features/languageConfiguration.ts b/extensions/typescript-language-features/src/features/languageConfiguration.ts index 99515964e..33691a615 100644 --- a/extensions/typescript-language-features/src/features/languageConfiguration.ts +++ b/extensions/typescript-language-features/src/features/languageConfiguration.ts @@ -23,25 +23,25 @@ const jsTsLanguageConfiguration: vscode.LanguageConfiguration = { // e.g. /** | */ beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/, afterText: /^\s*\*\/$/, - action: { indentAction: vscode.IndentAction.IndentOutdent, appendText: ' * ' } + action: { indentAction: vscode.IndentAction.IndentOutdent, appendText: ' * ' }, }, { // e.g. /** ...| beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/, - action: { indentAction: vscode.IndentAction.None, appendText: ' * ' } + action: { indentAction: vscode.IndentAction.None, appendText: ' * ' }, }, { // e.g. * ...| beforeText: /^(\t|[ ])*[ ]\*([ ]([^\*]|\*(?!\/))*)?$/, oneLineAboveText: /^(\s*(\/\*\*|\*)).*/, - action: { indentAction: vscode.IndentAction.None, appendText: '* ' } + action: { indentAction: vscode.IndentAction.None, appendText: '* ' }, }, { // e.g. */| beforeText: /^(\t|[ ])*[ ]\*\/\s*$/, - action: { indentAction: vscode.IndentAction.None, removeText: 1 } + action: { indentAction: vscode.IndentAction.None, removeText: 1 }, }, { // e.g. *-----*/| beforeText: /^(\t|[ ])*[ ]\*[^/]*\*\/\s*$/, - action: { indentAction: vscode.IndentAction.None, removeText: 1 } + action: { indentAction: vscode.IndentAction.None, removeText: 1 }, } ] }; diff --git a/extensions/typescript-language-features/src/features/quickFix.ts b/extensions/typescript-language-features/src/features/quickFix.ts index 1dd8135ad..23f428092 100644 --- a/extensions/typescript-language-features/src/features/quickFix.ts +++ b/extensions/typescript-language-features/src/features/quickFix.ts @@ -82,7 +82,7 @@ class ApplyFixAllCodeAction implements Command { type: 'file', args: { file } }, - fixId: tsAction.fixId + fixId: tsAction.fixId, }; const response = await this.client.execute('getCombinedCodeFix', args, nulToken); diff --git a/extensions/typescript-language-features/src/features/refactor.ts b/extensions/typescript-language-features/src/features/refactor.ts index 3e49abaae..c0ad9cb63 100644 --- a/extensions/typescript-language-features/src/features/refactor.ts +++ b/extensions/typescript-language-features/src/features/refactor.ts @@ -40,13 +40,13 @@ class ApplyRefactoringCommand implements Command { } */ this.telemetryReporter.logTelemetry('refactor.execute', { - action: action + action: action, }); const args: Proto.GetEditsForRefactorRequestArgs = { ...typeConverters.Range.toFileRangeRequestArgs(file, range), refactor, - action + action, }; const response = await this.client.execute('getEditsForRefactor', args, nulToken); if (response.type !== 'response' || !response.body || !response.body.edits.length) { @@ -94,7 +94,7 @@ class SelectRefactorCommand implements Command { ): Promise { const selected = await vscode.window.showQuickPick(info.actions.map((action): vscode.QuickPickItem => ({ label: action.name, - description: action.description + description: action.description, }))); if (!selected) { return false; @@ -119,7 +119,7 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { } public static readonly metadata: vscode.CodeActionProviderMetadata = { - providedCodeActionKinds: [vscode.CodeActionKind.Refactor] + providedCodeActionKinds: [vscode.CodeActionKind.Refactor], }; public async provideCodeActions( diff --git a/extensions/typescript-language-features/src/features/referencesCodeLens.ts b/extensions/typescript-language-features/src/features/referencesCodeLens.ts index ea227ac99..2fecabafb 100644 --- a/extensions/typescript-language-features/src/features/referencesCodeLens.ts +++ b/extensions/typescript-language-features/src/features/referencesCodeLens.ts @@ -10,7 +10,7 @@ import * as PConst from '../protocol.const'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { ConfigurationDependentRegistration, VersionDependentRegistration } from '../utils/dependentRegistration'; -import { CachedNavTreeResponse, ReferencesCodeLens, TypeScriptBaseCodeLensProvider } from './baseCodeLensProvider'; +import { CachedResponse, ReferencesCodeLens, TypeScriptBaseCodeLensProvider, getSymbolRange } from './baseCodeLensProvider'; const localize = nls.loadMessageBundle(); @@ -51,7 +51,7 @@ class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLensProvide parent: Proto.NavigationTree | null ): vscode.Range | null { if (parent && parent.kind === PConst.Kind.enum) { - return super.getSymbolRange(document, item); + return getSymbolRange(document, item); } switch (item.kind) { @@ -79,7 +79,7 @@ class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLensProvide case PConst.Kind.interface: case PConst.Kind.type: case PConst.Kind.enum: - return super.getSymbolRange(document, item); + return getSymbolRange(document, item); } return null; @@ -90,7 +90,7 @@ export function register( selector: vscode.DocumentSelector, modeId: string, client: ITypeScriptServiceClient, - cachedResponse: CachedNavTreeResponse, + cachedResponse: CachedResponse, ) { return new VersionDependentRegistration(client, API.v206, () => new ConfigurationDependentRegistration(modeId, 'referencesCodeLens.enabled', () => { diff --git a/extensions/typescript-language-features/src/features/rename.ts b/extensions/typescript-language-features/src/features/rename.ts index 8d0ceeeff..a82bcb6cc 100644 --- a/extensions/typescript-language-features/src/features/rename.ts +++ b/extensions/typescript-language-features/src/features/rename.ts @@ -11,14 +11,6 @@ import { ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; import API from '../utils/api'; import * as typeConverters from '../utils/typeConverters'; -// TODO: Remove when we pick up TS 3.2 -declare module '../protocol' { - interface RenameTextSpan extends Proto.TextSpan { - readonly prefixText?: string; - readonly suffixText?: string; - } -} - const localize = nls.loadMessageBundle(); class TypeScriptRenameProvider implements vscode.RenameProvider { diff --git a/extensions/typescript-language-features/src/features/signatureHelp.ts b/extensions/typescript-language-features/src/features/signatureHelp.ts index 001155c7e..8eac3a9ca 100644 --- a/extensions/typescript-language-features/src/features/signatureHelp.ts +++ b/extensions/typescript-language-features/src/features/signatureHelp.ts @@ -73,16 +73,20 @@ class TypeScriptSignatureHelpProvider implements vscode.SignatureHelpProvider { function toTsTriggerReason(context: vscode.SignatureHelpContext): Proto.SignatureHelpTriggerReason { switch (context.triggerReason) { - case vscode.SignatureHelpTriggerReason.Retrigger: - return { kind: 'retrigger' }; - case vscode.SignatureHelpTriggerReason.TriggerCharacter: if (context.triggerCharacter) { - return { kind: 'characterTyped', triggerCharacter: context.triggerCharacter as any }; + if (context.isRetrigger) { + return { kind: 'retrigger', triggerCharacter: context.triggerCharacter as any }; + } else { + return { kind: 'characterTyped', triggerCharacter: context.triggerCharacter as any }; + } } else { return { kind: 'invoked' }; } + case vscode.SignatureHelpTriggerReason.ContentChange: + return context.isRetrigger ? { kind: 'retrigger' } : { kind: 'invoked' }; + case vscode.SignatureHelpTriggerReason.Invoke: default: return { kind: 'invoked' }; diff --git a/extensions/typescript-language-features/src/features/tagClosing.ts b/extensions/typescript-language-features/src/features/tagClosing.ts index b9e59c4cd..c854db7d6 100644 --- a/extensions/typescript-language-features/src/features/tagClosing.ts +++ b/extensions/typescript-language-features/src/features/tagClosing.ts @@ -80,7 +80,6 @@ class TagClosing extends Disposable { return; } - const rangeStart = lastChange.range.start; const version = document.version; this._timeout = setTimeout(async () => { this._timeout = undefined; @@ -89,9 +88,12 @@ class TagClosing extends Disposable { return; } - let position = new vscode.Position(rangeStart.line, rangeStart.character + lastChange.text.length); - const args: Proto.JsxClosingTagRequestArgs = typeConverters.Position.toFileLocationRequestArgs(filepath, position); + const addedLines = lastChange.text.split(/\r\n|\n/g); + const position = addedLines.length <= 1 + ? lastChange.range.start.translate({ characterDelta: lastChange.text.length }) + : new vscode.Position(lastChange.range.start.line + addedLines.length - 1, addedLines[addedLines.length - 1].length); + const args: Proto.JsxClosingTagRequestArgs = typeConverters.Position.toFileLocationRequestArgs(filepath, position); this._cancel = new vscode.CancellationTokenSource(); const response = await this.client.execute('jsxClosingTag', args, this._cancel.token); if (response.type !== 'response' || !response.body) { diff --git a/extensions/typescript-language-features/src/features/task.ts b/extensions/typescript-language-features/src/features/task.ts index 62d36fbb7..6d6f1e04f 100644 --- a/extensions/typescript-language-features/src/features/task.ts +++ b/extensions/typescript-language-features/src/features/task.ts @@ -230,19 +230,7 @@ class TscTaskProvider implements vscode.TaskProvider { private getLabelForTasks(project: TSConfig): string { if (project.workspaceFolder) { - const projectFolder = project.workspaceFolder; - const workspaceFolders = vscode.workspace.workspaceFolders; - const relativePath = path.relative(project.workspaceFolder.uri.fsPath, project.path); - if (workspaceFolders && workspaceFolders.length > 1) { - // Use absolute path when we have multiple folders with the same name - if (workspaceFolders.filter(x => x.name === projectFolder.name).length > 1) { - return path.join(project.workspaceFolder.uri.fsPath, relativePath); - } else { - return path.join(project.workspaceFolder.name, relativePath); - } - } else { - return relativePath; - } + return path.relative(project.workspaceFolder.uri.fsPath, project.path); } return project.path; } diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index f91ad2aa4..66425a7c2 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -296,14 +296,14 @@ class UpdateImportsOnFileRenameHandler { start: change.start, end: { line: change.end.line, - offset: change.end.offset - match[1].length - } + offset: change.end.offset - match[1].length, + }, }; }); return { fileName: edit.fileName, - textChanges + textChanges, }; } } diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index 25cb080ec..e447baf09 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -5,7 +5,7 @@ import { basename } from 'path'; import * as vscode from 'vscode'; -import { CachedNavTreeResponse } from './features/baseCodeLensProvider'; +import { CachedResponse } from './features/baseCodeLensProvider'; import { DiagnosticKind } from './features/diagnostics'; import FileConfigurationManager from './features/fileConfigurationManager'; import TypeScriptServiceClient from './typescriptServiceClient'; @@ -29,18 +29,16 @@ export default class LanguageProvider extends Disposable { private readonly commandManager: CommandManager, private readonly telemetryReporter: TelemetryReporter, private readonly typingsStatus: TypingsStatus, - private readonly fileConfigurationManager: FileConfigurationManager + private readonly fileConfigurationManager: FileConfigurationManager, + private readonly onCompletionAccepted: (item: vscode.CompletionItem) => void, ) { super(); vscode.workspace.onDidChangeConfiguration(this.configurationChanged, this, this._disposables); this.configurationChanged(); - client.onReady(async () => { - await this.registerProviders(); - }); + client.onReady(() => this.registerProviders()); } - @memoize private get documentSelector(): vscode.DocumentFilter[] { const documentSelector = []; @@ -55,9 +53,9 @@ export default class LanguageProvider extends Disposable { private async registerProviders(): Promise { const selector = this.documentSelector; - const cachedResponse = new CachedNavTreeResponse(); + const cachedResponse = new CachedResponse(); - this._register((await import('./features/completions')).register(selector, this.description.id, this.client, this.typingsStatus, this.fileConfigurationManager, this.commandManager)); + this._register((await import('./features/completions')).register(selector, this.description.id, this.client, this.typingsStatus, this.fileConfigurationManager, this.commandManager, this.onCompletionAccepted)); this._register((await import('./features/definitions')).register(selector, this.client)); this._register((await import('./features/directiveCommentCompletions')).register(selector, this.client)); this._register((await import('./features/documentHighlight')).register(selector, this.client)); diff --git a/extensions/typescript-language-features/src/protocol.const.ts b/extensions/typescript-language-features/src/protocol.const.ts index 241bffd24..dcb1b33e1 100644 --- a/extensions/typescript-language-features/src/protocol.const.ts +++ b/extensions/typescript-language-features/src/protocol.const.ts @@ -39,4 +39,25 @@ export class DiagnosticCategory { public static readonly error = 'error'; public static readonly warning = 'warning'; public static readonly suggestion = 'suggestion'; +} + +export class KindModifiers { + public static readonly optional = 'optional'; + public static readonly color = 'color'; + + public static readonly dtsFile = '.d.ts'; + public static readonly tsFile = '.ts'; + public static readonly tsxFile = '.tsx'; + public static readonly jsFile = '.js'; + public static readonly jsxFile = '.jsx'; + public static readonly jsonFile = '.json'; + + public static readonly fileExtensionKindModifiers = [ + KindModifiers.dtsFile, + KindModifiers.tsFile, + KindModifiers.tsxFile, + KindModifiers.jsFile, + KindModifiers.jsxFile, + KindModifiers.jsonFile, + ]; } \ No newline at end of file diff --git a/extensions/typescript-language-features/src/test/jsdocSnippet.test.ts b/extensions/typescript-language-features/src/test/jsdocSnippet.test.ts index 2f796161c..7446a1500 100644 --- a/extensions/typescript-language-features/src/test/jsdocSnippet.test.ts +++ b/extensions/typescript-language-features/src/test/jsdocSnippet.test.ts @@ -75,4 +75,3 @@ suite('typescript.jsDocSnippet', () => { ].join('\n')); }); }); - diff --git a/extensions/typescript-language-features/src/test/requestQueue.test.ts b/extensions/typescript-language-features/src/test/requestQueue.test.ts new file mode 100644 index 000000000..910379268 --- /dev/null +++ b/extensions/typescript-language-features/src/test/requestQueue.test.ts @@ -0,0 +1,122 @@ +/*--------------------------------------------------------------------------------------------- + * 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 'mocha'; +import { RequestQueue, RequestQueueingType } from '../tsServer/requestQueue'; + +suite('RequestQueue', () => { + test('should be empty on creation', async () => { + const queue = new RequestQueue(); + assert.strictEqual(queue.length, 0); + assert.strictEqual(queue.dequeue(), undefined); + }); + + suite('RequestQueue.createRequest', () => { + test('should create items with increasing sequence numbers', async () => { + const queue = new RequestQueue(); + + for (let i = 0; i < 100; ++i) { + const command = `command-${i}`; + const request = queue.createRequest(command, i); + assert.strictEqual(request.seq, i); + assert.strictEqual(request.command, command); + assert.strictEqual(request.arguments, i); + } + }); + }); + + test('should queue normal requests in first in first out order', async () => { + const queue = new RequestQueue(); + assert.strictEqual(queue.length, 0); + + const request1 = queue.createRequest('a', 1); + queue.enqueue({ request: request1, expectsResponse: true, isAsync: false, queueingType: RequestQueueingType.Normal }); + assert.strictEqual(queue.length, 1); + + const request2 = queue.createRequest('b', 2); + queue.enqueue({ request: request2, expectsResponse: true, isAsync: false, queueingType: RequestQueueingType.Normal }); + assert.strictEqual(queue.length, 2); + + { + const item = queue.dequeue(); + assert.strictEqual(queue.length, 1); + assert.strictEqual(item!.request.command, 'a'); + } + { + const item = queue.dequeue(); + assert.strictEqual(queue.length, 0); + assert.strictEqual(item!.request.command, 'b'); + } + { + const item = queue.dequeue(); + assert.strictEqual(item, undefined); + assert.strictEqual(queue.length, 0); + } + }); + + test('should put normal requests in front of low priority requests', async () => { + const queue = new RequestQueue(); + assert.strictEqual(queue.length, 0); + + queue.enqueue({ request: queue.createRequest('low-1', 1), expectsResponse: true, isAsync: false, queueingType: RequestQueueingType.LowPriority }); + queue.enqueue({ request: queue.createRequest('low-2', 1), expectsResponse: true, isAsync: false, queueingType: RequestQueueingType.LowPriority }); + queue.enqueue({ request: queue.createRequest('normal-1', 2), expectsResponse: true, isAsync: false, queueingType: RequestQueueingType.Normal }); + queue.enqueue({ request: queue.createRequest('normal-2', 2), expectsResponse: true, isAsync: false, queueingType: RequestQueueingType.Normal }); + + { + const item = queue.dequeue(); + assert.strictEqual(queue.length, 3); + assert.strictEqual(item!.request.command, 'normal-1'); + } + { + const item = queue.dequeue(); + assert.strictEqual(queue.length, 2); + assert.strictEqual(item!.request.command, 'normal-2'); + } + { + const item = queue.dequeue(); + assert.strictEqual(queue.length, 1); + assert.strictEqual(item!.request.command, 'low-1'); + } + { + const item = queue.dequeue(); + assert.strictEqual(queue.length, 0); + assert.strictEqual(item!.request.command, 'low-2'); + } + }); + + test('should not push fence requests front of low priority requests', async () => { + const queue = new RequestQueue(); + assert.strictEqual(queue.length, 0); + + queue.enqueue({ request: queue.createRequest('low-1', 0), expectsResponse: true, isAsync: false, queueingType: RequestQueueingType.LowPriority }); + queue.enqueue({ request: queue.createRequest('fence', 0), expectsResponse: true, isAsync: false, queueingType: RequestQueueingType.Fence }); + queue.enqueue({ request: queue.createRequest('low-2', 0), expectsResponse: true, isAsync: false, queueingType: RequestQueueingType.LowPriority }); + queue.enqueue({ request: queue.createRequest('normal', 0), expectsResponse: true, isAsync: false, queueingType: RequestQueueingType.Normal }); + + { + const item = queue.dequeue(); + assert.strictEqual(queue.length, 3); + assert.strictEqual(item!.request.command, 'low-1'); + } + { + const item = queue.dequeue(); + assert.strictEqual(queue.length, 2); + assert.strictEqual(item!.request.command, 'fence'); + } + { + const item = queue.dequeue(); + assert.strictEqual(queue.length, 1); + assert.strictEqual(item!.request.command, 'normal'); + } + { + const item = queue.dequeue(); + assert.strictEqual(queue.length, 0); + assert.strictEqual(item!.request.command, 'low-2'); + } + }); +}); + diff --git a/extensions/typescript-language-features/src/tsServer/callbackMap.ts b/extensions/typescript-language-features/src/tsServer/callbackMap.ts new file mode 100644 index 000000000..5c187ec14 --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/callbackMap.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as Proto from '../protocol'; +import { CancelledResponse, ServerResponse } from '../typescriptService'; + +export interface CallbackItem { + readonly onSuccess: (value: R) => void; + readonly onError: (err: any) => void; + readonly startTime: number; + readonly isAsync: boolean; +} + +export class CallbackMap { + private readonly _callbacks = new Map | undefined>>(); + private readonly _asyncCallbacks = new Map | undefined>>(); + + public destroy(cause: string): void { + const cancellation = new CancelledResponse(cause); + for (const callback of this._callbacks.values()) { + callback.onSuccess(cancellation); + } + this._callbacks.clear(); + for (const callback of this._asyncCallbacks.values()) { + callback.onSuccess(cancellation); + } + this._asyncCallbacks.clear(); + } + + public add(seq: number, callback: CallbackItem | undefined>, isAsync: boolean) { + if (isAsync) { + this._asyncCallbacks.set(seq, callback); + } else { + this._callbacks.set(seq, callback); + } + } + + public fetch(seq: number): CallbackItem | undefined> | undefined { + const callback = this._callbacks.get(seq) || this._asyncCallbacks.get(seq); + this.delete(seq); + return callback; + } + + private delete(seq: number) { + if (!this._callbacks.delete(seq)) { + this._asyncCallbacks.delete(seq); + } + } +} \ No newline at end of file diff --git a/extensions/typescript-language-features/src/tsServer/requestQueue.ts b/extensions/typescript-language-features/src/tsServer/requestQueue.ts new file mode 100644 index 000000000..33dceef83 --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/requestQueue.ts @@ -0,0 +1,81 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as Proto from '../protocol'; + +export enum RequestQueueingType { + /** + * Normal request that is executed in order. + */ + Normal = 1, + + /** + * Request that normal requests jump in front of in the queue. + */ + LowPriority = 2, + + /** + * A fence that blocks request reordering. + * + * Fences are not reordered. Unlike a normal request, a fence will never jump in front of a low priority request + * in the request queue. + */ + Fence = 3, +} + +export interface RequestItem { + readonly request: Proto.Request; + readonly expectsResponse: boolean; + readonly isAsync: boolean; + readonly queueingType: RequestQueueingType; +} + +export class RequestQueue { + private readonly queue: RequestItem[] = []; + private sequenceNumber: number = 0; + + public get length(): number { + return this.queue.length; + } + + public enqueue(item: RequestItem): void { + if (item.queueingType === RequestQueueingType.Normal) { + let index = this.queue.length - 1; + while (index >= 0) { + if (this.queue[index].queueingType !== RequestQueueingType.LowPriority) { + break; + } + --index; + } + this.queue.splice(index + 1, 0, item); + } else { + // Only normal priority requests can be reordered. All other requests just go to the end. + this.queue.push(item); + } + } + + public dequeue(): RequestItem | undefined { + return this.queue.shift(); + } + + public tryDeletePendingRequest(seq: number): boolean { + for (let i = 0; i < this.queue.length; i++) { + if (this.queue[i].request.seq === seq) { + this.queue.splice(i, 1); + return true; + } + } + return false; + } + + public createRequest(command: string, args: any): Proto.Request { + return { + seq: this.sequenceNumber++, + type: 'request', + command: command, + arguments: args + }; + } +} \ No newline at end of file diff --git a/extensions/typescript-language-features/src/server.ts b/extensions/typescript-language-features/src/tsServer/server.ts similarity index 74% rename from extensions/typescript-language-features/src/server.ts rename to extensions/typescript-language-features/src/tsServer/server.ts index 38325a8d0..cde2df2c2 100644 --- a/extensions/typescript-language-features/src/server.ts +++ b/extensions/typescript-language-features/src/tsServer/server.ts @@ -7,122 +7,22 @@ import * as cp from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; -import * as Proto from './protocol'; -import { CancelledResponse, NoContentResponse, ServerResponse } from './typescriptService'; -import API from './utils/api'; -import { TsServerLogLevel, TypeScriptServiceConfiguration } from './utils/configuration'; -import { Disposable } from './utils/dispose'; -import * as electron from './utils/electron'; -import LogDirectoryProvider from './utils/logDirectoryProvider'; -import Logger from './utils/logger'; -import { TypeScriptPluginPathsProvider } from './utils/pluginPathsProvider'; -import { TypeScriptServerPlugin } from './utils/plugins'; -import TelemetryReporter from './utils/telemetry'; -import Tracer from './utils/tracer'; -import { TypeScriptVersion, TypeScriptVersionProvider } from './utils/versionProvider'; -import { Reader } from './utils/wireProtocol'; - -interface CallbackItem { - readonly onSuccess: (value: R) => void; - readonly onError: (err: any) => void; - readonly startTime: number; - readonly isAsync: boolean; -} - -class CallbackMap { - private readonly _callbacks = new Map | undefined>>(); - private readonly _asyncCallbacks = new Map | undefined>>(); - - public destroy(cause: string): void { - const cancellation = new CancelledResponse(cause); - for (const callback of this._callbacks.values()) { - callback.onSuccess(cancellation); - } - this._callbacks.clear(); - - for (const callback of this._asyncCallbacks.values()) { - callback.onSuccess(cancellation); - } - this._asyncCallbacks.clear(); - } - - public add(seq: number, callback: CallbackItem | undefined>, isAsync: boolean) { - if (isAsync) { - this._asyncCallbacks.set(seq, callback); - } else { - this._callbacks.set(seq, callback); - } - } - - - public fetch(seq: number): CallbackItem | undefined> | undefined { - const callback = this._callbacks.get(seq) || this._asyncCallbacks.get(seq); - this.delete(seq); - return callback; - } - - private delete(seq: number) { - if (!this._callbacks.delete(seq)) { - this._asyncCallbacks.delete(seq); - } - } -} - -interface RequestItem { - readonly request: Proto.Request; - readonly expectsResponse: boolean; - readonly isAsync: boolean; - readonly lowPriority?: boolean; -} - -class RequestQueue { - private readonly queue: RequestItem[] = []; - private sequenceNumber: number = 0; - - public get length(): number { - return this.queue.length; - } - - public push(item: RequestItem): void { - // insert before existing lowPriority requestItems in the queue. - if (!item.lowPriority && this.length) { - for (let i = this.length - 1; i > -1; i--) { - if (!this.queue[i].lowPriority) { - this.queue.splice(i + 1, 0, item); - return; - } - } - //if all of the items are lowPriority insert at top - this.queue.unshift(item); - return; - } - //if none is low priority just push to end - this.queue.push(item); - } - - public shift(): RequestItem | undefined { - return this.queue.shift(); - } - - public tryCancelPendingRequest(seq: number): boolean { - for (let i = 0; i < this.queue.length; i++) { - if (this.queue[i].request.seq === seq) { - this.queue.splice(i, 1); - return true; - } - } - return false; - } - - public createRequest(command: string, args: any): Proto.Request { - return { - seq: this.sequenceNumber++, - type: 'request', - command: command, - arguments: args - }; - } -} +import * as Proto from '../protocol'; +import { CancelledResponse, NoContentResponse } from '../typescriptService'; +import API from '../utils/api'; +import { TsServerLogLevel, TypeScriptServiceConfiguration } from '../utils/configuration'; +import { Disposable } from '../utils/dispose'; +import * as electron from '../utils/electron'; +import LogDirectoryProvider from '../utils/logDirectoryProvider'; +import Logger from '../utils/logger'; +import { TypeScriptPluginPathsProvider } from '../utils/pluginPathsProvider'; +import { PluginManager } from '../utils/plugins'; +import TelemetryReporter from '../utils/telemetry'; +import Tracer from '../utils/tracer'; +import { TypeScriptVersion, TypeScriptVersionProvider } from '../utils/versionProvider'; +import { Reader } from '../utils/wireProtocol'; +import { CallbackMap } from './callbackMap'; +import { RequestItem, RequestQueue, RequestQueueingType } from './requestQueue'; export class TypeScriptServerSpawner { public constructor( @@ -137,11 +37,11 @@ export class TypeScriptServerSpawner { public spawn( version: TypeScriptVersion, configuration: TypeScriptServiceConfiguration, - plugins: ReadonlyArray + pluginManager: PluginManager ): TypeScriptServer { const apiVersion = version.version || API.defaultVersion; - const { args, cancellationPipeName, tsServerLogFile } = this.getTsServerArgs(configuration, version, plugins); + const { args, cancellationPipeName, tsServerLogFile } = this.getTsServerArgs(configuration, version, pluginManager); if (TypeScriptServerSpawner.isLoggingEnabled(apiVersion, configuration)) { if (tsServerLogFile) { @@ -160,7 +60,7 @@ export class TypeScriptServerSpawner { private getForkOptions() { const debugPort = TypeScriptServerSpawner.getDebugPort(); - const tsServerForkOptions: electron.IForkOptions = { + const tsServerForkOptions: electron.ForkOptions = { execArgv: debugPort ? [`--inspect=${debugPort}`] : [], }; return tsServerForkOptions; @@ -169,11 +69,11 @@ export class TypeScriptServerSpawner { private getTsServerArgs( configuration: TypeScriptServiceConfiguration, currentVersion: TypeScriptVersion, - plugins: ReadonlyArray, + pluginManager: PluginManager, ): { args: string[], cancellationPipeName: string | undefined, tsServerLogFile: string | undefined } { const args: string[] = []; - let cancellationPipeName: string | undefined = undefined; - let tsServerLogFile: string | undefined = undefined; + let cancellationPipeName: string | undefined; + let tsServerLogFile: string | undefined; const apiVersion = currentVersion.version || API.defaultVersion; @@ -210,11 +110,11 @@ export class TypeScriptServerSpawner { if (apiVersion.gte(API.v230)) { const pluginPaths = this._pluginPathsProvider.getPluginPaths(); - if (plugins.length) { - args.push('--globalPlugins', plugins.map(x => x.name).join(',')); + if (pluginManager.plugins.length) { + args.push('--globalPlugins', pluginManager.plugins.map(x => x.name).join(',')); if (currentVersion.path === this._versionProvider.defaultVersion.path) { - pluginPaths.push(...plugins.map(x => x.path)); + pluginPaths.push(...pluginManager.plugins.map(x => x.path)); } } @@ -278,7 +178,7 @@ export class TypeScriptServer extends Disposable { private readonly _tracer: Tracer, ) { super(); - this._reader = new Reader(this._childProcess.stdout); + this._reader = this._register(new Reader(this._childProcess.stdout)); this._reader.onData(msg => this.dispatchMessage(msg)); this._childProcess.on('exit', code => this.handleExit(code)); this._childProcess.on('error', error => this.handleError(error)); @@ -353,7 +253,7 @@ export class TypeScriptServer extends Disposable { private tryCancelRequest(seq: number, command: string): boolean { try { - if (this._requestQueue.tryCancelPendingRequest(seq)) { + if (this._requestQueue.tryDeletePendingRequest(seq)) { this._tracer.logTrace(`TypeScript Server: canceled request with sequence number ${seq}`); return true; } @@ -398,10 +298,10 @@ export class TypeScriptServer extends Disposable { public executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean, lowPriority?: boolean }): Promise { const request = this._requestQueue.createRequest(command, args); const requestInfo: RequestItem = { - request: request, + request, expectsResponse: executeInfo.expectsResult, isAsync: executeInfo.isAsync, - lowPriority: executeInfo.lowPriority + queueingType: getQueueingType(command, executeInfo.lowPriority) }; let result: Promise; if (executeInfo.expectsResult) { @@ -437,7 +337,7 @@ export class TypeScriptServer extends Disposable { } else { result = Promise.resolve(null); } - this._requestQueue.push(requestInfo); + this._requestQueue.enqueue(requestInfo); this.sendNextRequests(); return result; @@ -469,7 +369,7 @@ export class TypeScriptServer extends Disposable { private sendNextRequests(): void { while (this._pendingResponses.size === 0 && this._requestQueue.length > 0) { - const item = this._requestQueue.shift(); + const item = this._requestQueue.dequeue(); if (item) { this.sendRequest(item); } @@ -505,3 +405,15 @@ export class TypeScriptServer extends Disposable { } } +const fenceCommands = new Set(['change', 'close', 'open']); + +function getQueueingType( + command: string, + lowPriority?: boolean +): RequestQueueingType { + if (fenceCommands.has(command)) { + return RequestQueueingType.Fence; + } + return lowPriority ? RequestQueueingType.LowPriority : RequestQueueingType.Normal; +} + diff --git a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts index df5613d84..9c76d5e50 100644 --- a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts +++ b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts @@ -18,9 +18,9 @@ import TypeScriptServiceClient from './typescriptServiceClient'; import API from './utils/api'; import { CommandManager } from './utils/commandManager'; import { Disposable } from './utils/dispose'; -import { LanguageDescription, DiagnosticLanguage } from './utils/languageDescription'; +import { DiagnosticLanguage, LanguageDescription } from './utils/languageDescription'; import LogDirectoryProvider from './utils/logDirectoryProvider'; -import { TypeScriptServerPlugin } from './utils/plugins'; +import { PluginManager } from './utils/plugins'; import * as typeConverters from './utils/typeConverters'; import TypingsStatus, { AtaProgressReporter } from './utils/typingsStatus'; import VersionStatus from './utils/versionStatus'; @@ -48,9 +48,10 @@ export default class TypeScriptServiceClientHost extends Disposable { constructor( descriptions: LanguageDescription[], workspaceState: vscode.Memento, - plugins: TypeScriptServerPlugin[], + pluginManager: PluginManager, private readonly commandManager: CommandManager, - logDirectoryProvider: LogDirectoryProvider + logDirectoryProvider: LogDirectoryProvider, + onCompletionAccepted: (item: vscode.CompletionItem) => void, ) { super(); const handleProjectCreateOrDelete = () => { @@ -71,7 +72,7 @@ export default class TypeScriptServiceClientHost extends Disposable { this.client = this._register(new TypeScriptServiceClient( workspaceState, version => this.versionStatus.onDidChangeTypeScriptVersion(version), - plugins, + pluginManager, logDirectoryProvider, allModeIds)); @@ -89,7 +90,7 @@ export default class TypeScriptServiceClientHost extends Disposable { this.fileConfigurationManager = this._register(new FileConfigurationManager(this.client)); for (const description of descriptions) { - const manager = new LanguageProvider(this.client, description, this.commandManager, this.client.telemetryReporter, this.typingsStatus, this.fileConfigurationManager); + const manager = new LanguageProvider(this.client, description, this.commandManager, this.client.telemetryReporter, this.typingsStatus, this.fileConfigurationManager, onCompletionAccepted); this.languages.push(manager); this._register(manager); this.languagePerId.set(description.id, manager); @@ -108,7 +109,7 @@ export default class TypeScriptServiceClientHost extends Disposable { } const languages = new Set(); - for (const plugin of plugins) { + for (const plugin of pluginManager.plugins) { for (const language of plugin.languages) { languages.add(language); } @@ -122,7 +123,7 @@ export default class TypeScriptServiceClientHost extends Disposable { diagnosticOwner: 'typescript', isExternal: true }; - const manager = new LanguageProvider(this.client, description, this.commandManager, this.client.telemetryReporter, this.typingsStatus, this.fileConfigurationManager); + const manager = new LanguageProvider(this.client, description, this.commandManager, this.client.telemetryReporter, this.typingsStatus, this.fileConfigurationManager, onCompletionAccepted); this.languages.push(manager); this._register(manager); this.languagePerId.set(description.id, manager); diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index d14201af7..ef6b7d31c 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -9,10 +9,10 @@ import * as Proto from './protocol'; import API from './utils/api'; import { TypeScriptServiceConfiguration } from './utils/configuration'; import Logger from './utils/logger'; -import { TypeScriptServerPlugin } from './utils/plugins'; +import { PluginManager } from './utils/plugins'; export class CancelledResponse { - readonly type: 'cancelled' = 'cancelled'; + public readonly type: 'cancelled' = 'cancelled'; constructor( public readonly reason: string @@ -20,7 +20,7 @@ export class CancelledResponse { } export class NoContentResponse { - readonly type: 'noContent' = 'noContent'; + public readonly type: 'noContent' = 'noContent'; } export type ServerResponse = T | CancelledResponse | NoContentResponse; @@ -57,7 +57,6 @@ interface TypeScriptRequestTypes { 'typeDefinition': [Proto.FileLocationRequestArgs, Proto.TypeDefinitionResponse]; } - export interface ITypeScriptServiceClient { /** * Convert a resource (VS Code) to a normalized path (TypeScript). @@ -87,7 +86,7 @@ export interface ITypeScriptServiceClient { readonly onTypesInstallerInitializationFailed: vscode.Event; readonly apiVersion: API; - readonly plugins: TypeScriptServerPlugin[]; + readonly pluginManager: PluginManager; readonly configuration: TypeScriptServiceConfiguration; readonly logger: Logger; readonly bufferSyncSupport: BufferSyncSupport; diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 233f0112d..4e275b7db 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -10,17 +10,16 @@ import * as nls from 'vscode-nls'; import BufferSyncSupport from './features/bufferSyncSupport'; import { DiagnosticKind, DiagnosticsManager } from './features/diagnostics'; import * as Proto from './protocol'; -import { TypeScriptServer, TypeScriptServerSpawner } from './server'; +import { TypeScriptServer, TypeScriptServerSpawner } from './tsServer/server'; import { ITypeScriptServiceClient } from './typescriptService'; import API from './utils/api'; import { TsServerLogLevel, TypeScriptServiceConfiguration } from './utils/configuration'; import { Disposable } from './utils/dispose'; import * as fileSchemes from './utils/fileSchemes'; -import * as is from './utils/is'; import LogDirectoryProvider from './utils/logDirectoryProvider'; import Logger from './utils/logger'; import { TypeScriptPluginPathsProvider } from './utils/pluginPathsProvider'; -import { TypeScriptServerPlugin } from './utils/plugins'; +import { PluginManager } from './utils/plugins'; import TelemetryReporter from './utils/telemetry'; import Tracer from './utils/tracer'; import { inferredProjectConfig } from './utils/tsconfig'; @@ -55,7 +54,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType private lastStart: number; private numberRestarts: number; private isRestarting: boolean = false; - private _tsServerLoading: { resolve: () => void, reject: () => void } | undefined; + private loadingIndicator = new ServerInitializingIndicator(); public readonly telemetryReporter: TelemetryReporter; /** @@ -74,7 +73,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType constructor( private readonly workspaceState: vscode.Memento, private readonly onDidChangeTypeScriptVersion: (version: TypeScriptVersion) => void, - public readonly plugins: TypeScriptServerPlugin[], + public readonly pluginManager: PluginManager, private readonly logDirectoryProvider: LogDirectoryProvider, allModeIds: string[] ) { @@ -132,6 +131,10 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.telemetryReporter = this._register(new TelemetryReporter(() => this._tsserverVersion || this._apiVersion.versionString)); this.typescriptServerSpawner = new TypeScriptServerSpawner(this.versionProvider, this.logDirectoryProvider, this.pluginPathsProvider, this.logger, this.telemetryReporter, this.tracer); + + this._register(this.pluginManager.onDidUpdateConfig(update => { + this.configurePlugin(update.pluginId, update.config); + })); } public get configuration() { @@ -147,10 +150,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.forkedTsServer.kill(); } - if (this._tsServerLoading) { - this._tsServerLoading.reject(); - this._tsServerLoading = undefined; - } + this.loadingIndicator.reset(); } public restartTsServer(): void { @@ -253,7 +253,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.lastError = null; let mytoken = ++this.token; - const handle = this.typescriptServerSpawner.spawn(currentVersion, this.configuration, this.plugins); + const handle = this.typescriptServerSpawner.spawn(currentVersion, this.configuration, this.pluginManager); this.lastStart = Date.now(); handle.onError((err: Error) => { @@ -319,16 +319,8 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.forkedTsServer = handle; this._onTsServerStarted.fire(currentVersion.version); - if (this._tsServerLoading) { - this._tsServerLoading.reject(); - } if (this._apiVersion.gte(API.v300)) { - vscode.window.withProgress({ - location: vscode.ProgressLocation.Window, - title: localize('serverLoading.progress', "Initializing JS/TS language features"), - }, () => new Promise((resolve, reject) => { - this._tsServerLoading = { resolve, reject }; - })); + this.loadingIndicator.startedLoadingProject(undefined /* projectName */); } this.serviceStarted(resendModels); @@ -399,13 +391,18 @@ export default class TypeScriptServiceClient extends Disposable implements IType private serviceStarted(resendModels: boolean): void { const configureOptions: Proto.ConfigureRequestArguments = { - hostInfo: 'vscode' + hostInfo: 'vscode', }; this.executeWithoutWaitingForResponse('configure', configureOptions); this.setCompilerOptionsForInferredProjects(this._configuration); if (resendModels) { this._onResendModelsRequested.fire(); } + + // Reconfigure any plugins + for (const [config, pluginName] of this.pluginManager.configurations()) { + this.configurePlugin(config, pluginName); + } } private setCompilerOptionsForInferredProjects(configuration: TypeScriptServiceConfiguration): void { @@ -429,10 +426,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType } private serviceExited(restart: boolean): void { - if (this._tsServerLoading) { - this._tsServerLoading.reject(); - this._tsServerLoading = undefined; - } + this.loadingIndicator.reset(); enum MessageAction { reportIssue @@ -459,7 +453,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType localize('serverDiedAfterStart', 'The TypeScript language service died 5 times right after it got started. The service will not be restarted.'), { title: localize('serverDiedReportIssue', 'Report Issue'), - id: MessageAction.reportIssue + id: MessageAction.reportIssue, }); /* __GDPR__ "serviceExited" : { @@ -602,13 +596,10 @@ export default class TypeScriptServiceClient extends Disposable implements IType case 'syntaxDiag': case 'semanticDiag': case 'suggestionDiag': - // This event also roughly signals that project has been loaded successfully - if (this._tsServerLoading) { - this._tsServerLoading.resolve(); - this._tsServerLoading = undefined; - } + // This event also roughly signals that the global project has been loaded successfully + this.loadingIndicator.finishedLoadingProject(undefined /* projectName */); - const diagnosticEvent: Proto.DiagnosticEvent = event; + const diagnosticEvent = event as Proto.DiagnosticEvent; if (diagnosticEvent.body && diagnosticEvent.body.diagnostics) { this._onDiagnosticsReceived.fire({ kind: getDignosticsKind(event), @@ -628,41 +619,37 @@ export default class TypeScriptServiceClient extends Disposable implements IType break; case 'projectLanguageServiceState': - if (event.body) { - this._onProjectLanguageServiceStateChanged.fire((event as Proto.ProjectLanguageServiceStateEvent).body); - } + this._onProjectLanguageServiceStateChanged.fire((event as Proto.ProjectLanguageServiceStateEvent).body); break; case 'projectsUpdatedInBackground': - if (event.body) { - const body = (event as Proto.ProjectsUpdatedInBackgroundEvent).body; - const resources = body.openFiles.map(vscode.Uri.file); - this.bufferSyncSupport.getErr(resources); - } + const body = (event as Proto.ProjectsUpdatedInBackgroundEvent).body; + const resources = body.openFiles.map(vscode.Uri.file); + this.bufferSyncSupport.getErr(resources); break; case 'beginInstallTypes': - if (event.body) { - this._onDidBeginInstallTypings.fire((event as Proto.BeginInstallTypesEvent).body); - } + this._onDidBeginInstallTypings.fire((event as Proto.BeginInstallTypesEvent).body); break; case 'endInstallTypes': - if (event.body) { - this._onDidEndInstallTypings.fire((event as Proto.EndInstallTypesEvent).body); - } + this._onDidEndInstallTypings.fire((event as Proto.EndInstallTypesEvent).body); break; case 'typesInstallerInitializationFailed': - if (event.body) { - this._onTypesInstallerInitializationFailed.fire((event as Proto.TypesInstallerInitializationFailedEvent).body); - } + this._onTypesInstallerInitializationFailed.fire((event as Proto.TypesInstallerInitializationFailedEvent).body); break; case 'surveyReady': - if (event.body) { - this._onSurveyReady.fire((event as Proto.SurveyReadyEvent).body); - } + this._onSurveyReady.fire((event as Proto.SurveyReadyEvent).body); + break; + + case 'projectLoadingStart': + this.loadingIndicator.startedLoadingProject((event as Proto.ProjectLoadingStartEvent).body.projectName); + break; + + case 'projectLoadingFinish': + this.loadingIndicator.finishedLoadingProject((event as Proto.ProjectLoadingFinishEvent).body.projectName); break; } } @@ -674,10 +661,10 @@ export default class TypeScriptServiceClient extends Disposable implements IType const typingsInstalledPayload: Proto.TypingsInstalledTelemetryEventPayload = (telemetryData.payload as Proto.TypingsInstalledTelemetryEventPayload); properties['installedPackages'] = typingsInstalledPayload.installedPackages; - if (is.defined(typingsInstalledPayload.installSuccess)) { + if (typeof typingsInstalledPayload.installSuccess === 'boolean') { properties['installSuccess'] = typingsInstalledPayload.installSuccess.toString(); } - if (is.string(typingsInstalledPayload.typingsInstallerVersion)) { + if (typeof typingsInstalledPayload.typingsInstallerVersion === 'string') { properties['typingsInstallerVersion'] = typingsInstalledPayload.typingsInstallerVersion; } break; @@ -688,7 +675,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType Object.keys(payload).forEach((key) => { try { if (payload.hasOwnProperty(key)) { - properties[key] = is.string(payload[key]) ? payload[key] : JSON.stringify(payload[key]); + properties[key] = typeof payload[key] === 'string' ? payload[key] : JSON.stringify(payload[key]); } } catch (e) { // noop @@ -719,8 +706,13 @@ export default class TypeScriptServiceClient extends Disposable implements IType this._apiVersion = API.defaultVersion; this._tsserverVersion = undefined; } -} + private configurePlugin(pluginName: string, configuration: {}): any { + if (this._apiVersion.gte(API.v314)) { + this.executeWithoutWaitingForResponse('configurePlugin', { pluginName, configuration }); + } + } +} function getDignosticsKind(event: Proto.Event) { switch (event.event) { @@ -730,3 +722,38 @@ function getDignosticsKind(event: Proto.Event) { } throw new Error('Unknown dignostics kind'); } + +class ServerInitializingIndicator extends Disposable { + private _task?: { project: string | undefined, resolve: () => void, reject: () => void }; + + public reset(): void { + if (this._task) { + this._task.reject(); + this._task = undefined; + } + } + + /** + * Signal that a project has started loading. + */ + public startedLoadingProject(projectName: string | undefined): void { + // TS projects are loaded sequentially. Cancel existing task because it should always be resolved before + // the incoming project loading task is. + this.reset(); + + vscode.window.withProgress({ + location: vscode.ProgressLocation.Window, + title: localize('serverLoading.progress', "Initializing JS/TS language features"), + }, () => new Promise((resolve, reject) => { + this._task = { project: projectName, resolve, reject }; + })); + } + + public finishedLoadingProject(projectName: string | undefined): void { + if (this._task && this._task.project === projectName) { + this._task.resolve(); + this._task = undefined; + } + } +} + diff --git a/extensions/typescript-language-features/src/utils/api.ts b/extensions/typescript-language-features/src/utils/api.ts index 9bcab14cf..0c7f5b9ff 100644 --- a/extensions/typescript-language-features/src/utils/api.ts +++ b/extensions/typescript-language-features/src/utils/api.ts @@ -31,6 +31,7 @@ export default class API { public static readonly v292 = API.fromSimpleString('2.9.2'); public static readonly v300 = API.fromSimpleString('3.0.0'); public static readonly v310 = API.fromSimpleString('3.1.0'); + public static readonly v314 = API.fromSimpleString('3.1.4'); public static readonly v320 = API.fromSimpleString('3.2.0'); diff --git a/extensions/typescript-language-features/src/utils/arrays.ts b/extensions/typescript-language-features/src/utils/arrays.ts index 3a15e3898..e8aa98a75 100644 --- a/extensions/typescript-language-features/src/utils/arrays.ts +++ b/extensions/typescript-language-features/src/utils/arrays.ts @@ -2,7 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -export function equals(one: T[], other: T[], itemEquals: (a: T, b: T) => boolean = (a, b) => a === b): boolean { +export function equals(one: ReadonlyArray, other: ReadonlyArray, itemEquals: (a: T, b: T) => boolean = (a, b) => a === b): boolean { if (one.length !== other.length) { return false; } @@ -16,6 +16,6 @@ export function equals(one: T[], other: T[], itemEquals: (a: T, b: T) => bool return true; } -export function flatten(arr: T[][]): T[] { - return [].concat.apply([], arr); +export function flatten(arr: ReadonlyArray>): T[] { + return ([] as T[]).concat.apply([], arr); } \ No newline at end of file diff --git a/extensions/typescript-language-features/src/utils/electron.ts b/extensions/typescript-language-features/src/utils/electron.ts index da11362b4..88fc9a999 100644 --- a/extensions/typescript-language-features/src/utils/electron.ts +++ b/extensions/typescript-language-features/src/utils/electron.ts @@ -38,7 +38,7 @@ function generatePatchedEnv(env: any, modulePath: string): any { return newEnv; } -export interface IForkOptions { +export interface ForkOptions { readonly cwd?: string; readonly execArgv?: string[]; } @@ -46,7 +46,7 @@ export interface IForkOptions { export function fork( modulePath: string, args: string[], - options: IForkOptions, + options: ForkOptions, ): cp.ChildProcess { const newEnv = generatePatchedEnv(process.env, modulePath); return cp.fork(modulePath, args, { diff --git a/extensions/typescript-language-features/src/utils/is.ts b/extensions/typescript-language-features/src/utils/is.ts deleted file mode 100644 index fc6fd6ad2..000000000 --- a/extensions/typescript-language-features/src/utils/is.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. - *--------------------------------------------------------------------------------------------*/ - -const toString = Object.prototype.toString; - -export function defined(value: any): boolean { - return typeof value !== 'undefined'; -} - -export function boolean(value: any): value is boolean { - return value === true || value === false; -} - -export function string(value: any): value is string { - return toString.call(value) === '[object String]'; -} diff --git a/extensions/typescript-language-features/src/utils/logger.ts b/extensions/typescript-language-features/src/utils/logger.ts index 7ba79adad..991e9d52b 100644 --- a/extensions/typescript-language-features/src/utils/logger.ts +++ b/extensions/typescript-language-features/src/utils/logger.ts @@ -5,11 +5,12 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import * as is from './is'; import { memoize } from './memoize'; const localize = nls.loadMessageBundle(); +type LogLevel = 'Trace' | 'Info' | 'Error'; + export default class Logger { @memoize @@ -19,17 +20,11 @@ export default class Logger { private data2String(data: any): string { if (data instanceof Error) { - if (is.string(data.stack)) { - return data.stack; - } - return (data as Error).message; + return data.stack || data.message; } - if (is.boolean(data.success) && !data.success && is.string(data.message)) { + if (data.success === false && data.message) { return data.message; } - if (is.string(data)) { - return data; - } return data.toString(); } @@ -37,10 +32,6 @@ export default class Logger { this.logLevel('Info', message, data); } - public warn(message: string, data?: any): void { - this.logLevel('Warn', message, data); - } - public error(message: string, data?: any): void { // See https://github.com/Microsoft/TypeScript/issues/10496 if (data && data.message === 'No content available.') { @@ -49,7 +40,7 @@ export default class Logger { this.logLevel('Error', message, data); } - public logLevel(level: string, message: string, data?: any): void { + public logLevel(level: LogLevel, message: string, data?: any): void { this.output.appendLine(`[${level} - ${(new Date().toLocaleTimeString())}] ${message}`); if (data) { this.output.appendLine(this.data2String(data)); diff --git a/extensions/typescript-language-features/src/utils/memoize.ts b/extensions/typescript-language-features/src/utils/memoize.ts index beeb0035b..12ce836dd 100644 --- a/extensions/typescript-language-features/src/utils/memoize.ts +++ b/extensions/typescript-language-features/src/utils/memoize.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ export function memoize(_target: any, key: string, descriptor: any) { - let fnKey: string | undefined = undefined; - let fn: Function | undefined = undefined; + let fnKey: string | undefined; + let fn: Function | undefined; if (typeof descriptor.value === 'function') { fnKey = 'value'; diff --git a/extensions/typescript-language-features/src/utils/plugins.ts b/extensions/typescript-language-features/src/utils/plugins.ts index e67df651b..9b398d963 100644 --- a/extensions/typescript-language-features/src/utils/plugins.ts +++ b/extensions/typescript-language-features/src/utils/plugins.ts @@ -4,26 +4,45 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import { Disposable } from './dispose'; +import { memoize } from './memoize'; export interface TypeScriptServerPlugin { readonly path: string; readonly name: string; - readonly languages: string[]; + readonly languages: ReadonlyArray; } -export function getContributedTypeScriptServerPlugins(): TypeScriptServerPlugin[] { - const plugins: TypeScriptServerPlugin[] = []; - for (const extension of vscode.extensions.all) { - const pack = extension.packageJSON; - if (pack.contributes && pack.contributes.typescriptServerPlugins && Array.isArray(pack.contributes.typescriptServerPlugins)) { - for (const plugin of pack.contributes.typescriptServerPlugins) { - plugins.push({ - name: plugin.name, - path: extension.extensionPath, - languages: Array.isArray(plugin.languages) ? plugin.languages : [], - }); +export class PluginManager extends Disposable { + private readonly _pluginConfigurations = new Map(); + + @memoize + public get plugins(): ReadonlyArray { + const plugins: TypeScriptServerPlugin[] = []; + for (const extension of vscode.extensions.all) { + const pack = extension.packageJSON; + if (pack.contributes && Array.isArray(pack.contributes.typescriptServerPlugins)) { + for (const plugin of pack.contributes.typescriptServerPlugins) { + plugins.push({ + name: plugin.name, + path: extension.extensionPath, + languages: Array.isArray(plugin.languages) ? plugin.languages : [], + }); + } } } + return plugins; } - return plugins; -} + + private readonly _onDidUpdateConfig = this._register(new vscode.EventEmitter<{ pluginId: string, config: {} }>()); + public readonly onDidUpdateConfig = this._onDidUpdateConfig.event; + + public setConfiguration(pluginId: string, config: {}) { + this._pluginConfigurations.set(pluginId, config); + this._onDidUpdateConfig.fire({ pluginId, config }); + } + + public configurations(): IterableIterator<[string, {}]> { + return this._pluginConfigurations.entries(); + } +} \ No newline at end of file diff --git a/extensions/typescript-language-features/src/utils/surveyor.ts b/extensions/typescript-language-features/src/utils/surveyor.ts index da05db553..bf3ba4ad6 100644 --- a/extensions/typescript-language-features/src/utils/surveyor.ts +++ b/extensions/typescript-language-features/src/utils/surveyor.ts @@ -5,6 +5,9 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; +import TypeScriptServiceClient from '../typescriptServiceClient'; +import { Disposable } from './dispose'; +import { memoize } from './memoize'; const localize = nls.loadMessageBundle(); @@ -121,18 +124,24 @@ class Survey { } } -export class Surveyor { - - private readonly surveys: Map; +export class Surveyor extends Disposable { public constructor( - memento: vscode.Memento + private readonly memento: vscode.Memento, + serviceClient: TypeScriptServiceClient, ) { - this.surveys = new Map(allSurveys.map(data => - [data.id, new Survey(data, memento)] as [string, Survey])); + super(); + + this._register(serviceClient.onSurveyReady(e => this.surveyReady(e.surveyId))); + } + + @memoize + private get surveys(): Map { + return new Map( + allSurveys.map(data => [data.id, new Survey(data, this.memento)] as [string, Survey])); } - public surveyReady(surveyId: string): void { + private surveyReady(surveyId: string): void { const survey = this.tryGetActiveSurvey(surveyId); if (survey && survey.trigger()) { survey.willShow(); diff --git a/extensions/typescript-language-features/src/utils/telemetry.ts b/extensions/typescript-language-features/src/utils/telemetry.ts index 4ac7a9539..e3b5f09ad 100644 --- a/extensions/typescript-language-features/src/utils/telemetry.ts +++ b/extensions/typescript-language-features/src/utils/telemetry.ts @@ -7,7 +7,7 @@ import * as vscode from 'vscode'; import VsCodeTelemetryReporter from 'vscode-extension-telemetry'; import { memoize } from './memoize'; -interface IPackageInfo { +interface PackageInfo { readonly name: string; readonly version: string; readonly aiKey: string; @@ -58,7 +58,7 @@ export default class TelemetryReporter { } @memoize - private get packageInfo(): IPackageInfo | null { + private get packageInfo(): PackageInfo | null { const { packageJSON } = vscode.extensions.getExtension('vscode.typescript-language-features')!; if (packageJSON) { return { diff --git a/extensions/typescript-language-features/src/utils/tracer.ts b/extensions/typescript-language-features/src/utils/tracer.ts index 2f2394dca..c80254c79 100644 --- a/extensions/typescript-language-features/src/utils/tracer.ts +++ b/extensions/typescript-language-features/src/utils/tracer.ts @@ -10,7 +10,7 @@ import Logger from './logger'; enum Trace { Off, Messages, - Verbose + Verbose, } namespace Trace { diff --git a/extensions/typescript-language-features/src/utils/tsconfig.ts b/extensions/typescript-language-features/src/utils/tsconfig.ts index bc2f9c232..994292dd6 100644 --- a/extensions/typescript-language-features/src/utils/tsconfig.ts +++ b/extensions/typescript-language-features/src/utils/tsconfig.ts @@ -8,7 +8,6 @@ import * as vscode from 'vscode'; import * as Proto from '../protocol'; import { TypeScriptServiceConfiguration } from './configuration'; - export function isImplicitProjectConfigFile(configFileName: string) { return configFileName.indexOf('/dev/null/') === 0; } diff --git a/extensions/typescript-language-features/src/utils/tsconfigProvider.ts b/extensions/typescript-language-features/src/utils/tsconfigProvider.ts index 851786d29..7e2b85ef2 100644 --- a/extensions/typescript-language-features/src/utils/tsconfigProvider.ts +++ b/extensions/typescript-language-features/src/utils/tsconfigProvider.ts @@ -5,8 +5,8 @@ import * as vscode from 'vscode'; export interface TSConfig { - path: string; - workspaceFolder?: vscode.WorkspaceFolder; + readonly path: string; + readonly workspaceFolder?: vscode.WorkspaceFolder; } export default class TsConfigProvider { diff --git a/extensions/typescript-language-features/src/utils/typeConverters.ts b/extensions/typescript-language-features/src/utils/typeConverters.ts index 44ad7f4ae..36a20544f 100644 --- a/extensions/typescript-language-features/src/utils/typeConverters.ts +++ b/extensions/typescript-language-features/src/utils/typeConverters.ts @@ -41,7 +41,7 @@ export namespace Position { export const toFileLocationRequestArgs = (file: string, position: vscode.Position): Proto.FileLocationRequestArgs => ({ file, line: position.line + 1, - offset: position.character + 1 + offset: position.character + 1, }); } diff --git a/extensions/typescript-language-features/src/utils/versionPicker.ts b/extensions/typescript-language-features/src/utils/versionPicker.ts index bf81cb5c4..afbda6ef2 100644 --- a/extensions/typescript-language-features/src/utils/versionPicker.ts +++ b/extensions/typescript-language-features/src/utils/versionPicker.ts @@ -19,7 +19,7 @@ interface MyQuickPickItem extends vscode.QuickPickItem { enum MessageAction { useLocal, useBundled, - learnMore + learnMore, } export class TypeScriptVersionPicker { @@ -61,7 +61,7 @@ export class TypeScriptVersionPicker { : '') + localize('useVSCodeVersionOption', 'Use VS Code\'s Version'), description: shippedVersion.versionString, detail: shippedVersion.pathLabel, - id: MessageAction.useBundled + id: MessageAction.useBundled, }); for (const version of this.versionProvider.localVersions) { @@ -72,7 +72,7 @@ export class TypeScriptVersionPicker { description: version.versionString, detail: version.pathLabel, id: MessageAction.useLocal, - version: version + version }); } @@ -86,7 +86,7 @@ export class TypeScriptVersionPicker { placeHolder: localize( 'selectTsVersion', 'Select the TypeScript version used for JavaScript and TypeScript language features'), - ignoreFocusOut: firstRun + ignoreFocusOut: firstRun, }); if (!selected) { @@ -112,7 +112,6 @@ export class TypeScriptVersionPicker { this._currentVersion = shippedVersion; return { oldVersion: previousVersion, newVersion: shippedVersion }; - case MessageAction.learnMore: vscode.commands.executeCommand('vscode.open', vscode.Uri.parse('https://go.microsoft.com/fwlink/?linkid=839919')); return { oldVersion: this.currentVersion }; diff --git a/extensions/typescript-language-features/src/utils/versionProvider.ts b/extensions/typescript-language-features/src/utils/versionProvider.ts index 8787d1746..eb50009cf 100644 --- a/extensions/typescript-language-features/src/utils/versionProvider.ts +++ b/extensions/typescript-language-features/src/utils/versionProvider.ts @@ -87,7 +87,6 @@ export class TypeScriptVersion { } } - export class TypeScriptVersionProvider { private readonly relativePathResolver: RelativeWorkspacePathResolver = new RelativeWorkspacePathResolver(); diff --git a/extensions/typescript-language-features/src/utils/versionStatus.ts b/extensions/typescript-language-features/src/utils/versionStatus.ts index 450b50a2d..2b6901ee3 100644 --- a/extensions/typescript-language-features/src/utils/versionStatus.ts +++ b/extensions/typescript-language-features/src/utils/versionStatus.ts @@ -18,7 +18,7 @@ export default class VersionStatus { this._onChangeEditorSub = vscode.window.onDidChangeActiveTextEditor(this.showHideStatus, this); } - dispose() { + public dispose() { this._versionBarEntry.dispose(); this._onChangeEditorSub.dispose(); } diff --git a/extensions/typescript-language-features/src/utils/wireProtocol.ts b/extensions/typescript-language-features/src/utils/wireProtocol.ts index 6de9d9d5e..64450f142 100644 --- a/extensions/typescript-language-features/src/utils/wireProtocol.ts +++ b/extensions/typescript-language-features/src/utils/wireProtocol.ts @@ -7,29 +7,29 @@ import * as stream from 'stream'; import * as vscode from 'vscode'; import { Disposable } from './dispose'; -const DefaultSize: number = 8192; -const ContentLength: string = 'Content-Length: '; -const ContentLengthSize: number = Buffer.byteLength(ContentLength, 'utf8'); -const Blank: number = Buffer.from(' ', 'utf8')[0]; -const BackslashR: number = Buffer.from('\r', 'utf8')[0]; -const BackslashN: number = Buffer.from('\n', 'utf8')[0]; +const defaultSize: number = 8192; +const contentLength: string = 'Content-Length: '; +const contentLengthSize: number = Buffer.byteLength(contentLength, 'utf8'); +const blank: number = Buffer.from(' ', 'utf8')[0]; +const backslashR: number = Buffer.from('\r', 'utf8')[0]; +const backslashN: number = Buffer.from('\n', 'utf8')[0]; class ProtocolBuffer { private index: number = 0; - private buffer: Buffer = Buffer.allocUnsafe(DefaultSize); + private buffer: Buffer = Buffer.allocUnsafe(defaultSize); public append(data: string | Buffer): void { let toAppend: Buffer | null = null; if (Buffer.isBuffer(data)) { - toAppend = data; + toAppend = data; } else { - toAppend = Buffer.from(data, 'utf8'); + toAppend = Buffer.from(data, 'utf8'); } if (this.buffer.length - this.index >= toAppend.length) { toAppend.copy(this.buffer, this.index, 0, toAppend.length); } else { - let newSize = (Math.ceil((this.index + toAppend.length) / DefaultSize) + 1) * DefaultSize; + let newSize = (Math.ceil((this.index + toAppend.length) / defaultSize) + 1) * defaultSize; if (this.index === 0) { this.buffer = Buffer.allocUnsafe(newSize); toAppend.copy(this.buffer, 0, 0, toAppend.length); @@ -44,18 +44,18 @@ class ProtocolBuffer { let result = -1; let current = 0; // we are utf8 encoding... - while (current < this.index && (this.buffer[current] === Blank || this.buffer[current] === BackslashR || this.buffer[current] === BackslashN)) { + while (current < this.index && (this.buffer[current] === blank || this.buffer[current] === backslashR || this.buffer[current] === backslashN)) { current++; } - if (this.index < current + ContentLengthSize) { + if (this.index < current + contentLengthSize) { return result; } - current += ContentLengthSize; + current += contentLengthSize; let start = current; - while (current < this.index && this.buffer[current] !== BackslashR) { + while (current < this.index && this.buffer[current] !== backslashR) { current++; } - if (current + 3 >= this.index || this.buffer[current + 1] !== BackslashN || this.buffer[current + 2] !== BackslashR || this.buffer[current + 3] !== BackslashN) { + if (current + 3 >= this.index || this.buffer[current + 1] !== backslashN || this.buffer[current + 2] !== backslashR || this.buffer[current + 3] !== backslashN) { return result; } let data = this.buffer.toString('utf8', start, current); @@ -71,7 +71,7 @@ class ProtocolBuffer { } let result = this.buffer.toString('utf8', 0, length); let sourceStart = length; - while (sourceStart < this.index && (this.buffer[sourceStart] === BackslashR || this.buffer[sourceStart] === BackslashN)) { + while (sourceStart < this.index && (this.buffer[sourceStart] === backslashR || this.buffer[sourceStart] === backslashN)) { sourceStart++; } this.buffer.copy(this.buffer, 0, sourceStart); diff --git a/extensions/vb/.vscodeignore b/extensions/vb/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/vb/.vscodeignore +++ b/extensions/vb/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/vb/OSSREADME.json b/extensions/vb/OSSREADME.json deleted file mode 100644 index 94faeb1d4..000000000 --- a/extensions/vb/OSSREADME.json +++ /dev/null @@ -1,23 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "textmate/asp.vb.net.tmbundle", - "version": "0.0.0", - "license": "TextMate Bundle License", - "repositoryURL": "https://github.com/textmate/asp.vb.net.tmbundle", - "licenseDetail": [ - "Copyright (c) textmate-asp.vb.net.tmbundle project authors", - "", - "If not otherwise specified (see below), files in this folder fall under the following license: ", - "", - "Permission to copy, use, modify, sell and distribute this", - "software is granted. This software is provided \"as is\" without", - "express or implied warranty, and with no claim as to its", - "suitability for any purpose.", - "", - "An exception is made for files in readable text which contain their own license information, ", - "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added ", - "to the base-name name of the original file, and an extension of txt, html, or similar. For example ", - "\"tidy\" is accompanied by \"tidy-license.txt\"." - ] -}] diff --git a/extensions/vb/cgmanifest.json b/extensions/vb/cgmanifest.json new file mode 100644 index 000000000..4cb7d33e0 --- /dev/null +++ b/extensions/vb/cgmanifest.json @@ -0,0 +1,32 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "textmate/asp.vb.net.tmbundle", + "repositoryUrl": "https://github.com/textmate/asp.vb.net.tmbundle", + "commitHash": "72d44550b3286d0382d7be0624140cf97857ff69" + } + }, + "licenseDetail": [ + "Copyright (c) textmate-asp.vb.net.tmbundle project authors", + "", + "If not otherwise specified (see below), files in this folder fall under the following license: ", + "", + "Permission to copy, use, modify, sell and distribute this", + "software is granted. This software is provided \"as is\" without", + "express or implied warranty, and with no claim as to its", + "suitability for any purpose.", + "", + "An exception is made for files in readable text which contain their own license information, ", + "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added ", + "to the base-name name of the original file, and an extension of txt, html, or similar. For example ", + "\"tidy\" is accompanied by \"tidy-license.txt\"." + ], + "license": "TextMate Bundle License", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/vscode-api-tests/.vscode/tasks.json b/extensions/vscode-api-tests/.vscode/tasks.json index e2a020d06..390a93a3a 100644 --- a/extensions/vscode-api-tests/.vscode/tasks.json +++ b/extensions/vscode-api-tests/.vscode/tasks.json @@ -1,30 +1,11 @@ -// Available variables which can be used inside of strings. -// ${workspaceFolder}: the root folder of the team -// ${file}: the current opened file -// ${fileBasename}: the current opened file's basename -// ${fileDirname}: the current opened file's dirname -// ${fileExtname}: the current opened file's extension -// ${cwd}: the current working directory of the spawned process - -// A task runner that calls a custom npm script that compiles the extension. { - "version": "0.1.0", - - // we want to run npm + "version": "2.0.0", "command": "npm", - - // the command is a shell script - "isShellCommand": true, - - // show the output window only if unrecognized errors occur. - "showOutput": "silent", - - // we run the custom script "compile" as defined in package.json - "args": ["run", "compile", "--loglevel", "silent"], - - // The tsc compiler is started in watching mode - "isWatching": true, - - // use the standard tsc in watch mode problem matcher to find compile problems in the output. + "type": "shell", + "presentation": { + "reveal": "silent" + }, + "args": ["run", "compile"], + "isBackground": true, "problemMatcher": "$tsc-watch" -} \ No newline at end of file +} diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts index 1913342c7..1ed84272d 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts @@ -422,6 +422,7 @@ suite('window namespace tests', () => { canPickMany: true }); const first = new Promise(resolve => resolves.push(resolve)); + await new Promise(resolve => setTimeout(resolve, 10)); // Allow UI to update. await commands.executeCommand('workbench.action.quickOpenSelectNext'); assert.equal(await first, 'eins'); await commands.executeCommand('workbench.action.quickPickManyToggle'); @@ -520,6 +521,7 @@ suite('window namespace tests', () => { test('showWorkspaceFolderPick', async function () { const p = window.showWorkspaceFolderPick(undefined); + await timeout(10); await commands.executeCommand('workbench.action.acceptSelectedQuickOpenItem'); try { await p; @@ -696,3 +698,7 @@ suite('window namespace tests', () => { }); }); }); + +async function timeout(ms = 0): Promise { + return new Promise(resolve => setTimeout(() => resolve(), ms)); +} \ No newline at end of file diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index 7af7b00b6..8921b4aee 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -520,8 +520,9 @@ suite('workspace-namespace', () => { }); assert.equal(results.length, 1); - assert(results[0].preview.text.indexOf('foo') >= 0); - assert.equal(vscode.workspace.asRelativePath(results[0].uri), '10linefile.ts'); + const match = results[0]; + assert(match.preview.text.indexOf('foo') >= 0); + assert.equal(vscode.workspace.asRelativePath(match.uri), '10linefile.ts'); }); test('findTextInFiles, cancellation', async () => { diff --git a/extensions/vscode-api-tests/testWorkspace/image.png b/extensions/vscode-api-tests/testWorkspace/image.png index ff8b42295..15b462975 100644 Binary files a/extensions/vscode-api-tests/testWorkspace/image.png and b/extensions/vscode-api-tests/testWorkspace/image.png differ diff --git a/extensions/vscode-api-tests/testWorkspace/sub/image.png b/extensions/vscode-api-tests/testWorkspace/sub/image.png index ff8b42295..15b462975 100644 Binary files a/extensions/vscode-api-tests/testWorkspace/sub/image.png and b/extensions/vscode-api-tests/testWorkspace/sub/image.png differ diff --git a/extensions/vscode-colorize-tests/.vscode/tasks.json b/extensions/vscode-colorize-tests/.vscode/tasks.json index 3b6c357da..390a93a3a 100644 --- a/extensions/vscode-colorize-tests/.vscode/tasks.json +++ b/extensions/vscode-colorize-tests/.vscode/tasks.json @@ -1,31 +1,11 @@ -// Available variables which can be used inside of strings. -// ${workspaceFolder}: the root folder of the team -// ${file}: the current opened file -// ${relativeFile}: the current opened file relative to cwd -// ${fileBasename}: the current opened file's basename -// ${fileDirname}: the current opened file's dirname -// ${fileExtname}: the current opened file's extension -// ${cwd}: the current working directory of the spawned process - -// A task runner that calls a custom npm script that compiles the extension. { - "version": "0.1.0", - - // we want to run npm + "version": "2.0.0", "command": "npm", - - // the command is a shell script - "isShellCommand": true, - - // show the output window only if unrecognized errors occur. - "showOutput": "silent", - - // we run the custom script "compile" as defined in package.json - "args": ["run", "compile", "--loglevel", "silent"], - - // The tsc compiler is started in watching mode - "isWatching": true, - - // use the standard tsc in watch mode problem matcher to find compile problems in the output. + "type": "shell", + "presentation": { + "reveal": "silent" + }, + "args": ["run", "compile"], + "isBackground": true, "problemMatcher": "$tsc-watch" -} \ No newline at end of file +} diff --git a/extensions/xml/.vscodeignore b/extensions/xml/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/xml/.vscodeignore +++ b/extensions/xml/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/xml/OSSREADME.json b/extensions/xml/OSSREADME.json deleted file mode 100644 index accbe0749..000000000 --- a/extensions/xml/OSSREADME.json +++ /dev/null @@ -1,10 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "atom/language-xml", - "version": "0.0.0", - "license": "MIT", - "repositoryURL": "https://github.com/atom/language-xml", - "description": "The files syntaxes/xml.json and syntaxes/xsl.json were derived from the Atom package https://github.com/atom/language-xml which were originally converted from the TextMate bundle https://github.com/textmate/xml.tmbundle." - -}] diff --git a/extensions/xml/cgmanifest.json b/extensions/xml/cgmanifest.json new file mode 100644 index 000000000..b209abe6e --- /dev/null +++ b/extensions/xml/cgmanifest.json @@ -0,0 +1,18 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "atom/language-xml", + "repositoryUrl": "https://github.com/atom/language-xml", + "commitHash": "7bc75dfe779ad5b35d9bf4013d9181864358cb49" + } + }, + "license": "MIT", + "description": "The files syntaxes/xml.json and syntaxes/xsl.json were derived from the Atom package https://github.com/atom/language-xml which were originally converted from the TextMate bundle https://github.com/textmate/xml.tmbundle.", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/yaml/.vscodeignore b/extensions/yaml/.vscodeignore index e51fa33d1..0a622e7e3 100644 --- a/extensions/yaml/.vscodeignore +++ b/extensions/yaml/.vscodeignore @@ -1,2 +1,2 @@ test/** -OSSREADME.json \ No newline at end of file +cgmanifest.json diff --git a/extensions/yaml/OSSREADME.json b/extensions/yaml/OSSREADME.json deleted file mode 100644 index bcdae2a4c..000000000 --- a/extensions/yaml/OSSREADME.json +++ /dev/null @@ -1,28 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "textmate/yaml.tmbundle", - "version": "0.0.0", - "license": "TextMate Bundle License", - "repositoryURL": "https://github.com/textmate/yaml.tmbundle", - "licenseDetail": [ - "Copyright (c) 2015 FichteFoll ", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all", - "copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." - ] -}] diff --git a/extensions/yaml/cgmanifest.json b/extensions/yaml/cgmanifest.json new file mode 100644 index 000000000..80ba8ae52 --- /dev/null +++ b/extensions/yaml/cgmanifest.json @@ -0,0 +1,37 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "textmate/yaml.tmbundle", + "repositoryUrl": "https://github.com/textmate/yaml.tmbundle", + "commitHash": "e54ceae3b719506dba7e481a77cea4a8b576ae46" + } + }, + "licenseDetail": [ + "Copyright (c) 2015 FichteFoll ", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ], + "license": "TextMate Bundle License", + "version": "0.0.0" + } + ], + "version": 1 +} diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 0630b4a19..7dfb337bf 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.3.tgz#01b70247a6d3c2467f70c45795ef5ea18ce191d5" - integrity sha512-+81MUSyX+BaSo+u2RbozuQk/UWx6hfG0a5gHu4ANEM4sU96XbuIyAB+rWBW1u70c6a5QuZfuYICn3s2UjuHUpA== +typescript@3.2.0-rc: + version "3.2.0-rc" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.0-rc.tgz#7c3816f1c761b096f4f1712382e872f4da8f263e" + integrity sha512-RgKDOpEdbU9dAkB4TzxWy46wiyNUKQo0NM0bB7WfvEFw50yu046ldQXpOUYMUTLIAHnToOCtHsu39MYE8o6NCw== diff --git a/package.json b/package.json index 042693afa..cdda5dbcb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", - "version": "1.29.0", - "distro": "eceef408e99ace55c0a17a2a90b88e46a8c9090a", + "version": "1.30.0", + "distro": "cedfc514fbce202dad3ec5f86033796d70369b2a", "author": { "name": "Microsoft Corporation" }, @@ -28,7 +28,6 @@ }, "dependencies": { "applicationinsights": "1.0.6", - "electron-proxy-agent": "^1.0.2", "fast-plist": "0.1.2", "gc-signals": "^0.0.1", "getmac": "1.4.1", @@ -46,14 +45,15 @@ "semver": "^5.5.0", "spdlog": "0.7.2", "sudo-prompt": "8.2.0", - "v8-inspect-profiler": "^0.0.8", - "vscode-chokidar": "1.6.4", - "vscode-debugprotocol": "1.32.0", + "v8-inspect-profiler": "^0.0.13", + "vscode-chokidar": "1.6.5", + "vscode-debugprotocol": "1.33.0-pre.0", "vscode-nsfw": "1.1.1", + "vscode-proxy-agent": "0.1.1", "vscode-ripgrep": "^1.2.4", - "vscode-sqlite3": "4.0.2", + "vscode-sqlite3": "4.0.5", "vscode-textmate": "^4.0.1", - "vscode-xterm": "3.9.0-beta11", + "vscode-xterm": "3.9.0-beta13", "winreg": "^1.2.4", "yauzl": "^2.9.1", "yazl": "^2.4.3" @@ -124,9 +124,10 @@ "sinon": "^1.17.2", "source-map": "^0.4.4", "ts-loader": "^4.4.2", - "tslint": "^5.9.1", - "typescript": "3.1.1", + "tslint": "^5.11.0", + "typescript": "3.1.4", "typescript-formatter": "7.1.0", + "typescript-tslint-plugin": "^0.0.7", "uglify-es": "^3.0.18", "underscore": "^1.8.2", "vinyl": "^0.4.5", diff --git a/product.json b/product.json index e95e36c0e..eb6526c2d 100644 --- a/product.json +++ b/product.json @@ -17,5 +17,8 @@ "win32ShellNameShort": "C&ode - OSS", "darwinBundleIdentifier": "com.visualstudio.code.oss", "reportIssueUrl": "https://github.com/Microsoft/vscode/issues/new", - "urlProtocol": "code-oss" + "urlProtocol": "code-oss", + "extensionAllowedProposedApi": [ + "ms-vscode.references-view" + ] } diff --git a/resources/linux/code.png b/resources/linux/code.png index a7b64b954..8d8646f82 100644 Binary files a/resources/linux/code.png and b/resources/linux/code.png differ diff --git a/resources/linux/snap/electron-launch b/resources/linux/snap/electron-launch index 65c91d6fc..6fdd68a34 100644 --- a/resources/linux/snap/electron-launch +++ b/resources/linux/snap/electron-launch @@ -1,3 +1,6 @@ #!/bin/sh -exec "$@" --executed-from="$(pwd)" --pid=$$ +# Create $XDG_RUNTIME_DIR if it doesn't exist +[ -n "$XDG_RUNTIME_DIR" ] && mkdir -p $XDG_RUNTIME_DIR -m 700 + +exec "$@" diff --git a/resources/linux/snap/snapUpdate.sh b/resources/linux/snap/snapUpdate.sh new file mode 100644 index 000000000..d9299ca4d --- /dev/null +++ b/resources/linux/snap/snapUpdate.sh @@ -0,0 +1,3 @@ +#!/bin/sh +sleep 2 +@@NAME@@ \ No newline at end of file diff --git a/resources/linux/snap/snapcraft.yaml b/resources/linux/snap/snapcraft.yaml index e8a5d48fd..e07723086 100644 --- a/resources/linux/snap/snapcraft.yaml +++ b/resources/linux/snap/snapcraft.yaml @@ -15,6 +15,7 @@ parts: source: . stage-packages: - libasound2 + - libc++1 - libgconf2-4 - libnotify4 - libnspr4 @@ -23,20 +24,21 @@ parts: - libpulse0 - libxss1 - libxtst6 - # desktop-gtk2 deps below - libxkbcommon0 - - libgtk2.0-0 - # - unity-gtk2-module + - libgtk-3-0 + - libgdk-pixbuf2.0-0 + - libglib2.0-bin + - unity-gtk2-module - libappindicator1 + - xdg-user-dirs + - libsecret-1-0 + # TODO@joao @Tyriar check these deps + # - libatomic1 + # - libgtk2.0-bin prime: - -usr/share/dh-python - electron-launch: - plugin: dump - source: . - organize: - electron-launch: bin/electron-launch apps: @@NAME@@: - command: bin/electron-launch ${SNAP}/usr/share/@@NAME@@/bin/@@NAME@@ + command: electron-launch ${SNAP}/usr/share/@@NAME@@/bin/@@NAME@@ desktop: usr/share/applications/@@NAME@@.desktop \ No newline at end of file diff --git a/resources/win32/code_150x150.png b/resources/win32/code_150x150.png index 72fc85119..55e9f8f30 100644 Binary files a/resources/win32/code_150x150.png and b/resources/win32/code_150x150.png differ diff --git a/scripts/test-integration.bat b/scripts/test-integration.bat index ba810088f..fc13a2d99 100644 --- a/scripts/test-integration.bat +++ b/scripts/test-integration.bat @@ -5,6 +5,10 @@ pushd %~dp0\.. set VSCODEUSERDATADIR=%TMP%\vscodeuserfolder-%RANDOM%-%TIME:~6,5% +:: Integration & performance tests in AMD +call .\scripts\test.bat --runGlob **\*.integrationTest.js %* +if %errorlevel% neq 0 exit /b %errorlevel% + :: Tests in the extension host REM call .\scripts\code.bat %~dp0\..\extensions\vscode-api-tests\testWorkspace --extensionDevelopmentPath=%~dp0\..\extensions\vscode-api-tests --extensionTestsPath=%~dp0\..\extensions\vscode-api-tests\out\singlefolder-tests --disableExtensions --user-data-dir=%VSCODEUSERDATADIR% REM if %errorlevel% neq 0 exit /b %errorlevel% @@ -18,10 +22,6 @@ if %errorlevel% neq 0 exit /b %errorlevel% call .\scripts\code.bat $%~dp0\..\extensions\emmet\test-fixtures --extensionDevelopmentPath=%~dp0\..\extensions\emmet --extensionTestsPath=%~dp0\..\extensions\emmet\out\test --disableExtensions --user-data-dir=%VSCODEUSERDATADIR% . if %errorlevel% neq 0 exit /b %errorlevel% -:: Integration & performance tests in AMD -call .\scripts\test.bat --runGlob **\*.integrationTest.js %* -if %errorlevel% neq 0 exit /b %errorlevel% - # Tests in commonJS (HTML, CSS, JSON language server tests...) call .\scripts\node-electron.bat .\node_modules\mocha\bin\_mocha .\extensions\*\server\out\test\**\*.test.js if %errorlevel% neq 0 exit /b %errorlevel% diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index d2225c447..a51f3ee17 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -12,6 +12,9 @@ fi cd $ROOT +# Integration tests in AMD +./scripts/test.sh --runGlob **/*.integrationTest.js "$@" + # Tests in the extension host ./scripts/code.sh $ROOT/extensions/vscode-api-tests/testWorkspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started ./scripts/code.sh $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started @@ -22,9 +25,6 @@ mkdir $ROOT/extensions/emmet/test-fixtures ./scripts/code.sh $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started . rm -r $ROOT/extensions/emmet/test-fixtures -# Integration tests in AMD -./scripts/test.sh --runGlob **/*.integrationTest.js "$@" - # Tests in commonJS cd $ROOT/extensions/css-language-features/server && $ROOT/scripts/node-electron.sh test/index.js cd $ROOT/extensions/html-language-features/server && $ROOT/scripts/node-electron.sh test/index.js diff --git a/src/main.js b/src/main.js index bc430b9c6..a551417c8 100644 --- a/src/main.js +++ b/src/main.js @@ -81,7 +81,7 @@ function onReady() { nlsConfiguration = Promise.resolve(undefined); } - // We first need to test a user defined locale. If it fails we try the app locale. + // 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) => { @@ -404,7 +404,7 @@ function rimraf(location) { }); } -// Language tags are case insensitve however an amd loader is case sensitive +// 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. @@ -605,4 +605,4 @@ function getNLSConfiguration(locale) { return defaultResult(locale); } } -//#endregion \ No newline at end of file +//#endregion diff --git a/src/tsconfig.monaco.json b/src/tsconfig.monaco.json index 9e295f406..1bf4732a4 100644 --- a/src/tsconfig.monaco.json +++ b/src/tsconfig.monaco.json @@ -1,5 +1,4 @@ { - "$schema": "https://schemastore.azurewebsites.net/schemas/json/tsconfig.json", "compilerOptions": { "noEmit": true, "module": "amd", diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 8c0d4a340..d537b38d2 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -27,11 +27,15 @@ "./vs/base/browser/ui/centered/centeredViewLayout.ts", "./vs/base/browser/ui/contextview/contextview.ts", "./vs/base/browser/ui/countBadge/countBadge.ts", + "./vs/base/browser/ui/grid/grid.ts", "./vs/base/browser/ui/grid/gridview.ts", "./vs/base/browser/ui/highlightedlabel/highlightedLabel.ts", "./vs/base/browser/ui/iconLabel/iconLabel.ts", "./vs/base/browser/ui/keybindingLabel/keybindingLabel.ts", "./vs/base/browser/ui/list/list.ts", + "./vs/base/browser/ui/list/listPaging.ts", + "./vs/base/browser/ui/list/listView.ts", + "./vs/base/browser/ui/list/listWidget.ts", "./vs/base/browser/ui/list/rangeMap.ts", "./vs/base/browser/ui/list/rowCache.ts", "./vs/base/browser/ui/list/splice.ts", @@ -49,20 +53,32 @@ "./vs/base/browser/ui/scrollbar/verticalScrollbar.ts", "./vs/base/browser/ui/splitview/panelview.ts", "./vs/base/browser/ui/splitview/splitview.ts", + "./vs/base/browser/ui/tree/abstractTree.ts", + "./vs/base/browser/ui/tree/dataTree.ts", + "./vs/base/browser/ui/tree/indexTree.ts", + "./vs/base/browser/ui/tree/indexTreeModel.ts", + "./vs/base/browser/ui/tree/objectTree.ts", + "./vs/base/browser/ui/tree/objectTreeModel.ts", "./vs/base/browser/ui/tree/tree.ts", "./vs/base/browser/ui/widget.ts", "./vs/base/common/json.ts", "./vs/base/common/jsonEdit.ts", "./vs/base/common/jsonFormatter.ts", + "./vs/base/common/paths.ts", + "./vs/base/common/uriIpc.ts", "./vs/base/node/console.ts", "./vs/base/node/crypto.ts", "./vs/base/node/decoder.ts", + "./vs/base/node/encoding.ts", + "./vs/base/node/extfs.ts", "./vs/base/node/flow.ts", "./vs/base/node/id.ts", "./vs/base/node/paths.ts", + "./vs/base/node/pfs.ts", "./vs/base/node/ports.ts", "./vs/base/node/processes.ts", "./vs/base/node/proxy.ts", + "./vs/base/node/ps.ts", "./vs/base/node/request.ts", "./vs/base/node/stats.ts", "./vs/base/node/storage.ts", @@ -70,30 +86,46 @@ "./vs/base/parts/contextmenu/common/contextmenu.ts", "./vs/base/parts/contextmenu/electron-browser/contextmenu.ts", "./vs/base/parts/contextmenu/electron-main/contextmenu.ts", + "./vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts", + "./vs/base/parts/ipc/electron-main/ipc.electron-main.ts", "./vs/base/parts/ipc/node/ipc.cp.ts", + "./vs/base/parts/ipc/node/ipc.electron.ts", "./vs/base/parts/ipc/node/ipc.net.ts", "./vs/base/parts/ipc/node/ipc.ts", "./vs/base/parts/ipc/test/node/testApp.ts", "./vs/base/parts/ipc/test/node/testService.ts", "./vs/base/parts/quickopen/common/quickOpen.ts", + "./vs/base/parts/quickopen/common/quickOpenScorer.ts", "./vs/base/test/browser/ui/grid/util.ts", "./vs/base/test/common/json.test.ts", "./vs/base/test/common/jsonEdit.test.ts", "./vs/base/test/common/jsonFormatter.test.ts", + "./vs/base/test/common/paths.test.ts", "./vs/base/test/common/utils.ts", "./vs/base/test/node/processes/fixtures/fork.ts", "./vs/base/test/node/processes/fixtures/fork_large.ts", "./vs/base/test/node/uri.test.perf.ts", + "./vs/base/test/node/utils.ts", "./vs/base/worker/defaultWorkerFactory.ts", "./vs/base/worker/workerMain.ts", "./vs/code/code.main.ts", "./vs/code/electron-browser/issue/issueReporterModel.ts", "./vs/code/electron-browser/issue/issueReporterPage.ts", "./vs/code/electron-browser/issue/issueReporterUtil.ts", + "./vs/code/electron-browser/processExplorer/processExplorerMain.ts", + "./vs/code/electron-browser/sharedProcess/contrib/contributions.ts", + "./vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts", + "./vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts", "./vs/code/electron-main/auth.ts", "./vs/code/electron-main/keyboard.ts", + "./vs/code/electron-main/logUploader.ts", + "./vs/code/electron-main/sharedProcess.ts", "./vs/code/electron-main/theme.ts", + "./vs/code/node/cli.ts", + "./vs/code/node/paths.ts", "./vs/code/node/shellEnv.ts", + "./vs/code/node/wait.ts", + "./vs/code/node/windowsFinder.ts", "./vs/editor/browser/config/charWidthReader.ts", "./vs/editor/browser/config/configuration.ts", "./vs/editor/browser/config/elementSizeObserver.ts", @@ -259,9 +291,11 @@ "./vs/editor/contrib/codeAction/codeActionModel.ts", "./vs/editor/contrib/codeAction/codeActionTrigger.ts", "./vs/editor/contrib/codeAction/lightBulbWidget.ts", + "./vs/editor/contrib/codelens/codelens.ts", "./vs/editor/contrib/colorPicker/color.ts", "./vs/editor/contrib/colorPicker/colorDetector.ts", "./vs/editor/contrib/colorPicker/colorPickerModel.ts", + "./vs/editor/contrib/colorPicker/colorPickerWidget.ts", "./vs/editor/contrib/comment/blockCommentCommand.ts", "./vs/editor/contrib/comment/comment.ts", "./vs/editor/contrib/comment/lineCommentCommand.ts", @@ -288,29 +322,41 @@ "./vs/editor/contrib/folding/test/indentRangeProvider.test.ts", "./vs/editor/contrib/folding/test/syntaxFold.test.ts", "./vs/editor/contrib/fontZoom/fontZoom.ts", + "./vs/editor/contrib/format/format.ts", + "./vs/editor/contrib/format/formattingEdit.ts", "./vs/editor/contrib/goToDefinition/clickLinkGesture.ts", "./vs/editor/contrib/goToDefinition/goToDefinition.ts", + "./vs/editor/contrib/gotoError/gotoError.ts", + "./vs/editor/contrib/gotoError/gotoErrorWidget.ts", "./vs/editor/contrib/hover/getHover.ts", "./vs/editor/contrib/hover/hoverOperation.ts", "./vs/editor/contrib/hover/hoverWidgets.ts", "./vs/editor/contrib/hover/modesGlyphHover.ts", + "./vs/editor/contrib/inPlaceReplace/inPlaceReplace.ts", "./vs/editor/contrib/inPlaceReplace/inPlaceReplaceCommand.ts", "./vs/editor/contrib/indentation/indentUtils.ts", + "./vs/editor/contrib/indentation/indentation.ts", "./vs/editor/contrib/linesOperations/copyLinesCommand.ts", "./vs/editor/contrib/linesOperations/deleteLinesCommand.ts", + "./vs/editor/contrib/linesOperations/linesOperations.ts", "./vs/editor/contrib/linesOperations/moveLinesCommand.ts", "./vs/editor/contrib/linesOperations/sortLinesCommand.ts", "./vs/editor/contrib/links/getLinks.ts", "./vs/editor/contrib/links/links.ts", "./vs/editor/contrib/markdown/markdownRenderer.ts", "./vs/editor/contrib/message/messageController.ts", + "./vs/editor/contrib/parameterHints/parameterHints.ts", + "./vs/editor/contrib/parameterHints/parameterHintsWidget.ts", "./vs/editor/contrib/parameterHints/provideSignatureHelp.ts", "./vs/editor/contrib/quickOpen/quickOpen.ts", "./vs/editor/contrib/referenceSearch/referencesModel.ts", "./vs/editor/contrib/rename/rename.ts", "./vs/editor/contrib/rename/renameInputField.ts", + "./vs/editor/contrib/smartSelect/smartSelect.ts", + "./vs/editor/contrib/smartSelect/tokenSelectionSupport.ts", "./vs/editor/contrib/smartSelect/tokenTree.ts", "./vs/editor/contrib/snippet/snippetParser.ts", + "./vs/editor/contrib/snippet/snippetVariables.ts", "./vs/editor/contrib/suggest/suggest.ts", "./vs/editor/contrib/suggest/wordContextKey.ts", "./vs/editor/contrib/suggest/wordDistance.ts", @@ -335,6 +381,7 @@ "./vs/editor/test/browser/controller/imeTester.ts", "./vs/editor/test/browser/editorTestServices.ts", "./vs/editor/test/browser/testCodeEditor.ts", + "./vs/editor/test/browser/testCommand.ts", "./vs/editor/test/browser/view/minimapFontCreator.ts", "./vs/editor/test/common/commentMode.ts", "./vs/editor/test/common/core/viewLineToken.ts", @@ -357,7 +404,9 @@ "./vs/nls.mock.ts", "./vs/platform/actions/common/actions.ts", "./vs/platform/actions/common/menu.ts", + "./vs/platform/actions/common/menuService.ts", "./vs/platform/backup/common/backup.ts", + "./vs/platform/backup/electron-main/backupMainService.ts", "./vs/platform/broadcast/electron-browser/broadcastService.ts", "./vs/platform/clipboard/common/clipboardService.ts", "./vs/platform/clipboard/electron-browser/clipboardService.ts", @@ -367,12 +416,19 @@ "./vs/platform/configuration/common/configurationRegistry.ts", "./vs/platform/contextkey/browser/contextKeyService.ts", "./vs/platform/contextkey/common/contextkey.ts", + "./vs/platform/diagnostics/electron-main/diagnosticsService.ts", "./vs/platform/dialogs/common/dialogs.ts", + "./vs/platform/dialogs/node/dialogIpc.ts", "./vs/platform/dialogs/node/dialogService.ts", "./vs/platform/download/common/download.ts", + "./vs/platform/download/node/downloadIpc.ts", + "./vs/platform/download/node/downloadService.ts", + "./vs/platform/driver/electron-main/driver.ts", + "./vs/platform/driver/node/driver.ts", "./vs/platform/editor/common/editor.ts", "./vs/platform/environment/common/environment.ts", "./vs/platform/environment/node/argv.ts", + "./vs/platform/environment/node/environmentService.ts", "./vs/platform/extensionManagement/common/extensionEnablementService.ts", "./vs/platform/extensionManagement/common/extensionManagement.ts", "./vs/platform/extensionManagement/common/extensionManagementUtil.ts", @@ -380,12 +436,14 @@ "./vs/platform/extensionManagement/node/extensionLifecycle.ts", "./vs/platform/extensionManagement/node/extensionManagementIpc.ts", "./vs/platform/extensionManagement/node/extensionManagementUtil.ts", + "./vs/platform/extensionManagement/node/extensionsManifestCache.ts", "./vs/platform/extensions/common/extensionHost.ts", "./vs/platform/extensions/common/extensions.ts", "./vs/platform/extensions/node/extensionValidator.ts", "./vs/platform/files/common/files.ts", "./vs/platform/files/node/files.ts", "./vs/platform/history/common/history.ts", + "./vs/platform/history/electron-main/historyMainService.ts", "./vs/platform/instantiation/common/descriptors.ts", "./vs/platform/instantiation/common/extensions.ts", "./vs/platform/instantiation/common/graph.ts", @@ -396,6 +454,8 @@ "./vs/platform/integrity/common/integrity.ts", "./vs/platform/integrity/node/integrityServiceImpl.ts", "./vs/platform/issue/common/issue.ts", + "./vs/platform/issue/electron-main/issueService.ts", + "./vs/platform/issue/node/issueIpc.ts", "./vs/platform/jsonschemas/common/jsonContributionRegistry.ts", "./vs/platform/keybinding/common/abstractKeybindingService.ts", "./vs/platform/keybinding/common/keybinding.ts", @@ -406,19 +466,24 @@ "./vs/platform/keybinding/test/common/mockKeybindingService.ts", "./vs/platform/label/common/label.ts", "./vs/platform/label/electron-browser/label.contribution.ts", + "./vs/platform/launch/electron-main/launchService.ts", "./vs/platform/lifecycle/common/lifecycle.ts", "./vs/platform/lifecycle/electron-browser/lifecycleService.ts", "./vs/platform/lifecycle/electron-main/lifecycleMain.ts", "./vs/platform/localizations/common/localizations.ts", + "./vs/platform/localizations/node/localizationsIpc.ts", "./vs/platform/log/common/bufferLog.ts", "./vs/platform/log/common/log.ts", + "./vs/platform/log/node/logIpc.ts", "./vs/platform/log/node/spdlogService.ts", "./vs/platform/markers/common/markerService.ts", "./vs/platform/markers/common/markers.ts", "./vs/platform/menubar/common/menubar.ts", + "./vs/platform/menubar/node/menubarIpc.ts", "./vs/platform/node/minimalTranslations.ts", "./vs/platform/node/package.ts", "./vs/platform/node/product.ts", + "./vs/platform/node/zip.ts", "./vs/platform/notification/common/notification.ts", "./vs/platform/notification/test/common/testNotificationService.ts", "./vs/platform/opener/common/opener.ts", @@ -427,20 +492,33 @@ "./vs/platform/quickOpen/common/quickOpen.ts", "./vs/platform/quickinput/common/quickInput.ts", "./vs/platform/registry/common/platform.ts", + "./vs/platform/remote/common/remoteAuthorityResolver.ts", + "./vs/platform/remote/common/remoteHosts.ts", + "./vs/platform/remote/node/remoteAgentConnection.ts", + "./vs/platform/remote/node/remoteAgentFileSystemChannel.ts", + "./vs/platform/remote/node/remoteAuthorityResolverChannel.ts", + "./vs/platform/remote/node/remoteAuthorityResolverService.ts", "./vs/platform/request/electron-browser/requestService.ts", "./vs/platform/request/electron-main/requestService.ts", "./vs/platform/request/node/request.ts", "./vs/platform/request/node/requestService.ts", + "./vs/platform/search/common/replace.ts", "./vs/platform/search/common/search.ts", + "./vs/platform/search/test/common/replace.test.ts", "./vs/platform/state/common/state.ts", + "./vs/platform/state/node/stateService.ts", "./vs/platform/statusbar/common/statusbar.ts", "./vs/platform/storage/common/storage.ts", "./vs/platform/storage/common/storageLegacyService.ts", + "./vs/platform/storage/node/storageService.ts", "./vs/platform/telemetry/browser/errorTelemetry.ts", "./vs/platform/telemetry/common/telemetry.ts", "./vs/platform/telemetry/common/telemetryService.ts", "./vs/platform/telemetry/common/telemetryUtils.ts", + "./vs/platform/telemetry/node/commonProperties.ts", + "./vs/platform/telemetry/node/telemetryIpc.ts", "./vs/platform/telemetry/node/telemetryNodeUtils.ts", + "./vs/platform/telemetry/node/workbenchCommonProperties.ts", "./vs/platform/theme/common/colorRegistry.ts", "./vs/platform/theme/common/styler.ts", "./vs/platform/theme/common/themeService.ts", @@ -449,19 +527,25 @@ "./vs/platform/update/electron-main/abstractUpdateService.ts", "./vs/platform/update/electron-main/updateService.darwin.ts", "./vs/platform/update/electron-main/updateService.linux.ts", + "./vs/platform/update/electron-main/updateService.snap.ts", "./vs/platform/update/node/update.config.contribution.ts", + "./vs/platform/update/node/updateIpc.ts", "./vs/platform/url/common/url.ts", "./vs/platform/url/common/urlService.ts", "./vs/platform/url/electron-main/electronUrlListener.ts", + "./vs/platform/url/node/urlIpc.ts", "./vs/platform/widget/common/contextScopedWidget.ts", "./vs/platform/windows/common/windows.ts", "./vs/platform/windows/electron-browser/windowService.ts", "./vs/platform/windows/electron-main/windows.ts", + "./vs/platform/windows/node/windowsIpc.ts", "./vs/platform/workbench/common/contextkeys.ts", "./vs/platform/workspace/common/workspace.ts", "./vs/platform/workspace/test/common/testWorkspace.ts", "./vs/platform/workspaces/common/workspaces.ts", + "./vs/platform/workspaces/electron-main/workspacesMainService.ts", "./vs/platform/workspaces/node/workspaces.ts", + "./vs/platform/workspaces/node/workspacesIpc.ts", "./vs/vscode.d.ts", "./vs/vscode.proposed.d.ts", "./vs/workbench/api/node/extHostExtensionActivator.ts", @@ -474,6 +558,7 @@ "./vs/workbench/browser/actions/toggleTabsVisibility.ts", "./vs/workbench/browser/actions/toggleZenMode.ts", "./vs/workbench/browser/part.ts", + "./vs/workbench/browser/parts/editor/editorWidgets.ts", "./vs/workbench/browser/parts/notifications/notificationsAlerts.ts", "./vs/workbench/browser/parts/quickinput/quickInputUtils.ts", "./vs/workbench/browser/parts/quickopen/quickopen.ts", @@ -491,6 +576,7 @@ "./vs/workbench/common/theme.ts", "./vs/workbench/common/viewlet.ts", "./vs/workbench/common/views.ts", + "./vs/workbench/parts/cli/electron-browser/cli.contribution.ts", "./vs/workbench/parts/codeEditor/browser/menuPreventer.ts", "./vs/workbench/parts/codeEditor/browser/simpleEditorOptions.ts", "./vs/workbench/parts/codeEditor/electron-browser/accessibility.ts", @@ -510,10 +596,13 @@ "./vs/workbench/parts/emmet/electron-browser/emmetActions.ts", "./vs/workbench/parts/emmet/test/electron-browser/emmetAction.test.ts", "./vs/workbench/parts/execution/common/execution.ts", + "./vs/workbench/parts/execution/electron-browser/terminal.ts", + "./vs/workbench/parts/execution/electron-browser/terminalService.ts", "./vs/workbench/parts/extensions/common/extensionQuery.ts", "./vs/workbench/parts/extensions/common/extensions.ts", "./vs/workbench/parts/extensions/common/extensionsFileTemplate.ts", "./vs/workbench/parts/extensions/electron-browser/extensionsActivationProgress.ts", + "./vs/workbench/parts/extensions/electron-browser/extensionsAutoProfiler.ts", "./vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts", "./vs/workbench/parts/logs/common/logConstants.ts", "./vs/workbench/parts/logs/electron-browser/logs.contribution.ts", @@ -527,20 +616,28 @@ "./vs/workbench/parts/outline/electron-browser/outline.ts", "./vs/workbench/parts/output/common/output.ts", "./vs/workbench/parts/output/common/outputLinkComputer.ts", + "./vs/workbench/parts/output/common/outputLinkProvider.ts", "./vs/workbench/parts/performance/electron-browser/stats.ts", "./vs/workbench/parts/preferences/common/smartSnippetInserter.ts", + "./vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts", "./vs/workbench/parts/scm/common/scm.ts", "./vs/workbench/parts/scm/electron-browser/scmUtil.ts", "./vs/workbench/parts/search/common/constants.ts", "./vs/workbench/parts/search/common/queryBuilder.ts", + "./vs/workbench/parts/snippets/electron-browser/configureSnippets.ts", + "./vs/workbench/parts/snippets/electron-browser/snippetCompletionProvider.ts", + "./vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts", + "./vs/workbench/parts/snippets/electron-browser/snippetsFile.ts", "./vs/workbench/parts/surveys/electron-browser/nps.contribution.ts", "./vs/workbench/parts/tasks/common/problemCollectors.ts", "./vs/workbench/parts/tasks/common/problemMatcher.ts", + "./vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts", "./vs/workbench/parts/tasks/common/taskService.ts", "./vs/workbench/parts/tasks/common/taskSystem.ts", "./vs/workbench/parts/tasks/common/taskTemplates.ts", "./vs/workbench/parts/tasks/common/tasks.ts", "./vs/workbench/parts/tasks/electron-browser/jsonSchemaCommon.ts", + "./vs/workbench/parts/tasks/node/tasks.ts", "./vs/workbench/parts/terminal/browser/terminalTab.ts", "./vs/workbench/parts/terminal/browser/terminalWidgetManager.ts", "./vs/workbench/parts/terminal/common/terminal.ts", @@ -548,24 +645,31 @@ "./vs/workbench/parts/terminal/common/terminalCommands.ts", "./vs/workbench/parts/terminal/common/terminalMenu.ts", "./vs/workbench/parts/terminal/common/terminalService.ts", + "./vs/workbench/parts/terminal/node/terminal.ts", "./vs/workbench/parts/terminal/node/terminalCommandTracker.ts", "./vs/workbench/parts/terminal/node/terminalEnvironment.ts", + "./vs/workbench/parts/terminal/node/terminalProcess.ts", + "./vs/workbench/parts/terminal/node/terminalProcessExtHostProxy.ts", + "./vs/workbench/parts/terminal/node/windowsShellHelper.ts", "./vs/workbench/parts/url/electron-browser/url.contribution.ts", "./vs/workbench/parts/webview/electron-browser/webviewProtocols.ts", "./vs/workbench/parts/welcome/gettingStarted/electron-browser/gettingStarted.ts", "./vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts", "./vs/workbench/parts/welcome/walkThrough/node/walkThroughUtils.ts", - "./vs/workbench/services/actions/common/menuService.ts", + "./vs/workbench/services/actions/electron-browser/menusExtensionPoint.ts", "./vs/workbench/services/activity/common/activity.ts", "./vs/workbench/services/backup/common/backup.ts", + "./vs/workbench/services/backup/node/backupFileService.ts", "./vs/workbench/services/commands/common/commandService.ts", "./vs/workbench/services/configuration/common/configuration.ts", "./vs/workbench/services/configuration/common/configurationExtensionPoint.ts", "./vs/workbench/services/configuration/common/configurationModels.ts", "./vs/workbench/services/configuration/common/jsonEditing.ts", "./vs/workbench/services/configurationResolver/common/configurationResolver.ts", + "./vs/workbench/services/configurationResolver/common/configurationResolverUtils.ts", "./vs/workbench/services/crashReporter/electron-browser/crashReporterService.ts", "./vs/workbench/services/decorations/browser/decorations.ts", + "./vs/workbench/services/decorations/browser/decorationsService.ts", "./vs/workbench/services/extensions/common/extensions.ts", "./vs/workbench/services/extensions/common/extensionsRegistry.ts", "./vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts", @@ -574,9 +678,16 @@ "./vs/workbench/services/extensions/node/lazyPromise.ts", "./vs/workbench/services/extensions/node/proxyIdentifier.ts", "./vs/workbench/services/extensions/node/rpcProtocol.ts", + "./vs/workbench/services/files/electron-browser/encoding.ts", "./vs/workbench/services/files/node/watcher/common.ts", "./vs/workbench/services/files/node/watcher/nsfw/watcher.ts", + "./vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts", + "./vs/workbench/services/files/node/watcher/nsfw/watcherService.ts", + "./vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts", "./vs/workbench/services/files/node/watcher/unix/watcher.ts", + "./vs/workbench/services/files/node/watcher/unix/watcherApp.ts", + "./vs/workbench/services/files/node/watcher/unix/watcherIpc.ts", + "./vs/workbench/services/files/node/watcher/unix/watcherService.ts", "./vs/workbench/services/files/node/watcher/win32/csharpWatcherService.ts", "./vs/workbench/services/files/node/watcher/win32/watcherService.ts", "./vs/workbench/services/files/test/electron-browser/utils.ts", @@ -589,15 +700,30 @@ "./vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper.ts", "./vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts", "./vs/workbench/services/keybinding/common/windowsKeyboardMapper.ts", + "./vs/workbench/services/keybinding/test/keyboardMapperTestUtils.ts", "./vs/workbench/services/mode/common/workbenchModeService.ts", + "./vs/workbench/services/notification/common/notificationService.ts", "./vs/workbench/services/panel/common/panelService.ts", "./vs/workbench/services/part/common/partService.ts", + "./vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts", + "./vs/workbench/services/remote/node/remoteAgentEnvironmentChannel.ts", + "./vs/workbench/services/remote/node/remoteAgentService.ts", "./vs/workbench/services/scm/common/scm.ts", "./vs/workbench/services/scm/common/scmService.ts", + "./vs/workbench/services/search/common/searchHelpers.ts", + "./vs/workbench/services/search/node/fileSearchManager.ts", "./vs/workbench/services/search/node/legacy/search.ts", + "./vs/workbench/services/search/node/ripgrepFileSearch.ts", + "./vs/workbench/services/search/node/ripgrepSearchProvider.ts", + "./vs/workbench/services/search/node/ripgrepSearchUtils.ts", + "./vs/workbench/services/search/node/ripgrepTextSearchEngine.ts", "./vs/workbench/services/search/node/search.ts", "./vs/workbench/services/search/node/searchHistoryService.ts", "./vs/workbench/services/search/node/searchIpc.ts", + "./vs/workbench/services/search/node/textSearchAdapter.ts", + "./vs/workbench/services/search/node/textSearchManager.ts", + "./vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts", + "./vs/workbench/services/search/test/node/textSearchManager.test.ts", "./vs/workbench/services/textMate/electron-browser/TMGrammars.ts", "./vs/workbench/services/textMate/electron-browser/TMHelper.ts", "./vs/workbench/services/textMate/electron-browser/textMateService.ts", @@ -611,7 +737,6 @@ "./vs/workbench/test/electron-browser/api/mock.ts" ], "exclude": [ - "./typings/require-monaco.d.ts", - "../node_modules/windows-process-tree/typings/windows-process-tree.d.ts" + "./typings/require-monaco.d.ts" ] } \ No newline at end of file diff --git a/src/typings/OSSREADME.json b/src/typings/OSSREADME.json deleted file mode 100644 index 63488ec55..000000000 --- a/src/typings/OSSREADME.json +++ /dev/null @@ -1,9 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -// All OSS in this folder is development time only -[{ - "name": "definitelytyped", - "repositoryURL": "https://github.com/DefinitelyTyped/DefinitelyTyped", - "license": "MIT", - "isDev": true -}] \ No newline at end of file diff --git a/src/typings/cgmanifest.json b/src/typings/cgmanifest.json new file mode 100644 index 000000000..6e529a79f --- /dev/null +++ b/src/typings/cgmanifest.json @@ -0,0 +1,16 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "definitelytyped", + "repositoryUrl": "https://github.com/DefinitelyTyped/DefinitelyTyped", + "commitHash": "69e3ac6bec3008271f76bbfa7cf69aa9198c4ff0" + } + }, + "license": "MIT" + } + ], + "version": 1 +} diff --git a/src/typings/node-pty.d.ts b/src/typings/node-pty.d.ts index e3ee561a6..2fdb9dff9 100644 --- a/src/typings/node-pty.d.ts +++ b/src/typings/node-pty.d.ts @@ -11,7 +11,7 @@ declare module 'node-pty' { * escaped properly. * @param options The options of the terminal. * @see CommandLineToArgvW https://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx - * @see Parsing C++ Comamnd-Line Arguments https://msdn.microsoft.com/en-us/library/17w5ykft.aspx + * @see Parsing C++ Command-Line Arguments https://msdn.microsoft.com/en-us/library/17w5ykft.aspx * @see GetCommandLine https://msdn.microsoft.com/en-us/library/windows/desktop/ms683156.aspx */ export function spawn(file: string, args: string[] | string, options: IPtyForkOptions): IPty; @@ -58,7 +58,7 @@ declare module 'node-pty' { /** * Resizes the dimensions of the pty. - * @param columns THe number of columns to use. + * @param columns The number of columns to use. * @param rows The number of rows to use. */ resize(columns: number, rows: number): void; diff --git a/src/typings/v8-inspect-profiler.d.ts b/src/typings/v8-inspect-profiler.d.ts index 50449c7ae..c60190b3f 100644 --- a/src/typings/v8-inspect-profiler.d.ts +++ b/src/typings/v8-inspect-profiler.d.ts @@ -31,7 +31,17 @@ declare module 'v8-inspect-profiler' { stop(afterDelay?: number): PromiseLike; } - export function startProfiling(options: { port: number, tries?: number, retyWait?: number }): PromiseLike; + export interface Target { + description: string, + devtoolsFrontendUrl: string, + id: string, + title: string, + type: string, + url: string, + webSocketDebuggerUrl: string + } + + export function startProfiling(options: { port: number, tries?: number, retyWait?: number, target?: (targets: Target[]) => Target }): PromiseLike; export function writeProfile(profile: ProfileResult, name?: string): PromiseLike; - export function rewriteAbsolutePaths(profile, replaceWith?); + export function rewriteAbsolutePaths(profile: ProfileResult, replaceWith?: string): ProfileResult; } diff --git a/src/vs/base/test/node/storage/broken.db b/src/typings/vscode-proxy-agent.d.ts similarity index 90% rename from src/vs/base/test/node/storage/broken.db rename to src/typings/vscode-proxy-agent.d.ts index 7762a2f10..a997fa978 100644 --- a/src/vs/base/test/node/storage/broken.db +++ b/src/typings/vscode-proxy-agent.d.ts @@ -3,4 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -this is not a sqlite DB \ No newline at end of file +declare module 'vscode-proxy-agent'; diff --git a/src/vs/base/browser/contextmenu.ts b/src/vs/base/browser/contextmenu.ts index 92e1e6d85..c45ef765e 100644 --- a/src/vs/base/browser/contextmenu.ts +++ b/src/vs/base/browser/contextmenu.ts @@ -7,6 +7,7 @@ import { IAction, IActionRunner } from 'vs/base/common/actions'; import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { SubmenuAction } from 'vs/base/browser/ui/menu/menu'; +import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; export interface IContextMenuEvent { shiftKey?: boolean; @@ -23,7 +24,7 @@ export class ContextSubMenu extends SubmenuAction { export interface IContextMenuDelegate { getAnchor(): HTMLElement | { x: number; y: number; width?: number; height?: number; }; - getActions(): Thenable<(IAction | ContextSubMenu)[]>; + getActions(): (IAction | ContextSubMenu)[]; getActionItem?(action: IAction): IActionItem; getActionsContext?(event?: IContextMenuEvent): any; getKeyBinding?(action: IAction): ResolvedKeybinding; @@ -31,4 +32,5 @@ export interface IContextMenuDelegate { onHide?(didCancel: boolean): void; actionRunner?: IActionRunner; autoSelectFirstItem?: boolean; + anchorAlignment?: AnchorAlignment; } diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index d1628b42d..4e5530e52 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -20,6 +20,12 @@ export function clearNode(node: HTMLElement): void { } } +export function removeNode(node: HTMLElement): void { + if (node.parentNode) { + node.parentNode.removeChild(node); + } +} + export function isInDOM(node: Node | null): boolean { while (node) { if (node === document.body) { diff --git a/src/vs/base/browser/htmlContentRenderer.ts b/src/vs/base/browser/htmlContentRenderer.ts index cf346270d..5e0c45a56 100644 --- a/src/vs/base/browser/htmlContentRenderer.ts +++ b/src/vs/base/browser/htmlContentRenderer.ts @@ -6,11 +6,12 @@ import * as DOM from 'vs/base/browser/dom'; import { defaultGenerator } from 'vs/base/common/idGenerator'; import { escape } from 'vs/base/common/strings'; -import { removeMarkdownEscapes, IMarkdownString } from 'vs/base/common/htmlContent'; +import { removeMarkdownEscapes, IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; import * as marked from 'vs/base/common/marked/marked'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { IDisposable } from 'vs/base/common/lifecycle'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { URI } from 'vs/base/common/uri'; export interface IContentActionHandler { callback: (content: string, event?: IMouseEvent) => void; @@ -52,6 +53,14 @@ export function renderFormattedText(formattedText: string, options: RenderOption export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions = {}): HTMLElement { const element = createElement(options); + const _href = function (href: string): string { + const data = markdown.uris && markdown.uris[href]; + if (data) { + href = URI.revive(data).toString(true); + } + return href; + }; + // signal to code-block render that the // element has been created let signalInnerHTML: Function; @@ -59,6 +68,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions const renderer = new marked.Renderer(); renderer.image = (href: string, title: string, text: string) => { + href = _href(href); let dimensions: string[] = []; if (href) { const splitted = href.split('|').map(s => s.trim()); @@ -99,6 +109,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions if (href === text) { // raw link case text = removeMarkdownEscapes(text); } + href = _href(href); title = removeMarkdownEscapes(title); href = removeMarkdownEscapes(href); if ( @@ -111,6 +122,12 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions return text; } else { + // HTML Encode href + href = href.replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); return `${text}`; } }; @@ -165,11 +182,11 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions } const markedOptions: marked.MarkedOptions = { - sanitize: true, + sanitize: markdown instanceof MarkdownString ? markdown.sanitize : true, renderer }; - element.innerHTML = marked(markdown.value, markedOptions); + element.innerHTML = marked.parse(markdown.value, markedOptions); signalInnerHTML!(); return element; diff --git a/src/vs/base/browser/mouseEvent.ts b/src/vs/base/browser/mouseEvent.ts index ab32deee7..89ff65ec4 100644 --- a/src/vs/base/browser/mouseEvent.ts +++ b/src/vs/base/browser/mouseEvent.ts @@ -113,6 +113,10 @@ export class DragMouseEvent extends StandardMouseEvent { } +export interface IMouseWheelEvent extends MouseEvent { + readonly wheelDelta: number; +} + interface IWebKitMouseWheelEvent { wheelDeltaY: number; wheelDeltaX: number; @@ -125,14 +129,14 @@ interface IGeckoMouseWheelEvent { detail: number; } -export class StandardMouseWheelEvent { +export class StandardWheelEvent { - public readonly browserEvent: MouseWheelEvent | null; + public readonly browserEvent: IMouseWheelEvent | null; public readonly deltaY: number; public readonly deltaX: number; public readonly target: Node; - constructor(e: MouseWheelEvent | null, deltaX: number = 0, deltaY: number = 0) { + constructor(e: IMouseWheelEvent | null, deltaX: number = 0, deltaY: number = 0) { this.browserEvent = e || null; this.target = e ? (e.target || (e).targetNode || e.srcElement) : null; diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index d07946d2a..e8b648a34 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -206,7 +206,7 @@ export class BaseActionItem extends Disposable implements IActionItem { dispose(): void { if (this.element) { - this.element.remove(); + DOM.removeNode(this.element); this.element = null; } @@ -726,7 +726,7 @@ export class ActionBar extends Disposable implements IActionRunner { for (let i = 0; i < this.items.length; i++) { let item = this.items[i]; - let actionItem = item; + let actionItem = item; if (i === this.focusedItem) { if (types.isFunction(actionItem.isEnabled)) { @@ -775,7 +775,7 @@ export class ActionBar extends Disposable implements IActionRunner { } this.items = null; - this.getContainer().remove(); + DOM.removeNode(this.getContainer()); super.dispose(); } diff --git a/src/vs/base/browser/ui/centered/centeredViewLayout.ts b/src/vs/base/browser/ui/centered/centeredViewLayout.ts index 0da0a7f3b..7964af23c 100644 --- a/src/vs/base/browser/ui/centered/centeredViewLayout.ts +++ b/src/vs/base/browser/ui/centered/centeredViewLayout.ts @@ -58,7 +58,7 @@ export class CenteredViewLayout { private emptyViews: ISplitViewView[] | undefined; private splitViewDisposables: IDisposable[] = []; - constructor(private container: HTMLElement, private view: IView, public readonly state: CenteredViewState = GOLDEN_RATIO) { + constructor(private container: HTMLElement, private view: IView, public readonly state: CenteredViewState = { leftMarginRatio: GOLDEN_RATIO.leftMarginRatio, rightMarginRatio: GOLDEN_RATIO.rightMarginRatio }) { this.container.appendChild(this.view.element); // Make sure to hide the split view overflow like sashes #52892 this.container.style.overflow = 'hidden'; diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index 1c44f12d2..55384d0bb 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -26,6 +26,7 @@ export const enum AnchorPosition { export interface IDelegate { getAnchor(): HTMLElement | IAnchor; render(container: HTMLElement): IDisposable; + focus?(): void; layout?(): void; anchorAlignment?: AnchorAlignment; // default: left anchorPosition?: AnchorPosition; // default: below @@ -165,6 +166,11 @@ export class ContextView extends Disposable { // Layout this.doLayout(); + + // Focus + if (this.delegate.focus) { + this.delegate.focus(); + } } public layout(): void { @@ -212,8 +218,8 @@ export class ContextView extends Disposable { around = { top: realAnchor.y, left: realAnchor.x, - width: realAnchor.width || 0, - height: realAnchor.height || 0 + width: realAnchor.width || 1, + height: realAnchor.height || 2 }; } @@ -223,7 +229,7 @@ export class ContextView extends Disposable { const anchorPosition = this.delegate!.anchorPosition || AnchorPosition.BELOW; const anchorAlignment = this.delegate!.anchorAlignment || AnchorAlignment.LEFT; - const verticalAnchor: ILayoutAnchor = { offset: around.top, size: around.height, position: anchorPosition === AnchorPosition.BELOW ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After }; + const verticalAnchor: ILayoutAnchor = { offset: around.top - window.pageYOffset, size: around.height, position: anchorPosition === AnchorPosition.BELOW ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After }; let horizontalAnchor: ILayoutAnchor; @@ -233,7 +239,7 @@ export class ContextView extends Disposable { horizontalAnchor = { offset: around.left + around.width, size: 0, position: LayoutAnchorPosition.After }; } - const top = layout(window.innerHeight, viewSizeHeight, verticalAnchor); + const top = layout(window.innerHeight, viewSizeHeight, verticalAnchor) + window.pageYOffset; // if view intersects vertically with anchor, shift it horizontally if (Range.intersects({ start: top, end: top + viewSizeHeight }, { start: verticalAnchor.offset, end: verticalAnchor.offset + verticalAnchor.size })) { diff --git a/src/vs/base/browser/ui/dropdown/dropdown.ts b/src/vs/base/browser/ui/dropdown/dropdown.ts index dc82ccd5b..49f01b74c 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.ts +++ b/src/vs/base/browser/ui/dropdown/dropdown.ts @@ -8,7 +8,7 @@ import { Gesture, EventType as GestureEventType } from 'vs/base/browser/touch'; import { ActionRunner, IAction, IActionRunner } from 'vs/base/common/actions'; import { BaseActionItem, IActionItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { IContextViewProvider, IAnchor } from 'vs/base/browser/ui/contextview/contextview'; +import { IContextViewProvider, IAnchor, AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { IMenuOptions } from 'vs/base/browser/ui/menu/menu'; import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes'; import { EventHelper, EventType, removeClass, addClass, append, $, addDisposableListener, addClasses } from 'vs/base/browser/dom'; @@ -243,13 +243,14 @@ export class DropdownMenu extends BaseDropdown { this._contextMenuProvider.showContextMenu({ getAnchor: () => this.element, - getActions: () => Promise.resolve(this.actions), + getActions: () => this.actions, getActionsContext: () => this.menuOptions ? this.menuOptions.context : null, getActionItem: action => this.menuOptions && this.menuOptions.actionItemProvider ? this.menuOptions.actionItemProvider(action) : null, getKeyBinding: action => this.menuOptions && this.menuOptions.getKeyBinding ? this.menuOptions.getKeyBinding(action) : null, getMenuClassName: () => this.menuClassName, onHide: () => this.onHide(), - actionRunner: this.menuOptions ? this.menuOptions.actionRunner : null + actionRunner: this.menuOptions ? this.menuOptions.actionRunner : null, + anchorAlignment: this.menuOptions.anchorAlignment }); } @@ -270,10 +271,11 @@ export class DropdownMenuActionItem extends BaseActionItem { private actionItemProvider: IActionItemProvider; private keybindings: (action: IAction) => ResolvedKeybinding; private clazz: string; + private anchorAlignmentProvider: (() => AnchorAlignment) | undefined; - constructor(action: IAction, menuActions: IAction[], contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider, actionRunner: IActionRunner, keybindings: (action: IAction) => ResolvedKeybinding, clazz: string); - constructor(action: IAction, actionProvider: IActionProvider, contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider, actionRunner: IActionRunner, keybindings: (action: IAction) => ResolvedKeybinding, clazz: string); - constructor(action: IAction, menuActionsOrProvider: any, contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider, actionRunner: IActionRunner, keybindings: (action: IAction) => ResolvedKeybinding, clazz: string) { + constructor(action: IAction, menuActions: IAction[], contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider, actionRunner: IActionRunner, keybindings: (action: IAction) => ResolvedKeybinding, clazz: string, anchorAlignmentProvider?: () => AnchorAlignment); + constructor(action: IAction, actionProvider: IActionProvider, contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider, actionRunner: IActionRunner, keybindings: (action: IAction) => ResolvedKeybinding, clazz: string, anchorAlignmentProvider?: () => AnchorAlignment); + constructor(action: IAction, menuActionsOrProvider: any, contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider, actionRunner: IActionRunner, keybindings: (action: IAction) => ResolvedKeybinding, clazz: string, anchorAlignmentProvider?: () => AnchorAlignment) { super(null, action); this.menuActionsOrProvider = menuActionsOrProvider; @@ -282,6 +284,7 @@ export class DropdownMenuActionItem extends BaseActionItem { this.actionRunner = actionRunner; this.keybindings = keybindings; this.clazz = clazz; + this.anchorAlignmentProvider = anchorAlignmentProvider; } render(container: HTMLElement): void { @@ -317,6 +320,17 @@ export class DropdownMenuActionItem extends BaseActionItem { getKeyBinding: this.keybindings, context: this._context }; + + if (this.anchorAlignmentProvider) { + const that = this; + + this.dropdownMenu.menuOptions = { + ...this.dropdownMenu.menuOptions, + get anchorAlignment(): AnchorAlignment { + return that.anchorAlignmentProvider(); + } + }; + } } setActionContext(newContext: any): void { @@ -332,4 +346,4 @@ export class DropdownMenuActionItem extends BaseActionItem { this.dropdownMenu.show(); } } -} \ No newline at end of file +} diff --git a/src/vs/base/browser/ui/findinput/findInput.css b/src/vs/base/browser/ui/findinput/findInput.css index c659f260e..4bb6b8cec 100644 --- a/src/vs/base/browser/ui/findinput/findInput.css +++ b/src/vs/base/browser/ui/findinput/findInput.css @@ -11,7 +11,6 @@ .monaco-findInput .monaco-inputbox { font-size: 13px; width: 100%; - height: 25px; } .monaco-findInput > .controls { diff --git a/src/vs/base/browser/ui/findinput/findInput.ts b/src/vs/base/browser/ui/findinput/findInput.ts index 4ab22a349..50116b7f9 100644 --- a/src/vs/base/browser/ui/findinput/findInput.ts +++ b/src/vs/base/browser/ui/findinput/findInput.ts @@ -23,6 +23,7 @@ export interface IFindInputOptions extends IFindInputStyles { readonly width?: number; readonly validation?: IInputValidator; readonly label: string; + readonly flexibleHeight?: boolean; readonly appendCaseSensitiveLabel?: string; readonly appendWholeWordsLabel?: string; @@ -118,7 +119,7 @@ export class FindInput extends Widget { this.domNode = null; this.inputBox = null; - this.buildDomNode(options.appendCaseSensitiveLabel || '', options.appendWholeWordsLabel || '', options.appendRegexLabel || '', options.history); + this.buildDomNode(options.appendCaseSensitiveLabel || '', options.appendWholeWordsLabel || '', options.appendRegexLabel || '', options.history, options.flexibleHeight); if (Boolean(parent)) { parent.appendChild(this.domNode); @@ -181,6 +182,10 @@ export class FindInput extends Widget { } } + public onSearchSubmit(): void { + this.inputBox.addToHistory(); + } + public style(styles: IFindInputStyles): void { this.inputActiveOptionBorder = styles.inputActiveOptionBorder; this.inputBackground = styles.inputBackground; @@ -283,7 +288,7 @@ export class FindInput extends Widget { this.inputBox.width = w; } - private buildDomNode(appendCaseSensitiveLabel: string, appendWholeWordsLabel: string, appendRegexLabel: string, history: string[]): void { + private buildDomNode(appendCaseSensitiveLabel: string, appendWholeWordsLabel: string, appendRegexLabel: string, history: string[], flexibleHeight: boolean): void { this.domNode = document.createElement('div'); this.domNode.style.width = this.width + 'px'; dom.addClass(this.domNode, 'monaco-findInput'); @@ -306,7 +311,8 @@ export class FindInput extends Widget { inputValidationErrorBackground: this.inputValidationErrorBackground, inputValidationErrorForeground: this.inputValidationErrorForeground, inputValidationErrorBorder: this.inputValidationErrorBorder, - history + history, + flexibleHeight })); this.regex = this._register(new RegexCheckbox({ diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index f09231bc0..336329a05 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -139,10 +139,15 @@ export function getRelativeLocation(rootOrientation: Orientation, location: numb function indexInParent(element: HTMLElement): number { const parentElement = element.parentElement; + + if (!parentElement) { + throw new Error('Invalid grid element'); + } + let el = parentElement.firstElementChild; let index = 0; - while (el !== element && el !== parentElement.lastElementChild) { + while (el !== element && el !== parentElement.lastElementChild && el) { el = el.nextElementSibling; index++; } @@ -157,12 +162,18 @@ function indexInParent(element: HTMLElement): number { * This will break as soon as DOM structures of the Splitview or Gridview change. */ function getGridLocation(element: HTMLElement): number[] { - if (/\bmonaco-grid-view\b/.test(element.parentElement.className)) { + const parentElement = element.parentElement; + + if (!parentElement) { + throw new Error('Invalid grid element'); + } + + if (/\bmonaco-grid-view\b/.test(parentElement.className)) { return []; } - const index = indexInParent(element.parentElement); - const ancestor = element.parentElement.parentElement.parentElement.parentElement; + const index = indexInParent(parentElement); + const ancestor = parentElement.parentElement!.parentElement!.parentElement!; return [...getGridLocation(ancestor), index]; } @@ -193,7 +204,7 @@ export class Grid implements IDisposable { get minimumHeight(): number { return this.gridview.minimumHeight; } get maximumWidth(): number { return this.gridview.maximumWidth; } get maximumHeight(): number { return this.gridview.maximumHeight; } - get onDidChange(): Event<{ width: number; height: number; }> { return this.gridview.onDidChange; } + get onDidChange(): Event<{ width: number; height: number; } | undefined> { return this.gridview.onDidChange; } get element(): HTMLElement { return this.gridview.element; } @@ -368,7 +379,7 @@ export interface ISerializableView extends IView { } export interface IViewDeserializer { - fromJSON(json: object): T; + fromJSON(json: object | null): T; } interface InitialLayoutContext { @@ -379,7 +390,7 @@ interface InitialLayoutContext { export interface ISerializedLeafNode { type: 'leaf'; - data: object; + data: object | null; size: number; } @@ -415,18 +426,15 @@ export class SerializableGrid extends Grid { throw new Error('Invalid JSON'); } - const type = json.type; - const data = json.data; - - if (type === 'branch') { - if (!Array.isArray(data)) { + if (json.type === 'branch') { + if (!Array.isArray(json.data)) { throw new Error('Invalid JSON: \'data\' property of branch must be an array.'); } const children: GridNode[] = []; let offset = 0; - for (const child of data) { + for (const child of json.data) { if (typeof child.size !== 'number') { throw new Error('Invalid JSON: \'size\' property of node must be a number.'); } @@ -441,8 +449,8 @@ export class SerializableGrid extends Grid { return { children, box }; - } else if (type === 'leaf') { - const view = deserializer.fromJSON(data) as T; + } else if (json.type === 'leaf') { + const view = deserializer.fromJSON(json.data) as T; return { view, box }; } @@ -527,13 +535,13 @@ export class SerializableGrid extends Grid { const firstLeaves = node.children.map(c => SerializableGrid.getFirstLeaf(c)); for (let i = 1; i < firstLeaves.length; i++) { - const size = orientation === Orientation.VERTICAL ? firstLeaves[i].box.height : firstLeaves[i].box.width; - this.addView(firstLeaves[i].view, size, referenceView, direction); - referenceView = firstLeaves[i].view; + const size = orientation === Orientation.VERTICAL ? firstLeaves[i]!.box.height : firstLeaves[i]!.box.width; + this.addView(firstLeaves[i]!.view, size, referenceView, direction); + referenceView = firstLeaves[i]!.view; } for (let i = 0; i < node.children.length; i++) { - this.restoreViews(firstLeaves[i].view, orthogonal(orientation), node.children[i]); + this.restoreViews(firstLeaves[i]!.view, orthogonal(orientation), node.children[i]); } } @@ -611,10 +619,10 @@ function getDimensions(node: ISerializedNode, orientation: Orientation): { width if (orientation === Orientation.VERTICAL) { const width = node.size || (childrenDimensions.length === 0 ? undefined : Math.max(...childrenDimensions.map(d => d.width || 0))); - const height = childrenDimensions.length === 0 ? undefined : childrenDimensions.reduce((r, d) => r + d.height, 0); + const height = childrenDimensions.length === 0 ? undefined : childrenDimensions.reduce((r, d) => r + (d.height || 0), 0); return { width, height }; } else { - const width = childrenDimensions.length === 0 ? undefined : childrenDimensions.reduce((r, d) => r + d.width, 0); + const width = childrenDimensions.length === 0 ? undefined : childrenDimensions.reduce((r, d) => r + (d.width || 0), 0); const height = node.size || (childrenDimensions.length === 0 ? undefined : Math.max(...childrenDimensions.map(d => d.height || 0))); return { width, height }; } diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index 2892dde50..f0ac2e098 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -6,13 +6,13 @@ import 'vs/css!./gridview'; import { Event, anyEvent, Emitter, mapEvent, Relay } from 'vs/base/common/event'; import { Orientation, Sash } from 'vs/base/browser/ui/sash/sash'; -import { SplitView, IView as ISplitView, Sizing, ISplitViewStyles } from 'vs/base/browser/ui/splitview/splitview'; +import { SplitView, IView as ISplitView, Sizing, LayoutPriority, ISplitViewStyles } from 'vs/base/browser/ui/splitview/splitview'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { $ } from 'vs/base/browser/dom'; import { tail2 as tail } from 'vs/base/common/arrays'; import { Color } from 'vs/base/common/color'; -export { Sizing } from 'vs/base/browser/ui/splitview/splitview'; +export { Sizing, LayoutPriority } from 'vs/base/browser/ui/splitview/splitview'; export { Orientation } from 'vs/base/browser/ui/sash/sash'; export interface IView { @@ -22,6 +22,8 @@ export interface IView { readonly minimumHeight: number; readonly maximumHeight: number; readonly onDidChange: Event<{ width: number; height: number; }>; + readonly priority?: LayoutPriority; + readonly snapSize?: number; layout(width: number, height: number): void; } @@ -60,6 +62,7 @@ const defaultStyles: IGridViewStyles = { export interface IGridViewOptions { styles?: IGridViewStyles; + proportionalLayout?: boolean; // default true } class BranchNode implements ISplitView, IDisposable { @@ -135,6 +138,7 @@ class BranchNode implements ISplitView, IDisposable { constructor( readonly orientation: Orientation, styles: IGridViewStyles, + readonly proportionalLayout: boolean, size: number = 0, orthogonalSize: number = 0 ) { @@ -450,6 +454,14 @@ class LeafNode implements ISplitView, IDisposable { return this.orientation === Orientation.HORIZONTAL ? this.maximumHeight : this.maximumWidth; } + get priority(): LayoutPriority | undefined { + return this.view.priority; + } + + get snapSize(): number | undefined { + return this.view.snapSize; + } + get minimumOrthogonalSize(): number { return this.orientation === Orientation.HORIZONTAL ? this.minimumWidth : this.minimumHeight; } @@ -483,7 +495,7 @@ type Node = BranchNode | LeafNode; function flipNode(node: T, size: number, orthogonalSize: number): T { if (node instanceof BranchNode) { - const result = new BranchNode(orthogonal(node.orientation), node.styles, size, orthogonalSize); + const result = new BranchNode(orthogonal(node.orientation), node.styles, node.proportionalLayout, size, orthogonalSize); let totalSize = 0; @@ -512,6 +524,7 @@ export class GridView implements IDisposable { readonly element: HTMLElement; private styles: IGridViewStyles; + private proportionalLayout: boolean; private _root: BranchNode; private onDidSashResetRelay = new Relay(); @@ -566,7 +579,8 @@ export class GridView implements IDisposable { constructor(options: IGridViewOptions = {}) { this.element = $('.monaco-grid-view'); this.styles = options.styles || defaultStyles; - this.root = new BranchNode(Orientation.VERTICAL, this.styles); + this.proportionalLayout = typeof options.proportionalLayout !== 'undefined' ? !!options.proportionalLayout : true; + this.root = new BranchNode(Orientation.VERTICAL, this.styles, this.proportionalLayout); } style(styles: IGridViewStyles): void { @@ -596,7 +610,7 @@ export class GridView implements IDisposable { const [, parentIndex] = tail(rest); grandParent.removeChild(parentIndex); - const newParent = new BranchNode(parent.orientation, this.styles, parent.size, parent.orthogonalSize); + const newParent = new BranchNode(parent.orientation, this.styles, this.proportionalLayout, parent.size, parent.orthogonalSize); grandParent.addChild(newParent, parent.size, parentIndex); newParent.orthogonalLayout(parent.orthogonalSize); diff --git a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts index c6f06f0f9..a8e3d6c32 100644 --- a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts +++ b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts @@ -21,7 +21,7 @@ export class HighlightedLabel implements IDisposable { private highlights: IHighlight[]; private didEverRender: boolean; - constructor(container: HTMLElement) { + constructor(container: HTMLElement, private supportOcticons: boolean) { this.domNode = document.createElement('span'); this.domNode.className = 'monaco-highlighted-label'; this.didEverRender = false; @@ -68,19 +68,22 @@ export class HighlightedLabel implements IDisposable { } if (pos < highlight.start) { htmlContent.push(''); - htmlContent.push(renderOcticons(this.text.substring(pos, highlight.start))); + const substring = this.text.substring(pos, highlight.start); + htmlContent.push(this.supportOcticons ? renderOcticons(substring) : substring); htmlContent.push(''); pos = highlight.end; } htmlContent.push(''); - htmlContent.push(renderOcticons(this.text.substring(highlight.start, highlight.end))); + const substring = this.text.substring(highlight.start, highlight.end); + htmlContent.push(this.supportOcticons ? renderOcticons(substring) : substring); htmlContent.push(''); pos = highlight.end; } if (pos < this.text.length) { htmlContent.push(''); - htmlContent.push(renderOcticons(this.text.substring(pos))); + const substring = this.text.substring(pos); + htmlContent.push(this.supportOcticons ? renderOcticons(substring) : substring); htmlContent.push(''); } diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index a2af0b7e7..752437df1 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -12,6 +12,7 @@ import { IDisposable, combinedDisposable, Disposable } from 'vs/base/common/life export interface IIconLabelCreationOptions { supportHighlights?: boolean; supportDescriptionHighlights?: boolean; + donotSupportOcticons?: boolean; } export interface IIconLabelValueOptions { @@ -99,13 +100,13 @@ export class IconLabel extends Disposable { this.labelDescriptionContainer = this._register(new FastLabelNode(dom.append(this.domNode.element, dom.$('.monaco-icon-label-description-container')))); if (options && options.supportHighlights) { - this.labelNode = this._register(new HighlightedLabel(dom.append(this.labelDescriptionContainer.element, dom.$('a.label-name')))); + this.labelNode = this._register(new HighlightedLabel(dom.append(this.labelDescriptionContainer.element, dom.$('a.label-name')), !options.donotSupportOcticons)); } else { this.labelNode = this._register(new FastLabelNode(dom.append(this.labelDescriptionContainer.element, dom.$('a.label-name')))); } if (options && options.supportDescriptionHighlights) { - this.descriptionNodeFactory = () => this._register(new HighlightedLabel(dom.append(this.labelDescriptionContainer.element, dom.$('span.label-description')))); + this.descriptionNodeFactory = () => this._register(new HighlightedLabel(dom.append(this.labelDescriptionContainer.element, dom.$('span.label-description')), !options.donotSupportOcticons)); } else { this.descriptionNodeFactory = () => this._register(new FastLabelNode(dom.append(this.labelDescriptionContainer.element, dom.$('span.label-description')))); } @@ -137,7 +138,7 @@ export class IconLabel extends Disposable { this.domNode.title = options && options.title ? options.title : ''; if (this.labelNode instanceof HighlightedLabel) { - this.labelNode.set(label || '', options ? options.matches : void 0, void 0, options && options.labelEscapeNewLines); + this.labelNode.set(label || '', options ? options.matches : void 0, options && options.title ? options.title : void 0, options && options.labelEscapeNewLines); } else { this.labelNode.textContent = label || ''; } diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index 947e36a2c..7b9a9cdb3 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -291,6 +291,9 @@ export class InputBox extends Widget { public set width(width: number) { this.input.style.width = width + 'px'; + if (this.mirror) { + this.mirror.style.width = width + 'px'; + } } public showMessage(message: IMessage, force?: boolean): void { diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index b9815b6ef..1af9112d4 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -8,6 +8,7 @@ import { GestureEvent } from 'vs/base/browser/touch'; export interface IListVirtualDelegate { getHeight(element: T): number; getTemplateId(element: T): string; + hasDynamicHeight?(element: T): boolean; } export interface IListRenderer { @@ -44,7 +45,7 @@ export interface IListGestureEvent { export interface IListContextMenuEvent { browserEvent: UIEvent; - element: T; + element: T | undefined; index: number; - anchor: HTMLElement | { x: number; y: number; }; + anchor: HTMLElement | { x: number; y: number; } | undefined; } diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index 1317c4f5b..d4491cece 100644 --- a/src/vs/base/browser/ui/list/listPaging.ts +++ b/src/vs/base/browser/ui/list/listPaging.ts @@ -17,8 +17,8 @@ export interface IPagedRenderer extends IListRenderer { - data: T; - disposable: IDisposable; + data?: T; + disposable?: IDisposable; } class PagedRenderer implements IListRenderer> { @@ -36,7 +36,13 @@ class PagedRenderer implements IListRenderer): void { - data.disposable.dispose(); + if (data.disposable) { + data.disposable.dispose(); + } + + if (!data.data) { + return; + } const model = this.modelProvider(); @@ -49,7 +55,7 @@ class PagedRenderer implements IListRenderer cts.cancel() }; this.renderer.renderPlaceholder(index, data.data); - promise.then(entry => this.renderer.renderElement(entry, index, data.data)); + promise.then(entry => this.renderer.renderElement(entry, index, data.data!)); } disposeElement(): void { @@ -57,10 +63,14 @@ class PagedRenderer implements IListRenderer): void { - data.disposable.dispose(); - data.disposable = null; - this.renderer.disposeTemplate(data.data); - data.data = null; + if (data.disposable) { + data.disposable.dispose(); + data.disposable = undefined; + } + if (data.data) { + this.renderer.disposeTemplate(data.data); + data.data = undefined; + } } } @@ -124,7 +134,7 @@ export class PagedList implements IDisposable { } get onContextMenu(): Event> { - return mapEvent(this.list.onContextMenu, ({ element, index, anchor, browserEvent }) => ({ element: this._model.get(element), index, anchor, browserEvent })); + return mapEvent(this.list.onContextMenu, ({ element, index, anchor, browserEvent }) => ({ element: this._model.get(element!), index, anchor, browserEvent })); } get model(): IPagedModel { diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 61b6d478b..0a96472db 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getOrDefault } from 'vs/base/common/objects'; +import { getOrDefault2 } from 'vs/base/common/objects'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Gesture, EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch'; import * as DOM from 'vs/base/browser/dom'; @@ -33,29 +33,34 @@ function canUseTranslate3d(): boolean { return true; } - interface IItem { - id: string; - element: T; + readonly id: string; + readonly element: T; + readonly templateId: string; + row: IRow | null; size: number; - templateId: string; - row: IRow; + hasDynamicHeight: boolean; + renderWidth: number | undefined; } export interface IListViewOptions { - useShadows?: boolean; - verticalScrollMode?: ScrollbarVisibility; - setRowLineHeight?: boolean; + readonly useShadows?: boolean; + readonly verticalScrollMode?: ScrollbarVisibility; + readonly setRowLineHeight?: boolean; + readonly supportDynamicHeights?: boolean; } -const DefaultOptions: IListViewOptions = { +const DefaultOptions = { useShadows: true, verticalScrollMode: ScrollbarVisibility.Auto, - setRowLineHeight: true + setRowLineHeight: true, + supportDynamicHeights: false }; export class ListView implements ISpliceable, IDisposable { + readonly domNode: HTMLElement; + private items: IItem[]; private itemId: number; private rangeMap: RangeMap; @@ -63,7 +68,7 @@ export class ListView implements ISpliceable, IDisposable { private renderers = new Map>(); private lastRenderTop: number; private lastRenderHeight: number; - private _domNode: HTMLElement; + private renderWidth = 0; private gesture: Gesture; private rowsContainer: HTMLElement; private scrollableElement: ScrollableElement; @@ -74,6 +79,7 @@ export class ListView implements ISpliceable, IDisposable { private dragAndDropScrollTimeout: number; private dragAndDropMouseY: number; private setRowLineHeight: boolean; + private supportDynamicHeights: boolean; private disposables: IDisposable[]; constructor( @@ -95,8 +101,8 @@ export class ListView implements ISpliceable, IDisposable { this.lastRenderTop = 0; this.lastRenderHeight = 0; - this._domNode = document.createElement('div'); - this._domNode.className = 'monaco-list'; + this.domNode = document.createElement('div'); + this.domNode.className = 'monaco-list'; this.rowsContainer = document.createElement('div'); this.rowsContainer.className = 'monaco-list-rows'; @@ -105,12 +111,12 @@ export class ListView implements ISpliceable, IDisposable { this.scrollableElement = new ScrollableElement(this.rowsContainer, { alwaysConsumeMouseWheel: true, horizontal: ScrollbarVisibility.Hidden, - vertical: getOrDefault(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode), - useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows) + vertical: getOrDefault2(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode), + useShadows: getOrDefault2(options, o => o.useShadows, DefaultOptions.useShadows) }); - this._domNode.appendChild(this.scrollableElement.getDomNode()); - container.appendChild(this._domNode); + this.domNode.appendChild(this.scrollableElement.getDomNode()); + container.appendChild(this.domNode); this.disposables = [this.rangeMap, this.gesture, this.scrollableElement, this.cache]; @@ -125,15 +131,12 @@ export class ListView implements ISpliceable, IDisposable { const onDragOver = mapEvent(domEvent(this.rowsContainer, 'dragover'), e => new DragMouseEvent(e)); onDragOver(this.onDragOver, this, this.disposables); - this.setRowLineHeight = getOrDefault(options, o => o.setRowLineHeight, DefaultOptions.setRowLineHeight); + this.setRowLineHeight = getOrDefault2(options, o => o.setRowLineHeight, DefaultOptions.setRowLineHeight); + this.supportDynamicHeights = getOrDefault2(options, o => o.supportDynamicHeights, DefaultOptions.supportDynamicHeights); this.layout(); } - get domNode(): HTMLElement { - return this._domNode; - } - splice(start: number, deleteCount: number, elements: T[] = []): T[] { if (this.splicing) { throw new Error('Can\'t run recursive splices.'); @@ -164,13 +167,25 @@ export class ListView implements ISpliceable, IDisposable { const inserted = elements.map>(element => ({ id: String(this.itemId++), element, - size: this.virtualDelegate.getHeight(element), templateId: this.virtualDelegate.getTemplateId(element), + size: this.virtualDelegate.getHeight(element), + hasDynamicHeight: !!this.virtualDelegate.hasDynamicHeight && this.virtualDelegate.hasDynamicHeight(element), + renderWidth: undefined, row: null })); - this.rangeMap.splice(start, deleteCount, ...inserted); - const deleted = this.items.splice(start, deleteCount, ...inserted); + let deleted: IItem[]; + + // TODO@joao: improve this optimization to catch even more cases + if (start === 0 && deleteCount >= this.items.length) { + this.rangeMap = new RangeMap(); + this.rangeMap.splice(0, 0, inserted); + this.items = inserted; + deleted = []; + } else { + this.rangeMap.splice(start, deleteCount, inserted); + deleted = this.items.splice(start, deleteCount, ...inserted); + } const delta = elements.length - deleteCount; const renderRange = this.getRenderRange(this.lastRenderTop, this.lastRenderHeight); @@ -183,10 +198,8 @@ export class ListView implements ISpliceable, IDisposable { const removeRanges = Range.relativeComplement(renderedRestRange, renderRange); - for (let r = 0; r < removeRanges.length; r++) { - const removeRange = removeRanges[r]; - - for (let i = removeRange.start; i < removeRange.end; i++) { + for (const range of removeRanges) { + for (let i = range.start; i < range.end; i++) { this.removeItemFromDOM(i); } } @@ -196,14 +209,22 @@ export class ListView implements ISpliceable, IDisposable { const insertRanges = [elementsRange, ...unrenderedRestRanges].map(r => Range.intersect(renderRange, r)); const beforeElement = this.getNextToLastElement(insertRanges); - for (let r = 0; r < insertRanges.length; r++) { - const insertRange = insertRanges[r]; - - for (let i = insertRange.start; i < insertRange.end; i++) { + for (const range of insertRanges) { + for (let i = range.start; i < range.end; i++) { this.insertItemInDOM(i, beforeElement); } } + this.updateScrollHeight(); + + if (this.supportDynamicHeights) { + this.rerender(this.scrollTop, this.renderHeight); + } + + return deleted.map(i => i.element); + } + + private updateScrollHeight(): void { this.scrollHeight = this.getContentHeight(); this.rowsContainer.style.height = `${this.scrollHeight}px`; @@ -215,8 +236,6 @@ export class ListView implements ISpliceable, IDisposable { this.didRequestScrollableElementUpdate = true; } - - return deleted.map(i => i.element); } get length(): number { @@ -232,7 +251,7 @@ export class ListView implements ISpliceable, IDisposable { return this.items[index].element; } - domElement(index: number): HTMLElement { + domElement(index: number): HTMLElement | null { const row = this.items[index].row; return row && row.domNode; } @@ -255,10 +274,18 @@ export class ListView implements ISpliceable, IDisposable { layout(height?: number): void { this.scrollableElement.setScrollDimensions({ - height: height || DOM.getContentHeight(this._domNode) + height: height || DOM.getContentHeight(this.domNode) }); } + layoutWidth(width: number): void { + this.renderWidth = width; + + if (this.supportDynamicHeights) { + this.rerender(this.scrollTop, this.renderHeight); + } + } + // Render private render(renderTop: number, renderHeight: number): void { @@ -302,20 +329,14 @@ export class ListView implements ISpliceable, IDisposable { item.row = this.cache.alloc(item.templateId); } - if (!item.row.domNode.parentElement) { + if (!item.row.domNode!.parentElement) { if (beforeElement) { - this.rowsContainer.insertBefore(item.row.domNode, beforeElement); + this.rowsContainer.insertBefore(item.row.domNode!, beforeElement); } else { - this.rowsContainer.appendChild(item.row.domNode); + this.rowsContainer.appendChild(item.row.domNode!); } } - item.row.domNode.style.height = `${item.size}px`; - - if (this.setRowLineHeight) { - item.row.domNode.style.lineHeight = `${item.size}px`; - } - this.updateItemInDOM(item, index); const renderer = this.renderers.get(item.templateId); @@ -323,11 +344,17 @@ export class ListView implements ISpliceable, IDisposable { } private updateItemInDOM(item: IItem, index: number): void { - item.row.domNode.style.top = `${this.elementTop(index)}px`; - item.row.domNode.setAttribute('data-index', `${index}`); - item.row.domNode.setAttribute('data-last-element', index === this.length - 1 ? 'true' : 'false'); - item.row.domNode.setAttribute('aria-setsize', `${this.length}`); - item.row.domNode.setAttribute('aria-posinset', `${index + 1}`); + item.row!.domNode!.style.top = `${this.elementTop(index)}px`; + item.row!.domNode!.style.height = `${item.size}px`; + + if (this.setRowLineHeight) { + item.row!.domNode!.style.lineHeight = `${item.size}px`; + } + + item.row!.domNode!.setAttribute('data-index', `${index}`); + item.row!.domNode!.setAttribute('data-last-element', index === this.length - 1 ? 'true' : 'false'); + item.row!.domNode!.setAttribute('aria-setsize', `${this.length}`); + item.row!.domNode!.setAttribute('aria-posinset', `${index + 1}`); } private removeItemFromDOM(index: number): void { @@ -335,10 +362,10 @@ export class ListView implements ISpliceable, IDisposable { const renderer = this.renderers.get(item.templateId); if (renderer.disposeElement) { - renderer.disposeElement(item.element, index, item.row.templateData); + renderer.disposeElement(item.element, index, item.row!.templateData); } - this.cache.release(item.row); + this.cache.release(item.row!); item.row = null; } @@ -378,21 +405,21 @@ export class ListView implements ISpliceable, IDisposable { @memoize get onTap(): Event> { return filterEvent(mapEvent(domEvent(this.rowsContainer, TouchEventType.Tap), e => this.toGestureEvent(e)), e => e.index >= 0); } private toMouseEvent(browserEvent: MouseEvent): IListMouseEvent { - const index = this.getItemIndexFromEventTarget(browserEvent.target); + const index = this.getItemIndexFromEventTarget(browserEvent.target || null); const item = index < 0 ? undefined : this.items[index]; const element = item && item.element; return { browserEvent, index, element }; } private toTouchEvent(browserEvent: TouchEvent): IListTouchEvent { - const index = this.getItemIndexFromEventTarget(browserEvent.target); + const index = this.getItemIndexFromEventTarget(browserEvent.target || null); const item = index < 0 ? undefined : this.items[index]; const element = item && item.element; return { browserEvent, index, element }; } private toGestureEvent(browserEvent: GestureEvent): IListGestureEvent { - const index = this.getItemIndexFromEventTarget(browserEvent.initialTarget); + const index = this.getItemIndexFromEventTarget(browserEvent.initialTarget || null); const item = index < 0 ? undefined : this.items[index]; const element = item && item.element; return { browserEvent, index, element }; @@ -401,6 +428,10 @@ export class ListView implements ISpliceable, IDisposable { private onScroll(e: ScrollEvent): void { try { this.render(e.scrollTop, e.height); + + if (this.supportDynamicHeights) { + this.rerender(e.scrollTop, e.height); + } } catch (err) { console.log('Got bad scroll event:', e); throw err; @@ -420,7 +451,7 @@ export class ListView implements ISpliceable, IDisposable { } private setupDragAndDropScrollInterval(): void { - var viewTop = DOM.getTopLeftOffset(this._domNode).top; + const viewTop = DOM.getTopLeftOffset(this.domNode).top; if (!this.dragAndDropScrollInterval) { this.dragAndDropScrollInterval = window.setInterval(() => { @@ -445,7 +476,7 @@ export class ListView implements ISpliceable, IDisposable { this.dragAndDropScrollTimeout = window.setTimeout(() => { this.cancelDragAndDropScrollInterval(); - this.dragAndDropScrollTimeout = null; + this.dragAndDropScrollTimeout = -1; }, 1000); } } @@ -453,7 +484,7 @@ export class ListView implements ISpliceable, IDisposable { private cancelDragAndDropScrollInterval(): void { if (this.dragAndDropScrollInterval) { window.clearInterval(this.dragAndDropScrollInterval); - this.dragAndDropScrollInterval = null; + this.dragAndDropScrollInterval = -1; } this.cancelDragAndDropScrollTimeout(); @@ -462,15 +493,16 @@ export class ListView implements ISpliceable, IDisposable { private cancelDragAndDropScrollTimeout(): void { if (this.dragAndDropScrollTimeout) { window.clearTimeout(this.dragAndDropScrollTimeout); - this.dragAndDropScrollTimeout = null; + this.dragAndDropScrollTimeout = -1; } } // Util - private getItemIndexFromEventTarget(target: EventTarget): number { - while (target instanceof HTMLElement && target !== this.rowsContainer) { - const element = target as HTMLElement; + private getItemIndexFromEventTarget(target: EventTarget | null): number { + let element: HTMLElement | null = target as (HTMLElement | null); + + while (element instanceof HTMLElement && element !== this.rowsContainer) { const rawIndex = element.getAttribute('data-index'); if (rawIndex) { @@ -481,7 +513,7 @@ export class ListView implements ISpliceable, IDisposable { } } - target = element.parentElement; + element = element.parentElement; } return -1; @@ -494,6 +526,94 @@ export class ListView implements ISpliceable, IDisposable { }; } + /** + * Given a stable rendered state, checks every rendered element whether it needs + * to be probed for dynamic height. Adjusts scroll height and top if necessary. + */ + private rerender(renderTop: number, renderHeight: number): void { + const previousRenderRange = this.getRenderRange(renderTop, renderHeight); + + // Let's remember the second element's position, this helps in scrolling up + // and preserving a linear upwards scroll movement + let secondElementIndex: number | undefined; + let secondElementTopDelta: number | undefined; + + if (previousRenderRange.end - previousRenderRange.start > 1) { + secondElementIndex = previousRenderRange.start + 1; + secondElementTopDelta = this.elementTop(secondElementIndex) - renderTop; + } + + let heightDiff = 0; + + while (true) { + const renderRange = this.getRenderRange(renderTop, renderHeight); + + let didChange = false; + + for (let i = renderRange.start; i < renderRange.end; i++) { + const diff = this.probeDynamicHeight(i); + + if (diff !== 0) { + this.rangeMap.splice(i, 1, [this.items[i]]); + } + + heightDiff += diff; + didChange = didChange || diff !== 0; + } + + if (!didChange) { + if (heightDiff !== 0) { + this.updateScrollHeight(); + } + + const unrenderRanges = Range.relativeComplement(previousRenderRange, renderRange); + + for (const range of unrenderRanges) { + for (let i = range.start; i < range.end; i++) { + if (this.items[i].row) { + this.removeItemFromDOM(i); + } + } + } + + for (let i = renderRange.start; i < renderRange.end; i++) { + if (this.items[i].row) { + this.updateItemInDOM(this.items[i], i); + } + } + + if (typeof secondElementIndex === 'number') { + this.scrollTop = this.elementTop(secondElementIndex) - secondElementTopDelta!; + } + + return; + } + } + + } + + private probeDynamicHeight(index: number): number { + const item = this.items[index]; + + if (!item.hasDynamicHeight || item.renderWidth === this.renderWidth) { + return 0; + } + + const size = item.size; + const renderer = this.renderers.get(item.templateId); + const row = this.cache.alloc(item.templateId); + + row.domNode!.style.height = ''; + this.rowsContainer.appendChild(row.domNode!); + renderer.renderElement(item.element, index, row.templateData); + item.size = row.domNode!.offsetHeight; + item.renderWidth = this.renderWidth; + this.rowsContainer.removeChild(row.domNode!); + this.cache.release(row); + + return item.size - size; + } + private getNextToLastElement(ranges: IRange[]): HTMLElement | null { const lastRange = ranges[ranges.length - 1]; @@ -522,16 +642,14 @@ export class ListView implements ISpliceable, IDisposable { if (item.row) { const renderer = this.renderers.get(item.row.templateId); renderer.disposeTemplate(item.row.templateData); - item.row = null; } } - this.items = null; + this.items = []; } - if (this._domNode && this._domNode.parentElement) { - this._domNode.parentNode.removeChild(this._domNode); - this._domNode = null; + if (this.domNode && this.domNode.parentNode) { + this.domNode.parentNode.removeChild(this.domNode); } this.disposables = dispose(this.disposables); diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 481146506..9a0457d48 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -25,8 +25,8 @@ import { ISpliceable } from 'vs/base/common/sequence'; import { CombinedSpliceable } from 'vs/base/browser/ui/list/splice'; import { clamp } from 'vs/base/common/numbers'; -export interface IIdentityProvider { - (element: T): string; +export interface IIdentityProvider { + (element: T): R; } interface ITraitChangeEvent { @@ -180,7 +180,6 @@ class Trait implements ISpliceable, IDisposable { } dispose() { - this.indexes = null; this._onChange = dispose(this._onChange); } } @@ -188,7 +187,7 @@ class Trait implements ISpliceable, IDisposable { class FocusTrait extends Trait { constructor( - private getDomId: IIdentityProvider + private getDomId: IIdentityProvider ) { super('focused'); } @@ -224,8 +223,9 @@ class TraitSpliceable implements ISpliceable { return this.trait.splice(start, deleteCount, elements.map(e => false)); } - const pastElementsWithTrait = this.trait.get().map(i => this.getId(this.view.element(i))); - const elementsWithTrait = elements.map(e => pastElementsWithTrait.indexOf(this.getId(e)) > -1); + const getId = this.getId; + const pastElementsWithTrait = this.trait.get().map(i => getId(this.view.element(i))); + const elementsWithTrait = elements.map(e => pastElementsWithTrait.indexOf(getId(e)) > -1); this.trait.splice(start, deleteCount, elementsWithTrait); } @@ -357,6 +357,11 @@ class DOMFocusController implements IDisposable { } const focusedDomElement = this.view.domElement(focus[0]); + + if (!focusedDomElement) { + return; + } + const tabIndexElement = focusedDomElement.querySelector('[tabIndex]'); if (!tabIndexElement || !(tabIndexElement instanceof HTMLElement) || tabIndexElement.tabIndex === -1) { @@ -421,7 +426,7 @@ class MouseController implements IDisposable { .map(event => { const index = this.list.getFocus()[0]; const element = this.view.element(index); - const anchor = this.view.domElement(index); + const anchor = this.view.domElement(index) || undefined; return { index, element, anchor, browserEvent: event.browserEvent }; }) .event; @@ -436,7 +441,7 @@ class MouseController implements IDisposable { .map(browserEvent => { const index = this.list.getFocus()[0]; const element = this.view.element(index); - const anchor = this.view.domElement(index); + const anchor = this.view.domElement(index) || undefined; return { index, element, anchor, browserEvent }; }) .filter(({ anchor }) => !!anchor) @@ -974,10 +979,7 @@ export class List implements ISpliceable, IDisposable { this.styleElement = DOM.createStyleSheet(this.view.domNode); - this.styleController = options.styleController; - if (!this.styleController) { - this.styleController = new DefaultStyleController(this.styleElement, this.idPrefix); - } + this.styleController = options.styleController || new DefaultStyleController(this.styleElement, this.idPrefix); this.spliceable = new CombinedSpliceable([ new TraitSpliceable(this.focus, this.view, options.identityProvider), @@ -987,8 +989,8 @@ export class List implements ISpliceable, IDisposable { this.disposables = [this.focus, this.selection, this.view, this._onDidDispose]; - this.onDidFocus = mapEvent(domEvent(this.view.domNode, 'focus', true), () => null); - this.onDidBlur = mapEvent(domEvent(this.view.domNode, 'blur', true), () => null); + this.onDidFocus = mapEvent(domEvent(this.view.domNode, 'focus', true), () => null!); + this.onDidBlur = mapEvent(domEvent(this.view.domNode, 'blur', true), () => null!); this.disposables.push(new DOMFocusController(this, this.view)); @@ -1053,6 +1055,10 @@ export class List implements ISpliceable, IDisposable { this.view.layout(height); } + layoutWidth(width: number): void { + this.view.layoutWidth(width); + } + setSelection(indexes: number[], browserEvent?: UIEvent): void { for (const index of indexes) { if (index < 0 || index >= this.length) { @@ -1087,7 +1093,7 @@ export class List implements ISpliceable, IDisposable { if (this.length === 0) { return; } const focus = this.focus.get(); let index = focus.length > 0 ? focus[0] + n : 0; - this.setFocus(loop ? [index % this.length] : [Math.min(index, this.length - 1)]); + this.setFocus(loop ? [index % this.length] : [Math.min(index, this.length - 1)], browserEvent); } focusPrevious(n = 1, loop = false, browserEvent?: UIEvent): void { @@ -1095,7 +1101,7 @@ export class List implements ISpliceable, IDisposable { const focus = this.focus.get(); let index = focus.length > 0 ? focus[0] - n : 0; if (loop && index < 0) { index = (this.length + (index % this.length)) % this.length; } - this.setFocus([Math.max(index, 0)]); + this.setFocus([Math.max(index, 0)], browserEvent); } focusNextPage(browserEvent?: UIEvent): void { @@ -1105,14 +1111,14 @@ export class List implements ISpliceable, IDisposable { const currentlyFocusedElement = this.getFocusedElements()[0]; if (currentlyFocusedElement !== lastPageElement) { - this.setFocus([lastPageIndex]); + this.setFocus([lastPageIndex], browserEvent); } else { const previousScrollTop = this.view.getScrollTop(); this.view.setScrollTop(previousScrollTop + this.view.renderHeight - this.view.elementHeight(lastPageIndex)); if (this.view.getScrollTop() !== previousScrollTop) { // Let the scroll event listener run - setTimeout(() => this.focusNextPage(), 0); + setTimeout(() => this.focusNextPage(browserEvent), 0); } } } @@ -1131,26 +1137,26 @@ export class List implements ISpliceable, IDisposable { const currentlyFocusedElement = this.getFocusedElements()[0]; if (currentlyFocusedElement !== firstPageElement) { - this.setFocus([firstPageIndex]); + this.setFocus([firstPageIndex], browserEvent); } else { const previousScrollTop = scrollTop; this.view.setScrollTop(scrollTop - this.view.renderHeight); if (this.view.getScrollTop() !== previousScrollTop) { // Let the scroll event listener run - setTimeout(() => this.focusPreviousPage(), 0); + setTimeout(() => this.focusPreviousPage(browserEvent), 0); } } } focusLast(browserEvent?: UIEvent): void { if (this.length === 0) { return; } - this.setFocus([this.length - 1]); + this.setFocus([this.length - 1], browserEvent); } focusFirst(browserEvent?: UIEvent): void { if (this.length === 0) { return; } - this.setFocus([0]); + this.setFocus([0], browserEvent); } getFocus(): number[] { diff --git a/src/vs/base/browser/ui/list/rangeMap.ts b/src/vs/base/browser/ui/list/rangeMap.ts index d70ddf174..45b164c53 100644 --- a/src/vs/base/browser/ui/list/rangeMap.ts +++ b/src/vs/base/browser/ui/list/rangeMap.ts @@ -92,7 +92,7 @@ export class RangeMap { private groups: IRangedGroup[] = []; private _size = 0; - splice(index: number, deleteCount: number, ...items: IItem[]): void { + splice(index: number, deleteCount: number, items: IItem[] = []): void { const diff = items.length - deleteCount; const before = groupIntersect({ start: 0, end: index }, this.groups); const after = groupIntersect({ start: index + deleteCount, end: Number.POSITIVE_INFINITY }, this.groups) diff --git a/src/vs/base/browser/ui/menu/menu.css b/src/vs/base/browser/ui/menu/menu.css index 63c186066..ecbb197f5 100644 --- a/src/vs/base/browser/ui/menu/menu.css +++ b/src/vs/base/browser/ui/menu/menu.css @@ -55,8 +55,8 @@ .monaco-menu .monaco-action-bar.vertical .submenu-indicator { height: 100%; - -webkit-mask: url('submenu.svg') no-repeat 90% 50%/14px 14px; - mask: url('submenu.svg') no-repeat 90% 50%/14px 14px; + -webkit-mask: url('submenu.svg') no-repeat 90% 50%/13px 13px; + mask: url('submenu.svg') no-repeat 90% 50%/13px 13px; } .monaco-menu .monaco-action-bar.vertical .action-item.disabled .keybinding, @@ -106,7 +106,7 @@ /* Context Menu */ .context-view.monaco-menu-container { - font-family: "Segoe WPC", "Segoe UI", ".SFNSDisplay-Light", "SFUIText-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback"; + font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif; outline: 0; border: none; -webkit-animation: fadeIn 0.083s linear; diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index 75554c14f..d3444151c 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -14,6 +14,7 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; +import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; export const MENU_MNEMONIC_REGEX: RegExp = /\(&{1,2}(.)\)|&{1,2}(.)/; export const MENU_ESCAPED_MNEMONIC_REGEX: RegExp = /(?:&){1,2}(.)/; @@ -25,6 +26,7 @@ export interface IMenuOptions { getKeyBinding?: (action: IAction) => ResolvedKeybinding; ariaLabel?: string; enableMnemonics?: boolean; + anchorAlignment?: AnchorAlignment; } export interface IMenuStyles { @@ -139,6 +141,10 @@ export class Menu extends ActionBar { this.mnemonics = new Map>(); this.push(actions, { icon: true, label: true, isMenu: true }); + + this.items.filter(item => !(item instanceof MenuSeparatorActionItem)).forEach((item: MenuActionItem, index: number, array: any[]) => { + item.updatePositionInSet(index + 1, array.length); + }); } style(style: IMenuStyles): void { @@ -323,6 +329,11 @@ class MenuActionItem extends BaseActionItem { this.applyStyle(); } + updatePositionInSet(pos: number, setSize: number): void { + this.item.setAttribute('aria-posinset', `${pos}`); + this.item.setAttribute('aria-setsize', `${setSize}`); + } + updateLabel(): void { if (this.options.label) { let label = this.getAction().label; diff --git a/src/vs/base/browser/ui/menu/submenu.svg b/src/vs/base/browser/ui/menu/submenu.svg index 8eeac7b7b..98a0aa592 100644 --- a/src/vs/base/browser/ui/menu/submenu.svg +++ b/src/vs/base/browser/ui/menu/submenu.svg @@ -1,3 +1,3 @@ - + diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/OSSREADME.json b/src/vs/base/browser/ui/octiconLabel/octicons/OSSREADME.json deleted file mode 100644 index 83493f39a..000000000 --- a/src/vs/base/browser/ui/octiconLabel/octicons/OSSREADME.json +++ /dev/null @@ -1,124 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "octicons-code", - "repositoryURL": "https://octicons.github.com", - "version": "3.1.0", - "license": "MIT", - "licenseDetail": [ - "The MIT License (MIT)", - "", - "(c) 2012-2015 GitHub", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in", - "all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN", - "THE SOFTWARE." - ] -},{ - "name": "octicons-font", - "repositoryURL": "https://octicons.github.com", - "version": "3.1.0", - "license": "SIL OFL 1.1", - "licenseDetail": [ - "(c) 2012-2015 GitHub", - "", - "SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007", - "", - "PREAMBLE", - "The goals of the Open Font License (OFL) are to stimulate worldwide", - "development of collaborative font projects, to support the font creation", - "efforts of academic and linguistic communities, and to provide a free and", - "open framework in which fonts may be shared and improved in partnership", - "with others.", - "", - "The OFL allows the licensed fonts to be used, studied, modified and", - "redistributed freely as long as they are not sold by themselves. The", - "fonts, including any derivative works, can be bundled, embedded,", - "redistributed and/or sold with any software provided that any reserved", - "names are not used by derivative works. The fonts and derivatives,", - "however, cannot be released under any other type of license. The", - "requirement for fonts to remain under this license does not apply", - "to any document created using the fonts or their derivatives.", - "", - "DEFINITIONS", - "\"Font Software\" refers to the set of files released by the Copyright", - "Holder(s) under this license and clearly marked as such. This may", - "include source files, build scripts and documentation.", - "", - "\"Reserved Font Name\" refers to any names specified as such after the", - "copyright statement(s).", - "", - "\"Original Version\" refers to the collection of Font Software components as", - "distributed by the Copyright Holder(s).", - "", - "\"Modified Version\" refers to any derivative made by adding to, deleting,", - "or substituting -- in part or in whole -- any of the components of the", - "Original Version, by changing formats or by porting the Font Software to a", - "new environment.", - "", - "\"Author\" refers to any designer, engineer, programmer, technical", - "writer or other person who contributed to the Font Software.", - "", - "PERMISSION & CONDITIONS", - "Permission is hereby granted, free of charge, to any person obtaining", - "a copy of the Font Software, to use, study, copy, merge, embed, modify,", - "redistribute, and sell modified and unmodified copies of the Font", - "Software, subject to the following conditions:", - "", - "1) Neither the Font Software nor any of its individual components,", - "in Original or Modified Versions, may be sold by itself.", - "", - "2) Original or Modified Versions of the Font Software may be bundled,", - "redistributed and/or sold with any software, provided that each copy", - "contains the above copyright notice and this license. These can be", - "included either as stand-alone text files, human-readable headers or", - "in the appropriate machine-readable metadata fields within text or", - "binary files as long as those fields can be easily viewed by the user.", - "", - "3) No Modified Version of the Font Software may use the Reserved Font", - "Name(s) unless explicit written permission is granted by the corresponding", - "Copyright Holder. This restriction only applies to the primary font name as", - "presented to the users.", - "", - "4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font", - "Software shall not be used to promote, endorse or advertise any", - "Modified Version, except to acknowledge the contribution(s) of the", - "Copyright Holder(s) and the Author(s) or with their explicit written", - "permission.", - "", - "5) The Font Software, modified or unmodified, in part or in whole,", - "must be distributed entirely under this license, and must not be", - "distributed under any other license. The requirement for fonts to", - "remain under this license does not apply to any document created", - "using the Font Software.", - "", - "TERMINATION", - "This license becomes null and void if any of the above conditions are", - "not met.", - "", - "DISCLAIMER", - "THE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,", - "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF", - "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT", - "OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE", - "COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,", - "INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL", - "DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", - "FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM", - "OTHER DEALINGS IN THE FONT SOFTWARE." - ] -}] diff --git a/src/vs/base/browser/ui/octiconLabel/octicons/cgmanifest.json b/src/vs/base/browser/ui/octiconLabel/octicons/cgmanifest.json new file mode 100644 index 000000000..3d13f99e3 --- /dev/null +++ b/src/vs/base/browser/ui/octiconLabel/octicons/cgmanifest.json @@ -0,0 +1,140 @@ +{ + "registrations": [ + { + "component": { + "type": "other", + "other": { + "name": "octicons-code", + "version": "3.1.0", + "downloadUrl": "https://registry.npmjs.org/octicons/-/octicons-3.1.0.tgz" + } + }, + "licenseDetail": [ + "The MIT License (MIT)", + "", + "(c) 2012-2015 GitHub", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN", + "THE SOFTWARE." + ], + "license": "MIT", + "version": "3.1.0" + }, + { + "component": { + "type": "other", + "other": { + "name": "octicons-font", + "version": "3.1.0", + "downloadUrl": "https://registry.npmjs.org/octicons/-/octicons-3.1.0.tgz" + } + }, + "licenseDetail": [ + "(c) 2012-2015 GitHub", + "", + "SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007", + "", + "PREAMBLE", + "The goals of the Open Font License (OFL) are to stimulate worldwide", + "development of collaborative font projects, to support the font creation", + "efforts of academic and linguistic communities, and to provide a free and", + "open framework in which fonts may be shared and improved in partnership", + "with others.", + "", + "The OFL allows the licensed fonts to be used, studied, modified and", + "redistributed freely as long as they are not sold by themselves. The", + "fonts, including any derivative works, can be bundled, embedded,", + "redistributed and/or sold with any software provided that any reserved", + "names are not used by derivative works. The fonts and derivatives,", + "however, cannot be released under any other type of license. The", + "requirement for fonts to remain under this license does not apply", + "to any document created using the fonts or their derivatives.", + "", + "DEFINITIONS", + "\"Font Software\" refers to the set of files released by the Copyright", + "Holder(s) under this license and clearly marked as such. This may", + "include source files, build scripts and documentation.", + "", + "\"Reserved Font Name\" refers to any names specified as such after the", + "copyright statement(s).", + "", + "\"Original Version\" refers to the collection of Font Software components as", + "distributed by the Copyright Holder(s).", + "", + "\"Modified Version\" refers to any derivative made by adding to, deleting,", + "or substituting -- in part or in whole -- any of the components of the", + "Original Version, by changing formats or by porting the Font Software to a", + "new environment.", + "", + "\"Author\" refers to any designer, engineer, programmer, technical", + "writer or other person who contributed to the Font Software.", + "", + "PERMISSION & CONDITIONS", + "Permission is hereby granted, free of charge, to any person obtaining", + "a copy of the Font Software, to use, study, copy, merge, embed, modify,", + "redistribute, and sell modified and unmodified copies of the Font", + "Software, subject to the following conditions:", + "", + "1) Neither the Font Software nor any of its individual components,", + "in Original or Modified Versions, may be sold by itself.", + "", + "2) Original or Modified Versions of the Font Software may be bundled,", + "redistributed and/or sold with any software, provided that each copy", + "contains the above copyright notice and this license. These can be", + "included either as stand-alone text files, human-readable headers or", + "in the appropriate machine-readable metadata fields within text or", + "binary files as long as those fields can be easily viewed by the user.", + "", + "3) No Modified Version of the Font Software may use the Reserved Font", + "Name(s) unless explicit written permission is granted by the corresponding", + "Copyright Holder. This restriction only applies to the primary font name as", + "presented to the users.", + "", + "4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font", + "Software shall not be used to promote, endorse or advertise any", + "Modified Version, except to acknowledge the contribution(s) of the", + "Copyright Holder(s) and the Author(s) or with their explicit written", + "permission.", + "", + "5) The Font Software, modified or unmodified, in part or in whole,", + "must be distributed entirely under this license, and must not be", + "distributed under any other license. The requirement for fonts to", + "remain under this license does not apply to any document created", + "using the Font Software.", + "", + "TERMINATION", + "This license becomes null and void if any of the above conditions are", + "not met.", + "", + "DISCLAIMER", + "THE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,", + "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF", + "MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT", + "OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE", + "COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,", + "INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL", + "DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM", + "OTHER DEALINGS IN THE FONT SOFTWARE." + ], + "license": "SIL OFL 1.1", + "version": "3.1.0" + } + ], + "version": 1 +} diff --git a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts index c9d3aa68d..7de8f3fa7 100644 --- a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts @@ -6,7 +6,7 @@ import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor'; -import { IMouseEvent, StandardMouseWheelEvent } from 'vs/base/browser/mouseEvent'; +import { IMouseEvent, StandardWheelEvent } from 'vs/base/browser/mouseEvent'; import { ScrollbarArrow, ScrollbarArrowOptions } from 'vs/base/browser/ui/scrollbar/scrollbarArrow'; import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; import { ScrollbarVisibilityController } from 'vs/base/browser/ui/scrollbar/scrollbarVisibilityController'; @@ -25,7 +25,7 @@ export interface ISimplifiedMouseEvent { } export interface ScrollbarHost { - onMouseWheel(mouseWheelEvent: StandardMouseWheelEvent): void; + onMouseWheel(mouseWheelEvent: StandardWheelEvent): void; onDragStart(): void; onDragEnd(): void; } diff --git a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts index f49deda9e..2be752aa6 100644 --- a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { StandardMouseWheelEvent } from 'vs/base/browser/mouseEvent'; +import { StandardWheelEvent } from 'vs/base/browser/mouseEvent'; import { AbstractScrollbar, ISimplifiedMouseEvent, ScrollbarHost } from 'vs/base/browser/ui/scrollbar/abstractScrollbar'; import { ScrollableElementResolvedOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions'; import { ARROW_IMG_SIZE } from 'vs/base/browser/ui/scrollbar/scrollbarArrow'; @@ -38,7 +38,7 @@ export class HorizontalScrollbar extends AbstractScrollbar { right: void 0, bgWidth: options.arrowSize, bgHeight: options.horizontalScrollbarSize, - onActivate: () => this._host.onMouseWheel(new StandardMouseWheelEvent(null, 1, 0)), + onActivate: () => this._host.onMouseWheel(new StandardWheelEvent(null, 1, 0)), }); this._createArrow({ @@ -49,7 +49,7 @@ export class HorizontalScrollbar extends AbstractScrollbar { right: arrowDelta, bgWidth: options.arrowSize, bgHeight: options.horizontalScrollbarSize, - onActivate: () => this._host.onMouseWheel(new StandardMouseWheelEvent(null, -1, 0)), + onActivate: () => this._host.onMouseWheel(new StandardWheelEvent(null, -1, 0)), }); } diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index ea42ceac6..a6256deeb 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/scrollbars'; import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; -import { IMouseEvent, StandardMouseWheelEvent } from 'vs/base/browser/mouseEvent'; +import { IMouseEvent, StandardWheelEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { ScrollbarHost } from 'vs/base/browser/ui/scrollbar/abstractScrollbar'; import { HorizontalScrollbar } from 'vs/base/browser/ui/scrollbar/horizontalScrollbar'; import { ScrollableElementChangeOptions, ScrollableElementCreationOptions, ScrollableElementResolvedOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions'; @@ -178,7 +178,7 @@ export abstract class AbstractScrollableElement extends Widget { })); let scrollbarHost: ScrollbarHost = { - onMouseWheel: (mouseWheelEvent: StandardMouseWheelEvent) => this._onMouseWheel(mouseWheelEvent), + onMouseWheel: (mouseWheelEvent: StandardWheelEvent) => this._onMouseWheel(mouseWheelEvent), onDragStart: () => this._onDragStart(), onDragEnd: () => this._onDragEnd(), }; @@ -307,17 +307,15 @@ export abstract class AbstractScrollableElement extends Widget { // Start listening (if necessary) if (shouldListen) { - let onMouseWheel = (browserEvent: MouseWheelEvent) => { - let e = new StandardMouseWheelEvent(browserEvent); - this._onMouseWheel(e); + let onMouseWheel = (browserEvent: IMouseWheelEvent) => { + this._onMouseWheel(new StandardWheelEvent(browserEvent)); }; this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, 'mousewheel', onMouseWheel)); - this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, 'DOMMouseScroll', onMouseWheel)); } } - private _onMouseWheel(e: StandardMouseWheelEvent): void { + private _onMouseWheel(e: StandardWheelEvent): void { const classifier = MouseWheelClassifier.INSTANCE; if (SCROLL_WHEEL_SMOOTH_SCROLL_ENABLED) { diff --git a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts index 77d19898d..9851f932b 100644 --- a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { StandardMouseWheelEvent } from 'vs/base/browser/mouseEvent'; +import { StandardWheelEvent } from 'vs/base/browser/mouseEvent'; import { AbstractScrollbar, ISimplifiedMouseEvent, ScrollbarHost } from 'vs/base/browser/ui/scrollbar/abstractScrollbar'; import { ScrollableElementResolvedOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions'; import { ARROW_IMG_SIZE } from 'vs/base/browser/ui/scrollbar/scrollbarArrow'; @@ -39,7 +39,7 @@ export class VerticalScrollbar extends AbstractScrollbar { right: void 0, bgWidth: options.verticalScrollbarSize, bgHeight: options.arrowSize, - onActivate: () => this._host.onMouseWheel(new StandardMouseWheelEvent(null, 0, 1)), + onActivate: () => this._host.onMouseWheel(new StandardWheelEvent(null, 0, 1)), }); this._createArrow({ @@ -50,7 +50,7 @@ export class VerticalScrollbar extends AbstractScrollbar { right: void 0, bgWidth: options.verticalScrollbarSize, bgHeight: options.arrowSize, - onActivate: () => this._host.onMouseWheel(new StandardMouseWheelEvent(null, 0, -1)), + onActivate: () => this._host.onMouseWheel(new StandardWheelEvent(null, 0, -1)), }); } diff --git a/src/vs/base/browser/ui/selectBox/selectBoxNative.ts b/src/vs/base/browser/ui/selectBox/selectBoxNative.ts index 4f40f01aa..53d0824c8 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxNative.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxNative.ts @@ -27,6 +27,13 @@ export class SelectBoxNative implements ISelectBoxDelegate { this.selectBoxOptions = selectBoxOptions || Object.create(null); this.selectElement = document.createElement('select'); + + // Workaround for Electron 2.x + // Native select should not require explicit role attribute, however, Electron 2.x + // incorrectly exposes select as menuItem which interferes with labeling and results + // in the unlabeled not been read. Electron 3 appears to fix. + this.selectElement.setAttribute('role', 'combobox'); + this.selectElement.className = 'monaco-select-box'; if (typeof this.selectBoxOptions.ariaLabel === 'string') { diff --git a/src/vs/base/browser/ui/splitview/panelview.ts b/src/vs/base/browser/ui/splitview/panelview.ts index 8ce998140..2aa77865f 100644 --- a/src/vs/base/browser/ui/splitview/panelview.ts +++ b/src/vs/base/browser/ui/splitview/panelview.ts @@ -46,6 +46,7 @@ export abstract class Panel implements IView { protected _expanded: boolean; protected disposables: IDisposable[] = []; + private expandedSize: number | undefined = undefined; private _headerVisible = true; private _minimumBodySize: number; private _maximumBodySize: number; @@ -54,9 +55,6 @@ export abstract class Panel implements IView { private header: HTMLElement; - private cachedExpandedSize: number | undefined = undefined; - private cachedBodySize: number | undefined = undefined; - private _onDidChange = new Emitter(); readonly onDidChange: Event = this._onDidChange.event; @@ -131,7 +129,7 @@ export abstract class Panel implements IView { this._expanded = !!expanded; this.updateHeader(); - this._onDidChange.fire(expanded ? this.cachedExpandedSize : undefined); + this._onDidChange.fire(expanded ? this.expandedSize : undefined); } get headerVisible(): boolean { @@ -192,14 +190,8 @@ export abstract class Panel implements IView { const headerSize = this.headerVisible ? Panel.HEADER_SIZE : 0; if (this.isExpanded()) { - const bodySize = size - headerSize; - - if (bodySize !== this.cachedBodySize) { - this.layoutBody(bodySize); - this.cachedBodySize = bodySize; - } - - this.cachedExpandedSize = size; + this.layoutBody(size - headerSize); + this.expandedSize = size; } } diff --git a/src/vs/base/browser/ui/splitview/splitview.ts b/src/vs/base/browser/ui/splitview/splitview.ts index bdef6277a..0de80a6c7 100644 --- a/src/vs/base/browser/ui/splitview/splitview.ts +++ b/src/vs/base/browser/ui/splitview/splitview.ts @@ -29,6 +29,16 @@ export interface ISplitViewOptions { orthogonalStartSash?: Sash; orthogonalEndSash?: Sash; inverseAltBehavior?: boolean; + proportionalLayout?: boolean; // default true +} + +/** + * Only used when `proportionalLayout` is false. + */ +export const enum LayoutPriority { + Normal, + Low, + High } export interface IView { @@ -36,6 +46,8 @@ export interface IView { readonly minimumSize: number; readonly maximumSize: number; readonly onDidChange: Event; + readonly priority?: LayoutPriority; + readonly snapSize?: number; layout(size: number, orientation: Orientation): void; } @@ -87,7 +99,6 @@ export namespace Sizing { export class SplitView extends Disposable { readonly orientation: Orientation; - // TODO@Joao have the same pattern as grid here readonly el: HTMLElement; private sashContainer: HTMLElement; private viewContainer: HTMLElement; @@ -99,6 +110,7 @@ export class SplitView extends Disposable { private sashDragState: ISashDragState; private state: State = State.Idle; private inverseAltBehavior: boolean; + private proportionalLayout: boolean; private _onDidSashChange = this._register(new Emitter()); readonly onDidSashChange = this._onDidSashChange.event; @@ -147,6 +159,7 @@ export class SplitView extends Disposable { this.orientation = types.isUndefined(options.orientation) ? Orientation.VERTICAL : options.orientation; this.inverseAltBehavior = !!options.inverseAltBehavior; + this.proportionalLayout = types.isUndefined(options.proportionalLayout) ? true : !!options.proportionalLayout; this.el = document.createElement('div'); dom.addClass(this.el, 'monaco-split-view2'); @@ -317,8 +330,10 @@ export class SplitView extends Disposable { private relayout(lowPriorityIndex?: number, highPriorityIndex?: number): void { const contentSize = this.viewItems.reduce((r, i) => r + i.size, 0); + const lowPriorityIndexes = typeof lowPriorityIndex === 'number' ? [lowPriorityIndex] : undefined; + const highPriorityIndexes = typeof highPriorityIndex === 'number' ? [highPriorityIndex] : undefined; - this.resize(this.viewItems.length - 1, this.size - contentSize, undefined, lowPriorityIndex, highPriorityIndex); + this.resize(this.viewItems.length - 1, this.size - contentSize, undefined, lowPriorityIndexes, highPriorityIndexes); this.distributeEmptySpace(); this.layoutViews(); this.saveProportions(); @@ -329,11 +344,15 @@ export class SplitView extends Disposable { this.size = size; if (!this.proportions) { - this.resize(this.viewItems.length - 1, size - previousSize); + const indexes = range(this.viewItems.length); + const lowPriorityIndexes = indexes.filter(i => this.viewItems[i].view.priority === LayoutPriority.Low); + const highPriorityIndexes = indexes.filter(i => this.viewItems[i].view.priority === LayoutPriority.High); + + this.resize(this.viewItems.length - 1, size - previousSize, undefined, lowPriorityIndexes, highPriorityIndexes); } else { for (let i = 0; i < this.viewItems.length; i++) { const item = this.viewItems[i]; - item.size = clamp(Math.round(this.proportions[i] * size), item.view.minimumSize, item.view.maximumSize); + item.size = SplitView.clamp(item, Math.round(this.proportions[i] * size)); } } @@ -342,7 +361,7 @@ export class SplitView extends Disposable { } private saveProportions(): void { - if (this.contentSize > 0) { + if (this.proportionalLayout && this.contentSize > 0) { this.proportions = this.viewItems.map(i => i.size / this.contentSize); } } @@ -425,7 +444,7 @@ export class SplitView extends Disposable { } size = typeof size === 'number' ? size : item.size; - size = clamp(size, item.view.minimumSize, item.view.maximumSize); + size = SplitView.clamp(item, size); if (this.inverseAltBehavior && index > 0) { // In this case, we want the view to grow or shrink both sides equally @@ -500,8 +519,8 @@ export class SplitView extends Disposable { index: number, delta: number, sizes = this.viewItems.map(i => i.size), - lowPriorityIndex?: number, - highPriorityIndex?: number, + lowPriorityIndexes?: number[], + highPriorityIndexes?: number[], overloadMinDelta: number = Number.NEGATIVE_INFINITY, overloadMaxDelta: number = Number.POSITIVE_INFINITY ): number { @@ -512,14 +531,18 @@ export class SplitView extends Disposable { const upIndexes = range(index, -1); const downIndexes = range(index + 1, this.viewItems.length); - if (typeof highPriorityIndex === 'number') { - pushToStart(upIndexes, highPriorityIndex); - pushToStart(downIndexes, highPriorityIndex); + if (highPriorityIndexes) { + for (const index of highPriorityIndexes) { + pushToStart(upIndexes, index); + pushToStart(downIndexes, index); + } } - if (typeof lowPriorityIndex === 'number') { - pushToEnd(upIndexes, lowPriorityIndex); - pushToEnd(downIndexes, lowPriorityIndex); + if (lowPriorityIndexes) { + for (const index of lowPriorityIndexes) { + pushToEnd(upIndexes, index); + pushToEnd(downIndexes, index); + } } const upItems = upIndexes.map(i => this.viewItems[i]); @@ -528,27 +551,29 @@ export class SplitView extends Disposable { const downItems = downIndexes.map(i => this.viewItems[i]); const downSizes = downIndexes.map(i => sizes[i]); - const minDeltaUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].view.minimumSize - sizes[i]), 0); + const minDeltaUp = upIndexes.reduce((r, i) => r + ((typeof this.viewItems[i].view.snapSize === 'number' ? 0 : this.viewItems[i].view.minimumSize) - sizes[i]), 0); const maxDeltaUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - sizes[i]), 0); - const maxDeltaDown = downIndexes.length === 0 ? Number.POSITIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].view.minimumSize), 0); + const maxDeltaDown = downIndexes.length === 0 ? Number.POSITIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - (typeof this.viewItems[i].view.snapSize === 'number' ? 0 : this.viewItems[i].view.minimumSize)), 0); const minDeltaDown = downIndexes.length === 0 ? Number.NEGATIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].view.maximumSize), 0); const minDelta = Math.max(minDeltaUp, minDeltaDown, overloadMinDelta); const maxDelta = Math.min(maxDeltaDown, maxDeltaUp, overloadMaxDelta); - delta = clamp(delta, minDelta, maxDelta); + const tentativeDelta = clamp(delta, minDelta, maxDelta); + let actualDelta = 0; - for (let i = 0, deltaUp = delta; i < upItems.length; i++) { + for (let i = 0, deltaUp = tentativeDelta; i < upItems.length; i++) { const item = upItems[i]; - const size = clamp(upSizes[i] + deltaUp, item.view.minimumSize, item.view.maximumSize); + const size = SplitView.clamp(item, upSizes[i] + deltaUp/* , upIndexes[i] === index */); const viewDelta = size - upSizes[i]; + actualDelta += viewDelta; deltaUp -= viewDelta; item.size = size; } - for (let i = 0, deltaDown = delta; i < downItems.length; i++) { + for (let i = 0, deltaDown = actualDelta; i < downItems.length; i++) { const item = downItems[i]; - const size = clamp(downSizes[i] - deltaDown, item.view.minimumSize, item.view.maximumSize); + const size = SplitView.clamp(item, downSizes[i] - deltaDown); const viewDelta = size - downSizes[i]; deltaDown += viewDelta; @@ -558,13 +583,24 @@ export class SplitView extends Disposable { return delta; } + private static clamp(item: IViewItem, size: number): number { + const result = clamp(size, item.view.minimumSize, item.view.maximumSize); + + if (typeof item.view.snapSize !== 'number' || size >= item.view.minimumSize) { + return result; + } + + const snapSize = Math.min(item.view.snapSize, item.view.minimumSize); + return size < snapSize ? 0 : item.view.minimumSize; + } + private distributeEmptySpace(): void { let contentSize = this.viewItems.reduce((r, i) => r + i.size, 0); let emptyDelta = this.size - contentSize; for (let i = this.viewItems.length - 1; emptyDelta !== 0 && i >= 0; i--) { const item = this.viewItems[i]; - const size = clamp(item.size + emptyDelta, item.view.minimumSize, item.view.maximumSize); + const size = SplitView.clamp(item, item.size + emptyDelta); const viewDelta = size - item.size; emptyDelta -= viewDelta; diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index f77e09e75..ff50ccd87 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -10,6 +10,7 @@ import { ActionBar, ActionsOrientation, IActionItemProvider } from 'vs/base/brow import { IContextMenuProvider, DropdownMenuActionItem } from 'vs/base/browser/ui/dropdown/dropdown'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; +import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; export const CONTEXT = 'context.toolbar'; @@ -20,6 +21,7 @@ export interface IToolBarOptions { getKeyBinding?: (action: IAction) => ResolvedKeybinding; actionRunner?: IActionRunner; toggleMenuTitle?: string; + anchorAlignmentProvider?: () => AnchorAlignment; } /** @@ -67,7 +69,8 @@ export class ToolBar extends Disposable { this.options.actionItemProvider, this.actionRunner, this.options.getKeyBinding, - 'toolbar-toggle-more' + 'toolbar-toggle-more', + this.options.anchorAlignmentProvider ); this.toggleMenuActionItem.setActionContext(this.actionBar.context); diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 807cead79..b270752b9 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -8,14 +8,13 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IListOptions, List, IIdentityProvider, IMultipleSelectionController, IListStyles, IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; import { append, $, toggleClass } from 'vs/base/browser/dom'; -import { Event, Relay, chain } from 'vs/base/common/event'; +import { Event, Relay, chain, mapEvent } from 'vs/base/common/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { ITreeModel, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; +import { ITreeModel, ITreeNode, ITreeRenderer, ITreeModelOptions } from 'vs/base/browser/ui/tree/tree'; import { ISpliceable } from 'vs/base/common/sequence'; -import { IIndexTreeModelOptions } from 'vs/base/browser/ui/tree/indexTreeModel'; -export function createComposedTreeListOptions(options?: IListOptions): IListOptions { +export function createComposedTreeListOptions(options?: IListOptions): IListOptions | undefined { if (!options) { return undefined; } @@ -23,18 +22,20 @@ export function createComposedTreeListOptions(optio let identityProvider: IIdentityProvider | undefined = undefined; if (options.identityProvider) { - identityProvider = el => options.identityProvider(el.element); + const ip = options.identityProvider; + identityProvider = el => ip(el.element); } let multipleSelectionController: IMultipleSelectionController | undefined = undefined; if (options.multipleSelectionController) { + const msc = options.multipleSelectionController; multipleSelectionController = { isSelectionSingleChangeEvent(e) { - return options.multipleSelectionController.isSelectionSingleChangeEvent({ ...e, element: e.element } as any); + return msc.isSelectionSingleChangeEvent({ ...e, element: e.element } as any); }, isSelectionRangeChangeEvent(e) { - return options.multipleSelectionController.isSelectionRangeChangeEvent({ ...e, element: e.element } as any); + return msc.isSelectionRangeChangeEvent({ ...e, element: e.element } as any); } }; } @@ -42,9 +43,10 @@ export function createComposedTreeListOptions(optio let accessibilityProvider: IAccessibilityProvider | undefined = undefined; if (options.accessibilityProvider) { + const ap = options.accessibilityProvider; accessibilityProvider = { getAriaLabel(e) { - return options.accessibilityProvider.getAriaLabel(e.element); + return ap.getAriaLabel(e.element); } }; } @@ -68,6 +70,10 @@ export class ComposedTreeDelegate implements IListV getTemplateId(element: N): string { return this.delegate.getTemplateId(element.element); } + + hasDynamicHeight(element: N): boolean { + return !!this.delegate.hasDynamicHeight && this.delegate.hasDynamicHeight(element.element); + } } interface ITreeListTemplateData { @@ -168,9 +174,45 @@ function isInputElement(e: HTMLElement): boolean { return e.tagName === 'INPUT' || e.tagName === 'TEXTAREA'; } -export interface ITreeOptions extends IListOptions, IIndexTreeModelOptions { } -export interface ITreeEvent extends IListEvent> { } -export interface ITreeContextMenuEvent extends IListContextMenuEvent> { } +export interface ITreeOptions extends IListOptions, ITreeModelOptions { } + +export interface ITreeEvent { + elements: T[]; + browserEvent?: UIEvent; +} + +export interface ITreeMouseEvent { + browserEvent: MouseEvent; + element: T | undefined; +} + +export interface ITreeContextMenuEvent { + browserEvent: UIEvent; + element: T | undefined; + anchor: HTMLElement | { x: number; y: number; } | undefined; +} + +function asTreeEvent(event: IListEvent>): ITreeEvent { + return { + elements: event.elements.map(node => node.element), + browserEvent: event.browserEvent + }; +} + +function asTreeMouseEvent(event: IListMouseEvent>): ITreeMouseEvent { + return { + browserEvent: event.browserEvent, + element: event.element && event.element.element + }; +} + +function asTreeContextMenuEvent(event: IListContextMenuEvent>): ITreeContextMenuEvent { + return { + element: event.element && event.element.element, + browserEvent: event.browserEvent, + anchor: event.anchor + }; +} export abstract class AbstractTree implements IDisposable { @@ -178,22 +220,25 @@ export abstract class AbstractTree implements IDisposable protected model: ITreeModel; protected disposables: IDisposable[] = []; - readonly onDidChangeCollapseState: Event>; - readonly onDidChangeRenderNodeCount: Event>; - readonly onDidChangeFocus: Event>; - readonly onDidChangeSelection: Event>; - - readonly onContextMenu: Event>; + get onDidChangeFocus(): Event> { return mapEvent(this.view.onFocusChange, asTreeEvent); } + get onDidChangeSelection(): Event> { return mapEvent(this.view.onSelectionChange, asTreeEvent); } + get onMouseClick(): Event> { return mapEvent(this.view.onMouseClick, asTreeMouseEvent); } + get onMouseDblClick(): Event> { return mapEvent(this.view.onMouseDblClick, asTreeMouseEvent); } + get onContextMenu(): Event> { return mapEvent(this.view.onContextMenu, asTreeContextMenuEvent); } get onDidFocus(): Event { return this.view.onDidFocus; } get onDidBlur(): Event { return this.view.onDidBlur; } + + get onDidChangeCollapseState(): Event> { return this.model.onDidChangeCollapseState; } + get onDidChangeRenderNodeCount(): Event> { return this.model.onDidChangeRenderNodeCount; } + get onDidDispose(): Event { return this.view.onDidDispose; } constructor( container: HTMLElement, delegate: IListVirtualDelegate, renderers: ITreeRenderer[], - options?: ITreeOptions + options: ITreeOptions = {} ) { const treeDelegate = new ComposedTreeDelegate>(delegate); @@ -202,17 +247,12 @@ export abstract class AbstractTree implements IDisposable this.disposables.push(...treeRenderers); this.view = new List(container, treeDelegate, treeRenderers, createComposedTreeListOptions(options)); - this.onDidChangeFocus = this.view.onFocusChange; - this.onDidChangeSelection = this.view.onSelectionChange; - this.onContextMenu = this.view.onContextMenu; this.model = this.createModel(this.view, options); onDidChangeCollapseStateRelay.input = this.model.onDidChangeCollapseState; - this.onDidChangeCollapseState = this.model.onDidChangeCollapseState; - this.onDidChangeRenderNodeCount = this.model.onDidChangeRenderNodeCount; if (options.mouseSupport !== false) { - this.view.onMouseClick(this.onMouseClick, this, this.disposables); + this.view.onMouseClick(this.reactOnMouseClick, this, this.disposables); } if (options.keyboardSupport !== false) { @@ -240,6 +280,10 @@ export abstract class AbstractTree implements IDisposable this.view.layout(height); } + layoutWidth(width: number): void { + this.view.layoutWidth(width); + } + style(styles: IListStyles): void { this.view.style(styles); } @@ -302,33 +346,33 @@ export abstract class AbstractTree implements IDisposable return nodes.map(n => n.element); } - setFocus(elements: TRef[]): void { + setFocus(elements: TRef[], browserEvent?: UIEvent): void { const indexes = elements.map(e => this.model.getListIndex(e)); - this.view.setFocus(indexes); + this.view.setFocus(indexes, browserEvent); } - focusNext(n = 1, loop = false): void { - this.view.focusNext(n, loop); + focusNext(n = 1, loop = false, browserEvent?: UIEvent): void { + this.view.focusNext(n, loop, browserEvent); } - focusPrevious(n = 1, loop = false): void { - this.view.focusPrevious(n, loop); + focusPrevious(n = 1, loop = false, browserEvent?: UIEvent): void { + this.view.focusPrevious(n, loop, browserEvent); } - focusNextPage(): void { - this.view.focusNextPage(); + focusNextPage(browserEvent?: UIEvent): void { + this.view.focusNextPage(browserEvent); } - focusPreviousPage(): void { - this.view.focusPreviousPage(); + focusPreviousPage(browserEvent?: UIEvent): void { + this.view.focusPreviousPage(browserEvent); } - focusLast(): void { - this.view.focusLast(); + focusLast(browserEvent?: UIEvent): void { + this.view.focusLast(browserEvent); } - focusFirst(): void { - this.view.focusFirst(); + focusFirst(browserEvent?: UIEvent): void { + this.view.focusFirst(browserEvent); } getFocus(): T[] { @@ -355,10 +399,14 @@ export abstract class AbstractTree implements IDisposable return this.view.getRelativeTop(index); } - private onMouseClick(e: IListMouseEvent>): void { + private reactOnMouseClick(e: IListMouseEvent>): void { const node = e.element; - const location = this.model.getNodeLocation(node); + if (!node) { + return; + } + + const location = this.model.getNodeLocation(node); this.model.toggleCollapsed(location); } @@ -437,7 +485,5 @@ export abstract class AbstractTree implements IDisposable dispose(): void { this.disposables = dispose(this.disposables); this.view.dispose(); - this.view = null; - this.model = null; } -} \ No newline at end of file +} diff --git a/src/vs/base/browser/ui/tree/dataTree.ts b/src/vs/base/browser/ui/tree/dataTree.ts index 2c7471795..daec2a319 100644 --- a/src/vs/base/browser/ui/tree/dataTree.ts +++ b/src/vs/base/browser/ui/tree/dataTree.ts @@ -3,23 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ITreeOptions, ComposedTreeDelegate, createComposedTreeListOptions } from 'vs/base/browser/ui/tree/abstractTree'; +import { ITreeOptions, ComposedTreeDelegate, createComposedTreeListOptions, ITreeEvent, ITreeContextMenuEvent, ITreeMouseEvent } from 'vs/base/browser/ui/tree/abstractTree'; import { ObjectTree } from 'vs/base/browser/ui/tree/objectTree'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { ITreeElement, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { Emitter, Event } from 'vs/base/common/event'; +import { Emitter, Event, mapEvent } from 'vs/base/common/event'; import { timeout } from 'vs/base/common/async'; -export interface IDataTreeElement { - readonly element: T; - readonly collapsible?: boolean; - readonly collapsed?: boolean; -} - export interface IDataSource> { hasChildren(element: T | null): boolean; - getChildren(element: T | null): Thenable[]>; + getChildren(element: T | null): Thenable; } enum DataTreeNodeState { @@ -95,16 +89,49 @@ class DataTreeRenderer implements ITreeRenderer(e: ITreeEvent>): ITreeEvent { + return { + browserEvent: e.browserEvent, + elements: e.elements.map(e => e.element!) + }; +} + +function asTreeMouseEvent(e: ITreeMouseEvent>): ITreeMouseEvent { + return { + browserEvent: e.browserEvent, + element: e.element && e.element.element! + }; +} + +function asTreeContextMenuEvent(e: ITreeContextMenuEvent>): ITreeContextMenuEvent { + return { + browserEvent: e.browserEvent, + element: e.element && e.element.element!, + anchor: e.anchor + }; +} + export class DataTree, TFilterData = void> implements IDisposable { private tree: ObjectTree, TFilterData>; private root: IDataTreeNode; - private nodes = new Map>(); + private nodes = new Map>(); private _onDidChangeNodeState = new Emitter>(); private disposables: IDisposable[] = []; + get onDidChangeFocus(): Event> { return mapEvent(this.tree.onDidChangeFocus, asTreeEvent); } + get onDidChangeSelection(): Event> { return mapEvent(this.tree.onDidChangeSelection, asTreeEvent); } + + get onMouseClick(): Event> { return mapEvent(this.tree.onMouseClick, asTreeMouseEvent); } + get onMouseDblClick(): Event> { return mapEvent(this.tree.onMouseDblClick, asTreeMouseEvent); } + get onContextMenu(): Event> { return mapEvent(this.tree.onContextMenu, asTreeContextMenuEvent); } + get onDidDOMFocus(): Event { return this.tree.onDidFocus; } + get onDidDOMBlur(): Event { return this.tree.onDidBlur; } + + get onDidDispose(): Event { return this.tree.onDidDispose; } + constructor( container: HTMLElement, delegate: IListVirtualDelegate, @@ -112,9 +139,9 @@ export class DataTree, TFilterData = void> implements private dataSource: IDataSource, options?: ITreeOptions ) { - const objectTreeDelegate = new ComposedTreeDelegate>(delegate); + const objectTreeDelegate = new ComposedTreeDelegate>(delegate); const objectTreeRenderers = renderers.map(r => new DataTreeRenderer(r, this._onDidChangeNodeState.event)); - const objectTreeOptions = createComposedTreeListOptions>(options); + const objectTreeOptions = createComposedTreeListOptions>(options); this.tree = new ObjectTree(container, objectTreeDelegate, objectTreeRenderers, objectTreeOptions); this.root = { @@ -125,17 +152,25 @@ export class DataTree, TFilterData = void> implements this.nodes.set(null, this.root); - this.tree.onDidChangeCollapseState(this.onDidChangeCollapseState, this, this.disposables); + this.tree.onDidChangeCollapseState(this._onDidChangeCollapseState, this, this.disposables); + } + + layout(height?: number): void { + this.tree.layout(height); } refresh(element: T | null): Thenable { + return this.refreshNode(this.getNode(element)); + } + + private getNode(element: T | null): IDataTreeNode { const node: IDataTreeNode = this.nodes.get(element); if (typeof node === 'undefined') { throw new Error(`Data tree node not found: ${element}`); } - return this.refreshNode(node); + return node; } private refreshNode(node: IDataTreeNode): Thenable { @@ -143,7 +178,7 @@ export class DataTree, TFilterData = void> implements if (!hasChildren) { this.tree.setChildren(node === this.root ? null : node); - return Promise.resolve(null); + return Promise.resolve(); } else { node.state = DataTreeNodeState.Loading; this._onDidChangeNodeState.fire(node); @@ -161,15 +196,17 @@ export class DataTree, TFilterData = void> implements node.state = DataTreeNodeState.Loaded; this._onDidChangeNodeState.fire(node); - const createTreeElement = (el: IDataTreeElement): ITreeElement> => { + const createTreeElement = (element: T): ITreeElement> => { + const collapsible = this.dataSource.hasChildren(element); + return { element: { - element: el.element, + element: element, state: DataTreeNodeState.Uninitialized, parent: node }, - collapsible: el.collapsible, - collapsed: typeof el.collapsed === 'boolean' ? el.collapsed : true + collapsible, + collapsed: true }; }; @@ -190,13 +227,100 @@ export class DataTree, TFilterData = void> implements } } - private onDidChangeCollapseState(treeNode: ITreeNode, any>): void { + private _onDidChangeCollapseState(treeNode: ITreeNode, any>): void { if (!treeNode.collapsed && treeNode.element.state === DataTreeNodeState.Uninitialized) { this.refreshNode(treeNode.element); } } + // Tree + + collapse(element: T): boolean { + return this.tree.collapse(this.getNode(element)); + } + + expand(element: T): boolean { + return this.tree.expand(this.getNode(element)); + } + + toggleCollapsed(element: T): void { + this.tree.toggleCollapsed(this.getNode(element)); + } + + collapseAll(): void { + this.tree.collapseAll(); + } + + isCollapsed(element: T): boolean { + return this.tree.isCollapsed(this.getNode(element)); + } + + isExpanded(element: T): boolean { + return this.tree.isExpanded(this.getNode(element)); + } + + refilter(): void { + this.tree.refilter(); + } + + setSelection(elements: T[], browserEvent?: UIEvent): void { + const nodes = elements.map(e => this.getNode(e)); + this.tree.setSelection(nodes, browserEvent); + } + + getSelection(): T[] { + const nodes = this.tree.getSelection(); + return nodes.map(n => n.element!); + } + + setFocus(elements: T[], browserEvent?: UIEvent): void { + const nodes = elements.map(e => this.getNode(e)); + this.tree.setFocus(nodes, browserEvent); + } + + focusNext(n = 1, loop = false, browserEvent?: UIEvent): void { + this.tree.focusNext(n, loop, browserEvent); + } + + focusPrevious(n = 1, loop = false, browserEvent?: UIEvent): void { + this.tree.focusPrevious(n, loop, browserEvent); + } + + focusNextPage(browserEvent?: UIEvent): void { + this.tree.focusNextPage(browserEvent); + } + + focusPreviousPage(browserEvent?: UIEvent): void { + this.tree.focusPreviousPage(browserEvent); + } + + focusLast(browserEvent?: UIEvent): void { + this.tree.focusLast(browserEvent); + } + + focusFirst(browserEvent?: UIEvent): void { + this.tree.focusFirst(browserEvent); + } + + getFocus(): T[] { + const nodes = this.tree.getFocus(); + return nodes.map(n => n.element!); + } + + open(elements: T[]): void { + const nodes = elements.map(e => this.getNode(e)); + this.tree.open(nodes); + } + + reveal(element: T, relativeTop?: number): void { + this.tree.reveal(this.getNode(element), relativeTop); + } + + getRelativeTop(element: T): number | null { + return this.tree.getRelativeTop(this.getNode(element)); + } + dispose(): void { this.disposables = dispose(this.disposables); } -} \ No newline at end of file +} diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index bd0d0809b..624d528df 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -7,7 +7,7 @@ import { ISpliceable } from 'vs/base/common/sequence'; import { Iterator, ISequence } from 'vs/base/common/iterator'; import { Emitter, Event, EventBufferer } from 'vs/base/common/event'; import { tail2 } from 'vs/base/common/arrays'; -import { ITreeFilterDataResult, TreeVisibility, ITreeFilter, ITreeModel, ITreeNode, ITreeElement } from 'vs/base/browser/ui/tree/tree'; +import { ITreeFilterDataResult, TreeVisibility, ITreeFilter, ITreeModel, ITreeNode, ITreeElement, ITreeModelOptions } from 'vs/base/browser/ui/tree/tree'; interface IMutableTreeNode extends ITreeNode { readonly parent: IMutableTreeNode | undefined; @@ -30,25 +30,19 @@ function treeNodeToElement(node: IMutableTreeNode): ITreeElement { return { element, children, collapsed }; } -function getVisibleState(visibility: boolean | TreeVisibility): boolean | undefined { +function getVisibleState(visibility: boolean | TreeVisibility): TreeVisibility { switch (visibility) { - case true: return true; - case false: return false; - case TreeVisibility.Hidden: return false; - case TreeVisibility.Visible: return true; - case TreeVisibility.Recurse: return undefined; + case true: return TreeVisibility.Visible; + case false: return TreeVisibility.Hidden; + default: return visibility; } } -export interface IIndexTreeModelOptions { - filter?: ITreeFilter; -} - export class IndexTreeModel implements ITreeModel { private root: IMutableTreeNode = { parent: undefined, - element: undefined, + element: undefined!, children: [], depth: 0, collapsible: false, @@ -68,7 +62,7 @@ export class IndexTreeModel implements ITreeModel; - constructor(private list: ISpliceable>, options: IIndexTreeModelOptions = {}) { + constructor(private list: ISpliceable>, options: ITreeModelOptions = {}) { this.filter = options.filter; } @@ -85,7 +79,7 @@ export class IndexTreeModel implements ITreeModel[] = []; - const nodesToInsertIterator = Iterator.map(Iterator.from(toInsert), el => this.createTreeNode(el, parentNode, revealed, treeListElementsToInsert, onDidCreateNode)); + const nodesToInsertIterator = Iterator.map(Iterator.from(toInsert), el => this.createTreeNode(el, parentNode, parentNode.visible ? TreeVisibility.Visible : TreeVisibility.Hidden, revealed, treeListElementsToInsert, onDidCreateNode)); const nodesToInsert: IMutableTreeNode[] = []; let renderNodeCount = 0; @@ -137,7 +131,7 @@ export class IndexTreeModel implements ITreeModel { while (queue.length > 0) { - const node = queue.shift(); + const node = queue.shift()!; const revealed = listIndex < this.root.children.length; this._setCollapsed(node, listIndex, revealed, true); @@ -186,6 +180,7 @@ export class IndexTreeModel implements ITreeModel, parent: IMutableTreeNode, + parentVisibility: TreeVisibility, revealed: boolean, treeListElements: ITreeNode[], onDidCreateNode?: (node: ITreeNode) => void @@ -202,15 +197,15 @@ export class IndexTreeModel implements ITreeModel this.createTreeNode(el, node, childRevealed, treeListElements, onDidCreateNode)); + const childRevealed = revealed && visibility !== TreeVisibility.Hidden && !node.collapsed; + const childNodes = Iterator.map(childElements, el => this.createTreeNode(el, node, visibility, childRevealed, treeListElements, onDidCreateNode)); let hasVisibleDescendants = false; let renderNodeCount = 1; @@ -222,7 +217,7 @@ export class IndexTreeModel implements ITreeModel 0; - node.visible = typeof visible === 'undefined' ? hasVisibleDescendants : visible; + node.visible = visibility === TreeVisibility.Recurse ? hasVisibleDescendants : (visibility === TreeVisibility.Visible); if (!node.visible) { node.renderNodeCount = 0; @@ -273,19 +268,19 @@ export class IndexTreeModel implements ITreeModel[] = []; - this._updateNodeAfterFilterChange(node, result); + this._updateNodeAfterFilterChange(node, node.visible ? TreeVisibility.Visible : TreeVisibility.Hidden, result); this._updateAncestorsRenderNodeCount(node.parent, result.length - previousRenderNodeCount); return result; } - private _updateNodeAfterFilterChange(node: IMutableTreeNode, result: ITreeNode[], revealed = true): boolean { - let visible: boolean | undefined; + private _updateNodeAfterFilterChange(node: IMutableTreeNode, parentVisibility: TreeVisibility, result: ITreeNode[], revealed = true): boolean { + let visibility: TreeVisibility; if (node !== this.root) { - visible = this._filterNode(node); + visibility = this._filterNode(node, parentVisibility); - if (visible === false) { + if (visibility === TreeVisibility.Hidden) { node.visible = false; return false; } @@ -299,14 +294,14 @@ export class IndexTreeModel implements ITreeModel implements ITreeModel, diff: number): void { + private _updateAncestorsRenderNodeCount(node: IMutableTreeNode | undefined, diff: number): void { if (diff === 0) { return; } @@ -335,12 +330,12 @@ export class IndexTreeModel implements ITreeModel): boolean | undefined { - const result = this.filter ? this.filter.filter(node.element) : TreeVisibility.Visible; + private _filterNode(node: IMutableTreeNode, parentVisibility: TreeVisibility): TreeVisibility { + const result = this.filter ? this.filter.filter(node.element, parentVisibility) : TreeVisibility.Visible; if (typeof result === 'boolean') { node.filterData = undefined; - return result; + return result ? TreeVisibility.Visible : TreeVisibility.Hidden; } else if (isFilterResult(result)) { node.filterData = result.data; return getVisibleState(result.visibility); @@ -351,8 +346,8 @@ export class IndexTreeModel implements ITreeModel = this.root): IMutableTreeNode { - if (location.length === 0) { + private getTreeNode(location: number[] | null, node: IMutableTreeNode = this.root): IMutableTreeNode { + if (!location || location.length === 0) { return node; } diff --git a/src/vs/base/browser/ui/tree/media/tree.css b/src/vs/base/browser/ui/tree/media/tree.css index 131529eb5..62a7ea676 100644 --- a/src/vs/base/browser/ui/tree/media/tree.css +++ b/src/vs/base/browser/ui/tree/media/tree.css @@ -18,6 +18,7 @@ font-size: 10px; text-align: right; margin-right: 6px; + flex-shrink: 0; } .monaco-tl-contents { diff --git a/src/vs/base/browser/ui/tree/objectTreeModel.ts b/src/vs/base/browser/ui/tree/objectTreeModel.ts index 54d40157a..742aed056 100644 --- a/src/vs/base/browser/ui/tree/objectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/objectTreeModel.ts @@ -3,15 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import { ISpliceable } from 'vs/base/common/sequence'; import { Iterator, ISequence } from 'vs/base/common/iterator'; -import { IndexTreeModel, IIndexTreeModelOptions } from 'vs/base/browser/ui/tree/indexTreeModel'; +import { IndexTreeModel } from 'vs/base/browser/ui/tree/indexTreeModel'; import { Event } from 'vs/base/common/event'; -import { ITreeModel, ITreeNode, ITreeElement } from 'vs/base/browser/ui/tree/tree'; - -export interface IObjectTreeModelOptions extends IIndexTreeModelOptions { } +import { ITreeModel, ITreeNode, ITreeElement, ITreeModelOptions } from 'vs/base/browser/ui/tree/tree'; export class ObjectTreeModel, TFilterData = void> implements ITreeModel { @@ -23,7 +19,7 @@ export class ObjectTreeModel, TFilterData = void> imp get size(): number { return this.nodes.size; } - constructor(list: ISpliceable>, options: IObjectTreeModelOptions = {}) { + constructor(list: ISpliceable>, options: ITreeModelOptions = {}) { this.model = new IndexTreeModel(list, options); this.onDidChangeCollapseState = this.model.onDidChangeCollapseState; this.onDidChangeRenderNodeCount = this.model.onDidChangeRenderNodeCount; @@ -90,7 +86,7 @@ export class ObjectTreeModel, TFilterData = void> imp this.model.refilter(); } - getNode(element: T = null): ITreeNode { + getNode(element: T | null = null): ITreeNode { const location = this.getElementLocation(element); return this.model.getNode(location); } @@ -106,7 +102,7 @@ export class ObjectTreeModel, TFilterData = void> imp throw new Error(`Tree element not found: ${element}`); } - return node.parent.element; + return node.parent!.element; } private getElementLocation(element: T | null): number[] { diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index b66284dcc..2d0705b9c 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -64,7 +64,7 @@ export interface ITreeFilter { * * @param element The tree element. */ - filter(element: T): TreeFilterResult; + filter(element: T, parentVisibility: TreeVisibility): TreeFilterResult; } export interface ITreeElement { @@ -94,9 +94,9 @@ export interface ITreeModel { getNodeLocation(node: ITreeNode): TRef; getParentNodeLocation(location: TRef): TRef | null; - getParentElement(location: TRef): T | null; - getFirstChildElement(location: TRef): T | null; - getLastAncestorElement(location: TRef): T | null; + getParentElement(location: TRef | null): T | null; + getFirstChildElement(location: TRef | null): T | null; + getLastAncestorElement(location: TRef | null): T | null; isCollapsed(location: TRef): boolean; setCollapsed(location: TRef, collapsed: boolean): boolean; @@ -109,4 +109,8 @@ export interface ITreeModel { export interface ITreeRenderer extends IListRenderer, TTemplateData> { renderTwistie?(element: T, twistieElement: HTMLElement): boolean; onDidChangeTwistieState?: Event; +} + +export interface ITreeModelOptions { + filter?: ITreeFilter; } \ No newline at end of file diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 408750697..c6f0dba5d 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -24,7 +24,7 @@ export function tail2(arr: T[]): [T[], T] { return [arr.slice(0, arr.length - 1), arr[arr.length - 1]]; } -export function equals(one: ReadonlyArray, other: ReadonlyArray, itemEquals: (a: T, b: T) => boolean = (a, b) => a === b): boolean { +export function equals(one: ReadonlyArray | undefined, other: ReadonlyArray | undefined, itemEquals: (a: T, b: T) => boolean = (a, b) => a === b): boolean { if (one === other) { return true; } @@ -334,7 +334,14 @@ export function move(array: any[], from: number, to: number): void { * and not empty. */ export function isFalsyOrEmpty(obj: any): boolean { - return !Array.isArray(obj) || (>obj).length === 0; + return !Array.isArray(obj) || obj.length === 0; +} + +/** + * @returns {{true}} if the provided object is an array and has at least one element. + */ +export function isNonEmptyArray(obj: ReadonlyArray | undefined | null): obj is Array { + return Array.isArray(obj) && obj.length > 0; } /** @@ -540,3 +547,9 @@ export function find(arr: ArrayLike, predicate: (value: T, index: number, return undefined; } + +export function mapArrayOrNot(items: T | T[], fn: (_: T) => U): U | U[] { + return Array.isArray(items) ? + items.map(fn) : + fn(items); +} diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index c47f0f49a..5f0f1682a 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -8,7 +8,6 @@ import * as errors from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { ErrorCallback, TPromise, ValueCallback } from 'vs/base/common/winjs.base'; export function isThenable(obj: any): obj is Thenable { return obj && typeof (>obj).then === 'function'; @@ -91,9 +90,9 @@ export interface ITask { */ export class Throttler { - private activePromise: TPromise | null; - private queuedPromise: TPromise | null; - private queuedPromiseFactory: ITask | null; + private activePromise: Thenable | null; + private queuedPromise: Thenable | null; + private queuedPromiseFactory: ITask> | null; constructor() { this.activePromise = null; @@ -101,7 +100,7 @@ export class Throttler { this.queuedPromiseFactory = null; } - queue(promiseFactory: ITask>): TPromise { + queue(promiseFactory: ITask>): Thenable { if (this.activePromise) { this.queuedPromiseFactory = promiseFactory; @@ -115,19 +114,19 @@ export class Throttler { return result; }; - this.queuedPromise = new TPromise(c => { + this.queuedPromise = new Promise(c => { this.activePromise!.then(onComplete, onComplete).then(c); }); } - return new TPromise((c, e) => { + return new Promise((c, e) => { this.queuedPromise!.then(c, e); }); } this.activePromise = promiseFactory(); - return new TPromise((c, e) => { + return new Promise((c, e) => { this.activePromise!.then((result: any) => { this.activePromise = null; c(result); @@ -139,12 +138,11 @@ export class Throttler { } } -// TODO@Joao: can the previous throttler be replaced with this? -export class SimpleThrottler { +export class Sequencer { - private current = TPromise.wrap(null); + private current: Promise = Promise.resolve(null); - queue(promiseTask: ITask>): TPromise { + queue(promiseTask: ITask>): Thenable { return this.current = this.current.then(() => promiseTask()); } } @@ -175,10 +173,10 @@ export class SimpleThrottler { export class Delayer implements IDisposable { private timeout: any; - private completionPromise: TPromise | null; - private doResolve: ValueCallback | null; + private completionPromise: Thenable | null; + private doResolve: ((value?: any | Thenable) => void) | null; private doReject: (err: any) => void; - private task: ITask> | null; + private task: ITask> | null; constructor(public defaultDelay: number) { this.timeout = null; @@ -187,12 +185,12 @@ export class Delayer implements IDisposable { this.task = null; } - trigger(task: ITask>, delay: number = this.defaultDelay): Thenable { + trigger(task: ITask>, delay: number = this.defaultDelay): Thenable { this.task = task; this.cancelTimeout(); if (!this.completionPromise) { - this.completionPromise = new TPromise((c, e) => { + this.completionPromise = new Promise((c, e) => { this.doResolve = c; this.doReject = e; }).then(() => { @@ -242,21 +240,35 @@ export class Delayer implements IDisposable { * A helper to delay execution of a task that is being requested often, while * preventing accumulation of consecutive executions, while the task runs. * - * Simply combine the two mail men's strategies from the Throttler and Delayer - * helpers, for an analogy. + * The mail man is clever and waits for a certain amount of time, before going + * out to deliver letters. While the mail man is going out, more letters arrive + * and can only be delivered once he is back. Once he is back the mail man will + * do one more trip to deliver the letters that have accumulated while he was out. */ -export class ThrottledDelayer extends Delayer> { +export class ThrottledDelayer { + private delayer: Delayer>; private throttler: Throttler; constructor(defaultDelay: number) { - super(defaultDelay); - + this.delayer = new Delayer(defaultDelay); this.throttler = new Throttler(); } - trigger(promiseFactory: ITask>, delay?: number): TPromise { - return super.trigger(() => this.throttler.queue(promiseFactory), delay); + trigger(promiseFactory: ITask>, delay?: number): Thenable { + return this.delayer.trigger(() => this.throttler.queue(promiseFactory), delay) as any as Thenable; + } + + isTriggered(): boolean { + return this.delayer.isTriggered(); + } + + cancel(): void { + this.delayer.cancel(); + } + + dispose(): void { + this.delayer.dispose(); } } @@ -266,12 +278,12 @@ export class ThrottledDelayer extends Delayer> { export class Barrier { private _isOpen: boolean; - private _promise: TPromise; + private _promise: Promise; private _completePromise: (v: boolean) => void; constructor() { this._isOpen = false; - this._promise = new TPromise((c, e) => { + this._promise = new Promise((c, e) => { this._completePromise = c; }); } @@ -285,7 +297,7 @@ export class Barrier { this._completePromise(true); } - wait(): TPromise { + wait(): Promise { return this._promise; } } @@ -391,10 +403,10 @@ export function first(promiseFactories: ITask>[], shouldStop: (t: return loop(); } -interface ILimitedTaskFactory { - factory: ITask; - c: ValueCallback; - e: ErrorCallback; +interface ILimitedTaskFactory { + factory: ITask>; + c: (value?: T | Thenable) => void; + e: (error?: any) => void; } /** @@ -402,9 +414,11 @@ interface ILimitedTaskFactory { * ensures that at any time no more than M promises are running at the same time. */ export class Limiter { + + private _size = 0; private runningPromises: number; private maxDegreeOfParalellism: number; - private outstandingPromises: ILimitedTaskFactory[]; + private outstandingPromises: ILimitedTaskFactory[]; private readonly _onFinished: Emitter; constructor(maxDegreeOfParalellism: number) { @@ -419,14 +433,15 @@ export class Limiter { } public get size(): number { - return this.runningPromises + this.outstandingPromises.length; + return this._size; + // return this.runningPromises + this.outstandingPromises.length; } - queue(promiseFactory: ITask): TPromise; - queue(factory: ITask>): TPromise { - return new TPromise((c, e) => { - this.outstandingPromises.push({ factory, c, e }); + queue(factory: ITask>): Thenable { + this._size++; + return new Promise((c, e) => { + this.outstandingPromises.push({ factory, c, e }); this.consume(); }); } @@ -443,6 +458,7 @@ export class Limiter { } private consumed(): void { + this._size--; this.runningPromises--; if (this.outstandingPromises.length > 0) { @@ -665,13 +681,13 @@ export class RunOnceWorker extends RunOnceScheduler { export function nfcall(fn: Function, ...args: any[]): Promise; export function nfcall(fn: Function, ...args: any[]): Promise; export function nfcall(fn: Function, ...args: any[]): any { - return new TPromise((c, e) => fn(...args, (err: any, result: any) => err ? e(err) : c(result))); + return new Promise((c, e) => fn(...args, (err: any, result: any) => err ? e(err) : c(result))); } -export function ninvoke(thisArg: any, fn: Function, ...args: any[]): TPromise; -export function ninvoke(thisArg: any, fn: Function, ...args: any[]): TPromise; +export function ninvoke(thisArg: any, fn: Function, ...args: any[]): Thenable; +export function ninvoke(thisArg: any, fn: Function, ...args: any[]): Thenable; export function ninvoke(thisArg: any, fn: Function, ...args: any[]): any { - return new TPromise((c, e) => fn.call(thisArg, ...args, (err: any, result: any) => err ? e(err) : c(result))); + return new Promise((resolve, reject) => fn.call(thisArg, ...args, (err: any, result: any) => err ? reject(err) : resolve(result))); } diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index ec367f23a..066ea8ffe 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -7,7 +7,6 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { once as onceFn } from 'vs/base/common/functional'; import { combinedDisposable, Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { LinkedList } from 'vs/base/common/linkedList'; -import { TPromise } from 'vs/base/common/winjs.base'; /** * To an event a function with one or zero parameters @@ -29,6 +28,71 @@ export interface EmitterOptions { onFirstListenerDidAdd?: Function; onListenerDidAdd?: Function; onLastListenerRemove?: Function; + leakWarningThreshold?: number; +} + +let _globalLeakWarningThreshold = -1; +export function setGlobalLeakWarningThreshold(n: number): IDisposable { + let oldValue = _globalLeakWarningThreshold; + _globalLeakWarningThreshold = n; + return { + dispose() { + _globalLeakWarningThreshold = oldValue; + } + }; +} + +class LeakageMonitor { + + private _stacks: Map | undefined; + private _warnCountdown: number = 0; + + constructor( + readonly customThreshold?: number, + readonly name: string = Math.random().toString(18).slice(2, 5), + ) { } + + dispose(): void { + if (this._stacks) { + this._stacks.clear(); + } + } + + check(listenerCount: number): void { + + let threshold = _globalLeakWarningThreshold; + if (typeof this.customThreshold === 'number') { + threshold = this.customThreshold; + } + if (threshold > 1 && threshold < listenerCount) { + if (!this._stacks) { + this._stacks = new Map(); + } + let stack = new Error().stack!.split('\n').slice(3).join('\n'); + let count = (this._stacks.get(stack) || 0) + 1; + this._stacks.set(stack, count); + this._warnCountdown -= 1; + + if (this._warnCountdown <= 0) { + // only warn on first exceed and then every time the limit + // is exceeded by 50% again + this._warnCountdown = threshold * .5; + + // find most frequent listener and print warning + let topStack: string; + let topCount: number = 0; + this._stacks.forEach((count, stack) => { + if (!topStack || topCount < count) { + topStack = stack; + topCount = count; + } + }); + + console.warn(`[${this.name}] potential listener LEAK detected, having ${listenerCount} listeners already. MOST frequent listener (${topCount}):`); + console.warn(topStack!); + } + } + } } /** @@ -56,16 +120,16 @@ export class Emitter { private static readonly _noop = function () { }; - private _event: Event | null; - private _disposed: boolean; - private _deliveryQueue: [Listener, (T | undefined)][] | null; - protected _listeners: LinkedList | null; + private readonly _options: EmitterOptions | undefined; + private readonly _leakageMon: LeakageMonitor; + private _disposed: boolean = false; + private _event: Event | undefined; + private _deliveryQueue: [Listener, (T | undefined)][] | undefined; + protected _listeners: LinkedList | undefined; - constructor(private _options: EmitterOptions | null = null) { - this._event = null; - this._disposed = false; - this._deliveryQueue = null; - this._listeners = null; + constructor(options?: EmitterOptions) { + this._options = options; + this._leakageMon = new LeakageMonitor(this._options && this._options.leakWarningThreshold); } /** @@ -95,6 +159,9 @@ export class Emitter { this._options.onListenerDidAdd(this, listener, thisArgs); } + // check and record this emitter for potential leakage + this._leakageMon.check(this._listeners.size); + let result: IDisposable; result = { dispose: () => { @@ -155,11 +222,12 @@ export class Emitter { dispose() { if (this._listeners) { - this._listeners = null; + this._listeners = undefined; } if (this._deliveryQueue) { this._deliveryQueue.length = 0; } + this._leakageMon.dispose(); this._disposed = true; } } @@ -292,10 +360,6 @@ export function fromPromise(promise: Thenable): Event { } export function toPromise(event: Event): Thenable { - return new TPromise(c => once(event)(c)); -} - -export function toNativePromise(event: Event): Thenable { return new Promise(c => once(event)(c)); } diff --git a/src/vs/base/common/glob.ts b/src/vs/base/common/glob.ts index cb4771b2d..29828e1e7 100644 --- a/src/vs/base/common/glob.ts +++ b/src/vs/base/common/glob.ts @@ -8,7 +8,6 @@ import * as strings from 'vs/base/common/strings'; import * as paths from 'vs/base/common/paths'; import { LRUCache } from 'vs/base/common/map'; import { CharCode } from 'vs/base/common/charCode'; -import { TPromise } from 'vs/base/common/winjs.base'; import { isThenable } from 'vs/base/common/async'; export interface IExpression { @@ -248,7 +247,7 @@ const T5 = /^([\w\.-]+(\/[\w\.-]+)*)\/?$/; // something/else export type ParsedPattern = (path: string, basename?: string) => boolean; // The ParsedExpression returns a Promise iff hasSibling returns a Promise. -export type ParsedExpression = (path: string, basename?: string, hasSibling?: (name: string) => boolean | TPromise) => string | null | TPromise /* the matching pattern */; +export type ParsedExpression = (path: string, basename?: string, hasSibling?: (name: string) => boolean | Promise) => string | null | Promise /* the matching pattern */; export interface IGlobOptions { /** @@ -258,14 +257,14 @@ export interface IGlobOptions { } interface ParsedStringPattern { - (path: string, basename: string): string | null | TPromise /* the matching pattern */; + (path: string, basename: string): string | null | Promise /* the matching pattern */; basenames?: string[]; patterns?: string[]; allBasenames?: string[]; allPaths?: string[]; } interface ParsedExpressionPattern { - (path: string, basename: string, name?: string, hasSibling?: (name: string) => boolean | TPromise): string | null | TPromise /* the matching pattern */; + (path: string, basename: string, name?: string, hasSibling?: (name: string) => boolean | Promise): string | null | Promise /* the matching pattern */; requiresSiblings?: boolean; allBasenames?: string[]; allPaths?: string[]; @@ -481,15 +480,15 @@ export function parse(arg1: string | IExpression | IRelativePattern, options: IG return parsedExpression(arg1, options); } -export function hasSiblingPromiseFn(siblingsFn?: () => TPromise) { +export function hasSiblingPromiseFn(siblingsFn?: () => Promise) { if (!siblingsFn) { return undefined; } - let siblings: TPromise>; + let siblings: Promise>; return (name: string) => { if (!siblings) { - siblings = (siblingsFn() || TPromise.as([])) + siblings = (siblingsFn() || Promise.resolve([])) .then(list => list ? listToMap(list) : {}); } return siblings.then(map => !!map[name]); @@ -530,9 +529,9 @@ export function isRelativePattern(obj: any): obj is IRelativePattern { */ export function parseToAsync(expression: IExpression, options?: IGlobOptions): ParsedExpression { const parsedExpression = parse(expression, options); - return (path: string, basename?: string, hasSibling?: (name: string) => boolean | TPromise): string | null | TPromise => { + return (path: string, basename?: string, hasSibling?: (name: string) => boolean | Promise): string | null | Promise => { const result = parsedExpression(path, basename, hasSibling); - return result instanceof TPromise ? result : TPromise.as(result); + return isThenable(result) ? result : Promise.resolve(result); }; } @@ -584,7 +583,7 @@ function parsedExpression(expression: IExpression, options: IGlobOptions): Parse return resultExpression; } - const resultExpression: ParsedStringPattern = function (path: string, basename: string, hasSibling?: (name: string) => boolean | TPromise) { + const resultExpression: ParsedStringPattern = function (path: string, basename: string, hasSibling?: (name: string) => boolean | Promise) { let name: string | undefined = undefined; for (let i = 0, n = parsedPatterns.length; i < n; i++) { @@ -639,7 +638,7 @@ function parseExpressionPattern(pattern: string, value: any, options: IGlobOptio if (value) { const when = (value).when; if (typeof when === 'string') { - const result: ParsedExpressionPattern = (path: string, basename: string, name: string, hasSibling: (name: string) => boolean | TPromise) => { + const result: ParsedExpressionPattern = (path: string, basename: string, name: string, hasSibling: (name: string) => boolean | Promise) => { if (!hasSibling || !parsedPattern(path, basename)) { return null; } diff --git a/src/vs/base/common/hash.ts b/src/vs/base/common/hash.ts index 613c5e307..6c0025b1d 100644 --- a/src/vs/base/common/hash.ts +++ b/src/vs/base/common/hash.ts @@ -56,3 +56,17 @@ function objectHash(obj: any, initialHashVal: number): number { return hash(obj[key], hashVal); }, initialHashVal); } + +export class Hasher { + + private _value = 0; + + get value(): number { + return this._value; + } + + hash(obj: any): number { + this._value = hash(obj, this._value); + return this._value; + } +} \ No newline at end of file diff --git a/src/vs/base/common/htmlContent.ts b/src/vs/base/common/htmlContent.ts index f470327b8..b7e99fbf6 100644 --- a/src/vs/base/common/htmlContent.ts +++ b/src/vs/base/common/htmlContent.ts @@ -4,16 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import { equals } from 'vs/base/common/arrays'; +import { UriComponents } from 'vs/base/common/uri'; export interface IMarkdownString { value: string; isTrusted?: boolean; + uris?: { [href: string]: UriComponents }; } export class MarkdownString implements IMarkdownString { value: string; isTrusted?: boolean; + sanitize: boolean = true; constructor(value: string = '') { this.value = value; diff --git a/src/vs/base/common/json.ts b/src/vs/base/common/json.ts index bed9da5ed..9158a73fe 100644 --- a/src/vs/base/common/json.ts +++ b/src/vs/base/common/json.ts @@ -2,11 +2,6 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; export const enum ScanError { None = 0, diff --git a/src/vs/base/common/jsonEdit.ts b/src/vs/base/common/jsonEdit.ts index 33ab2fdca..485d9f48b 100644 --- a/src/vs/base/common/jsonEdit.ts +++ b/src/vs/base/common/jsonEdit.ts @@ -2,7 +2,6 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; import { ParseError, Node, JSONPath, Segment, parseTree, findNodeAtLocation } from './json'; import { Edit, format, isEOL, FormattingOptions } from './jsonFormatter'; diff --git a/src/vs/base/common/jsonFormatter.ts b/src/vs/base/common/jsonFormatter.ts index a2306b5cb..df2a97ee5 100644 --- a/src/vs/base/common/jsonFormatter.ts +++ b/src/vs/base/common/jsonFormatter.ts @@ -2,7 +2,6 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; import { createScanner, SyntaxKind, ScanError } from './json'; diff --git a/src/vs/base/common/labels.ts b/src/vs/base/common/labels.ts index 854fbbee0..94f776bb6 100644 --- a/src/vs/base/common/labels.ts +++ b/src/vs/base/common/labels.ts @@ -24,7 +24,7 @@ export interface IUserHomeProvider { /** * @deprecated use LabelService instead */ -export function getPathLabel(resource: URI | string, userHomeProvider: IUserHomeProvider, rootProvider?: IWorkspaceFolderProvider): string { +export function getPathLabel(resource: URI | string, userHomeProvider?: IUserHomeProvider, rootProvider?: IWorkspaceFolderProvider): string { if (typeof resource === 'string') { resource = URI.file(resource); } diff --git a/src/vs/base/common/linkedList.ts b/src/vs/base/common/linkedList.ts index 898586f29..0867506c4 100644 --- a/src/vs/base/common/linkedList.ts +++ b/src/vs/base/common/linkedList.ts @@ -19,6 +19,11 @@ export class LinkedList { private _first: Node | undefined; private _last: Node | undefined; + private _size: number = 0; + + get size(): number { + return this._size; + } isEmpty(): boolean { return !this._first; @@ -57,6 +62,7 @@ export class LinkedList { newNode.next = oldFirst; oldFirst.prev = newNode; } + this._size += 1; return () => { let candidate: Node | undefined = this._first; @@ -88,6 +94,7 @@ export class LinkedList { } // done + this._size -= 1; break; } }; diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index d7ea548d5..a23d09b66 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -449,7 +449,7 @@ export class ResourceMap { } public keys(): URI[] { - return keys(this.map).map(URI.parse); + return keys(this.map).map(k => URI.parse(k)); } public clone(): ResourceMap { diff --git a/src/vs/base/common/marked/OSSREADME.json b/src/vs/base/common/marked/OSSREADME.json deleted file mode 100644 index 671955e8b..000000000 --- a/src/vs/base/common/marked/OSSREADME.json +++ /dev/null @@ -1,9 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: -[ - { - "name": "marked", - "repositoryURL": "https://github.com/markedjs/marked", - "version": "0.5.0", - "license": "MIT" - } -] diff --git a/src/vs/base/common/marked/cgmanifest.json b/src/vs/base/common/marked/cgmanifest.json new file mode 100644 index 000000000..b0c1ce8f3 --- /dev/null +++ b/src/vs/base/common/marked/cgmanifest.json @@ -0,0 +1,17 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "marked", + "repositoryUrl": "https://github.com/markedjs/marked", + "commitHash": "78c977bc3a47f9e2fb146477d1ca3dad0cb134e6" + } + }, + "license": "MIT", + "version": "0.5.0" + } + ], + "version": 1 +} diff --git a/src/vs/base/common/marked/marked.js b/src/vs/base/common/marked/marked.js index 480ea6a5b..bde18dff9 100644 --- a/src/vs/base/common/marked/marked.js +++ b/src/vs/base/common/marked/marked.js @@ -4,6 +4,10 @@ * https://github.com/markedjs/marked */ +// BEGIN MONACOCHANGE +var __marked_exports; +// END MONACOCHANGE + ;(function(root) { 'use strict'; @@ -1563,11 +1567,32 @@ marked.inlineLexer = InlineLexer.output; marked.parse = marked; -if (typeof module !== 'undefined' && typeof exports === 'object') { - module.exports = marked; -} else if (typeof define === 'function' && define.amd) { - define(function() { return marked; }); -} else { - root.marked = marked; -} -})(this || (typeof window !== 'undefined' ? window : global)); \ No newline at end of file +// BEGIN MONACOCHANGE +// if (typeof module !== 'undefined' && typeof exports === 'object') { +// module.exports = marked; +// } else if (typeof define === 'function' && define.amd) { +// define(function() { return marked; }); +// } else { +// root.marked = marked; +// } +// })(this || (typeof window !== 'undefined' ? window : global)); +__marked_exports = marked; +}).call(this); + +// ESM-comment-begin +define(function() { return __marked_exports; }); +// ESM-comment-end + +// ESM-uncomment-begin +// export var marked = __marked_exports; +// export var Parser = __marked_exports.Parser; +// export var parser = __marked_exports.parser; +// export var Renderer = __marked_exports.Renderer; +// export var TextRenderer = __marked_exports.TextRenderer; +// export var Lexer = __marked_exports.Lexer; +// export var lexer = __marked_exports.lexer; +// export var InlineLexer = __marked_exports.InlineLexer; +// export var inlineLexer = __marked_exports.inlineLexer; +// export var parse = __marked_exports.parse; +// ESM-uncomment-end +// END MONACOCHANGE diff --git a/src/vs/base/common/mime.ts b/src/vs/base/common/mime.ts index 507067b26..d7777d9dd 100644 --- a/src/vs/base/common/mime.ts +++ b/src/vs/base/common/mime.ts @@ -106,7 +106,7 @@ export function clearTextMimes(onlyUserConfigured?: boolean): void { /** * Given a file, return the best matching mime type for it */ -export function guessMimeTypes(path: string, firstLine?: string): string[] { +export function guessMimeTypes(path: string, firstLine?: string, skipUserAssociations: boolean = false): string[] { if (!path) { return [MIME_UNKNOWN]; } @@ -114,10 +114,12 @@ export function guessMimeTypes(path: string, firstLine?: string): string[] { path = path.toLowerCase(); const filename = paths.basename(path); - // 1.) User configured mappings have highest priority - const configuredMime = guessMimeTypeByPath(path, filename, userRegisteredAssociations); - if (configuredMime) { - return [configuredMime, MIME_TEXT]; + if (!skipUserAssociations) { + // 1.) User configured mappings have highest priority + const configuredMime = guessMimeTypeByPath(path, filename, userRegisteredAssociations); + if (configuredMime) { + return [configuredMime, MIME_TEXT]; + } } // 2.) Registered mappings have middle priority diff --git a/src/vs/base/common/objects.ts b/src/vs/base/common/objects.ts index 5e90f440f..fe461fe6b 100644 --- a/src/vs/base/common/objects.ts +++ b/src/vs/base/common/objects.ts @@ -223,6 +223,11 @@ export function getOrDefault(obj: T, fn: (obj: T) => R, defaultValue: R | return typeof result === 'undefined' ? defaultValue : result; } +export function getOrDefault2(obj: T, fn: (obj: T) => R | undefined, defaultValue: R): R { + const result = fn(obj); + return typeof result === 'undefined' ? defaultValue : result; +} + type obj = { [key: string]: any; }; /** * Returns an object that has keys for each value that is different in the base object. Keys diff --git a/src/vs/base/common/paging.ts b/src/vs/base/common/paging.ts index b19e26184..a6518831a 100644 --- a/src/vs/base/common/paging.ts +++ b/src/vs/base/common/paging.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { isArray } from 'vs/base/common/types'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { canceled } from 'vs/base/common/errors'; @@ -93,7 +92,7 @@ export class PagedModel implements IPagedModel { resolve(index: number, cancellationToken: CancellationToken): Thenable { if (cancellationToken.isCancellationRequested) { - return TPromise.wrapError(canceled()); + return Promise.reject(canceled()); } const pageIndex = Math.floor(index / this.pager.pageSize); @@ -101,7 +100,7 @@ export class PagedModel implements IPagedModel { const page = this.pages[pageIndex]; if (page.isResolved) { - return TPromise.as(page.elements[indexInPage]); + return Promise.resolve(page.elements[indexInPage]); } if (!page.promise) { @@ -116,7 +115,7 @@ export class PagedModel implements IPagedModel { page.isResolved = false; page.promise = null; page.cts = null; - return TPromise.wrapError(err); + return Promise.reject(err); }); } @@ -153,7 +152,7 @@ export class DelayedPagedModel implements IPagedModel { } resolve(index: number, cancellationToken: CancellationToken): Thenable { - return new TPromise((c, e) => { + return new Promise((c, e) => { if (cancellationToken.isCancellationRequested) { return e(canceled()); } @@ -198,7 +197,7 @@ export function mergePagers(one: IPager, other: IPager): IPager { total: one.total + other.total, pageSize: one.pageSize + other.pageSize, getPage(pageIndex: number, token): Thenable { - return TPromise.join([one.getPage(pageIndex, token), other.getPage(pageIndex, token)]) + return Promise.all([one.getPage(pageIndex, token), other.getPage(pageIndex, token)]) .then(([onePage, otherPage]) => [...onePage, ...otherPage]); } }; diff --git a/src/vs/base/common/paths.ts b/src/vs/base/common/paths.ts index b2b255b0f..1255aed38 100644 --- a/src/vs/base/common/paths.ts +++ b/src/vs/base/common/paths.ts @@ -72,7 +72,10 @@ function _isNormal(path: string, win: boolean): boolean { : !_posixBadPath.test(path); } -export function normalize(path: string, toOSPath?: boolean): string { +export function normalize(path: undefined, toOSPath?: boolean): undefined; +export function normalize(path: null, toOSPath?: boolean): null; +export function normalize(path: string, toOSPath?: boolean): string; +export function normalize(path: string | null | undefined, toOSPath?: boolean): string | null | undefined { if (path === null || path === void 0) { return path; @@ -288,7 +291,7 @@ export function isUNC(path: string): boolean { // Reference: https://en.wikipedia.org/wiki/Filename const INVALID_FILE_CHARS = isWindows ? /[\\/:\*\?"<>\|]/g : /[\\/]/g; const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])$/i; -export function isValidBasename(name: string): boolean { +export function isValidBasename(name: string | null | undefined): boolean { if (!name || name.length === 0 || /^\s+$/.test(name)) { return false; // require a name that is not just whitespace } diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index 6e0f3908e..05be7f703 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -14,7 +14,7 @@ export function getComparisonKey(resource: URI): string { return hasToIgnoreCase(resource) ? resource.toString().toLowerCase() : resource.toString(); } -export function hasToIgnoreCase(resource: URI): boolean { +export function hasToIgnoreCase(resource: URI | undefined): boolean { // A file scheme resource is in the same platform as code, so ignore case for non linux platforms // Resource can be from another platform. Lowering the case as an hack. Should come from File system provider return resource && resource.scheme === Schemas.file ? !isLinux : true; @@ -25,7 +25,7 @@ export function basenameOrAuthority(resource: URI): string { } /** - * Tests wheter a `candidate` URI is a parent or equal of a given `base` URI. + * Tests whether a `candidate` URI is a parent or equal of a given `base` URI. * @param base A uri which is "longer" * @param parentCandidate A uri which is "shorter" then `base` */ @@ -45,7 +45,7 @@ function isEqualAuthority(a1: string, a2: string, ignoreCase?: boolean) { return a1 === a2 || ignoreCase && a1 && a2 && equalsIgnoreCase(a1, a2); } -export function isEqual(first: URI, second: URI, ignoreCase = hasToIgnoreCase(first)): boolean { +export function isEqual(first: URI | undefined, second: URI | undefined, ignoreCase = hasToIgnoreCase(first)): boolean { const identityEquals = (first === second); if (identityEquals) { return true; @@ -73,6 +73,9 @@ export function basename(resource: URI): string { * @returns The URI representing the directory of the input URI. */ export function dirname(resource: URI): URI | null { + if (resource.scheme === Schemas.file) { + return URI.file(paths.dirname(fsPath(resource))); + } let dirname = paths.dirname(resource.path, '/'); if (resource.authority && dirname.length && dirname.charCodeAt(0) !== CharCode.Slash) { return null; // If a URI contains an authority component, then the path component must either be empty or begin with a CharCode.Slash ("/") character @@ -173,7 +176,7 @@ export function distinctParents(items: T[], resourceAccessor: (item: T) => UR } /** - * Tests wheter the given URL is a file URI created by `URI.parse` instead of `URI.file`. + * Tests whether the given URL is a file URI created by `URI.parse` instead of `URI.file`. * Such URI have no scheme or scheme that consist of a single letter (windows drive letter) * @param candidate The URI to test * @returns A corrected, real file URI if the input seems to be malformed. diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index 7c6a9820f..e3206199e 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -10,7 +10,7 @@ import { CharCode } from 'vs/base/common/charCode'; */ export const empty = ''; -export function isFalsyOrWhitespace(str: string): boolean { +export function isFalsyOrWhitespace(str: string | undefined): boolean { if (!str || typeof str !== 'string') { return true; } @@ -78,7 +78,7 @@ export function escapeRegExpCharacters(value: string): string { * @param haystack string to trim * @param needle the thing to trim (default is a blank) */ -export function trim(haystack: string, needle: string = ' '): string | undefined { +export function trim(haystack: string, needle: string = ' '): string { let trimmed = ltrim(haystack, needle); return rtrim(trimmed, needle); } diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 2bddd1c6b..4424cc055 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -21,11 +21,11 @@ export function setUriThrowOnMissingScheme(value: boolean): boolean { return old; } -function _validateUri(ret: URI): void { +function _validateUri(ret: URI, _strict?: boolean): void { // scheme, must be set if (!ret.scheme) { - if (_throwOnMissingSchema) { + if (_strict || _throwOnMissingSchema) { throw new Error(`[UriError]: Scheme is missing: {scheme: "", authority: "${ret.authority}", path: "${ret.path}", query: "${ret.query}", fragment: "${ret.fragment}"}`); } else { console.warn(`[UriError]: Scheme is missing: {scheme: "", authority: "${ret.authority}", path: "${ret.path}", query: "${ret.query}", fragment: "${ret.fragment}"}`); @@ -141,7 +141,7 @@ export class URI implements UriComponents { /** * @internal */ - protected constructor(scheme: string, authority?: string, path?: string, query?: string, fragment?: string); + protected constructor(scheme: string, authority?: string, path?: string, query?: string, fragment?: string, _strict?: boolean); /** * @internal @@ -151,7 +151,7 @@ export class URI implements UriComponents { /** * @internal */ - protected constructor(schemeOrData: string | UriComponents, authority?: string, path?: string, query?: string, fragment?: string) { + protected constructor(schemeOrData: string | UriComponents, authority?: string, path?: string, query?: string, fragment?: string, _strict?: boolean) { if (typeof schemeOrData === 'object') { this.scheme = schemeOrData.scheme || _empty; @@ -169,7 +169,7 @@ export class URI implements UriComponents { this.query = query || _empty; this.fragment = fragment || _empty; - _validateUri(this); + _validateUri(this, _strict); } } @@ -261,7 +261,7 @@ export class URI implements UriComponents { * * @param value A string which represents an URI (see `URI#toString`). */ - public static parse(value: string): URI { + public static parse(value: string, _strict: boolean = false): URI { const match = _regexp.exec(value); if (!match) { return new _URI(_empty, _empty, _empty, _empty, _empty); @@ -272,6 +272,7 @@ export class URI implements UriComponents { decodeURIComponent(match[5] || _empty), decodeURIComponent(match[7] || _empty), decodeURIComponent(match[9] || _empty), + _strict ); } diff --git a/src/vs/base/common/uriIpc.ts b/src/vs/base/common/uriIpc.ts index 42e28c888..374ffd990 100644 --- a/src/vs/base/common/uriIpc.ts +++ b/src/vs/base/common/uriIpc.ts @@ -8,9 +8,17 @@ import { URI, UriComponents } from 'vs/base/common/uri'; export interface IURITransformer { transformIncoming(uri: UriComponents): UriComponents; transformOutgoing(uri: URI): URI; + transformOutgoing(uri: UriComponents): UriComponents; } -export const DefaultURITransformer: IURITransformer = { - transformIncoming: (uri: UriComponents) => uri, - transformOutgoing: (uri: URI) => uri, +export const DefaultURITransformer: IURITransformer = new class { + transformIncoming(uri: UriComponents) { + return uri; + } + + transformOutgoing(uri: URI): URI; + transformOutgoing(uri: UriComponents): UriComponents; + transformOutgoing(uri: URI | UriComponents): URI | UriComponents { + return uri; + } }; \ No newline at end of file diff --git a/src/vs/base/common/worker/simpleWorker.ts b/src/vs/base/common/worker/simpleWorker.ts index 08531cd34..48fb1527d 100644 --- a/src/vs/base/common/worker/simpleWorker.ts +++ b/src/vs/base/common/worker/simpleWorker.ts @@ -6,6 +6,15 @@ import { transformErrorForSerialization } from 'vs/base/common/errors'; import { Disposable } from 'vs/base/common/lifecycle'; import { isWeb } from 'vs/base/common/platform'; +import { PolyfillPromise } from 'vs/base/common/winjs.polyfill.promise'; + +var global: any = self; + +// When missing, polyfill the native promise +// with our winjs-based polyfill +if (typeof global.Promise === 'undefined') { + global.Promise = PolyfillPromise; +} const INITIALIZE = '$initialize'; diff --git a/src/vs/base/node/config.ts b/src/vs/base/node/config.ts index b9be322ea..a134a70cf 100644 --- a/src/vs/base/node/config.ts +++ b/src/vs/base/node/config.ts @@ -46,7 +46,7 @@ export class ConfigWatcher implements IConfigWatcher, IDisposable { private parseErrors: json.ParseError[]; private disposed: boolean; private loaded: boolean; - private timeoutHandle: NodeJS.Timer; + private timeoutHandle: NodeJS.Timer | null; private disposables: IDisposable[]; private readonly _onDidUpdateConfiguration: Emitter>; private configName: string; diff --git a/src/vs/base/node/crypto.ts b/src/vs/base/node/crypto.ts index 6e556b97c..8d3dc0e63 100644 --- a/src/vs/base/node/crypto.ts +++ b/src/vs/base/node/crypto.ts @@ -6,11 +6,10 @@ import * as fs from 'fs'; import * as crypto from 'crypto'; import * as stream from 'stream'; -import { TPromise } from 'vs/base/common/winjs.base'; import { once } from 'vs/base/common/functional'; -export function checksum(path: string, sha1hash: string): TPromise { - const promise = new TPromise((c, e) => { +export function checksum(path: string, sha1hash: string): Promise { + const promise = new Promise((c, e) => { const input = fs.createReadStream(path); const hash = crypto.createHash('sha1'); const hashStream = hash as any as stream.PassThrough; @@ -35,9 +34,9 @@ export function checksum(path: string, sha1hash: string): TPromise { return promise.then(hash => { if (hash !== sha1hash) { - return TPromise.wrapError(new Error('Hash mismatch')); + return Promise.reject(new Error('Hash mismatch')); } - return TPromise.as(void 0); + return Promise.resolve(); }); -} \ No newline at end of file +} diff --git a/src/vs/base/node/encoding.ts b/src/vs/base/node/encoding.ts index c1d248beb..516afbc93 100644 --- a/src/vs/base/node/encoding.ts +++ b/src/vs/base/node/encoding.ts @@ -17,7 +17,7 @@ export const UTF16le = 'utf16le'; export interface IDecodeStreamOptions { guessEncoding?: boolean; minBytesRequiredForDetection?: number; - overwriteEncoding?(detectedEncoding: string): string; + overwriteEncoding?(detectedEncoding: string | null): string; } export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions): Promise<{ detected: IDetectedEncodingResult, stream: NodeJS.ReadableStream }> { @@ -78,7 +78,9 @@ export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions this._decodeStreamConstruction = Promise.resolve(detectEncodingFromBuffer({ buffer: Buffer.concat(this._buffer), bytesRead: this._bytesBuffered }, options.guessEncoding)).then(detected => { - detected.encoding = options.overwriteEncoding(detected.encoding); + if (options.overwriteEncoding) { + detected.encoding = options.overwriteEncoding(detected.encoding); + } this._decodeStream = decodeStream(detected.encoding); for (const buffer of this._buffer) { this._decodeStream.write(buffer); @@ -129,7 +131,7 @@ export function encodingExists(encoding: string): boolean { return iconv.encodingExists(toNodeEncoding(encoding)); } -export function decodeStream(encoding: string): NodeJS.ReadWriteStream { +export function decodeStream(encoding: string | null): NodeJS.ReadWriteStream { return iconv.decodeStream(toNodeEncoding(encoding)); } @@ -137,15 +139,15 @@ export function encodeStream(encoding: string, options?: { addBOM?: boolean }): return iconv.encodeStream(toNodeEncoding(encoding), options); } -function toNodeEncoding(enc: string): string { - if (enc === UTF8_with_bom) { +function toNodeEncoding(enc: string | null): string { + if (enc === UTF8_with_bom || enc === null) { return UTF8; // iconv does not distinguish UTF 8 with or without BOM, so we need to help it } return enc; } -export function detectEncodingByBOMFromBuffer(buffer: Buffer, bytesRead: number): string | null { +export function detectEncodingByBOMFromBuffer(buffer: Buffer | null, bytesRead: number): string | null { if (!buffer || bytesRead < 2) { return null; } @@ -181,7 +183,7 @@ export function detectEncodingByBOMFromBuffer(buffer: Buffer, bytesRead: number) * Detects the Byte Order Mark in a given file. * If no BOM is detected, null will be passed to callback. */ -export function detectEncodingByBOM(file: string): Promise { +export function detectEncodingByBOM(file: string): Promise { return stream.readExactlyByFile(file, 3).then(({ buffer, bytesRead }) => detectEncodingByBOMFromBuffer(buffer, bytesRead)); } @@ -191,7 +193,7 @@ const IGNORE_ENCODINGS = ['ascii', 'utf-8', 'utf-16', 'utf-32']; /** * Guesses the encoding from buffer. */ -export function guessEncodingByBuffer(buffer: Buffer): Promise { +export function guessEncodingByBuffer(buffer: Buffer): Promise { return import('jschardet').then(jschardet => { jschardet.Constants.MINIMUM_THRESHOLD = MINIMUM_THRESHOLD; @@ -266,7 +268,7 @@ const NO_GUESS_BUFFER_MAX_LEN = 512; // when not auto guessing the encoding, const AUTO_GUESS_BUFFER_MAX_LEN = 512 * 8; // with auto guessing we want a lot more content to be read for guessing export interface IDetectedEncodingResult { - encoding: string; + encoding: string | null; seemsBinary: boolean; } @@ -280,7 +282,7 @@ export function detectEncodingFromBuffer({ buffer, bytesRead }: stream.ReadResul // Detect 0 bytes to see if file is binary or UTF-16 LE/BE // unless we already know that this file has a UTF-16 encoding let seemsBinary = false; - if (encoding !== UTF16be && encoding !== UTF16le) { + if (encoding !== UTF16be && encoding !== UTF16le && buffer) { let couldBeUTF16LE = true; // e.g. 0xAA 0x00 let couldBeUTF16BE = true; // e.g. 0x00 0xAA let containsZeroByte = false; @@ -328,7 +330,7 @@ export function detectEncodingFromBuffer({ buffer, bytesRead }: stream.ReadResul } // Auto guess encoding if configured - if (autoGuessEncoding && !seemsBinary && !encoding) { + if (autoGuessEncoding && !seemsBinary && !encoding && buffer) { return guessEncodingByBuffer(buffer.slice(0, bytesRead)).then(guessedEncoding => { return { seemsBinary: false, diff --git a/src/vs/base/node/extfs.ts b/src/vs/base/node/extfs.ts index ab91d0dc2..3cc6f7cbe 100644 --- a/src/vs/base/node/extfs.ts +++ b/src/vs/base/node/extfs.ts @@ -28,13 +28,13 @@ export function readdirSync(path: string): string[] { return fs.readdirSync(path); } -export function readdir(path: string, callback: (error: Error, files: string[]) => void): void { +export function readdir(path: string, callback: (error: Error | null, files: string[]) => void): void { // Mac: uses NFD unicode form on disk, but we want NFC // See also https://github.com/nodejs/node/issues/2165 if (platform.isMacintosh) { return fs.readdir(path, (error, children) => { if (error) { - return callback(error, null); + return callback(error, []); } return callback(null, children.map(c => normalizeNFC(c))); @@ -49,7 +49,7 @@ export interface IStatAndLink { isSymbolicLink: boolean; } -export function statLink(path: string, callback: (error: Error, statAndIsLink: IStatAndLink) => void): void { +export function statLink(path: string, callback: (error: Error | null, statAndIsLink: IStatAndLink | null) => void): void { fs.lstat(path, (error, lstat) => { if (error || lstat.isSymbolicLink()) { fs.stat(path, (error, stat) => { @@ -65,10 +65,8 @@ export function statLink(path: string, callback: (error: Error, statAndIsLink: I }); } -export function copy(source: string, target: string, callback: (error: Error) => void, copiedSources?: { [path: string]: boolean }): void { - if (!copiedSources) { - copiedSources = Object.create(null); - } +export function copy(source: string, target: string, callback: (error: Error | null) => void, copiedSourcesIn?: { [path: string]: boolean }): void { + const copiedSources = copiedSourcesIn ? copiedSourcesIn : Object.create(null); fs.stat(source, (error, stat) => { if (error) { @@ -87,8 +85,8 @@ export function copy(source: string, target: string, callback: (error: Error) => const proceed = function () { readdir(source, (err, files) => { - loop(files, (file: string, clb: (error: Error, result: string[]) => void) => { - copy(paths.join(source, file), paths.join(target, file), (error: Error) => clb(error, void 0), copiedSources); + loop(files, (file: string, clb: (error: Error | null, result: string[]) => void) => { + copy(paths.join(source, file), paths.join(target, file), (error: Error) => clb(error, []), copiedSources); }, callback); }); }; @@ -130,7 +128,7 @@ function doCopyFile(source: string, target: string, mode: number, callback: (err } export function mkdirp(path: string, mode?: number, token?: CancellationToken): TPromise { - const mkdir = () => { + const mkdir = (): Promise => { return nfcall(fs.mkdir, path, mode).then(null, (mkdirErr: NodeJS.ErrnoException) => { // ENOENT: a parent folder does not exist yet @@ -180,7 +178,7 @@ export function mkdirp(path: string, mode?: number, token?: CancellationToken): // after the rename, the contents are out of the workspace although not yet deleted. The greater benefit however is that this operation // will fail in case any file is used by another process. fs.unlink() in node will not bail if a file unlinked is used by another process. // However, the consequences are bad as outlined in all the related bugs from https://github.com/joyent/node/issues/7164 -export function del(path: string, tmpFolder: string, callback: (error: Error) => void, done?: (error: Error) => void): void { +export function del(path: string, tmpFolder: string, callback: (error: Error | null) => void, done?: (error: Error | null) => void): void { fs.exists(path, exists => { if (!exists) { return callback(null); @@ -198,7 +196,7 @@ export function del(path: string, tmpFolder: string, callback: (error: Error) => } const pathInTemp = paths.join(tmpFolder, uuid.generateUuid()); - fs.rename(path, pathInTemp, (error: Error) => { + fs.rename(path, pathInTemp, (error: Error | null) => { if (error) { return rmRecursive(path, callback); // if rename fails, delete without tmp dir } @@ -221,7 +219,7 @@ export function del(path: string, tmpFolder: string, callback: (error: Error) => }); } -function rmRecursive(path: string, callback: (error: Error) => void): void { +function rmRecursive(path: string, callback: (error: Error | null) => void): void { if (path === '\\' || path === '/') { return callback(new Error('Will not delete root!')); } @@ -297,12 +295,12 @@ export function delSync(path: string): void { } } -export function mv(source: string, target: string, callback: (error: Error) => void): void { +export function mv(source: string, target: string, callback: (error: Error | null) => void): void { if (source === target) { return callback(null); } - function updateMtime(err: Error): void { + function updateMtime(err: Error | null): void { if (err) { return callback(err); } @@ -481,7 +479,7 @@ function doWriteFileAndFlush(path: string, data: string | Buffer, options: IWrit } // Open the file with same flags and mode as fs.writeFile() - fs.open(path, options.flag, options.mode, (openError, fd) => { + fs.open(path, typeof options.flag === 'string' ? options.flag : 'r', options.mode, (openError, fd) => { if (openError) { return callback(openError); } @@ -520,7 +518,7 @@ export function writeFileAndFlushSync(path: string, data: string | Buffer, optio } // Open the file with same flags and mode as fs.writeFile() - const fd = fs.openSync(path, options.flag, options.mode); + const fd = fs.openSync(path, typeof options.flag === 'string' ? options.flag : 'r', options.mode); try { @@ -566,7 +564,7 @@ function ensureOptions(options?: IWriteFileOptions): IWriteFileOptions { * In case of errors, null is returned. But you cannot use this function to verify that a path exists. * realcaseSync does not handle '..' or '.' path segments and it does not take the locale into account. */ -export function realcaseSync(path: string): string { +export function realcaseSync(path: string): string | null { const dir = paths.dirname(path); if (path === dir) { // end recursion return path; @@ -616,7 +614,7 @@ export function realpathSync(path: string): string { } } -export function realpath(path: string, callback: (error: Error, realpath: string) => void): void { +export function realpath(path: string, callback: (error: Error | null, realpath: string) => void): void { return fs.realpath(path, (error, realpath) => { if (!error) { return callback(null, realpath); @@ -644,7 +642,7 @@ export function watch(path: string, onChange: (type: string, path?: string) => v const watcher = fs.watch(path); watcher.on('change', (type, raw) => { - let file: string | null = null; + let file: string | undefined; if (raw) { // https://github.com/Microsoft/vscode/issues/38191 file = raw.toString(); if (platform.isMacintosh) { diff --git a/src/vs/base/node/pfs.ts b/src/vs/base/node/pfs.ts index 8aa1f32b9..0b07975c4 100644 --- a/src/vs/base/node/pfs.ts +++ b/src/vs/base/node/pfs.ts @@ -64,6 +64,12 @@ export function rename(oldPath: string, newPath: string): Promise { return nfcall(fs.rename, oldPath, newPath); } +export function renameIgnoreError(oldPath: string, newPath: string): Promise { + return new Promise(resolve => { + fs.rename(oldPath, newPath, () => resolve()); + }); +} + export function rmdir(path: string): Promise { return nfcall(fs.rmdir, path); } @@ -72,6 +78,12 @@ export function unlink(path: string): Promise { return nfcall(fs.unlink, path); } +export function unlinkIgnoreError(path: string): Promise { + return new Promise(resolve => { + fs.unlink(path, () => resolve()); + }); +} + export function symlink(target: string, path: string, type?: string): Promise { return nfcall(fs.symlink, target, path, type); } @@ -183,7 +195,7 @@ export function whenDeleted(path: string): TPromise { if (!exists) { clearInterval(interval); - c(null); + c(void 0); } }); } diff --git a/src/vs/base/node/processes.ts b/src/vs/base/node/processes.ts index 0bd5e6723..8ea9f1880 100644 --- a/src/vs/base/node/processes.ts +++ b/src/vs/base/node/processes.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as path from 'path'; +import * as fs from 'fs'; import * as cp from 'child_process'; import * as nls from 'vs/nls'; -import { TPromise, TValueCallback, ErrorCallback } from 'vs/base/common/winjs.base'; import * as Types from 'vs/base/common/types'; import { IStringDictionary } from 'vs/base/common/collections'; import * as Objects from 'vs/base/common/objects'; @@ -17,7 +17,9 @@ import { CommandOptions, ForkOptions, SuccessData, Source, TerminateResponse, Te import { getPathFromAmdModule } from 'vs/base/common/amd'; export { CommandOptions, ForkOptions, SuccessData, Source, TerminateResponse, TerminateResponseCode }; -export type TProgressCallback = (progress: T) => void; +export type ValueCallback = (value?: T | Thenable) => void; +export type ErrorCallback = (error?: any) => void; +export type ProgressCallback = (progress: T) => void; export interface LineData { line: string; @@ -77,8 +79,8 @@ export abstract class AbstractProcess { protected shell: boolean; private childProcess: cp.ChildProcess | null; - protected childProcessPromise: TPromise | null; - private pidResolve?: TValueCallback; + protected childProcessPromise: Promise | null; + private pidResolve?: ValueCallback; protected terminateRequested: boolean; private static WellKnowCommands: IStringDictionary = { @@ -145,14 +147,14 @@ export abstract class AbstractProcess { return 'other'; } - public start(pp: TProgressCallback): TPromise { + public start(pp: ProgressCallback): Promise { if (Platform.isWindows && ((this.options && this.options.cwd && TPath.isUNC(this.options.cwd)) || !this.options && TPath.isUNC(process.cwd()))) { - return TPromise.wrapError(new Error(nls.localize('TaskRunner.UNC', 'Can\'t execute a shell command on a UNC drive.'))); + return Promise.reject(new Error(nls.localize('TaskRunner.UNC', 'Can\'t execute a shell command on a UNC drive.'))); } return this.useExec().then((useExec) => { - let cc: TValueCallback; + let cc: ValueCallback; let ee: ErrorCallback; - let result = new TPromise((c, e) => { + let result = new Promise((c, e) => { cc = c; ee = e; }); @@ -228,7 +230,7 @@ export abstract class AbstractProcess { } if (childProcess) { this.childProcess = childProcess; - this.childProcessPromise = TPromise.as(childProcess); + this.childProcessPromise = Promise.resolve(childProcess); if (this.pidResolve) { this.pidResolve(Types.isNumber(childProcess.pid) ? childProcess.pid : -1); this.pidResolve = undefined; @@ -247,10 +249,10 @@ export abstract class AbstractProcess { }); } - protected abstract handleExec(cc: TValueCallback, pp: TProgressCallback, error: Error | null, stdout: Buffer, stderr: Buffer): void; - protected abstract handleSpawn(childProcess: cp.ChildProcess, cc: TValueCallback, pp: TProgressCallback, ee: ErrorCallback, sync: boolean): void; + protected abstract handleExec(cc: ValueCallback, pp: ProgressCallback, error: Error | null, stdout: Buffer, stderr: Buffer): void; + protected abstract handleSpawn(childProcess: cp.ChildProcess, cc: ValueCallback, pp: ProgressCallback, ee: ErrorCallback, sync: boolean): void; - protected handleClose(data: any, cc: TValueCallback, pp: TProgressCallback, ee: ErrorCallback): void { + protected handleClose(data: any, cc: ValueCallback, pp: ProgressCallback, ee: ErrorCallback): void { // Default is to do nothing. } @@ -269,19 +271,19 @@ export abstract class AbstractProcess { } } - public get pid(): TPromise { + public get pid(): Promise { if (this.childProcessPromise) { return this.childProcessPromise.then(childProcess => childProcess.pid, err => -1); } else { - return new TPromise((resolve) => { + return new Promise((resolve) => { this.pidResolve = resolve; }); } } - public terminate(): TPromise { + public terminate(): Promise { if (!this.childProcessPromise) { - return TPromise.as({ success: true }); + return Promise.resolve({ success: true }); } return this.childProcessPromise.then((childProcess) => { this.terminateRequested = true; @@ -295,8 +297,8 @@ export abstract class AbstractProcess { }); } - private useExec(): TPromise { - return new TPromise((c, e) => { + private useExec(): Promise { + return new Promise((c, e) => { if (!this.shell || !Platform.isWindows) { c(false); } @@ -322,7 +324,7 @@ export class LineProcess extends AbstractProcess { super(arg1, arg2, arg3, arg4); } - protected handleExec(cc: TValueCallback, pp: TProgressCallback, error: Error, stdout: Buffer, stderr: Buffer) { + protected handleExec(cc: ValueCallback, pp: ProgressCallback, error: Error, stdout: Buffer, stderr: Buffer) { [stdout, stderr].forEach((buffer: Buffer, index: number) => { let lineDecoder = new LineDecoder(); let lines = lineDecoder.write(buffer); @@ -337,7 +339,7 @@ export class LineProcess extends AbstractProcess { cc({ terminated: this.terminateRequested, error: error }); } - protected handleSpawn(childProcess: cp.ChildProcess, cc: TValueCallback, pp: TProgressCallback, ee: ErrorCallback, sync: boolean): void { + protected handleSpawn(childProcess: cp.ChildProcess, cc: ValueCallback, pp: ProgressCallback, ee: ErrorCallback, sync: boolean): void { this.stdoutLineDecoder = new LineDecoder(); this.stderrLineDecoder = new LineDecoder(); childProcess.stdout.on('data', (data: Buffer) => { @@ -350,7 +352,7 @@ export class LineProcess extends AbstractProcess { }); } - protected handleClose(data: any, cc: TValueCallback, pp: TProgressCallback, ee: ErrorCallback): void { + protected handleClose(data: any, cc: ValueCallback, pp: ProgressCallback, ee: ErrorCallback): void { [this.stdoutLineDecoder.end(), this.stderrLineDecoder.end()].forEach((line, index) => { if (line) { pp({ line: line, source: index === 0 ? Source.stdout : Source.stderr }); @@ -400,3 +402,51 @@ export function createQueuedSender(childProcess: cp.ChildProcess): IQueuedSender return { send }; } + +export namespace win32 { + export function findExecutable(command: string, cwd?: string, paths?: string[]): string { + // If we have an absolute path then we take it. + if (path.isAbsolute(command)) { + return command; + } + if (cwd === void 0) { + cwd = process.cwd(); + } + let dir = path.dirname(command); + if (dir !== '.') { + // We have a directory and the directory is relative (see above). Make the path absolute + // to the current working directory. + return path.join(cwd, command); + } + if (paths === void 0 && Types.isString(process.env.PATH)) { + paths = process.env.PATH.split(path.delimiter); + } + // No PATH environment. Make path absolute to the cwd. + if (paths === void 0 || paths.length === 0) { + return path.join(cwd, command); + } + // We have a simple file name. We get the path variable from the env + // and try to find the executable on the path. + for (let pathEntry of paths) { + // The path entry is absolute. + let fullPath: string; + if (path.isAbsolute(pathEntry)) { + fullPath = path.join(pathEntry, command); + } else { + fullPath = path.join(cwd, pathEntry, command); + } + if (fs.existsSync(fullPath)) { + return fullPath; + } + let withExtension = fullPath + '.com'; + if (fs.existsSync(withExtension)) { + return withExtension; + } + withExtension = fullPath + '.exe'; + if (fs.existsSync(withExtension)) { + return withExtension; + } + } + return path.join(cwd, command); + } +} \ No newline at end of file diff --git a/src/vs/base/node/storage.ts b/src/vs/base/node/storage.ts index 91aa98750..fb5c7a391 100644 --- a/src/vs/base/node/storage.ts +++ b/src/vs/base/node/storage.ts @@ -4,12 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { Database, Statement } from 'vscode-sqlite3'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; -import { RunOnceScheduler } from 'vs/base/common/async'; +import { ThrottledDelayer, timeout } from 'vs/base/common/async'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { mapToString, setToString } from 'vs/base/common/map'; import { basename } from 'path'; +import { mark } from 'vs/base/common/performance'; +import { rename, unlinkIgnoreError, copy, renameIgnoreError } from 'vs/base/node/pfs'; export interface IStorageOptions { path: string; @@ -18,12 +20,8 @@ export interface IStorageOptions { } export interface IStorageLoggingOptions { - errorLogger?: (error: string | Error) => void; - infoLogger?: (msg: string) => void; - - info?: boolean; - trace?: boolean; - profile?: boolean; + logError?: (error: string | Error) => void; + logTrace?: (msg: string) => void; } enum StorageState { @@ -32,7 +30,32 @@ enum StorageState { Closed } -export class Storage extends Disposable { +export interface IStorage extends IDisposable { + + readonly size: number; + readonly onDidChangeStorage: Event; + + init(): Promise; + + get(key: string, fallbackValue: string): string; + get(key: string, fallbackValue?: string): string | undefined; + + getBoolean(key: string, fallbackValue: boolean): boolean; + getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; + + getInteger(key: string, fallbackValue: number): number; + getInteger(key: string, fallbackValue?: number): number | undefined; + + set(key: string, value: any): Thenable; + delete(key: string): Thenable; + + close(): Thenable; + + getItems(): Promise>; + checkIntegrity(full: boolean): Promise; +} + +export class Storage extends Disposable implements IStorage { _serviceBrand: any; private static readonly FLUSH_DELAY = 100; @@ -45,17 +68,17 @@ export class Storage extends Disposable { private storage: SQLiteStorageImpl; private cache: Map = new Map(); - private pendingScheduler: RunOnceScheduler; + private flushDelayer: ThrottledDelayer; + private pendingDeletes: Set = new Set(); private pendingInserts: Map = new Map(); - private pendingPromises: { resolve: Function, reject: Function }[] = []; constructor(options: IStorageOptions) { super(); this.storage = new SQLiteStorageImpl(options); - this.pendingScheduler = this._register(new RunOnceScheduler(() => this.flushPending(), Storage.FLUSH_DELAY)); + this.flushDelayer = this._register(new ThrottledDelayer(Storage.FLUSH_DELAY)); } get size(): number { @@ -74,6 +97,8 @@ export class Storage extends Disposable { }); } + get(key: string, fallbackValue: string): string; + get(key: string, fallbackValue?: string): string | undefined; get(key: string, fallbackValue?: string): string | undefined { const value = this.cache.get(key); @@ -84,7 +109,9 @@ export class Storage extends Disposable { return value; } - getBoolean(key: string, fallbackValue: boolean = false): boolean { + getBoolean(key: string, fallbackValue: boolean): boolean; + getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; + getBoolean(key: string, fallbackValue?: boolean): boolean | undefined { const value = this.get(key); if (isUndefinedOrNull(value)) { @@ -94,7 +121,9 @@ export class Storage extends Disposable { return value === 'true'; } - getInteger(key: string, fallbackValue: number = 0): number { + getInteger(key: string, fallbackValue: number): number; + getInteger(key: string, fallbackValue?: number): number | undefined; + getInteger(key: string, fallbackValue?: number): number | undefined { const value = this.get(key); if (isUndefinedOrNull(value)) { @@ -104,7 +133,7 @@ export class Storage extends Disposable { return parseInt(value, 10); } - set(key: string, value: any): Promise { + set(key: string, value: any): Thenable { if (this.state === StorageState.Closed) { return Promise.resolve(); // Return early if we are already closed } @@ -131,10 +160,11 @@ export class Storage extends Disposable { // Event this._onDidChangeStorage.fire(key); - return this.update(); + // Accumulate work by scheduling after timeout + return this.flushDelayer.trigger(() => this.flushPending()); } - delete(key: string): Promise { + delete(key: string): Thenable { if (this.state === StorageState.Closed) { return Promise.resolve(); // Return early if we are already closed } @@ -154,20 +184,11 @@ export class Storage extends Disposable { // Event this._onDidChangeStorage.fire(key); - return this.update(); + // Accumulate work by scheduling after timeout + return this.flushDelayer.trigger(() => this.flushPending()); } - private update(): Promise { - - // Schedule - if (!this.pendingScheduler.isScheduled()) { - this.pendingScheduler.schedule(); - } - - return new Promise((resolve, reject) => this.pendingPromises.push({ resolve, reject })); - } - - close(): Promise { + close(): Thenable { if (this.state === StorageState.Closed) { return Promise.resolve(); // return if already closed } @@ -175,36 +196,24 @@ export class Storage extends Disposable { // Update state this.state = StorageState.Closed; - // Dispose scheduler (no more scheduling possible) - this.pendingScheduler.dispose(); - - // Flush & close - return this.flushPending().then(() => { - return this.storage.close(); - }); + // Trigger new flush to ensure data is persisted and then close + // even if there is an error flushing. We must always ensure + // the DB is closed to avoid corruption. + const onDone = () => this.storage.close(); + return this.flushDelayer.trigger(() => this.flushPending(), 0 /* immediately */).then(onDone, onDone); } - private flushPending(): Promise { + private flushPending(): Thenable { // Get pending data - const pendingPromises = this.pendingPromises; - const pendingDeletes = this.pendingDeletes; - const pendingInserts = this.pendingInserts; + const updateRequest: IUpdateRequest = { insert: this.pendingInserts, delete: this.pendingDeletes }; // Reset pending data for next run - this.pendingPromises = []; this.pendingDeletes = new Set(); this.pendingInserts = new Map(); - return this.storage.updateItems({ insert: pendingInserts, delete: pendingDeletes }).then(() => { - - // Resolve pending - pendingPromises.forEach(promise => promise.resolve()); - }, error => { - - // Forward error to pending - pendingPromises.forEach(promise => promise.reject(error)); - }); + // Update in storage + return this.storage.updateItems(updateRequest); } getItems(): Promise> { @@ -217,30 +226,43 @@ export class Storage extends Disposable { } export interface IUpdateRequest { - insert?: Map; - delete?: Set; + readonly insert?: Map; + readonly delete?: Set; +} + +interface IOpenDatabaseResult { + db: Database; + path: string; } export class SQLiteStorageImpl { - private db: Promise; + + private static measuredRequireDuration: boolean; // TODO@Ben remove me after a while + + private static IN_MEMORY_PATH = ':memory:'; + private static BUSY_OPEN_TIMEOUT = 2000; // timeout in ms to retry when opening DB fails with SQLITE_BUSY + private name: string; private logger: SQLiteStorageLogger; - constructor(private options: IStorageOptions) { + private whenOpened: Promise; + + constructor(options: IStorageOptions) { this.name = basename(options.path); this.logger = new SQLiteStorageLogger(options.logging); - this.db = this.open(); + + this.whenOpened = this.open(options.path); } getItems(): Promise> { - return this.db.then(db => { + return this.whenOpened.then(({ db }) => { const items = new Map(); - return this.each(db, 'SELECT * FROM ItemTable', row => { - items.set(row.key, row.value); - }).then(() => { - if (this.logger.verbose) { - this.logger.info(`[storage ${this.name}] getItems(): ${mapToString(items)}`); + return this.all(db, 'SELECT * FROM ItemTable').then(rows => { + rows.forEach(row => items.set(row.key, row.value)); + + if (this.logger.isTracing) { + this.logger.trace(`[storage ${this.name}] getItems(): ${mapToString(items)}`); } return items; @@ -261,11 +283,11 @@ export class SQLiteStorageImpl { return Promise.resolve(); } - if (this.logger.verbose) { - this.logger.info(`[storage ${this.name}] updateItems(): insert(${request.insert ? mapToString(request.insert) : '0'}), delete(${request.delete ? setToString(request.delete) : '0'})`); + if (this.logger.isTracing) { + this.logger.trace(`[storage ${this.name}] updateItems(): insert(${request.insert ? mapToString(request.insert) : '0'}), delete(${request.delete ? setToString(request.delete) : '0'})`); } - return this.db.then(db => { + return this.whenOpened.then(({ db }) => { return this.transaction(db, () => { if (request.insert && request.insert.size > 0) { this.prepare(db, 'INSERT INTO ItemTable VALUES (?,?)', stmt => { @@ -287,74 +309,161 @@ export class SQLiteStorageImpl { } close(): Promise { - this.logger.info(`[storage ${this.name}] close()`); + this.logger.trace(`[storage ${this.name}] close()`); - return this.db.then(db => { + return this.whenOpened.then(result => { return new Promise((resolve, reject) => { - db.close(error => { + result.db.close(error => { if (error) { this.logger.error(`[storage ${this.name}] close(): ${error}`); return reject(error); } + // If the DB closed successfully and we are not running in-memory + // make a backup of the DB so that we can use it as fallback in + // case the actual DB becomes corrupt. + if (result.path !== SQLiteStorageImpl.IN_MEMORY_PATH) { + return this.backup(result).then(resolve, error => { + this.logger.error(`[storage ${this.name}] backup(): ${error}`); + + return resolve(); // ignore failing backup + }); + } + return resolve(); }); }); }); } + private backup(db: IOpenDatabaseResult): Promise { + if (db.path === SQLiteStorageImpl.IN_MEMORY_PATH) { + return Promise.resolve(); // no backups when running in-memory + } + + const backupPath = this.toBackupPath(db.path); + + return unlinkIgnoreError(backupPath).then(() => copy(db.path, backupPath)); + } + + private toBackupPath(path: string): string { + return `${path}.backup`; + } + checkIntegrity(full: boolean): Promise { - this.logger.info(`[storage ${this.name}] checkIntegrity(full: ${full})`); + this.logger.trace(`[storage ${this.name}] checkIntegrity(full: ${full})`); - return this.db.then(db => { + return this.whenOpened.then(({ db }) => { return this.get(db, full ? 'PRAGMA integrity_check' : 'PRAGMA quick_check').then(row => { return full ? row['integrity_check'] : row['quick_check']; }); }); } - private open(): Promise { - this.logger.info(`[storage ${this.name}] open()`); + private open(path: string): Promise { + this.logger.trace(`[storage ${this.name}] open()`); return new Promise((resolve, reject) => { - this.doOpen(this.options.path).then(resolve, error => { + const fallbackToInMemoryDatabase = (error: Error) => { this.logger.error(`[storage ${this.name}] open(): Error (open DB): ${error}`); this.logger.error(`[storage ${this.name}] open(): Falling back to in-memory DB`); // In case of any error to open the DB, use an in-memory // DB so that we always have a valid DB to talk to. - this.doOpen(':memory:').then(resolve, reject); + this.doOpen(SQLiteStorageImpl.IN_MEMORY_PATH).then(resolve, reject); + }; + + this.doOpen(path).then(resolve, error => { + + // TODO@Ben check if this is still happening. This error code should only arise if + // another process is locking the same DB we want to open at that time. This typically + // never happens because a DB connection is limited per window. However, in the event + // of a window reload, it may be possible that the previous connection was not properly + // closed while the new connection is already established. + if (error.code === 'SQLITE_BUSY') { + return this.handleSQLiteBusy(path).then(resolve, fallbackToInMemoryDatabase); + } + + // This error code indicates that even though the DB file exists, + // SQLite cannot open it and signals it is corrupt or not a DB. + if (error.code === 'SQLITE_CORRUPT' || error.code === 'SQLITE_NOTADB') { + return this.handleSQLiteCorrupt(path, error).then(resolve, fallbackToInMemoryDatabase); + } + + // Otherwise give up and fallback to in-memory DB + return fallbackToInMemoryDatabase(error); }); }); } - private doOpen(path: string): Promise { + private handleSQLiteBusy(path: string): Promise { + this.logger.error(`[storage ${this.name}] open(): Retrying after ${SQLiteStorageImpl.BUSY_OPEN_TIMEOUT}ms due to SQLITE_BUSY`); + + // Retry after some time if the DB is busy + return timeout(SQLiteStorageImpl.BUSY_OPEN_TIMEOUT).then(() => this.doOpen(path)); + } + + private handleSQLiteCorrupt(path: string, error: any): Promise { + this.logger.error(`[storage ${this.name}] open(): Unable to open DB due to ${error.code}`); + + // Move corrupt DB to a different filename and try to load from backup + // If that fails, a new empty DB is being created automatically + return rename(path, this.toCorruptPath(path)) + .then(() => renameIgnoreError(this.toBackupPath(path), path)) + .then(() => this.doOpen(path)); + } + + private toCorruptPath(path: string): string { + const randomSuffix = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 4); + + return `${path}.${randomSuffix}.corrupt`; + } + + private doOpen(path: string): Promise { + // TODO@Ben clean up performance markers return new Promise((resolve, reject) => { + let measureRequireDuration = false; + if (!SQLiteStorageImpl.measuredRequireDuration) { + SQLiteStorageImpl.measuredRequireDuration = true; + measureRequireDuration = true; + + mark('willRequireSQLite'); + } import('vscode-sqlite3').then(sqlite3 => { - const db = new (this.logger.verbose ? sqlite3.verbose().Database : sqlite3.Database)(path, error => { + if (measureRequireDuration) { + mark('didRequireSQLite'); + } + + const db: Database = new (this.logger.isTracing ? sqlite3.verbose().Database : sqlite3.Database)(path, error => { if (error) { - return reject(error); + return db ? db.close(() => reject(error)) : reject(error); } - // Setup schema + // The following exec() statement serves two purposes: + // - create the DB if it does not exist yet + // - validate that the DB is not corrupt (the open() call does not throw otherwise) + mark('willSetupSQLiteSchema'); this.exec(db, [ 'PRAGMA user_version = 1;', 'CREATE TABLE IF NOT EXISTS ItemTable (key TEXT UNIQUE ON CONFLICT REPLACE, value BLOB)' - ].join('')).then(() => resolve(db), error => reject(error)); + ].join('')).then(() => { + mark('didSetupSQLiteSchema'); + + return resolve({ path, db }); + }, error => { + mark('didSetupSQLiteSchema'); + + return db.close(() => reject(error)); + }); }); - // Check for errors + // Errors db.on('error', error => this.logger.error(`[storage ${this.name}] Error (event): ${error}`)); // Tracing - if (this.logger.trace) { - db.on('trace', sql => this.logger.info(`[storage ${this.name}] Trace (event): ${sql}`)); - } - - // Profiling - if (this.logger.profile) { - db.on('profile', (sql, time) => this.logger.info(`[storage ${this.name}] Profile (event): ${sql} (${time}ms)`)); + if (this.logger.isTracing) { + db.on('trace', sql => this.logger.trace(`[storage ${this.name}] Trace (event): ${sql}`)); } }); }); @@ -388,29 +497,16 @@ export class SQLiteStorageImpl { }); } - private each(db: Database, sql: string, callback: (row: any) => void): Promise { + private all(db: Database, sql: string): Promise<{ key: string, value: string }[]> { return new Promise((resolve, reject) => { - let hadError = false; - db.each(sql, (error, row) => { - if (error) { - this.logger.error(`[storage ${this.name}] each(): ${error}`); - - hadError = true; - - return reject(error); - } - - if (!hadError) { - callback(row); - } - }, error => { + db.all(sql, (error, rows) => { if (error) { - this.logger.error(`[storage ${this.name}] each(): ${error}`); + this.logger.error(`[storage ${this.name}] all(): ${error}`); return reject(error); } - return resolve(); + return resolve(rows); }); }); } @@ -438,14 +534,14 @@ export class SQLiteStorageImpl { private prepare(db: Database, sql: string, runCallback: (stmt: Statement) => void): void { const stmt = db.prepare(sql); - runCallback(stmt); - const statementErrorListener = error => { this.logger.error(`[storage ${this.name}] prepare(): ${error} (${sql})`); }; stmt.on('error', statementErrorListener); + runCallback(stmt); + stmt.finalize(error => { if (error) { statementErrorListener(error); @@ -457,35 +553,80 @@ export class SQLiteStorageImpl { } class SQLiteStorageLogger { - private readonly logInfo: boolean; - private readonly logError: boolean; + private readonly logTrace: (msg: string) => void; + private readonly logError: (error: string | Error) => void; - constructor(private readonly options?: IStorageLoggingOptions) { - this.logInfo = !!(this.verbose && options && options.infoLogger); - this.logError = !!(options && options.errorLogger); - } - - get verbose(): boolean { - return !!(this.options && (this.options.info || this.options.trace || this.options.profile)); - } + constructor(options?: IStorageLoggingOptions) { + if (options && typeof options.logTrace === 'function') { + this.logTrace = options.logTrace; + } - get trace(): boolean { - return !!(this.options && this.options.trace); + if (options && typeof options.logError === 'function') { + this.logError = options.logError; + } } - get profile(): boolean { - return !!(this.options && this.options.profile); + get isTracing(): boolean { + return !!this.logTrace; } - info(msg: string): void { - if (this.logInfo) { - this.options!.infoLogger!(msg); + trace(msg: string): void { + if (this.logTrace) { + this.logTrace(msg); } } error(error: string | Error): void { if (this.logError) { - this.options!.errorLogger!(error); + this.logError(error); } } +} + +export class NullStorage extends Disposable implements IStorage { + + readonly size = 0; + readonly onDidChangeStorage = Event.None; + + private items = new Map(); + + init(): Promise { return Promise.resolve(); } + + get(key: string, fallbackValue: string): string; + get(key: string, fallbackValue?: string): string | undefined; + get(key: string, fallbackValue?: string): string | undefined { + return void 0; + } + + getBoolean(key: string, fallbackValue: boolean): boolean; + getBoolean(key: string, fallbackValue?: boolean): boolean | undefined; + getBoolean(key: string, fallbackValue?: boolean): boolean | undefined { + return void 0; + } + + getInteger(key: string, fallbackValue: number): number; + getInteger(key: string, fallbackValue?: number): number | undefined; + getInteger(key: string, fallbackValue?: number): number | undefined { + return void 0; + } + + set(key: string, value: any): Promise { + return Promise.resolve(); + } + + delete(key: string): Promise { + return Promise.resolve(); + } + + close(): Promise { + return Promise.resolve(); + } + + getItems(): Promise> { + return Promise.resolve(this.items); + } + + checkIntegrity(full: boolean): Promise { + return Promise.resolve('ok'); + } } \ No newline at end of file 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 46daa9b10..eb9cef3e4 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 @@ -8,29 +8,25 @@ import { IPCServer, ClientConnectionEvent } from 'vs/base/parts/ipc/node/ipc'; import { Protocol } from 'vs/base/parts/ipc/node/ipc.electron'; import { ipcMain } from 'electron'; -interface WebContents extends Electron.WebContents { - getId(): number; -} - interface IIPCEvent { - event: { sender: WebContents; }; + event: { sender: Electron.WebContents; }; message: string; } function createScopedOnMessageEvent(senderId: number, eventName: string): Event { const onMessage = fromNodeEventEmitter(ipcMain, eventName, (event, message: string) => ({ event, message })); - const onMessageFromSender = filterEvent(onMessage, ({ event }) => event.sender.getId() === senderId); + const onMessageFromSender = filterEvent(onMessage, ({ event }) => event.sender.id === senderId); return mapEvent(onMessageFromSender, ({ message }) => message); } export class Server extends IPCServer { private static getOnDidClientConnect(): Event { - const onHello = fromNodeEventEmitter(ipcMain, 'ipc:hello', ({ sender }) => sender); + const onHello = fromNodeEventEmitter(ipcMain, 'ipc:hello', ({ sender }) => sender); return mapEvent(onHello, webContents => { - const onMessage = createScopedOnMessageEvent(webContents.getId(), 'ipc:message'); - const onDidClientDisconnect = signalEvent(createScopedOnMessageEvent(webContents.getId(), 'ipc:disconnect')); + const onMessage = createScopedOnMessageEvent(webContents.id, 'ipc:message'); + const onDidClientDisconnect = signalEvent(createScopedOnMessageEvent(webContents.id, 'ipc:disconnect')); const protocol = new Protocol(webContents, onMessage); return { protocol, onDidClientDisconnect }; @@ -40,4 +36,4 @@ export class Server extends IPCServer { constructor() { super(Server.getOnDidClientConnect()); } -} +} \ No newline at end of file diff --git a/src/vs/base/parts/ipc/node/ipc.cp.ts b/src/vs/base/parts/ipc/node/ipc.cp.ts index 0d0f2c001..678415bd3 100644 --- a/src/vs/base/parts/ipc/node/ipc.cp.ts +++ b/src/vs/base/parts/ipc/node/ipc.cp.ts @@ -19,8 +19,8 @@ import * as errors from 'vs/base/common/errors'; * We should move all implementations to use named ipc.net, so we stop depending on cp.fork. */ -export class Server extends IPCServer { - constructor() { +export class Server extends IPCServer { + constructor(ctx: TContext) { super({ send: r => { try { @@ -30,7 +30,7 @@ export class Server extends IPCServer { } catch (e) { /* not much to do */ } }, onMessage: fromNodeEventEmitter(process, 'message', msg => Buffer.from(msg, 'base64')) - }); + }, ctx); process.once('disconnect', () => this.dispose()); } diff --git a/src/vs/base/parts/ipc/node/ipc.electron.ts b/src/vs/base/parts/ipc/node/ipc.electron.ts index 5932eee4a..831a27377 100644 --- a/src/vs/base/parts/ipc/node/ipc.electron.ts +++ b/src/vs/base/parts/ipc/node/ipc.electron.ts @@ -13,7 +13,7 @@ import { Event, Emitter } from 'vs/base/common/event'; */ export interface Sender { - send(channel: string, msg: string): void; + send(channel: string, msg: string | null): void; } export class Protocol implements IMessagePassingProtocol { diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts index 675e15516..ec7cb7724 100644 --- a/src/vs/base/parts/ipc/node/ipc.net.ts +++ b/src/vs/base/parts/ipc/node/ipc.net.ts @@ -231,15 +231,15 @@ export class Server extends IPCServer { } } -export class Client extends IPCClient { +export class Client extends IPCClient { - static fromSocket(socket: Socket, id: string): Client { + static fromSocket(socket: Socket, id: TContext): Client { return new Client(new Protocol(socket), id); } get onClose(): Event { return this.protocol.onClose; } - constructor(private protocol: Protocol, id: string) { + constructor(private protocol: Protocol, id: TContext) { super(protocol, id); } diff --git a/src/vs/base/parts/ipc/node/ipc.ts b/src/vs/base/parts/ipc/node/ipc.ts index a3318454b..50fc50bbf 100644 --- a/src/vs/base/parts/ipc/node/ipc.ts +++ b/src/vs/base/parts/ipc/node/ipc.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; -import { Event, Emitter, once, filterEvent, toNativePromise, Relay } from 'vs/base/common/event'; +import { Event, Emitter, once, toPromise, Relay } from 'vs/base/common/event'; import { always, CancelablePromise, createCancelablePromise, timeout } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; @@ -62,12 +62,22 @@ export interface IChannel { listen(event: string, arg?: any): Event; } +/** + * An `IServerChannel` is the couter part to `IChannel`, + * on the server-side. You should implement this interface + * if you'd like to handle remote promises or events. + */ +export interface IServerChannel { + call(ctx: TContext, command: string, arg?: any, cancellationToken?: CancellationToken): Thenable; + listen(ctx: TContext, event: string, arg?: any): Event; +} + /** * An `IChannelServer` hosts a collection of channels. You are * able to register channels onto it, provided a channel name. */ -export interface IChannelServer { - registerChannel(channelName: string, channel: IChannel): void; +export interface IChannelServer { + registerChannel(channelName: string, channel: IServerChannel): void; } /** @@ -78,14 +88,23 @@ export interface IChannelClient { getChannel(channelName: string): T; } +export interface Client { + readonly ctx: TContext; +} + +export interface IConnectionHub { + readonly connections: Connection[]; + readonly onDidChangeConnections: Event>; +} + /** * An `IClientRouter` is responsible for routing calls to specific * channels, in scenarios in which there are multiple possible * channels (each from a separate client) to pick from. */ -export interface IClientRouter { - routeCall(clientIds: string[], command: string, arg?: any, cancellationToken?: CancellationToken): Thenable; - routeEvent(clientIds: string[], event: string, arg?: any): Thenable; +export interface IClientRouter { + routeCall(hub: IConnectionHub, command: string, arg?: any, cancellationToken?: CancellationToken): Thenable>; + routeEvent(hub: IConnectionHub, event: string, arg?: any): Thenable>; } /** @@ -95,87 +114,153 @@ export interface IClientRouter { * the same channel. You'll need to pass in an `IClientRouter` in * order to pick the right one. */ -export interface IRoutingChannelClient { - getChannel(channelName: string, router: IClientRouter): T; +export interface IRoutingChannelClient { + getChannel(channelName: string, router: IClientRouter): T; } -enum BodyType { - Undefined, - String, - Buffer, - Object +interface IReader { + read(bytes: number): Buffer; } -const empty = Buffer.allocUnsafe(0); +interface IWriter { + write(buffer: Buffer): void; +} -function serializeBody(body: any): { buffer: Buffer, type: BodyType } { - if (typeof body === 'undefined') { - return { buffer: empty, type: BodyType.Undefined }; - } else if (typeof body === 'string') { - return { buffer: Buffer.from(body), type: BodyType.String }; - } else if (Buffer.isBuffer(body)) { - return { buffer: body, type: BodyType.Buffer }; - } else { - return { buffer: Buffer.from(JSON.stringify(body)), type: BodyType.Object }; +class BufferReader implements IReader { + + private pos = 0; + + constructor(private buffer: Buffer) { } + + read(bytes: number): Buffer { + const result = this.buffer.slice(this.pos, this.pos + bytes); + this.pos += result.length; + return result; + } +} + +class BufferWriter implements IWriter { + + private buffers: Buffer[] = []; + + get buffer(): Buffer { + return Buffer.concat(this.buffers); + } + + write(buffer: Buffer): void { + this.buffers.push(buffer); } } -function serialize(header: any, body: any = undefined): Buffer { - const headerSizeBuffer = Buffer.allocUnsafe(4); - const { buffer: bodyBuffer, type: bodyType } = serializeBody(body); - const headerBuffer = Buffer.from(JSON.stringify([header, bodyType])); - headerSizeBuffer.writeUInt32BE(headerBuffer.byteLength, 0); +enum DataType { + Undefined = 0, + String = 1, + Buffer = 2, + Array = 3, + Object = 4 +} + +function createSizeBuffer(size: number): Buffer { + const result = Buffer.allocUnsafe(4); + result.writeUInt32BE(size, 0); + return result; +} - return Buffer.concat([headerSizeBuffer, headerBuffer, bodyBuffer]); +function readSizeBuffer(reader: IReader): number { + return reader.read(4).readUInt32BE(0); } -function deserializeBody(bodyBuffer: Buffer, bodyType: BodyType): any { - switch (bodyType) { - case BodyType.Undefined: return undefined; - case BodyType.String: return bodyBuffer.toString(); - case BodyType.Buffer: return bodyBuffer; - case BodyType.Object: return JSON.parse(bodyBuffer.toString()); +const BufferPresets = { + Undefined: Buffer.alloc(1, DataType.Undefined), + String: Buffer.alloc(1, DataType.String), + Buffer: Buffer.alloc(1, DataType.Buffer), + Array: Buffer.alloc(1, DataType.Array), + Object: Buffer.alloc(1, DataType.Object) +}; + +function serialize(writer: IWriter, data: any): void { + if (typeof data === 'undefined') { + writer.write(BufferPresets.Undefined); + } else if (typeof data === 'string') { + const buffer = Buffer.from(data); + writer.write(BufferPresets.String); + writer.write(createSizeBuffer(buffer.length)); + writer.write(buffer); + } else if (Buffer.isBuffer(data)) { + writer.write(BufferPresets.Buffer); + writer.write(createSizeBuffer(data.length)); + writer.write(data); + } else if (Array.isArray(data)) { + writer.write(BufferPresets.Array); + writer.write(createSizeBuffer(data.length)); + + for (const el of data) { + serialize(writer, el); + } + } else { + const buffer = Buffer.from(JSON.stringify(data)); + writer.write(BufferPresets.Object); + writer.write(createSizeBuffer(buffer.length)); + writer.write(buffer); } } -function deserialize(buffer: Buffer): { header: any, body: any } { - const headerSize = buffer.readUInt32BE(0); - const headerBuffer = buffer.slice(4, 4 + headerSize); - const bodyBuffer = buffer.slice(4 + headerSize); - const [header, bodyType] = JSON.parse(headerBuffer.toString()); - const body = deserializeBody(bodyBuffer, bodyType); +function deserialize(reader: IReader): any { + const type = reader.read(1).readUInt8(0); + + switch (type) { + case DataType.Undefined: return undefined; + case DataType.String: return reader.read(readSizeBuffer(reader)).toString(); + case DataType.Buffer: return reader.read(readSizeBuffer(reader)); + case DataType.Array: { + const length = readSizeBuffer(reader); + const result: any[] = []; + + for (let i = 0; i < length; i++) { + result.push(deserialize(reader)); + } - return { header, body }; + return result; + } + case DataType.Object: return JSON.parse(reader.read(readSizeBuffer(reader)).toString()); + } } -export class ChannelServer implements IChannelServer, IDisposable { +export class ChannelServer implements IChannelServer, IDisposable { - private channels = new Map(); + private channels = new Map>(); private activeRequests = new Map(); private protocolListener: IDisposable | null; - constructor(private protocol: IMessagePassingProtocol) { + constructor(private protocol: IMessagePassingProtocol, private ctx: TContext) { this.protocolListener = this.protocol.onMessage(msg => this.onRawMessage(msg)); this.sendResponse({ type: ResponseType.Initialize }); } - registerChannel(channelName: string, channel: IChannel): void { + registerChannel(channelName: string, channel: IServerChannel): void { this.channels.set(channelName, channel); } private sendResponse(response: IRawResponse): void { switch (response.type) { case ResponseType.Initialize: - return this.sendBuffer(serialize([response.type])); + return this.send([response.type]); case ResponseType.PromiseSuccess: case ResponseType.PromiseError: case ResponseType.EventFire: case ResponseType.PromiseErrorObj: - return this.sendBuffer(serialize([response.type, response.id], response.data)); + return this.send([response.type, response.id], response.data); } } + private send(header: any, body: any = undefined): void { + const writer = new BufferWriter(); + serialize(writer, header); + serialize(writer, body); + this.sendBuffer(writer.buffer); + } + private sendBuffer(message: Buffer): void { try { this.protocol.send(message); @@ -185,7 +270,9 @@ export class ChannelServer implements IChannelServer, IDisposable { } private onRawMessage(message: Buffer): void { - const { header, body } = deserialize(message); + const reader = new BufferReader(message); + const header = deserialize(reader); + const body = deserialize(reader); const type = header[0] as RequestType; switch (type) { @@ -206,7 +293,7 @@ export class ChannelServer implements IChannelServer, IDisposable { let promise: Thenable; try { - promise = channel.call(request.name, request.arg, cancellationTokenSource.token); + promise = channel.call(this.ctx, request.name, request.arg, cancellationTokenSource.token); } catch (err) { promise = Promise.reject(err); } @@ -240,7 +327,7 @@ export class ChannelServer implements IChannelServer, IDisposable { const channel = this.channels.get(request.channelName); const id = request.id; - const event = channel.listen(request.name, request.arg); + const event = channel.listen(this.ctx, request.name, request.arg); const disposable = event(data => this.sendResponse({ id, data, type: ResponseType.EventFire })); this.activeRequests.set(request.id, disposable); @@ -397,14 +484,21 @@ export class ChannelClient implements IChannelClient, IDisposable { switch (request.type) { case RequestType.Promise: case RequestType.EventListen: - return this.sendBuffer(serialize([request.type, request.id, request.channelName, request.name], request.arg)); + return this.send([request.type, request.id, request.channelName, request.name], request.arg); case RequestType.PromiseCancel: case RequestType.EventDispose: - return this.sendBuffer(serialize([request.type, request.id])); + return this.send([request.type, request.id]); } } + private send(header: any, body: any = undefined): void { + const writer = new BufferWriter(); + serialize(writer, header); + serialize(writer, body); + this.sendBuffer(writer.buffer); + } + private sendBuffer(message: Buffer): void { try { this.protocol.send(message); @@ -414,7 +508,9 @@ export class ChannelClient implements IChannelClient, IDisposable { } private onBuffer(message: Buffer): void { - const { header, body } = deserialize(message); + const reader = new BufferReader(message); + const header = deserialize(reader); + const body = deserialize(reader); const type: ResponseType = header[0]; switch (type) { @@ -447,7 +543,7 @@ export class ChannelClient implements IChannelClient, IDisposable { if (this.state === State.Idle) { return Promise.resolve(); } else { - return toNativePromise(this.onDidInitialize); + return toPromise(this.onDidInitialize); } } @@ -466,6 +562,10 @@ export interface ClientConnectionEvent { onDidClientDisconnect: Event; } +interface Connection extends Client { + readonly channelClient: ChannelClient; +} + /** * An `IPCServer` is both a channel server and a routing channel * client. @@ -474,15 +574,17 @@ export interface ClientConnectionEvent { * and the `IPCClient` classes to get IPC implementations * for your protocol. */ -export class IPCServer implements IChannelServer, IRoutingChannelClient, IDisposable { +export class IPCServer implements IChannelServer, IRoutingChannelClient, IConnectionHub, IDisposable { - private channels = new Map(); - private channelClients = new Map(); - private onClientAdded = new Emitter(); + private channels = new Map>(); + private _connections = new Set>(); - private get clientKeys(): string[] { - const result: string[] = []; - this.channelClients.forEach((_, key) => result.push(key)); + private _onDidChangeConnections = new Emitter>(); + readonly onDidChangeConnections: Event> = this._onDidChangeConnections.event; + + get connections(): Connection[] { + const result: Connection[] = []; + this._connections.forEach(ctx => result.push(ctx)); return result; } @@ -490,46 +592,42 @@ export class IPCServer implements IChannelServer, IRoutingChannelClient, IDispos onDidClientConnect(({ protocol, onDidClientDisconnect }) => { const onFirstMessage = once(protocol.onMessage); - onFirstMessage(rawId => { - const channelServer = new ChannelServer(protocol); + onFirstMessage(msg => { + const reader = new BufferReader(msg); + const ctx = deserialize(reader) as TContext; + + const channelServer = new ChannelServer(protocol, ctx); const channelClient = new ChannelClient(protocol); this.channels.forEach((channel, name) => channelServer.registerChannel(name, channel)); - const id = rawId.toString(); - - if (this.channelClients.has(id)) { - console.warn(`IPC client with id '${id}' is already registered.`); - } - - this.channelClients.set(id, channelClient); - this.onClientAdded.fire(id); + const connection: Connection = { channelClient, ctx }; + this._connections.add(connection); + this._onDidChangeConnections.fire(connection); onDidClientDisconnect(() => { channelServer.dispose(); channelClient.dispose(); - this.channelClients.delete(id); + this._connections.delete(connection); }); }); }); } - getChannel(channelName: string, router: IClientRouter): T { + getChannel(channelName: string, router: IClientRouter): T { const that = this; return { call(command: string, arg?: any, cancellationToken?: CancellationToken) { - const channelPromise = router.routeCall(that.clientKeys, command, arg) - .then(id => that.getClient(id)) - .then(client => client.getChannel(channelName)); + const channelPromise = router.routeCall(that, command, arg) + .then(connection => (connection as Connection).channelClient.getChannel(channelName)); return getDelayedChannel(channelPromise) .call(command, arg, cancellationToken); }, listen(event: string, arg: any) { - const channelPromise = router.routeEvent(that.clientKeys, event, arg) - .then(id => that.getClient(id)) - .then(client => client.getChannel(channelName)); + const channelPromise = router.routeEvent(that, event, arg) + .then(connection => (connection as Connection).channelClient.getChannel(channelName)); return getDelayedChannel(channelPromise) .listen(event, arg); @@ -537,31 +635,14 @@ export class IPCServer implements IChannelServer, IRoutingChannelClient, IDispos } as T; } - registerChannel(channelName: string, channel: IChannel): void { + registerChannel(channelName: string, channel: IServerChannel): void { this.channels.set(channelName, channel); } - private getClient(clientId: string): Thenable { - if (!clientId) { - return Promise.reject(new Error('Client id should be provided')); - } - - const client = this.channelClients.get(clientId); - - if (client) { - return Promise.resolve(client); - } - - return new Promise(c => { - const onClient = once(filterEvent(this.onClientAdded.event, id => id === clientId)); - onClient(() => c(this.channelClients.get(clientId))); - }); - } - dispose(): void { this.channels.clear(); - this.channelClients.clear(); - this.onClientAdded.dispose(); + this._connections.clear(); + this._onDidChangeConnections.dispose(); } } @@ -572,22 +653,25 @@ export class IPCServer implements IChannelServer, IRoutingChannelClient, IDispos * and the `IPCClient` classes to get IPC implementations * for your protocol. */ -export class IPCClient implements IChannelClient, IChannelServer, IDisposable { +export class IPCClient implements IChannelClient, IChannelServer, IDisposable { private channelClient: ChannelClient; - private channelServer: ChannelServer; + private channelServer: ChannelServer; + + constructor(protocol: IMessagePassingProtocol, ctx: TContext) { + const writer = new BufferWriter(); + serialize(writer, ctx); + protocol.send(writer.buffer); - constructor(protocol: IMessagePassingProtocol, id: string) { - protocol.send(Buffer.from(id)); this.channelClient = new ChannelClient(protocol); - this.channelServer = new ChannelServer(protocol); + this.channelServer = new ChannelServer(protocol, ctx); } getChannel(channelName: string): T { return this.channelClient.getChannel(channelName) as T; } - registerChannel(channelName: string, channel: IChannel): void { + registerChannel(channelName: string, channel: IServerChannel): void { this.channelServer.registerChannel(channelName, channel); } @@ -638,4 +722,28 @@ export function getNextTickChannel(channel: T): T { return relay.event; } } as T; +} + +export class StaticRouter implements IClientRouter { + + constructor(private fn: (ctx: TContext) => boolean | Thenable) { } + + routeCall(hub: IConnectionHub): Thenable> { + return this.route(hub); + } + + routeEvent(hub: IConnectionHub): Thenable> { + return this.route(hub); + } + + private async route(hub: IConnectionHub): Promise> { + for (const connection of hub.connections) { + if (await Promise.resolve(this.fn(connection.ctx))) { + return Promise.resolve(connection); + } + } + + await toPromise(hub.onDidChangeConnections); + return await this.route(hub); + } } \ No newline at end of file diff --git a/src/vs/base/parts/ipc/test/node/ipc.cp.test.ts b/src/vs/base/parts/ipc/test/node/ipc.cp.test.ts index 6aa818040..9f5e08b10 100644 --- a/src/vs/base/parts/ipc/test/node/ipc.cp.test.ts +++ b/src/vs/base/parts/ipc/test/node/ipc.cp.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; import { always } from 'vs/base/common/async'; -import { ITestChannel, TestServiceClient } from './testService'; +import { TestServiceClient } from './testService'; import { getPathFromAmdModule } from 'vs/base/common/amd'; function createClient(): Client { @@ -19,7 +19,7 @@ function createClient(): Client { suite('IPC, Child Process', () => { test('createChannel', () => { const client = createClient(); - const channel = client.getChannel('test'); + const channel = client.getChannel('test'); const service = new TestServiceClient(channel); const result = service.pong('ping').then(r => { @@ -32,7 +32,7 @@ suite('IPC, Child Process', () => { test('events', () => { const client = createClient(); - const channel = client.getChannel('test'); + const channel = client.getChannel('test'); const service = new TestServiceClient(channel); const event = new Promise((c, e) => { @@ -54,7 +54,7 @@ suite('IPC, Child Process', () => { test('event dispose', () => { const client = createClient(); - const channel = client.getChannel('test'); + const channel = client.getChannel('test'); const service = new TestServiceClient(channel); let count = 0; diff --git a/src/vs/base/parts/ipc/test/node/ipc.test.ts b/src/vs/base/parts/ipc/test/node/ipc.test.ts index 6297a934b..03810849f 100644 --- a/src/vs/base/parts/ipc/test/node/ipc.test.ts +++ b/src/vs/base/parts/ipc/test/node/ipc.test.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IMessagePassingProtocol, IPCServer, ClientConnectionEvent, IPCClient, IChannel } from 'vs/base/parts/ipc/node/ipc'; -import { Emitter, toNativePromise, Event } from 'vs/base/common/event'; +import { IMessagePassingProtocol, IPCServer, ClientConnectionEvent, IPCClient, IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { Emitter, toPromise, Event } from 'vs/base/common/event'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { canceled } from 'vs/base/common/errors'; import { timeout } from 'vs/base/common/async'; @@ -54,7 +54,7 @@ function createProtocolPair(): [IMessagePassingProtocol, IMessagePassingProtocol return [one, other]; } -class TestIPCClient extends IPCClient { +class TestIPCClient extends IPCClient { private _onDidDisconnect = new Emitter(); readonly onDidDisconnect = this._onDidDisconnect.event; @@ -69,7 +69,7 @@ class TestIPCClient extends IPCClient { } } -class TestIPCServer extends IPCServer { +class TestIPCServer extends IPCServer { private onDidClientConnect: Emitter; @@ -79,7 +79,7 @@ class TestIPCServer extends IPCServer { this.onDidClientConnect = onDidClientConnect; } - createConnection(id: string): IPCClient { + createConnection(id: string): IPCClient { const [pc, ps] = createProtocolPair(); const client = new TestIPCClient(pc, id); @@ -99,6 +99,7 @@ interface ITestService { error(message: string): Thenable; neverComplete(): Thenable; neverCompleteCT(cancellationToken: CancellationToken): Thenable; + buffersLength(buffers: Buffer[]): Thenable; pong: Event; } @@ -128,37 +129,31 @@ class TestService implements ITestService { return new Promise((_, e) => cancellationToken.onCancellationRequested(() => e(canceled()))); } + buffersLength(buffers: Buffer[]): Thenable { + return Promise.resolve(buffers.reduce((r, b) => r + b.length, 0)); + } + ping(msg: string): void { this._pong.fire(msg); } } -interface ITestChannel extends IChannel { - call(command: 'marco'): Thenable; - call(command: 'error'): Thenable; - call(command: 'neverComplete'): Thenable; - call(command: 'neverCompleteCT', arg: undefined, cancellationToken: CancellationToken): Thenable; - call(command: string, arg?: any, cancellationToken?: CancellationToken): Thenable; - - listen(event: 'pong'): Event; - listen(event: string, arg?: any): Event; -} - -class TestChannel implements ITestChannel { +class TestChannel implements IServerChannel { constructor(private service: ITestService) { } - call(command: string, arg?: any, cancellationToken?: CancellationToken): Thenable { + call(_, command: string, arg?: any, cancellationToken?: CancellationToken): Thenable { switch (command) { case 'marco': return this.service.marco(); case 'error': return this.service.error(arg); case 'neverComplete': return this.service.neverComplete(); case 'neverCompleteCT': return this.service.neverCompleteCT(cancellationToken); + case 'buffersLength': return this.service.buffersLength(arg); default: return Promise.reject(new Error('not implemented')); } } - listen(event: string, arg?: any): Event { + listen(_, event: string, arg?: any): Event { switch (event) { case 'pong': return this.service.pong; default: throw new Error('not implemented'); @@ -172,7 +167,7 @@ class TestChannelClient implements ITestService { return this.channel.listen('pong'); } - constructor(private channel: ITestChannel) { } + constructor(private channel: IChannel) { } marco(): Thenable { return this.channel.call('marco'); @@ -189,6 +184,10 @@ class TestChannelClient implements ITestService { neverCompleteCT(cancellationToken: CancellationToken): Thenable { return this.channel.call('neverCompleteCT', undefined, cancellationToken); } + + buffersLength(buffers: Buffer[]): Thenable { + return this.channel.call('buffersLength', buffers); + } } suite('Base IPC', function () { @@ -202,8 +201,8 @@ suite('Base IPC', function () { const b3 = Buffer.alloc(0); serverProtocol.send(b3); - const b2 = await toNativePromise(serverProtocol.onMessage); - const b4 = await toNativePromise(clientProtocol.onMessage); + const b2 = await toPromise(serverProtocol.onMessage); + const b4 = await toPromise(clientProtocol.onMessage); assert.strictEqual(b1, b2); assert.strictEqual(b3, b4); @@ -294,5 +293,10 @@ suite('Base IPC', function () { assert.deepEqual(messages, ['hello', 'world']); }); + + test('buffers in arrays', async function () { + const r = await ipcService.buffersLength([Buffer.allocUnsafe(2), Buffer.allocUnsafe(3)]); + return assert.equal(r, 5); + }); }); }); diff --git a/src/vs/base/parts/ipc/test/node/testApp.ts b/src/vs/base/parts/ipc/test/node/testApp.ts index 0bdfaec73..905bf3cf9 100644 --- a/src/vs/base/parts/ipc/test/node/testApp.ts +++ b/src/vs/base/parts/ipc/test/node/testApp.ts @@ -6,6 +6,6 @@ import { Server } from 'vs/base/parts/ipc/node/ipc.cp'; import { TestChannel, TestService } from './testService'; -const server = new Server(); +const server = new Server('test'); const service = new TestService(); server.registerChannel('test', new TestChannel(service)); \ No newline at end of file diff --git a/src/vs/base/parts/ipc/test/node/testService.ts b/src/vs/base/parts/ipc/test/node/testService.ts index c673014e4..241402397 100644 --- a/src/vs/base/parts/ipc/test/node/testService.ts +++ b/src/vs/base/parts/ipc/test/node/testService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; import { Event, Emitter } from 'vs/base/common/event'; import { timeout } from 'vs/base/common/async'; @@ -37,21 +37,11 @@ export class TestService implements ITestService { } } -export interface ITestChannel extends IChannel { - listen(event: 'marco'): Event; - listen(event: string, arg?: any): Event; - - call(command: 'marco'): Thenable; - call(command: 'pong', ping: string): Thenable; - call(command: 'cancelMe'): Thenable; - call(command: string, ...args: any[]): Thenable; -} - -export class TestChannel implements ITestChannel { +export class TestChannel implements IServerChannel { constructor(private testService: ITestService) { } - listen(event: string, arg?: any): Event { + listen(_, event: string): Event { switch (event) { case 'marco': return this.testService.onMarco; } @@ -59,12 +49,12 @@ export class TestChannel implements ITestChannel { throw new Error('Event not found'); } - call(command: string, ...args: any[]): Thenable { + call(_, command: string, ...args: any[]): Thenable { switch (command) { case 'pong': return this.testService.pong(args[0]); case 'cancelMe': return this.testService.cancelMe(); case 'marco': return this.testService.marco(); - default: return Promise.reject(new Error('command not found')); + default: return Promise.reject(new Error(`command not found: ${command}`)); } } } @@ -73,7 +63,7 @@ export class TestServiceClient implements ITestService { get onMarco(): Event { return this.channel.listen('marco'); } - constructor(private channel: ITestChannel) { } + constructor(private channel: IChannel) { } marco(): Thenable { return this.channel.call('marco'); diff --git a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts index 4855968e0..0fb0bae29 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as types from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree'; @@ -289,16 +288,16 @@ class NoActionProvider implements IActionProvider { return false; } - getActions(tree: ITree, element: any): TPromise { - return TPromise.as(null); + getActions(tree: ITree, element: any): IAction[] { + return null; } hasSecondaryActions(tree: ITree, element: any): boolean { return false; } - getSecondaryActions(tree: ITree, element: any): TPromise { - return TPromise.as(null); + getSecondaryActions(tree: ITree, element: any): IAction[] { + return null; } getActionItem(tree: ITree, element: any, action: Action): IActionItem { @@ -377,7 +376,7 @@ class Renderer implements IRenderer { const detailContainer = document.createElement('div'); row2.appendChild(detailContainer); DOM.addClass(detailContainer, 'quick-open-entry-meta'); - const detail = new HighlightedLabel(detailContainer); + const detail = new HighlightedLabel(detailContainer, true); // Entry Group let group: HTMLDivElement; @@ -421,13 +420,12 @@ class Renderer implements IRenderer { data.actionBar.context = entry; // make sure the context is the current element - this.actionProvider.getActions(null, entry).then((actions) => { - if (data.actionBar.isEmpty() && actions && actions.length > 0) { - data.actionBar.push(actions, { icon: true, label: false }); - } else if (!data.actionBar.isEmpty() && (!actions || actions.length === 0)) { - data.actionBar.clear(); - } - }); + const actions = this.actionProvider.getActions(null, entry); + if (data.actionBar.isEmpty() && actions && actions.length > 0) { + data.actionBar.push(actions, { icon: true, label: false }); + } else if (!data.actionBar.isEmpty() && (!actions || actions.length === 0)) { + data.actionBar.clear(); + } // Entry group class if (entry instanceof QuickOpenEntryGroup && entry.getGroupLabel()) { @@ -620,4 +618,4 @@ export function compareEntries(elementA: QuickOpenEntry, elementB: QuickOpenEntr } return compareAnything(nameA, nameB, lookFor); -} \ No newline at end of file +} diff --git a/src/vs/base/parts/quickopen/common/quickOpenScorer.ts b/src/vs/base/parts/quickopen/common/quickOpenScorer.ts index ace8984e7..bb2efd7ad 100644 --- a/src/vs/base/parts/quickopen/common/quickOpenScorer.ts +++ b/src/vs/base/parts/quickopen/common/quickOpenScorer.ts @@ -304,20 +304,18 @@ export interface IPreparedQuery { * Helper function to prepare a search value for scoring in quick open by removing unwanted characters. */ export function prepareQuery(original: string): IPreparedQuery { - let lowercase: string; - let containsPathSeparator: boolean; - let value: string; - - if (original) { - value = stripWildcards(original).replace(/\s/g, ''); // get rid of all wildcards and whitespace - if (isWindows) { - value = value.replace(/\//g, nativeSep); // Help Windows users to search for paths when using slash - } + if (!original) { + original = ''; + } - lowercase = value.toLowerCase(); - containsPathSeparator = value.indexOf(nativeSep) >= 0; + let value = stripWildcards(original).replace(/\s/g, ''); // get rid of all wildcards and whitespace + if (isWindows) { + value = value.replace(/\//g, nativeSep); // Help Windows users to search for paths when using slash } + const lowercase = value.toLowerCase(); + const containsPathSeparator = value.indexOf(nativeSep) >= 0; + return { original, value, lowercase, containsPathSeparator }; } @@ -503,28 +501,25 @@ export function compareItemsByScore(itemA: T, itemB: T, query: IPreparedQuery } function computeLabelAndDescriptionMatchDistance(item: T, score: IItemScore, accessor: IItemAccessor): number { - const hasLabelMatches = (score.labelMatch && score.labelMatch.length); - const hasDescriptionMatches = (score.descriptionMatch && score.descriptionMatch.length); - let matchStart: number = -1; let matchEnd: number = -1; // If we have description matches, the start is first of description match - if (hasDescriptionMatches) { + if (score.descriptionMatch && score.descriptionMatch.length) { matchStart = score.descriptionMatch[0].start; } // Otherwise, the start is the first label match - else if (hasLabelMatches) { + else if (score.labelMatch && score.labelMatch.length) { matchStart = score.labelMatch[0].start; } // If we have label match, the end is the last label match // If we had a description match, we add the length of the description // as offset to the end to indicate this. - if (hasLabelMatches) { + if (score.labelMatch && score.labelMatch.length) { matchEnd = score.labelMatch[score.labelMatch.length - 1].end; - if (hasDescriptionMatches) { + if (score.descriptionMatch && score.descriptionMatch.length) { const itemDescription = accessor.getItemDescription(item); if (itemDescription) { matchEnd += itemDescription.length; @@ -533,7 +528,7 @@ function computeLabelAndDescriptionMatchDistance(item: T, score: IItemScore, } // If we have just a description match, the end is the last description match - else if (hasDescriptionMatches) { + else if (score.descriptionMatch && score.descriptionMatch.length) { matchEnd = score.descriptionMatch[score.descriptionMatch.length - 1].end; } @@ -541,7 +536,7 @@ function computeLabelAndDescriptionMatchDistance(item: T, score: IItemScore, } function compareByMatchLength(matchesA?: IMatch[], matchesB?: IMatch[]): number { - if ((!matchesA && !matchesB) || (!matchesA.length && !matchesB.length)) { + if ((!matchesA && !matchesB) || ((!matchesA || !matchesA.length) && (!matchesB || !matchesB.length))) { return 0; // make sure to not cause bad comparing when matches are not provided } diff --git a/src/vs/base/parts/tree/browser/tree.ts b/src/vs/base/parts/tree/browser/tree.ts index 3cb0b9323..307b19f95 100644 --- a/src/vs/base/parts/tree/browser/tree.ts +++ b/src/vs/base/parts/tree/browser/tree.ts @@ -731,7 +731,7 @@ export interface IActionProvider { /** * Returns a promise of an array with the actions of the element that should show up in place right to the element in the tree. */ - getActions(tree: ITree, element: any): WinJS.TPromise; + getActions(tree: ITree, element: any): IAction[]; /** * Returns whether or not the element has secondary actions. These show up once the user has expanded the element's action bar. @@ -741,7 +741,7 @@ export interface IActionProvider { /** * Returns a promise of an array with the secondary actions of the element that should show up once the user has expanded the element's action bar. */ - getSecondaryActions(tree: ITree, element: any): WinJS.TPromise; + getSecondaryActions(tree: ITree, element: any): IAction[]; /** * Returns an action item to render an action. diff --git a/src/vs/base/parts/tree/browser/treeModel.ts b/src/vs/base/parts/tree/browser/treeModel.ts index 9b4049178..ecfbe852b 100644 --- a/src/vs/base/parts/tree/browser/treeModel.ts +++ b/src/vs/base/parts/tree/browser/treeModel.ts @@ -8,7 +8,6 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; import * as arrays from 'vs/base/common/arrays'; import { INavigator } from 'vs/base/common/iterator'; -import * as WinJS from 'vs/base/common/winjs.base'; import * as _ from './tree'; import { Event, Emitter, once, EventMultiplexer, Relay } from 'vs/base/common/event'; @@ -79,20 +78,20 @@ export class Lock { return !!this.locks[item.id]; } - public run(item: Item, fn: () => WinJS.Promise): WinJS.Promise { + public run(item: Item, fn: () => Thenable): Thenable { var lock = this.getLock(item); if (lock) { - return new WinJS.TPromise((c, e) => { + return new Promise((c, e) => { once(lock.onDispose)(() => { return this.run(item, fn).then(c, e); }); }); } - var result: WinJS.Promise; + var result: Thenable; - return new WinJS.TPromise((c, e) => { + return new Promise((c, e) => { if (item.isDisposed()) { return e(new Error('Item is disposed.')); @@ -351,24 +350,24 @@ export class Item { this._onDidReveal.fire(eventData); } - public expand(): WinJS.Promise { + public expand(): Thenable { if (this.isExpanded() || !this.doesHaveChildren || this.lock.isLocked(this)) { - return WinJS.TPromise.as(false); + return Promise.resolve(false); } var result = this.lock.run(this, () => { if (this.isExpanded() || !this.doesHaveChildren) { - return WinJS.TPromise.as(false); + return Promise.resolve(false); } var eventData: IItemExpandEvent = { item: this }; - var result: WinJS.Promise; + var result: Thenable; this._onExpand.fire(eventData); if (this.needsChildrenRefresh) { result = this.refreshChildren(false, true, true); } else { - result = WinJS.TPromise.as(null); + result = Promise.resolve(null); } return result.then(() => { @@ -392,9 +391,9 @@ export class Item { }); } - public collapse(recursive: boolean = false): WinJS.Promise { + public collapse(recursive: boolean = false): Thenable { if (recursive) { - var collapseChildrenPromise = WinJS.TPromise.as(null); + var collapseChildrenPromise = Promise.resolve(null); this.forEachChild((child) => { collapseChildrenPromise = collapseChildrenPromise.then(() => child.collapse(true)); }); @@ -403,7 +402,7 @@ export class Item { }); } else { if (!this.isExpanded() || this.lock.isLocked(this)) { - return WinJS.TPromise.as(false); + return Promise.resolve(false); } return this.lock.run(this, () => { @@ -412,7 +411,7 @@ export class Item { this._setExpanded(false); this._onDidCollapse.fire(eventData); - return WinJS.TPromise.as(true); + return Promise.resolve(true); }); } } @@ -448,7 +447,7 @@ export class Item { return this.height; } - private refreshChildren(recursive: boolean, safe: boolean = false, force: boolean = false): WinJS.Promise { + private refreshChildren(recursive: boolean, safe: boolean = false, force: boolean = false): Thenable { if (!force && !this.isExpanded()) { const setNeedsChildrenRefresh = (item: Item) => { item.needsChildrenRefresh = true; @@ -457,7 +456,7 @@ export class Item { setNeedsChildrenRefresh(this); - return WinJS.TPromise.as(this); + return Promise.resolve(this); } this.needsChildrenRefresh = false; @@ -466,20 +465,20 @@ export class Item { var eventData: IItemChildrenRefreshEvent = { item: this, isNested: safe }; this._onRefreshChildren.fire(eventData); - var childrenPromise: WinJS.Promise; + var childrenPromise: Thenable; if (this.doesHaveChildren) { childrenPromise = this.context.dataSource.getChildren(this.context.tree, this.element); } else { - childrenPromise = WinJS.TPromise.as([]); + childrenPromise = Promise.resolve([]); } const result = childrenPromise.then((elements: any[]) => { if (this.isDisposed() || this.registry.isDisposed()) { - return WinJS.TPromise.as(null); + return Promise.resolve(null); } if (!Array.isArray(elements)) { - return WinJS.TPromise.wrapError(new Error('Please return an array of children.')); + return Promise.reject(new Error('Please return an array of children.')); } elements = !elements ? [] : elements.slice(0); @@ -510,16 +509,16 @@ export class Item { } if (recursive) { - return WinJS.Promise.join(this.mapEachChild((child) => { + return Promise.all(this.mapEachChild((child) => { return child.doRefresh(recursive, true); })); } else { - return WinJS.Promise.join(this.mapEachChild((child) => { + return Promise.all(this.mapEachChild((child) => { if (child.isExpanded() && child.needsChildrenRefresh) { return child.doRefresh(recursive, true); } else { child.updateVisibility(); - return WinJS.TPromise.as(null); + return Promise.resolve(null); } })); } @@ -533,7 +532,7 @@ export class Item { return safe ? doRefresh() : this.lock.run(this, doRefresh); } - private doRefresh(recursive: boolean, safe: boolean = false): WinJS.Promise { + private doRefresh(recursive: boolean, safe: boolean = false): Thenable { this.doesHaveChildren = this.context.dataSource.hasChildren(this.context.tree, this.element); this.height = this._getHeight(); this.updateVisibility(); @@ -547,7 +546,7 @@ export class Item { this.setVisible(this._isVisible()); } - public refresh(recursive: boolean): WinJS.Promise { + public refresh(recursive: boolean): Thenable { return this.doRefresh(recursive); } @@ -930,7 +929,7 @@ export class TreeModel { this.traitsToItems = {}; } - public setInput(element: any): WinJS.Promise { + public setInput(element: any): Thenable { var eventData: IInputEvent = { item: this.input }; this._onSetInput.fire(eventData); @@ -977,11 +976,11 @@ export class TreeModel { return this.input ? this.input.getElement() : null; } - public refresh(element: any = null, recursive: boolean = true): WinJS.Promise { + public refresh(element: any = null, recursive: boolean = true): Thenable { var item = this.getItem(element); if (!item) { - return WinJS.TPromise.as(null); + return Promise.resolve(null); } var eventData: IRefreshEvent = { item: item, recursive: recursive }; @@ -991,17 +990,17 @@ export class TreeModel { }); } - public expand(element: any): WinJS.Promise { + public expand(element: any): Thenable { var item = this.getItem(element); if (!item) { - return WinJS.TPromise.as(false); + return Promise.resolve(false); } return item.expand(); } - public expandAll(elements?: any[]): WinJS.Promise { + public expandAll(elements?: any[]): Thenable { if (!elements) { elements = []; @@ -1013,24 +1012,54 @@ export class TreeModel { } } + return this._expandAll(elements); + } + + private _expandAll(elements: any[]): Thenable { + if (elements.length === 0) { + return Promise.resolve(null); + } + + const elementsToExpand: any[] = []; + const elementsToDelay: any[] = []; + + for (const element of elements) { + var item = this.getItem(element); + + if (item) { + elementsToExpand.push(element); + } else { + elementsToDelay.push(element); + } + } + + if (elementsToExpand.length === 0) { + return Promise.resolve(null); + } + + return this.__expandAll(elementsToExpand) + .then(() => this._expandAll(elementsToDelay)); + } + + private __expandAll(elements: any[]): Thenable { var promises = []; for (var i = 0, len = elements.length; i < len; i++) { promises.push(this.expand(elements[i])); } - return WinJS.Promise.join(promises); + return Promise.all(promises); } - public collapse(element: any, recursive: boolean = false): WinJS.Promise { + public collapse(element: any, recursive: boolean = false): Thenable { var item = this.getItem(element); if (!item) { - return WinJS.TPromise.as(false); + return Promise.resolve(false); } return item.collapse(recursive); } - public collapseAll(elements: any[] | null = null, recursive: boolean = false): WinJS.Promise { + public collapseAll(elements: any[] | null = null, recursive: boolean = false): Thenable { if (!elements) { elements = [this.input]; recursive = true; @@ -1039,19 +1068,19 @@ export class TreeModel { for (var i = 0, len = elements.length; i < len; i++) { promises.push(this.collapse(elements[i], recursive)); } - return WinJS.Promise.join(promises); + return Promise.all(promises); } - public toggleExpansion(element: any, recursive: boolean = false): WinJS.Promise { + public toggleExpansion(element: any, recursive: boolean = false): Thenable { return this.isExpanded(element) ? this.collapse(element, recursive) : this.expand(element); } - public toggleExpansionAll(elements: any[]): WinJS.Promise { + public toggleExpansionAll(elements: any[]): Thenable { var promises = []; for (var i = 0, len = elements.length; i < len; i++) { promises.push(this.toggleExpansion(elements[i])); } - return WinJS.Promise.join(promises); + return Promise.all(promises); } public isExpanded(element: any): boolean { @@ -1078,9 +1107,9 @@ export class TreeModel { return result; } - public reveal(element: any, relativeTop: number | null = null): WinJS.Promise { + public reveal(element: any, relativeTop: number | null = null): Thenable { return this.resolveUnknownParentChain(element).then((chain: any[]) => { - var result = WinJS.TPromise.as(null); + var result = Promise.resolve(null); chain.forEach((e) => { result = result.then(() => this.expand(e)); @@ -1096,10 +1125,10 @@ export class TreeModel { }); } - private resolveUnknownParentChain(element: any): WinJS.Promise { + private resolveUnknownParentChain(element: any): Thenable { return this.context.dataSource.getParent(this.context.tree, element).then((parent) => { if (!parent) { - return WinJS.TPromise.as([]); + return Promise.resolve([]); } return this.resolveUnknownParentChain(parent).then((result) => { diff --git a/src/vs/base/parts/tree/browser/treeView.ts b/src/vs/base/parts/tree/browser/treeView.ts index 474316757..c85df8c07 100644 --- a/src/vs/base/parts/tree/browser/treeView.ts +++ b/src/vs/base/parts/tree/browser/treeView.ts @@ -849,6 +849,7 @@ export class TreeView extends HeightMap { } private set scrollHeight(scrollHeight: number) { + scrollHeight = scrollHeight + (this.horizontalScrolling ? 10 : 0); this.scrollableElement.setScrollDimensions({ scrollHeight }); } @@ -871,12 +872,9 @@ export class TreeView extends HeightMap { } public set scrollTop(scrollTop: number) { - this.scrollableElement.setScrollDimensions({ - scrollHeight: this.getContentHeight() - }); - this.scrollableElement.setScrollPosition({ - scrollTop: scrollTop - }); + const scrollHeight = this.getContentHeight() + (this.horizontalScrolling ? 10 : 0); + this.scrollableElement.setScrollDimensions({ scrollHeight }); + this.scrollableElement.setScrollPosition({ scrollTop }); } public getScrollPosition(): number { diff --git a/src/vs/base/parts/tree/test/browser/treeModel.test.ts b/src/vs/base/parts/tree/test/browser/treeModel.test.ts index 6a06ade83..6bdc99dfc 100644 --- a/src/vs/base/parts/tree/test/browser/treeModel.test.ts +++ b/src/vs/base/parts/tree/test/browser/treeModel.test.ts @@ -6,7 +6,6 @@ import * as assert from 'assert'; import * as lifecycle from 'vs/base/common/lifecycle'; import * as _ from 'vs/base/parts/tree/browser/tree'; -import * as WinJS from 'vs/base/common/winjs.base'; import * as model from 'vs/base/parts/tree/browser/treeModel'; import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults'; import { Event, Emitter } from 'vs/base/common/event'; @@ -170,11 +169,11 @@ class TestDataSource implements _.IDataSource { return !!element.children; } - public getChildren(tree, element): WinJS.Promise { - return WinJS.TPromise.as(element.children); + public getChildren(tree, element): Thenable { + return Promise.resolve(element.children); } - public getParent(tree, element): WinJS.Promise { + public getParent(tree, element): Thenable { throw new Error('Not implemented'); } } @@ -254,14 +253,14 @@ suite('TreeModel', () => { test('refresh(expanded element) refreshes the element and descendants', () => { return model.setInput(SAMPLE.AB).then(() => { - model.expand(SAMPLE.AB.children[0]); - - counter.listen(model.onRefresh); // 1 - counter.listen(model.onDidRefresh); // 1 - counter.listen(model.onDidRefreshItem); // 3 - counter.listen(model.onRefreshItemChildren); // 1 - counter.listen(model.onDidRefreshItemChildren); // 1 - return model.refresh(SAMPLE.AB.children[0]); + return model.expand(SAMPLE.AB.children[0]).then(() => { + counter.listen(model.onRefresh); // 1 + counter.listen(model.onDidRefresh); // 1 + counter.listen(model.onDidRefreshItem); // 3 + counter.listen(model.onRefreshItemChildren); // 1 + counter.listen(model.onDidRefreshItemChildren); // 1 + return model.refresh(SAMPLE.AB.children[0]); + }); }).then(() => { assert.equal(counter.count, 7); }); @@ -269,17 +268,17 @@ suite('TreeModel', () => { test('refresh(element, false) refreshes the element', () => { return model.setInput(SAMPLE.AB).then(() => { - model.expand(SAMPLE.AB.children[0]); - - counter.listen(model.onRefresh); // 1 - counter.listen(model.onDidRefresh); // 1 - counter.listen(model.onDidRefreshItem, item => { // 1 - assert.equal(item.id, 'a'); - counter.up(); + return model.expand(SAMPLE.AB.children[0]).then(() => { + counter.listen(model.onRefresh); // 1 + counter.listen(model.onDidRefresh); // 1 + counter.listen(model.onDidRefreshItem, item => { // 1 + assert.equal(item.id, 'a'); + counter.up(); + }); + counter.listen(model.onRefreshItemChildren); // 1 + counter.listen(model.onDidRefreshItemChildren); // 1 + return model.refresh(SAMPLE.AB.children[0], false); }); - counter.listen(model.onRefreshItemChildren); // 1 - counter.listen(model.onDidRefreshItemChildren); // 1 - return model.refresh(SAMPLE.AB.children[0], false); }).then(() => { assert.equal(counter.count, 6); }); @@ -680,11 +679,11 @@ suite('TreeModel - Expansion', () => { getId: (_, e) => e, hasChildren: (_, e) => true, getChildren: (_, e) => { - if (e === 'root') { return WinJS.TPromise.wrap(['a', 'b', 'c']); } - if (e === 'b') { return WinJS.TPromise.wrap(['b1']); } - return WinJS.TPromise.as([]); + if (e === 'root') { return Promise.resolve(['a', 'b', 'c']); } + if (e === 'b') { return Promise.resolve(['b1']); } + return Promise.resolve([]); }, - getParent: (_, e): WinJS.Promise => { throw new Error('not implemented'); }, + getParent: (_, e): Thenable => { throw new Error('not implemented'); }, shouldAutoexpand: (_, e) => e === 'b' } }); @@ -1080,7 +1079,7 @@ suite('TreeModel - Traits', () => { class DynamicModel implements _.IDataSource { private data: any; - public promiseFactory: { (): WinJS.Promise; }; + public promiseFactory: { (): Thenable; }; private _onGetChildren = new Emitter(); readonly onGetChildren: Event = this._onGetChildren.event; @@ -1125,16 +1124,16 @@ class DynamicModel implements _.IDataSource { return !!this.data[element]; } - public getChildren(tree, element): WinJS.Promise { + public getChildren(tree, element): Thenable { this._onGetChildren.fire(element); - var result = this.promiseFactory ? this.promiseFactory() : WinJS.TPromise.as(null); + var result = this.promiseFactory ? this.promiseFactory() : Promise.resolve(null); return result.then(() => { this._onDidGetChildren.fire(element); - return WinJS.TPromise.as(this.data[element]); + return Promise.resolve(this.data[element]); }); } - public getParent(tree, element): WinJS.Promise { + public getParent(tree, element): Thenable { throw new Error('Not implemented'); } } @@ -1273,27 +1272,28 @@ suite('TreeModel - Dynamic data model', () => { dataModel.addChild('father', 'son'); return model.setInput('root').then(() => { - model.expand('grandfather'); - model.collapse('father'); - - var times = 0; - var listener = dataModel.onGetChildren((element) => { - times++; - assert.equal(element, 'grandfather'); - }); + return model.expand('grandfather').then(() => { + return model.collapse('father').then(() => { + var times = 0; + var listener = dataModel.onGetChildren((element) => { + times++; + assert.equal(element, 'grandfather'); + }); - return model.refresh('grandfather').then(() => { - assert.equal(times, 1); - listener.dispose(); + return model.refresh('grandfather').then(() => { + assert.equal(times, 1); + listener.dispose(); - listener = dataModel.onGetChildren((element) => { - times++; - assert.equal(element, 'father'); - }); + listener = dataModel.onGetChildren((element) => { + times++; + assert.equal(element, 'father'); + }); - return model.expand('father').then(() => { - assert.equal(times, 2); - listener.dispose(); + return model.expand('father').then(() => { + assert.equal(times, 2); + listener.dispose(); + }); + }); }); }); }); @@ -1306,49 +1306,51 @@ suite('TreeModel - Dynamic data model', () => { dataModel.addChild('mother', 'daughter'); return model.setInput('root').then(() => { - model.expand('father'); - model.expand('mother'); - - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'father'); - assert.equal(nav.next().id, 'son'); - assert.equal(nav.next().id, 'mother'); - assert.equal(nav.next().id, 'daughter'); - assert.equal(nav.next() && false, null); + return model.expand('father').then(() => { + return model.expand('mother').then(() => { + + var nav = model.getNavigator(); + assert.equal(nav.next().id, 'father'); + assert.equal(nav.next().id, 'son'); + assert.equal(nav.next().id, 'mother'); + assert.equal(nav.next().id, 'daughter'); + assert.equal(nav.next() && false, null); - dataModel.removeChild('father', 'son'); - dataModel.removeChild('mother', 'daughter'); - dataModel.addChild('father', 'brother'); - dataModel.addChild('mother', 'sister'); + dataModel.removeChild('father', 'son'); + dataModel.removeChild('mother', 'daughter'); + dataModel.addChild('father', 'brother'); + dataModel.addChild('mother', 'sister'); - dataModel.promiseFactory = () => { return WinJS.TPromise.wrap(timeout(0)); }; + dataModel.promiseFactory = () => { return timeout(0); }; - var getTimes = 0; - var gotTimes = 0; - var getListener = dataModel.onGetChildren((element) => { getTimes++; }); - var gotListener = dataModel.onDidGetChildren((element) => { gotTimes++; }); + var getTimes = 0; + var gotTimes = 0; + var getListener = dataModel.onGetChildren((element) => { getTimes++; }); + var gotListener = dataModel.onDidGetChildren((element) => { gotTimes++; }); - var p1 = model.refresh('father'); - assert.equal(getTimes, 1); - assert.equal(gotTimes, 0); + var p1 = model.refresh('father'); + assert.equal(getTimes, 1); + assert.equal(gotTimes, 0); - var p2 = model.refresh('mother'); - assert.equal(getTimes, 2); - assert.equal(gotTimes, 0); + var p2 = model.refresh('mother'); + assert.equal(getTimes, 2); + assert.equal(gotTimes, 0); - return WinJS.Promise.join([p1, p2]).then(() => { - assert.equal(getTimes, 2); - assert.equal(gotTimes, 2); + return Promise.all([p1, p2]).then(() => { + assert.equal(getTimes, 2); + assert.equal(gotTimes, 2); - nav = model.getNavigator(); - assert.equal(nav.next().id, 'father'); - assert.equal(nav.next().id, 'brother'); - assert.equal(nav.next().id, 'mother'); - assert.equal(nav.next().id, 'sister'); - assert.equal(nav.next() && false, null); + nav = model.getNavigator(); + assert.equal(nav.next().id, 'father'); + assert.equal(nav.next().id, 'brother'); + assert.equal(nav.next().id, 'mother'); + assert.equal(nav.next().id, 'sister'); + assert.equal(nav.next() && false, null); - getListener.dispose(); - gotListener.dispose(); + getListener.dispose(); + gotListener.dispose(); + }); + }); }); }); }); @@ -1359,139 +1361,76 @@ suite('TreeModel - Dynamic data model', () => { dataModel.addChild('father', 'son'); return model.setInput('root').then(() => { - model.expand('grandfather'); - model.expand('father'); - - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'grandfather'); - assert.equal(nav.next().id, 'father'); - assert.equal(nav.next().id, 'son'); - assert.equal(nav.next() && false, null); - - var refreshTimes = 0; - counter.listen(model.onDidRefreshItem, (e) => { refreshTimes++; }); - - var getTimes = 0; - var getListener = dataModel.onGetChildren((element) => { getTimes++; }); - - var gotTimes = 0; - var gotListener = dataModel.onDidGetChildren((element) => { gotTimes++; }); - - var p1Completes = []; - dataModel.promiseFactory = () => { return new WinJS.TPromise((c) => { p1Completes.push(c); }); }; - - model.refresh('grandfather'); - - // just a single get - assert.equal(refreshTimes, 1); // (+1) grandfather - assert.equal(getTimes, 1); - assert.equal(gotTimes, 0); - - // unblock the first get - p1Completes.shift()(); - - // once the first get is unblocked, the second get should appear - assert.equal(refreshTimes, 2); // (+1) first father refresh - assert.equal(getTimes, 2); - assert.equal(gotTimes, 1); - - var p2Complete; - dataModel.promiseFactory = () => { return new WinJS.TPromise((c) => { p2Complete = c; }); }; - var p2 = model.refresh('father'); - - // same situation still - assert.equal(refreshTimes, 3); // (+1) second father refresh - assert.equal(getTimes, 2); - assert.equal(gotTimes, 1); - - // unblock the second get - p1Completes.shift()(); - - // the third get should have appeared, it should've been waiting for the second one - assert.equal(refreshTimes, 4); // (+1) first son request - assert.equal(getTimes, 3); - assert.equal(gotTimes, 2); - - p2Complete(); - - // all good - assert.equal(refreshTimes, 5); // (+1) second son request - assert.equal(getTimes, 3); - assert.equal(gotTimes, 3); - - return p2.then(() => { - nav = model.getNavigator(); - assert.equal(nav.next().id, 'grandfather'); - assert.equal(nav.next().id, 'father'); - assert.equal(nav.next().id, 'son'); - assert.equal(nav.next() && false, null); - - getListener.dispose(); - gotListener.dispose(); - }); - }); - }); + return model.expand('grandfather').then(() => { + return model.expand('father').then(() => { + var nav = model.getNavigator(); + assert.equal(nav.next().id, 'grandfather'); + assert.equal(nav.next().id, 'father'); + assert.equal(nav.next().id, 'son'); + assert.equal(nav.next() && false, null); - test('simultaneously recursively refreshing two intersecting elements should concatenate the refreshes - ancestor second', () => { - dataModel.addChild('root', 'grandfather'); - dataModel.addChild('grandfather', 'father'); - dataModel.addChild('father', 'son'); + var refreshTimes = 0; + counter.listen(model.onDidRefreshItem, (e) => { refreshTimes++; }); - return model.setInput('root').then(() => { - model.expand('grandfather'); - model.expand('father'); + var getTimes = 0; + var getListener = dataModel.onGetChildren((element) => { getTimes++; }); - var nav = model.getNavigator(); - assert.equal(nav.next().id, 'grandfather'); - assert.equal(nav.next().id, 'father'); - assert.equal(nav.next().id, 'son'); - assert.equal(nav.next() && false, null); - - var getTimes = 0; - var gotTimes = 0; - var getListener = dataModel.onGetChildren((element) => { getTimes++; }); - var gotListener = dataModel.onDidGetChildren((element) => { gotTimes++; }); - var p2; + var gotTimes = 0; + var gotListener = dataModel.onDidGetChildren((element) => { gotTimes++; }); - var p1Complete; - dataModel.promiseFactory = () => { return new WinJS.TPromise((c) => { p1Complete = c; }); }; + var p1Completes = []; + dataModel.promiseFactory = () => { return new Promise((c) => { p1Completes.push(c); }); }; - model.refresh('father'); + model.refresh('grandfather').then(() => { + // just a single get + assert.equal(refreshTimes, 1); // (+1) grandfather + assert.equal(getTimes, 1); + assert.equal(gotTimes, 0); - assert.equal(getTimes, 1); - assert.equal(gotTimes, 0); + // unblock the first get + p1Completes.shift()(); - var p2Completes = []; - dataModel.promiseFactory = () => { return new WinJS.TPromise((c) => { p2Completes.push(c); }); }; - p2 = model.refresh('grandfather'); + // once the first get is unblocked, the second get should appear + assert.equal(refreshTimes, 2); // (+1) first father refresh + assert.equal(getTimes, 2); + assert.equal(gotTimes, 1); - assert.equal(getTimes, 1); - assert.equal(gotTimes, 0); + var p2Complete; + dataModel.promiseFactory = () => { return new Promise((c) => { p2Complete = c; }); }; + var p2 = model.refresh('father'); - p1Complete(); + // same situation still + assert.equal(refreshTimes, 3); // (+1) second father refresh + assert.equal(getTimes, 2); + assert.equal(gotTimes, 1); - assert.equal(getTimes, 2); - assert.equal(gotTimes, 1); + // unblock the second get + p1Completes.shift()(); - p2Completes.shift()(); + // the third get should have appeared, it should've been waiting for the second one + assert.equal(refreshTimes, 4); // (+1) first son request + assert.equal(getTimes, 3); + assert.equal(gotTimes, 2); - assert.equal(getTimes, 3); - assert.equal(gotTimes, 2); + p2Complete(); - p2Completes.shift()(); + // all good + assert.equal(refreshTimes, 5); // (+1) second son request + assert.equal(getTimes, 3); + assert.equal(gotTimes, 3); - assert.equal(getTimes, 3); - assert.equal(gotTimes, 3); + return p2.then(() => { + nav = model.getNavigator(); + assert.equal(nav.next().id, 'grandfather'); + assert.equal(nav.next().id, 'father'); + assert.equal(nav.next().id, 'son'); + assert.equal(nav.next() && false, null); - return p2.then(() => { - nav = model.getNavigator(); - assert.equal(nav.next().id, 'grandfather'); - assert.equal(nav.next().id, 'father'); - assert.equal(nav.next().id, 'son'); - assert.equal(nav.next() && false, null); - - getListener.dispose(); - gotListener.dispose(); + getListener.dispose(); + gotListener.dispose(); + }); + }); + }); }); }); }); @@ -1501,15 +1440,16 @@ suite('TreeModel - Dynamic data model', () => { dataModel.addChild('grandfather', 'father'); return model.setInput('root').then(() => { - model.expand('grandfather'); - model.expand('father'); - - assert(!model.isExpanded('father')); + return model.expand('grandfather').then(() => { + return model.expand('father').then(() => { + assert(!model.isExpanded('father')); - dataModel.addChild('father', 'son'); + dataModel.addChild('father', 'son'); - return model.refresh('father').then(() => { - assert(!model.isExpanded('father')); + return model.refresh('father').then(() => { + assert(!model.isExpanded('father')); + }); + }); }); }); }); @@ -1520,16 +1460,18 @@ suite('TreeModel - Dynamic data model', () => { dataModel.addChild('father', 'son'); return model.setInput('root').then(() => { - model.expand('grandfather'); - model.expand('father'); - model.collapse('father'); - - assert(!model.isExpanded('father')); + return model.expand('grandfather').then(() => { + return model.expand('father').then(() => { + return model.collapse('father').then(() => { + assert(!model.isExpanded('father')); - dataModel.addChild('father', 'daughter'); + dataModel.addChild('father', 'daughter'); - return model.refresh('father').then(() => { - assert(!model.isExpanded('father')); + return model.refresh('father').then(() => { + assert(!model.isExpanded('father')); + }); + }); + }); }); }); }); @@ -1540,15 +1482,16 @@ suite('TreeModel - Dynamic data model', () => { dataModel.addChild('father', 'son'); return model.setInput('root').then(() => { - model.expand('grandfather'); - model.expand('father'); - - assert(model.isExpanded('grandfather')); - assert(model.isExpanded('father')); + return model.expand('grandfather').then(() => { + return model.expand('father').then(() => { + assert(model.isExpanded('grandfather')); + assert(model.isExpanded('father')); - return model.refresh('grandfather').then(() => { - assert(model.isExpanded('grandfather')); - assert(model.isExpanded('father')); + return model.refresh('grandfather').then(() => { + assert(model.isExpanded('grandfather')); + assert(model.isExpanded('father')); + }); + }); }); }); }); @@ -1559,16 +1502,18 @@ suite('TreeModel - Dynamic data model', () => { dataModel.addChild('father', 'son'); return model.setInput('root').then(() => { - model.expand('grandfather'); - model.expand('father'); - model.collapse('father'); - - assert(model.isExpanded('grandfather')); - assert(!model.isExpanded('father')); + return model.expand('grandfather').then(() => { + return model.expand('father').then(() => { + return model.collapse('father').then(() => { + assert(model.isExpanded('grandfather')); + assert(!model.isExpanded('father')); - return model.refresh('grandfather').then(() => { - assert(model.isExpanded('grandfather')); - assert(!model.isExpanded('father')); + return model.refresh('grandfather').then(() => { + assert(model.isExpanded('grandfather')); + assert(!model.isExpanded('father')); + }); + }); + }); }); }); }); @@ -1582,9 +1527,9 @@ suite('TreeModel - Dynamic data model', () => { return model.setInput('root').then(() => { // delay expansions and refreshes - dataModel.promiseFactory = () => { return WinJS.TPromise.wrap(timeout(0)); }; + dataModel.promiseFactory = () => { return timeout(0); }; - var promises: WinJS.Promise[] = []; + var promises: Thenable[] = []; promises.push(model.expand('father')); dataModel.removeChild('root', 'father'); @@ -1594,7 +1539,7 @@ suite('TreeModel - Dynamic data model', () => { dataModel.removeChild('root', 'mother'); promises.push(model.refresh('root')); - return WinJS.Promise.join(promises).then(() => { + return Promise.all(promises).then(() => { assert(true, 'all good'); }, (errs) => { assert(false, 'should not fail'); @@ -1626,18 +1571,18 @@ suite('TreeModel - bugs', () => { getChildren: (_, e) => { if (e === 'root') { return getRootChildren(); } if (e === 'bart') { return getBartChildren(); } - return WinJS.TPromise.as([]); + return Promise.resolve([]); }, - getParent: (_, e): WinJS.Promise => { throw new Error('not implemented'); }, + getParent: (_, e): Thenable => { throw new Error('not implemented'); }, } }); let listeners = []; // helpers - var getGetRootChildren = (children: string[], millis = 0) => () => WinJS.TPromise.wrap(timeout(millis)).then(() => children); + var getGetRootChildren = (children: string[], millis = 0) => () => timeout(millis).then(() => children); var getRootChildren = getGetRootChildren(['homer', 'bart', 'lisa', 'marge', 'maggie'], 0); - var getGetBartChildren = (millis = 0) => () => WinJS.TPromise.wrap(timeout(millis)).then(() => ['milhouse', 'nelson']); + var getGetBartChildren = (millis = 0) => () => timeout(millis).then(() => ['milhouse', 'nelson']); var getBartChildren = getGetBartChildren(0); // item expanding should not exist! @@ -1664,7 +1609,7 @@ suite('TreeModel - bugs', () => { }); // what now? - return WinJS.Promise.join([p1, p2]); + return Promise.all([p1, p2]); }).then(() => { diff --git a/src/vs/base/test/browser/highlightedLabel.test.ts b/src/vs/base/test/browser/highlightedLabel.test.ts index 72cbac613..30105b727 100644 --- a/src/vs/base/test/browser/highlightedLabel.test.ts +++ b/src/vs/base/test/browser/highlightedLabel.test.ts @@ -9,7 +9,7 @@ suite('HighlightedLabel', () => { let label: HighlightedLabel; setup(() => { - label = new HighlightedLabel(document.createElement('div')); + label = new HighlightedLabel(document.createElement('div'), true); }); teardown(() => { diff --git a/src/vs/base/test/browser/ui/list/rangeMap.test.ts b/src/vs/base/test/browser/ui/list/rangeMap.test.ts index 8d4ae2542..3e1aa1069 100644 --- a/src/vs/base/test/browser/ui/list/rangeMap.test.ts +++ b/src/vs/base/test/browser/ui/list/rangeMap.test.ts @@ -156,52 +156,52 @@ suite('RangeMap', () => { const ten = { size: 10 }; test('length & count', () => { - rangeMap.splice(0, 0, one); + rangeMap.splice(0, 0, [one]); assert.equal(rangeMap.size, 1); assert.equal(rangeMap.count, 1); }); test('length & count #2', () => { - rangeMap.splice(0, 0, one, one, one, one, one); + rangeMap.splice(0, 0, [one, one, one, one, one]); assert.equal(rangeMap.size, 5); assert.equal(rangeMap.count, 5); }); test('length & count #3', () => { - rangeMap.splice(0, 0, five); + rangeMap.splice(0, 0, [five]); assert.equal(rangeMap.size, 5); assert.equal(rangeMap.count, 1); }); test('length & count #4', () => { - rangeMap.splice(0, 0, five, five, five, five, five); + rangeMap.splice(0, 0, [five, five, five, five, five]); assert.equal(rangeMap.size, 25); assert.equal(rangeMap.count, 5); }); test('insert', () => { - rangeMap.splice(0, 0, five, five, five, five, five); + rangeMap.splice(0, 0, [five, five, five, five, five]); assert.equal(rangeMap.size, 25); assert.equal(rangeMap.count, 5); - rangeMap.splice(0, 0, five, five, five, five, five); + rangeMap.splice(0, 0, [five, five, five, five, five]); assert.equal(rangeMap.size, 50); assert.equal(rangeMap.count, 10); - rangeMap.splice(5, 0, ten, ten); + rangeMap.splice(5, 0, [ten, ten]); assert.equal(rangeMap.size, 70); assert.equal(rangeMap.count, 12); - rangeMap.splice(12, 0, { size: 200 }); + rangeMap.splice(12, 0, [{ size: 200 }]); assert.equal(rangeMap.size, 270); assert.equal(rangeMap.count, 13); }); test('delete', () => { - rangeMap.splice(0, 0, five, five, five, five, five, + rangeMap.splice(0, 0, [five, five, five, five, five, five, five, five, five, five, five, five, five, five, five, - five, five, five, five, five); + five, five, five, five, five]); assert.equal(rangeMap.size, 100); assert.equal(rangeMap.count, 20); @@ -226,7 +226,7 @@ suite('RangeMap', () => { assert.equal(rangeMap.size, 0); assert.equal(rangeMap.count, 0); - rangeMap.splice(0, 0, one); + rangeMap.splice(0, 0, [one]); assert.equal(rangeMap.size, 1); assert.equal(rangeMap.count, 1); @@ -236,29 +236,29 @@ suite('RangeMap', () => { }); test('insert & delete #2', () => { - rangeMap.splice(0, 0, one, one, one, one, one, - one, one, one, one, one); + rangeMap.splice(0, 0, [one, one, one, one, one, + one, one, one, one, one]); rangeMap.splice(2, 6); assert.equal(rangeMap.count, 4); assert.equal(rangeMap.size, 4); }); test('insert & delete #3', () => { - rangeMap.splice(0, 0, one, one, one, one, one, + rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one, two, two, two, two, two, - two, two, two, two, two); + two, two, two, two, two]); rangeMap.splice(8, 4); assert.equal(rangeMap.count, 16); assert.equal(rangeMap.size, 24); }); test('insert & delete #3', () => { - rangeMap.splice(0, 0, one, one, one, one, one, + rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one, two, two, two, two, two, - two, two, two, two, two); - rangeMap.splice(5, 0, three, three, three, three, three); + two, two, two, two, two]); + rangeMap.splice(5, 0, [three, three, three, three, three]); assert.equal(rangeMap.count, 25); assert.equal(rangeMap.size, 45); @@ -278,7 +278,7 @@ suite('RangeMap', () => { }); test('simple', () => { - rangeMap.splice(0, 0, one); + rangeMap.splice(0, 0, [one]); assert.equal(rangeMap.indexAt(0), 0); assert.equal(rangeMap.indexAt(1), 1); assert.equal(rangeMap.positionAt(0), 0); @@ -286,7 +286,7 @@ suite('RangeMap', () => { }); test('simple #2', () => { - rangeMap.splice(0, 0, ten); + rangeMap.splice(0, 0, [ten]); assert.equal(rangeMap.indexAt(0), 0); assert.equal(rangeMap.indexAt(5), 0); assert.equal(rangeMap.indexAt(9), 0); @@ -296,7 +296,7 @@ suite('RangeMap', () => { }); test('insert', () => { - rangeMap.splice(0, 0, one, one, one, one, one, one, one, one, one, one); + rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); assert.equal(rangeMap.indexAt(0), 0); assert.equal(rangeMap.indexAt(1), 1); assert.equal(rangeMap.indexAt(5), 5); @@ -304,7 +304,7 @@ suite('RangeMap', () => { assert.equal(rangeMap.indexAt(10), 10); assert.equal(rangeMap.indexAt(11), 10); - rangeMap.splice(10, 0, one, one, one, one, one, one, one, one, one, one); + rangeMap.splice(10, 0, [one, one, one, one, one, one, one, one, one, one]); assert.equal(rangeMap.indexAt(10), 10); assert.equal(rangeMap.indexAt(19), 19); assert.equal(rangeMap.indexAt(20), 20); @@ -316,7 +316,7 @@ suite('RangeMap', () => { }); test('delete', () => { - rangeMap.splice(0, 0, one, one, one, one, one, one, one, one, one, one); + rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]); rangeMap.splice(2, 6); assert.equal(rangeMap.indexAt(0), 0); @@ -331,7 +331,7 @@ suite('RangeMap', () => { }); test('delete #2', () => { - rangeMap.splice(0, 0, ten, ten, ten, ten, ten, ten, ten, ten, ten, ten); + rangeMap.splice(0, 0, [ten, ten, ten, ten, ten, ten, ten, ten, ten, ten]); rangeMap.splice(2, 6); assert.equal(rangeMap.indexAt(0), 0); diff --git a/src/vs/base/test/browser/ui/splitview/splitview.test.ts b/src/vs/base/test/browser/ui/splitview/splitview.test.ts index 9d7c4f345..185c9f25a 100644 --- a/src/vs/base/test/browser/ui/splitview/splitview.test.ts +++ b/src/vs/base/test/browser/ui/splitview/splitview.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { Emitter } from 'vs/base/common/event'; -import { SplitView, IView, Orientation, Sizing } from 'vs/base/browser/ui/splitview/splitview'; +import { SplitView, IView, Orientation, Sizing, LayoutPriority } from 'vs/base/browser/ui/splitview/splitview'; import { Sash, SashState } from 'vs/base/browser/ui/sash/sash'; class TestView implements IView { @@ -35,7 +35,8 @@ class TestView implements IView { constructor( private _minimumSize: number, - private _maximumSize: number + private _maximumSize: number, + readonly priority: LayoutPriority = LayoutPriority.Normal ) { assert(_minimumSize <= _maximumSize, 'splitview view minimum size must be <= maximum size'); } @@ -430,4 +431,100 @@ suite('Splitview', () => { view2.dispose(); view1.dispose(); }); + + test('proportional layout', () => { + const view1 = new TestView(20, Number.POSITIVE_INFINITY); + const view2 = new TestView(20, Number.POSITIVE_INFINITY); + const splitview = new SplitView(container); + splitview.layout(200); + + splitview.addView(view1, Sizing.Distribute); + splitview.addView(view2, Sizing.Distribute); + assert.deepEqual([view1.size, view2.size], [100, 100]); + + splitview.layout(100); + assert.deepEqual([view1.size, view2.size], [50, 50]); + + splitview.dispose(); + view2.dispose(); + view1.dispose(); + }); + + test('disable proportional layout', () => { + const view1 = new TestView(20, Number.POSITIVE_INFINITY); + const view2 = new TestView(20, Number.POSITIVE_INFINITY); + const splitview = new SplitView(container, { proportionalLayout: false }); + splitview.layout(200); + + splitview.addView(view1, Sizing.Distribute); + splitview.addView(view2, Sizing.Distribute); + assert.deepEqual([view1.size, view2.size], [100, 100]); + + splitview.layout(100); + assert.deepEqual([view1.size, view2.size], [80, 20]); + + splitview.dispose(); + view2.dispose(); + view1.dispose(); + }); + + test('high layout priority', () => { + const view1 = new TestView(20, Number.POSITIVE_INFINITY); + const view2 = new TestView(20, Number.POSITIVE_INFINITY, LayoutPriority.High); + const view3 = new TestView(20, Number.POSITIVE_INFINITY); + const splitview = new SplitView(container, { proportionalLayout: false }); + splitview.layout(200); + + splitview.addView(view1, Sizing.Distribute); + splitview.addView(view2, Sizing.Distribute); + splitview.addView(view3, Sizing.Distribute); + assert.deepEqual([view1.size, view2.size, view3.size], [66, 66, 68]); + + splitview.layout(180); + assert.deepEqual([view1.size, view2.size, view3.size], [66, 46, 68]); + + splitview.layout(124); + assert.deepEqual([view1.size, view2.size, view3.size], [66, 20, 38]); + + splitview.layout(60); + assert.deepEqual([view1.size, view2.size, view3.size], [20, 20, 20]); + + splitview.layout(200); + assert.deepEqual([view1.size, view2.size, view3.size], [20, 160, 20]); + + splitview.dispose(); + view3.dispose(); + view2.dispose(); + view1.dispose(); + }); + + test('low layout priority', () => { + const view1 = new TestView(20, Number.POSITIVE_INFINITY); + const view2 = new TestView(20, Number.POSITIVE_INFINITY); + const view3 = new TestView(20, Number.POSITIVE_INFINITY, LayoutPriority.Low); + const splitview = new SplitView(container, { proportionalLayout: false }); + splitview.layout(200); + + splitview.addView(view1, Sizing.Distribute); + splitview.addView(view2, Sizing.Distribute); + splitview.addView(view3, Sizing.Distribute); + assert.deepEqual([view1.size, view2.size, view3.size], [66, 66, 68]); + + splitview.layout(180); + assert.deepEqual([view1.size, view2.size, view3.size], [66, 46, 68]); + + splitview.layout(132); + assert.deepEqual([view1.size, view2.size, view3.size], [44, 20, 68]); + + splitview.layout(60); + assert.deepEqual([view1.size, view2.size, view3.size], [20, 20, 20]); + + splitview.layout(200); + assert.deepEqual([view1.size, view2.size, view3.size], [20, 160, 20]); + + splitview.dispose(); + view3.dispose(); + view2.dispose(); + view1.dispose(); + }); }); \ No newline at end of file diff --git a/src/vs/base/test/common/async.test.ts b/src/vs/base/test/common/async.test.ts index 23cb2ec8e..f5c3af9ba 100644 --- a/src/vs/base/test/common/async.test.ts +++ b/src/vs/base/test/common/async.test.ts @@ -2,11 +2,11 @@ * 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 * as async from 'vs/base/common/async'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; suite('Async', () => { @@ -151,34 +151,34 @@ suite('Async', () => { test('Throttler - non async', function () { let count = 0; let factory = () => { - return TPromise.as(++count); + return Promise.resolve(++count); }; let throttler = new async.Throttler(); - return TPromise.join([ + return Promise.all([ throttler.queue(factory).then((result) => { assert.equal(result, 1); }), throttler.queue(factory).then((result) => { assert.equal(result, 2); }), - throttler.queue(factory).then((result) => { assert.equal(result, 3); }), - throttler.queue(factory).then((result) => { assert.equal(result, 4); }), - throttler.queue(factory).then((result) => { assert.equal(result, 5); }) - ]); + throttler.queue(factory).then((result) => { assert.equal(result, 2); }), + throttler.queue(factory).then((result) => { assert.equal(result, 2); }), + throttler.queue(factory).then((result) => { assert.equal(result, 2); }) + ]).then(() => assert.equal(count, 2)); }); test('Throttler', () => { let count = 0; - let factory = () => TPromise.wrap(async.timeout(0)).then(() => ++count); + let factory = () => async.timeout(0).then(() => ++count); let throttler = new async.Throttler(); - return TPromise.join([ + return Promise.all([ throttler.queue(factory).then((result) => { assert.equal(result, 1); }), throttler.queue(factory).then((result) => { assert.equal(result, 2); }), throttler.queue(factory).then((result) => { assert.equal(result, 2); }), throttler.queue(factory).then((result) => { assert.equal(result, 2); }), throttler.queue(factory).then((result) => { assert.equal(result, 2); }) ]).then(() => { - TPromise.join([ + return Promise.all([ throttler.queue(factory).then((result) => { assert.equal(result, 3); }), throttler.queue(factory).then((result) => { assert.equal(result, 4); }), throttler.queue(factory).then((result) => { assert.equal(result, 4); }), @@ -190,28 +190,28 @@ suite('Async', () => { test('Throttler - last factory should be the one getting called', function () { let factoryFactory = (n: number) => () => { - return TPromise.wrap(async.timeout(0)).then(() => n); + return async.timeout(0).then(() => n); }; let throttler = new async.Throttler(); - let promises: TPromise[] = []; + let promises: Thenable[] = []; promises.push(throttler.queue(factoryFactory(1)).then((n) => { assert.equal(n, 1); })); promises.push(throttler.queue(factoryFactory(2)).then((n) => { assert.equal(n, 3); })); promises.push(throttler.queue(factoryFactory(3)).then((n) => { assert.equal(n, 3); })); - return TPromise.join(promises); + return Promise.all(promises); }); test('Delayer', () => { let count = 0; let factory = () => { - return TPromise.as(++count); + return Promise.resolve(++count); }; let delayer = new async.Delayer(0); - let promises: TPromise[] = []; + let promises: Thenable[] = []; assert(!delayer.isTriggered()); @@ -224,7 +224,7 @@ suite('Async', () => { promises.push(delayer.trigger(factory).then((result) => { assert.equal(result, 1); assert(!delayer.isTriggered()); })); assert(delayer.isTriggered()); - return TPromise.join(promises).then(() => { + return Promise.all(promises).then(() => { assert(!delayer.isTriggered()); }); }); @@ -232,7 +232,7 @@ suite('Async', () => { test('Delayer - simple cancel', function () { let count = 0; let factory = () => { - return TPromise.as(++count); + return Promise.resolve(++count); }; let delayer = new async.Delayer(0); @@ -255,11 +255,11 @@ suite('Async', () => { test('Delayer - cancel should cancel all calls to trigger', function () { let count = 0; let factory = () => { - return TPromise.as(++count); + return Promise.resolve(++count); }; let delayer = new async.Delayer(0); - let promises: TPromise[] = []; + let promises: Thenable[] = []; assert(!delayer.isTriggered()); @@ -274,7 +274,7 @@ suite('Async', () => { delayer.cancel(); - return TPromise.join(promises).then(() => { + return Promise.all(promises).then(() => { assert(!delayer.isTriggered()); }); }); @@ -282,11 +282,11 @@ suite('Async', () => { test('Delayer - trigger, cancel, then trigger again', function () { let count = 0; let factory = () => { - return TPromise.as(++count); + return Promise.resolve(++count); }; let delayer = new async.Delayer(0); - let promises: TPromise[] = []; + let promises: Thenable[] = []; assert(!delayer.isTriggered()); @@ -302,7 +302,7 @@ suite('Async', () => { delayer.cancel(); - const p = TPromise.join(promises).then(() => { + const p = Promise.all(promises).then(() => { promises = []; assert(!delayer.isTriggered()); @@ -313,7 +313,7 @@ suite('Async', () => { promises.push(delayer.trigger(factory).then(() => { assert.equal(result, 1); assert(!delayer.isTriggered()); })); assert(delayer.isTriggered()); - const p = TPromise.join(promises).then(() => { + const p = Promise.all(promises).then(() => { assert(!delayer.isTriggered()); }); @@ -322,8 +322,6 @@ suite('Async', () => { return p; }); - assert(delayer.isTriggered()); - return p; }); @@ -334,11 +332,11 @@ suite('Async', () => { test('Delayer - last task should be the one getting called', function () { let factoryFactory = (n: number) => () => { - return TPromise.as(n); + return Promise.resolve(n); }; let delayer = new async.Delayer(0); - let promises: TPromise[] = []; + let promises: Thenable[] = []; assert(!delayer.isTriggered()); @@ -346,7 +344,7 @@ suite('Async', () => { promises.push(delayer.trigger(factoryFactory(2)).then((n) => { assert.equal(n, 3); })); promises.push(delayer.trigger(factoryFactory(3)).then((n) => { assert.equal(n, 3); })); - const p = TPromise.join(promises).then(() => { + const p = Promise.all(promises).then(() => { assert(!delayer.isTriggered()); }); @@ -357,7 +355,7 @@ suite('Async', () => { test('Sequence', () => { let factoryFactory = (n: number) => () => { - return TPromise.as(n); + return Promise.resolve(n); }; return async.sequence([ @@ -378,15 +376,15 @@ suite('Async', () => { test('Limiter - sync', function () { let factoryFactory = (n: number) => () => { - return TPromise.as(n); + return Promise.resolve(n); }; let limiter = new async.Limiter(1); - let promises: TPromise[] = []; + let promises: Thenable[] = []; [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n)))); - return TPromise.join(promises).then((res) => { + return Promise.all(promises).then((res) => { assert.equal(10, res.length); limiter = new async.Limiter(100); @@ -394,20 +392,20 @@ suite('Async', () => { promises = []; [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n)))); - return TPromise.join(promises).then((res) => { + return Promise.all(promises).then((res) => { assert.equal(10, res.length); }); }); }); test('Limiter - async', function () { - let factoryFactory = (n: number) => () => TPromise.wrap(async.timeout(0)).then(() => n); + let factoryFactory = (n: number) => () => async.timeout(0).then(() => n); let limiter = new async.Limiter(1); - let promises: TPromise[] = []; + let promises: Thenable[] = []; [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n)))); - return TPromise.join(promises).then((res) => { + return Promise.all(promises).then((res) => { assert.equal(10, res.length); limiter = new async.Limiter(100); @@ -415,7 +413,7 @@ suite('Async', () => { promises = []; [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n)))); - return TPromise.join(promises).then((res) => { + return Promise.all(promises).then((res) => { assert.equal(10, res.length); }); }); @@ -426,15 +424,15 @@ suite('Async', () => { let factoryFactory = (n: number) => () => { activePromises++; assert(activePromises < 6); - return TPromise.wrap(async.timeout(0)).then(() => { activePromises--; return n; }); + return async.timeout(0).then(() => { activePromises--; return n; }); }; let limiter = new async.Limiter(5); - let promises: TPromise[] = []; + let promises: Thenable[] = []; [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n)))); - return TPromise.join(promises).then((res) => { + return Promise.all(promises).then((res) => { assert.equal(10, res.length); assert.deepEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], res); }); @@ -444,20 +442,20 @@ suite('Async', () => { let queue = new async.Queue(); let syncPromise = false; - let f1 = () => TPromise.as(true).then(() => syncPromise = true); + let f1 = () => Promise.resolve(true).then(() => syncPromise = true); let asyncPromise = false; - let f2 = () => TPromise.wrap(async.timeout(10)).then(() => asyncPromise = true); + let f2 = () => async.timeout(10).then(() => asyncPromise = true); assert.equal(queue.size, 0); queue.queue(f1); - assert.equal(queue.size, 0); // sync promise is already done + assert.equal(queue.size, 1); const p = queue.queue(f2); - assert.equal(queue.size, 1); + assert.equal(queue.size, 2); return p.then(() => { - assert.equal(queue.size, 1); + assert.equal(queue.size, 0); assert.ok(syncPromise); assert.ok(asyncPromise); }); @@ -468,11 +466,11 @@ suite('Async', () => { let res: number[] = []; - let f1 = () => TPromise.as(true).then(() => res.push(1)); - let f2 = () => TPromise.wrap(async.timeout(10)).then(() => res.push(2)); - let f3 = () => TPromise.as(true).then(() => res.push(3)); - let f4 = () => TPromise.wrap(async.timeout(20)).then(() => res.push(4)); - let f5 = () => TPromise.wrap(async.timeout(0)).then(() => res.push(5)); + let f1 = () => Promise.resolve(true).then(() => res.push(1)); + let f2 = () => async.timeout(10).then(() => res.push(2)); + let f3 = () => Promise.resolve(true).then(() => res.push(3)); + let f4 = () => async.timeout(20).then(() => res.push(4)); + let f5 = () => async.timeout(0).then(() => res.push(5)); queue.queue(f1); queue.queue(f2); @@ -493,11 +491,11 @@ suite('Async', () => { let res: number[] = []; let error = false; - let f1 = () => TPromise.as(true).then(() => res.push(1)); - let f2 = () => TPromise.wrap(async.timeout(10)).then(() => res.push(2)); - let f3 = () => TPromise.as(true).then(() => TPromise.wrapError(new Error('error'))); - let f4 = () => TPromise.wrap(async.timeout(20)).then(() => res.push(4)); - let f5 = () => TPromise.wrap(async.timeout(0)).then(() => res.push(5)); + let f1 = () => Promise.resolve(true).then(() => res.push(1)); + let f2 = () => async.timeout(10).then(() => res.push(2)); + let f3 = () => Promise.resolve(true).then(() => Promise.reject(new Error('error'))); + let f4 = () => async.timeout(20).then(() => res.push(4)); + let f5 = () => async.timeout(0).then(() => res.push(5)); queue.queue(f1); queue.queue(f2); @@ -517,11 +515,11 @@ suite('Async', () => { let res: number[] = []; - let f1 = () => TPromise.as(true).then(() => res.push(1)); - let f2 = () => TPromise.wrap(async.timeout(10)).then(() => res.push(2)); - let f3 = () => TPromise.as(true).then(() => res.push(3)); - let f4 = () => TPromise.wrap(async.timeout(20)).then(() => res.push(4)); - let f5 = () => TPromise.wrap(async.timeout(0)).then(() => res.push(5)); + let f1 = () => Promise.resolve(true).then(() => res.push(1)); + let f2 = () => async.timeout(10).then(() => res.push(2)); + let f3 = () => Promise.resolve(true).then(() => res.push(3)); + let f4 = () => async.timeout(20).then(() => res.push(4)); + let f5 = () => async.timeout(0).then(() => res.push(5)); return queue.queue(f1).then(() => { return queue.queue(f2).then(() => { @@ -550,9 +548,9 @@ suite('Async', () => { let res: number[] = []; - let f1 = () => TPromise.wrap(async.timeout(10)).then(() => res.push(2)); - let f2 = () => TPromise.wrap(async.timeout(20)).then(() => res.push(4)); - let f3 = () => TPromise.wrap(async.timeout(0)).then(() => res.push(5)); + let f1 = () => async.timeout(10).then(() => res.push(2)); + let f2 = () => async.timeout(20).then(() => res.push(4)); + let f3 = () => async.timeout(0).then(() => res.push(5)); const q1 = queue.queue(f1); const q2 = queue.queue(f2); @@ -570,17 +568,22 @@ suite('Async', () => { let queue = new async.ResourceQueue(); const r1Queue = queue.queueFor(URI.file('/some/path')); + + r1Queue.onFinished(() => console.log('DONE')); + const r2Queue = queue.queueFor(URI.file('/some/other/path')); assert.ok(r1Queue); assert.ok(r2Queue); assert.equal(r1Queue, queue.queueFor(URI.file('/some/path'))); // same queue returned - let syncPromiseFactory = () => TPromise.as(true); + let syncPromiseFactory = () => Promise.resolve(null); r1Queue.queue(syncPromiseFactory); - const r1Queue2 = queue.queueFor(URI.file('/some/path')); - assert.notEqual(r1Queue, r1Queue2); // previous one got disposed after finishing + return new Promise(c => setTimeout(() => c(), 0)).then(() => { + const r1Queue2 = queue.queueFor(URI.file('/some/path')); + assert.notEqual(r1Queue, r1Queue2); // previous one got disposed after finishing + }); }); }); diff --git a/src/vs/base/test/common/event.test.ts b/src/vs/base/test/common/event.test.ts index c33c84bd2..19acb225e 100644 --- a/src/vs/base/test/common/event.test.ts +++ b/src/vs/base/test/common/event.test.ts @@ -6,7 +6,6 @@ import * as assert from 'assert'; import { Event, Emitter, debounceEvent, EventBufferer, once, fromPromise, stopwatch, buffer, echo, EventMultiplexer, latch, AsyncEmitter, IWaitUntil } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import * as Errors from 'vs/base/common/errors'; -import { TPromise } from 'vs/base/common/winjs.base'; import { timeout } from 'vs/base/common/async'; namespace Samples { @@ -441,7 +440,7 @@ suite('Event utils', () => { const emitter = new Emitter(); const event = stopwatch(emitter.event); - return new TPromise((c, e) => { + return new Promise((c, e) => { event(duration => { try { assert(duration > 0); diff --git a/src/vs/base/test/common/linkedList.test.ts b/src/vs/base/test/common/linkedList.test.ts index 38a86dc9d..caaf3736c 100644 --- a/src/vs/base/test/common/linkedList.test.ts +++ b/src/vs/base/test/common/linkedList.test.ts @@ -9,10 +9,14 @@ import { LinkedList } from 'vs/base/common/linkedList'; suite('LinkedList', function () { function assertElements(list: LinkedList, ...elements: E[]) { - // first: assert toArray + + // check size + assert.equal(list.size, elements.length); + + // assert toArray assert.deepEqual(list.toArray(), elements); - // second: assert iterator + // assert iterator for (let iter = list.iterator(), element = iter.next(); !element.done; element = iter.next()) { assert.equal(elements.shift(), element.value); } diff --git a/src/vs/base/test/common/paging.test.ts b/src/vs/base/test/common/paging.test.ts index 68c5b3b93..fe332c0bd 100644 --- a/src/vs/base/test/common/paging.test.ts +++ b/src/vs/base/test/common/paging.test.ts @@ -5,16 +5,15 @@ import * as assert from 'assert'; import { IPager, PagedModel } from 'vs/base/common/paging'; -import { TPromise } from 'vs/base/common/winjs.base'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { isPromiseCanceledError, canceled } from 'vs/base/common/errors'; function getPage(pageIndex: number, cancellationToken: CancellationToken): Thenable { if (cancellationToken.isCancellationRequested) { - return TPromise.wrapError(canceled()); + return Promise.reject(canceled()); } - return TPromise.as([0, 1, 2, 3, 4].map(i => i + (pageIndex * 5))); + return Promise.resolve([0, 1, 2, 3, 4].map(i => i + (pageIndex * 5))); } class TestPager implements IPager { @@ -102,7 +101,7 @@ suite('PagedModel', () => { test('preemptive cancellation works', async function () { const pager = new TestPager(() => { assert(false); - return TPromise.wrap([]); + return Promise.resolve([]); }); const model = new PagedModel(pager); @@ -117,7 +116,7 @@ suite('PagedModel', () => { }); test('cancellation works', function () { - const pager = new TestPager((_, token) => new TPromise((_, e) => { + const pager = new TestPager((_, token) => new Promise((_, e) => { token.onCancellationRequested(() => e(canceled())); })); @@ -140,7 +139,7 @@ suite('PagedModel', () => { const pager = new TestPager((pageIndex, token) => { state = 'resolving'; - return new TPromise((_, e) => { + return new Promise((_, e) => { token.onCancellationRequested(() => { state = 'idle'; e(canceled()); @@ -180,6 +179,6 @@ suite('PagedModel', () => { }, 10); }, 10); - return TPromise.join([promise1, promise2]); + return Promise.all([promise1, promise2]); }); -}); \ No newline at end of file +}); diff --git a/src/vs/base/test/common/utils.ts b/src/vs/base/test/common/utils.ts index a00e86f3e..dae0e054b 100644 --- a/src/vs/base/test/common/utils.ts +++ b/src/vs/base/test/common/utils.ts @@ -5,33 +5,46 @@ import * as paths from 'vs/base/common/paths'; import { URI } from 'vs/base/common/uri'; -import { TPromise, TValueCallback } from 'vs/base/common/winjs.base'; import { canceled } from 'vs/base/common/errors'; -export class DeferredTPromise extends TPromise { +export type ValueCallback = (value: T | Thenable) => void; - private completeCallback: TValueCallback; +export class DeferredPromise { + + private completeCallback: ValueCallback; private errorCallback: (err: any) => void; + public p: Promise; + constructor() { - let captured: any; - super((c, e) => { - captured = { c, e }; + this.p = new Promise((c, e) => { + this.completeCallback = c; + this.errorCallback = e; }); - this.completeCallback = captured.c; - this.errorCallback = captured.e; } public complete(value: T) { - this.completeCallback(value); + return new Promise(resolve => { + process.nextTick(() => { + this.completeCallback(value); + resolve(); + }); + }); } public error(err: any) { - this.errorCallback(err); + return new Promise(resolve => { + process.nextTick(() => { + this.errorCallback(err); + resolve(); + }); + }); } public cancel() { - this.errorCallback(canceled()); + process.nextTick(() => { + this.errorCallback(canceled()); + }); } } diff --git a/src/vs/base/test/node/pfs.test.ts b/src/vs/base/test/node/pfs.test.ts index 75b38405a..b62e72717 100644 --- a/src/vs/base/test/node/pfs.test.ts +++ b/src/vs/base/test/node/pfs.test.ts @@ -2,7 +2,6 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import * as assert from 'assert'; import * as os from 'os'; @@ -46,7 +45,7 @@ suite('PFS', () => { return pfs.mkdirp(newDir, 493).then(() => { assert.ok(fs.existsSync(newDir)); - return TPromise.join([ + return Promise.all([ pfs.writeFile(testFile1, 'Hello World 1', null), pfs.writeFile(testFile2, 'Hello World 2', null), pfs.writeFile(testFile3, 'Hello World 3', null), @@ -73,7 +72,7 @@ suite('PFS', () => { return pfs.mkdirp(newDir, 493).then(() => { assert.ok(fs.existsSync(newDir)); - return TPromise.join([ + return Promise.all([ pfs.writeFile(testFile, 'Hello World 1', null), pfs.writeFile(testFile, 'Hello World 2', null), timeout(10).then(() => pfs.writeFile(testFile, 'Hello World 3', null)), @@ -119,4 +118,38 @@ suite('PFS', () => { }); }); }); -}); \ No newline at end of file + + test('unlinkIgnoreError', function () { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'extfs', id); + + return pfs.mkdirp(newDir, 493).then(() => { + return pfs.unlinkIgnoreError(path.join(newDir, 'foo')).then(() => { + + return pfs.del(parentDir, os.tmpdir()); + }, error => { + assert.fail(error); + + return Promise.reject(error); + }); + }); + }); + + test('moveIgnoreError', function () { + const id = uuid.generateUuid(); + const parentDir = path.join(os.tmpdir(), 'vsctests', id); + const newDir = path.join(parentDir, 'extfs', id); + + return pfs.mkdirp(newDir, 493).then(() => { + return pfs.renameIgnoreError(path.join(newDir, 'foo'), path.join(newDir, 'bar')).then(() => { + + return pfs.del(parentDir, os.tmpdir()); + }, error => { + assert.fail(error); + + return Promise.reject(error); + }); + }); + }); +}); diff --git a/src/vs/base/test/node/storage/storage.test.ts b/src/vs/base/test/node/storage/storage.test.ts index d199c2ff1..ffb338ecc 100644 --- a/src/vs/base/test/node/storage/storage.test.ts +++ b/src/vs/base/test/node/storage/storage.test.ts @@ -8,7 +8,8 @@ import { generateUuid } from 'vs/base/common/uuid'; import { join } from 'path'; import { tmpdir } from 'os'; import { equal, ok } from 'assert'; -import { mkdirp, del } from 'vs/base/node/pfs'; +import { mkdirp, del, writeFile } from 'vs/base/node/pfs'; +import { timeout } from 'vs/base/common/async'; suite('Storage Library', () => { @@ -204,11 +205,11 @@ suite('SQLite Storage Library', () => { return set; } - async function testDBBasics(path, errorLogger?: (error) => void) { + async function testDBBasics(path, logError?: (error) => void) { const options: IStorageOptions = { path }; - if (errorLogger) { + if (logError) { options.logging = { - errorLogger + logError }; } @@ -288,14 +289,82 @@ suite('SQLite Storage Library', () => { await del(storageDir, tmpdir()); }); - test('basics (broken DB falls back to in-memory)', async () => { - let expectedError: any; + test('basics (corrupt DB falls back to empty DB)', async () => { + const storageDir = uniqueStorageDir(); + + await mkdirp(storageDir); + + const corruptDBPath = join(storageDir, 'broken.db'); + await writeFile(corruptDBPath, 'This is a broken DB'); - await testDBBasics(join(__dirname, 'broken.db'), error => { + let expectedError: any; + await testDBBasics(corruptDBPath, error => { expectedError = error; }); ok(expectedError); + + await del(storageDir, tmpdir()); + }); + + test('basics (corrupt DB restores from previous backup)', async () => { + const storageDir = uniqueStorageDir(); + + await mkdirp(storageDir); + + const storagePath = join(storageDir, 'storage.db'); + let storage = new SQLiteStorageImpl({ path: storagePath }); + + const items = new Map(); + items.set('foo', 'bar'); + items.set('some/foo/path', 'some/bar/path'); + items.set(JSON.stringify({ foo: 'bar' }), JSON.stringify({ bar: 'foo' })); + + await storage.updateItems({ insert: items }); + await storage.close(); + + await writeFile(storagePath, 'This is now a broken DB'); + + storage = new SQLiteStorageImpl({ path: storagePath }); + + const storedItems = await storage.getItems(); + equal(storedItems.size, items.size); + equal(storedItems.get('foo'), 'bar'); + equal(storedItems.get('some/foo/path'), 'some/bar/path'); + equal(storedItems.get(JSON.stringify({ foo: 'bar' })), JSON.stringify({ bar: 'foo' })); + + await storage.close(); + + await del(storageDir, tmpdir()); + }); + + test('basics (corrupt DB falls back to empty DB if backup is corrupt)', async () => { + const storageDir = uniqueStorageDir(); + + await mkdirp(storageDir); + + const storagePath = join(storageDir, 'storage.db'); + let storage = new SQLiteStorageImpl({ path: storagePath }); + + const items = new Map(); + items.set('foo', 'bar'); + items.set('some/foo/path', 'some/bar/path'); + items.set(JSON.stringify({ foo: 'bar' }), JSON.stringify({ bar: 'foo' })); + + await storage.updateItems({ insert: items }); + await storage.close(); + + await writeFile(storagePath, 'This is now a broken DB'); + await writeFile(`${storagePath}.backup`, 'This is now also a broken DB'); + + storage = new SQLiteStorageImpl({ path: storagePath }); + + const storedItems = await storage.getItems(); + equal(storedItems.size, 0); + + await testDBBasics(storagePath); + + await del(storageDir, tmpdir()); }); test('real world example', async () => { @@ -445,4 +514,55 @@ suite('SQLite Storage Library', () => { await del(storageDir, tmpdir()); }); -}); \ No newline at end of file + + test('multiple concurrent writes execute in sequence', async () => { + const storageDir = uniqueStorageDir(); + await mkdirp(storageDir); + + const storage = new Storage({ path: join(storageDir, 'storage.db') }); + + await storage.init(); + + storage.set('foo', 'bar'); + storage.set('some/foo/path', 'some/bar/path'); + + await timeout(10); + + storage.set('foo1', 'bar'); + storage.set('some/foo1/path', 'some/bar/path'); + + await timeout(10); + + storage.set('foo2', 'bar'); + storage.set('some/foo2/path', 'some/bar/path'); + + await timeout(10); + + storage.delete('foo1'); + storage.delete('some/foo1/path'); + + await timeout(10); + + storage.delete('foo4'); + storage.delete('some/foo4/path'); + + await timeout(70); + + storage.set('foo3', 'bar'); + await storage.set('some/foo3/path', 'some/bar/path'); + + const items = await storage.getItems(); + equal(items.get('foo'), 'bar'); + equal(items.get('some/foo/path'), 'some/bar/path'); + equal(items.has('foo1'), false); + equal(items.has('some/foo1/path'), false); + equal(items.get('foo2'), 'bar'); + equal(items.get('some/foo2/path'), 'some/bar/path'); + equal(items.get('foo3'), 'bar'); + equal(items.get('some/foo3/path'), 'some/bar/path'); + + await storage.close(); + + await del(storageDir, tmpdir()); + }); +}); diff --git a/src/vs/base/test/node/utils.ts b/src/vs/base/test/node/utils.ts index 439ed82eb..2ee7de39d 100644 --- a/src/vs/base/test/node/utils.ts +++ b/src/vs/base/test/node/utils.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { generateUuid } from 'vs/base/common/uuid'; import { join } from 'path'; import { tmpdir } from 'os'; @@ -11,10 +10,10 @@ import { mkdirp, del } from 'vs/base/node/pfs'; export interface ITestFileResult { testFile: string; - cleanUp: () => TPromise; + cleanUp: () => Thenable; } -export function testFile(folder: string, file: string): TPromise { +export function testFile(folder: string, file: string): Thenable { const id = generateUuid(); const parentDir = join(tmpdir(), 'vsctests', id); const newDir = join(parentDir, 'config', id); @@ -26,4 +25,4 @@ export function testFile(folder: string, file: string): TPromise del(parentDir, tmpdir()) } as ITestFileResult; }); -} \ No newline at end of file +} diff --git a/src/vs/base/worker/workerMain.ts b/src/vs/base/worker/workerMain.ts index 986e3ea4d..0ec8b7834 100644 --- a/src/vs/base/worker/workerMain.ts +++ b/src/vs/base/worker/workerMain.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ (function () { - 'use strict'; let MonacoEnvironment = (self).MonacoEnvironment; let monacoBaseUrl = MonacoEnvironment && MonacoEnvironment.baseUrl ? MonacoEnvironment.baseUrl : '../../../'; diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index 1e6320579..5cb0d8887 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -24,16 +24,15 @@ import { IWindowConfiguration, IWindowsService } from 'vs/platform/windows/commo import { NullTelemetryService, combinedAppender, LogAppender } from 'vs/platform/telemetry/common/telemetryUtils'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ITelemetryServiceConfig, TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; -import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc'; +import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; import { WindowsChannelClient } from 'vs/platform/windows/node/windowsIpc'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IssueReporterModel } from 'vs/code/electron-browser/issue/issueReporterModel'; -import { IssueReporterData, IssueReporterStyles, IssueType, ISettingsSearchIssueReporterData, IssueReporterFeatures } from 'vs/platform/issue/common/issue'; +import { IssueReporterData, IssueReporterStyles, IssueType, ISettingsSearchIssueReporterData, IssueReporterFeatures, IssueReporterExtensionData } from 'vs/platform/issue/common/issue'; import BaseHtml from 'vs/code/electron-browser/issue/issueReporterPage'; -import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/node/logIpc'; import { ILogService, getLogLevel } from 'vs/platform/log/common/log'; @@ -211,11 +210,9 @@ export class IssueReporter extends Disposable { document.body.style.color = styles.color; } - private handleExtensionData(extensions: ILocalExtension[]) { + private handleExtensionData(extensions: IssueReporterExtensionData[]) { const { nonThemes, themes } = collections.groupBy(extensions, ext => { - const manifestKeys = ext.manifest.contributes ? Object.keys(ext.manifest.contributes) : []; - const onlyTheme = !ext.manifest.activationEvents && manifestKeys.length === 1 && manifestKeys[0] === 'themes'; - return onlyTheme ? 'themes' : 'nonThemes'; + return ext.isTheme ? 'themes' : 'nonThemes'; }); const numberOfThemeExtesions = themes && themes.length; @@ -286,7 +283,7 @@ export class IssueReporter extends Disposable { const instantiationService = new InstantiationService(serviceCollection, true); if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) { - const channel = getDelayedChannel(sharedProcess.then(c => c.getChannel('telemetryAppender'))); + const channel = getDelayedChannel(sharedProcess.then(c => c.getChannel('telemetryAppender'))); const appender = combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(logService)); const commonProperties = resolveCommonProperties(product.commit, pkg.version, configuration.machineId, this.environmentService.installSourcePath); const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath]; @@ -457,12 +454,12 @@ export class IssueReporter extends Disposable { private getExtensionRepositoryUrl(): string { const selectedExtension = this.issueReporterModel.getData().selectedExtension; - return selectedExtension && selectedExtension.manifest && selectedExtension.manifest.repository && selectedExtension.manifest.repository.url; + return selectedExtension && selectedExtension.repositoryUrl; } private getExtensionBugsUrl(): string { const selectedExtension = this.issueReporterModel.getData().selectedExtension; - return selectedExtension && selectedExtension.manifest && selectedExtension.manifest.bugs && selectedExtension.manifest.bugs.url; + return selectedExtension && selectedExtension.bugsUrl; } private searchVSCodeIssues(title: string, issueDescription: string): void { @@ -830,7 +827,7 @@ export class IssueReporter extends Disposable { target.innerHTML = `${tableHtml}
`; } - private updateExtensionSelector(extensions: ILocalExtension[]): void { + private updateExtensionSelector(extensions: IssueReporterExtensionData[]): void { interface IOption { name: string; id: string; @@ -838,8 +835,8 @@ export class IssueReporter extends Disposable { const extensionOptions: IOption[] = extensions.map(extension => { return { - name: extension.manifest.displayName || extension.manifest.name || '', - id: extension.identifier.id + name: extension.displayName || extension.name || '', + id: extension.id }; }); @@ -865,7 +862,7 @@ export class IssueReporter extends Disposable { this.addEventListener('extension-selector', 'change', (e: Event) => { const selectedExtensionId = (e.target).value; const extensions = this.issueReporterModel.getData().allExtensions; - const matches = extensions.filter(extension => extension.identifier.id === selectedExtensionId); + const matches = extensions.filter(extension => extension.id === selectedExtensionId); if (matches.length) { this.issueReporterModel.update({ selectedExtension: matches[0] }); @@ -887,7 +884,7 @@ export class IssueReporter extends Disposable { document.querySelector('.block-workspace .block-info code').textContent = '\n' + state.workspaceInfo; } - private updateExtensionTable(extensions: ILocalExtension[], numThemeExtensions: number): void { + private updateExtensionTable(extensions: IssueReporterExtensionData[], numThemeExtensions: number): void { const target = document.querySelector('.block-extensions .block-info'); if (this.environmentService.disableExtensions) { @@ -907,7 +904,7 @@ export class IssueReporter extends Disposable { target.innerHTML = `${table}
${themeExclusionStr}`; } - private updateSearchedExtensionTable(extensions: ILocalExtension[]): void { + private updateSearchedExtensionTable(extensions: IssueReporterExtensionData[]): void { const target = document.querySelector('.block-searchedExtensions .block-info'); if (!extensions.length) { @@ -919,7 +916,7 @@ export class IssueReporter extends Disposable { target.innerHTML = `${table}
`; } - private getExtensionTableHtml(extensions: ILocalExtension[]): string { + private getExtensionTableHtml(extensions: IssueReporterExtensionData[]): string { let table = ` Extension @@ -930,9 +927,9 @@ export class IssueReporter extends Disposable { table += extensions.map(extension => { return ` - ${extension.manifest.name} - ${extension.manifest.publisher.substr(0, 3)} - ${extension.manifest.version} + ${extension.name} + ${extension.publisher.substr(0, 3)} + ${extension.version} `; }).join(''); diff --git a/src/vs/code/electron-browser/issue/issueReporterModel.ts b/src/vs/code/electron-browser/issue/issueReporterModel.ts index afd544ed0..2ef6e5f8e 100644 --- a/src/vs/code/electron-browser/issue/issueReporterModel.ts +++ b/src/vs/code/electron-browser/issue/issueReporterModel.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { assign } from 'vs/base/common/objects'; -import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IssueType, ISettingSearchResult } from 'vs/platform/issue/common/issue'; +import { IssueType, ISettingSearchResult, IssueReporterExtensionData } from 'vs/platform/issue/common/issue'; export interface IssueReporterData { issueType?: IssueType; @@ -24,11 +23,11 @@ export interface IssueReporterData { includeSettingsSearchDetails?: boolean; numberOfThemeExtesions?: number; - allExtensions?: ILocalExtension[]; - enabledNonThemeExtesions?: ILocalExtension[]; + allExtensions?: IssueReporterExtensionData[]; + enabledNonThemeExtesions?: IssueReporterExtensionData[]; extensionsDisabled?: boolean; fileOnExtension?: boolean; - selectedExtension?: ILocalExtension; + selectedExtension?: IssueReporterExtensionData; actualSearchResults?: ISettingSearchResult[]; query?: string; filterResultCount?: number; @@ -81,7 +80,7 @@ ${this.getInfos()} private getExtensionVersion(): string { if (this.fileOnExtension() && this._data.selectedExtension) { - return `\nExtension version: ${this._data.selectedExtension.manifest.version}`; + return `\nExtension version: ${this._data.selectedExtension.version}`; } else { return ''; } @@ -198,7 +197,7 @@ ${this._data.workspaceInfo}; let tableHeader = `Extension|Author (truncated)|Version ---|---|---`; const table = this._data.enabledNonThemeExtesions.map(e => { - return `${e.manifest.name}|${e.manifest.publisher.substr(0, 3)}|${e.manifest.version}`; + return `${e.name}|${e.publisher.substr(0, 3)}|${e.version}`; }).join('\n'); return `
Extensions (${this._data.enabledNonThemeExtesions.length}) diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/contributions.ts b/src/vs/code/electron-browser/sharedProcess/contrib/contributions.ts index 1231824cf..6784a68eb 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/contributions.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/contributions.ts @@ -7,10 +7,12 @@ import { NodeCachedDataCleaner } from 'vs/code/electron-browser/sharedProcess/co import { LanguagePackCachedDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; +import { StorageDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner'; export function createSharedProcessContributions(service: IInstantiationService): IDisposable { return combinedDisposable([ service.createInstance(NodeCachedDataCleaner), - service.createInstance(LanguagePackCachedDataCleaner) + service.createInstance(LanguagePackCachedDataCleaner), + service.createInstance(StorageDataCleaner) ]); } diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts index 953d7efaf..2da898399 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts @@ -50,7 +50,7 @@ export class LanguagePackCachedDataCleaner { } private _manageCachedDataSoon(): void { - let handle = setTimeout(async () => { + let handle: any = setTimeout(async () => { handle = undefined; this._logService.info('Starting to clean up unused language packs.'); const maxAge = product.nameLong.indexOf('Insiders') >= 0 diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts index 78be7ec10..3f14814bd 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts @@ -41,7 +41,7 @@ export class NodeCachedDataCleaner { const nodeCachedDataRootDir = dirname(this._environmentService.nodeCachedDataDir); const nodeCachedDataCurrent = basename(this._environmentService.nodeCachedDataDir); - let handle = setTimeout(() => { + let handle: any = setTimeout(() => { handle = undefined; readdir(nodeCachedDataRootDir).then(entries => { diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts new file mode 100644 index 000000000..1328e4edb --- /dev/null +++ b/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { join } from 'path'; +import { readdir, readFile, rimraf } from 'vs/base/node/pfs'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IBackupWorkspacesFormat } from 'vs/platform/backup/common/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; + + constructor( + @IEnvironmentService private environmentService: IEnvironmentService + ) { + super(); + + this.cleanUpStorageSoon(); + } + + private cleanUpStorageSoon(): void { + let handle: any = setTimeout(() => { + handle = void 0; + + // Leverage the backup workspace file to find out which empty workspace is currently in use to + // determine which empty workspace storage can safely be deleted + readFile(this.environmentService.backupWorkspacesPath, 'utf8').then(contents => { + const workspaces = JSON.parse(contents) as IBackupWorkspacesFormat; + const emptyWorkspaces = workspaces.emptyWorkspaceInfos.map(info => info.backupFolder); + + // Read all workspace storage folders that exist + return readdir(this.environmentService.workspaceStorageHome).then(storageFolders => { + const deletes: Promise[] = []; + + storageFolders.forEach(storageFolder => { + if (storageFolder.length === StorageDataCleaner.NON_EMPTY_WORKSPACE_ID_LENGTH) { + return; + } + + if (emptyWorkspaces.indexOf(storageFolder) === -1) { + deletes.push(rimraf(join(this.environmentService.workspaceStorageHome, storageFolder))); + } + }); + + return Promise.all(deletes); + }); + }).then(null, onUnexpectedError); + }, 30 * 1000); + + this._register(toDisposable(() => clearTimeout(handle))); + } +} diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 5b574e3f3..97dd14837 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -8,7 +8,6 @@ import * as platform from 'vs/base/common/platform'; import product from 'vs/platform/node/product'; import pkg from 'vs/platform/node/package'; import { serve, Server, connect } from 'vs/base/parts/ipc/node/ipc.net'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; @@ -43,6 +42,11 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { DownloadService } from 'vs/platform/download/node/downloadService'; import { IDownloadService } from 'vs/platform/download/common/download'; +import { RemoteAuthorityResolverService } from 'vs/platform/remote/node/remoteAuthorityResolverService'; +import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { RemoteAuthorityResolverChannel } from 'vs/platform/remote/node/remoteAuthorityResolverChannel'; +import { StaticRouter } from 'vs/base/parts/ipc/node/ipc'; +import { DefaultURITransformer } from 'vs/base/common/uriIpc'; export interface ISharedProcessConfiguration { readonly machineId: string; @@ -62,12 +66,19 @@ const eventPrefix = 'monacoworkbench'; function main(server: Server, initData: ISharedProcessInitData, configuration: ISharedProcessConfiguration): void { const services = new ServiceCollection(); + const disposables: IDisposable[] = []; - process.once('exit', () => dispose(disposables)); + + const onExit = () => dispose(disposables); + process.once('exit', onExit); + ipcRenderer.once('handshake:goodbye', onExit); + + disposables.push(server); const environmentService = new EnvironmentService(initData.args, process.execPath); - const mainRoute = () => TPromise.as('main'); - const logLevelClient = new LogLevelSetterChannelClient(server.getChannel('loglevel', { routeCall: mainRoute, routeEvent: mainRoute })); + + const mainRouter = new StaticRouter(ctx => ctx === 'main'); + const logLevelClient = new LogLevelSetterChannelClient(server.getChannel('loglevel', mainRouter)); const logService = new FollowerLogService(logLevelClient, createSpdLogService('sharedprocess', initData.logLevel, environmentService.logsPath)); disposables.push(logService); @@ -79,14 +90,13 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I services.set(IRequestService, new SyncDescriptor(RequestService)); services.set(IDownloadService, new SyncDescriptor(DownloadService)); - const windowsChannel = server.getChannel('windows', { routeCall: mainRoute, routeEvent: mainRoute }); + const windowsChannel = server.getChannel('windows', mainRouter); const windowsService = new WindowsChannelClient(windowsChannel); services.set(IWindowsService, windowsService); const activeWindowManager = new ActiveWindowManager(windowsService); - const route = () => activeWindowManager.getActiveClientId(); - - const dialogChannel = server.getChannel('dialog', { routeCall: route, routeEvent: route }); + const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id)); + const dialogChannel = server.getChannel('dialog', activeWindowRouter); services.set(IDialogService, new DialogChannelClient(dialogChannel)); const instantiationService = new InstantiationService(services); @@ -100,33 +110,38 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I telemetryLogService.info('==========================================================='); let appInsightsAppender: ITelemetryAppender | null = NullAppender; - if (product.aiConfig && product.aiConfig.asimovKey && isBuilt) { - appInsightsAppender = new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey, telemetryLogService); - disposables.push(appInsightsAppender); // Ensure the AI appender is disposed so that it flushes remaining data - } - server.registerChannel('telemetryAppender', new TelemetryAppenderChannel(appInsightsAppender)); - if (!extensionDevelopmentLocationURI && !environmentService.args['disable-telemetry'] && product.enableTelemetry) { + if (product.aiConfig && product.aiConfig.asimovKey && isBuilt) { + appInsightsAppender = new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey, telemetryLogService); + disposables.push(appInsightsAppender); // Ensure the AI appender is disposed so that it flushes remaining data + } const config: ITelemetryServiceConfig = { appender: combinedAppender(appInsightsAppender, new LogAppender(logService)), commonProperties: resolveCommonProperties(product.commit, pkg.version, configuration.machineId, installSourcePath), piiPaths: [appRoot, extensionsPath] }; - services.set(ITelemetryService, new SyncDescriptor(TelemetryService, config)); + services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config])); } else { services.set(ITelemetryService, NullTelemetryService); } + server.registerChannel('telemetryAppender', new TelemetryAppenderChannel(appInsightsAppender)); services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService)); + services.set(IRemoteAuthorityResolverService, new SyncDescriptor(RemoteAuthorityResolverService)); const instantiationService2 = instantiationService.createChild(services); instantiationService2.invokeFunction(accessor => { + + const remoteAuthorityResolverService = accessor.get(IRemoteAuthorityResolverService); + const remoteAuthorityResolverChannel = new RemoteAuthorityResolverChannel(remoteAuthorityResolverService); + server.registerChannel('remoteAuthorityResolver', remoteAuthorityResolverChannel); + const extensionManagementService = accessor.get(IExtensionManagementService); - const channel = new ExtensionManagementChannel(extensionManagementService); + const channel = new ExtensionManagementChannel(extensionManagementService, () => DefaultURITransformer); server.registerChannel('extensions', channel); // clean up deprecated extensions @@ -138,6 +153,7 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I createSharedProcessContributions(instantiationService2); disposables.push(extensionManagementService as ExtensionManagementService); + disposables.push(remoteAuthorityResolverService as RemoteAuthorityResolverService); }); }); } @@ -176,15 +192,14 @@ function setupIPC(hook: string): Thenable { return setup(true); } -function startHandshake(): TPromise { - return new TPromise((c, e) => { +async function handshake(configuration: ISharedProcessConfiguration): Promise { + const data = await new Promise(c => { ipcRenderer.once('handshake:hey there', (_: any, r: ISharedProcessInitData) => c(r)); ipcRenderer.send('handshake:hello'); }); -} -function handshake(configuration: ISharedProcessConfiguration): TPromise { - return startHandshake() - .then(data => setupIPC(data.sharedIPCHandle).then(server => main(server, data, configuration))) - .then(() => ipcRenderer.send('handshake:im ready')); -} \ No newline at end of file + const server = await setupIPC(data.sharedIPCHandle); + + main(server, data, configuration); + ipcRenderer.send('handshake:im ready'); +} diff --git a/src/vs/code/electron-browser/workbench/workbench.html b/src/vs/code/electron-browser/workbench/workbench.html index f2c83331d..ab864aa34 100644 --- a/src/vs/code/electron-browser/workbench/workbench.html +++ b/src/vs/code/electron-browser/workbench/workbench.html @@ -3,7 +3,7 @@ - + diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index 646975957..d5017d778 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -53,15 +53,11 @@ bootstrapWindow.load([ function showPartsSplash(configuration) { perf.mark('willShowPartsSplash'); - // TODO@Ben remove me after a while - perf.mark('willAccessLocalStorage'); - let storage = window.localStorage; - perf.mark('didAccessLocalStorage'); - let data; try { + // TODO@Ben remove me after a while perf.mark('willReadLocalStorage'); - let raw = storage.getItem('storage://global/parts-splash-data'); + let raw = window.localStorage.getItem('storage://global/parts-splash-data'); perf.mark('didReadLocalStorage'); data = JSON.parse(raw); } catch (e) { diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 44464ac80..10391c492 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { app, ipcMain as ipc, systemPreferences, shell, Event, contentTracing } from 'electron'; +import { app, ipcMain as ipc, systemPreferences, shell, Event, contentTracing, protocol } from 'electron'; import * as platform from 'vs/base/common/platform'; import { WindowsManager } from 'vs/code/electron-main/windows'; import { IWindowsService, OpenContext, ActiveWindowManager } from 'vs/platform/windows/common/windows'; @@ -29,10 +29,10 @@ import { IURLService } from 'vs/platform/url/common/url'; import { URLHandlerChannelClient, URLServiceChannel } from 'vs/platform/url/node/urlIpc'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService, combinedAppender, LogAppender } from 'vs/platform/telemetry/common/telemetryUtils'; -import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc'; +import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc'; import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; -import { getDelayedChannel } from 'vs/base/parts/ipc/node/ipc'; +import { getDelayedChannel, StaticRouter } from 'vs/base/parts/ipc/node/ipc'; import product from 'vs/platform/node/product'; import pkg from 'vs/platform/node/package'; import { ProxyAuthHandler } from 'vs/code/electron-main/auth'; @@ -57,6 +57,7 @@ import { LogLevelSetterChannel } from 'vs/platform/log/node/logIpc'; import * as errors from 'vs/base/common/errors'; import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener'; import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver'; +import { connectRemoteAgentManagement, RemoteAgentConnectionContext } from 'vs/platform/remote/node/remoteAgentConnection'; import { IMenubarService } from 'vs/platform/menubar/common/menubar'; import { MenubarService } from 'vs/platform/menubar/electron-main/menubarService'; import { MenubarChannel } from 'vs/platform/menubar/node/menubarIpc'; @@ -68,6 +69,11 @@ import { THEME_STORAGE_KEY, THEME_BG_STORAGE_KEY } from 'vs/code/electron-main/t import { nativeSep, join } from 'vs/base/common/paths'; import { homedir } from 'os'; import { localize } from 'vs/nls'; +import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; +import { RemoteAuthorityResolverChannelClient } from 'vs/platform/remote/node/remoteAuthorityResolverChannel'; +import { REMOTE_FILE_SYSTEM_CHANNEL_NAME } from 'vs/platform/remote/node/remoteAgentFileSystemChannel'; +import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { SnapUpdateService } from 'vs/platform/update/electron-main/updateService.snap'; export class CodeApplication { @@ -164,6 +170,69 @@ export class CodeApplication { }); }); + const connectionPool: Map = new Map(); + + class ActiveConnection { + private _authority: string; + private _client: TPromise>; + private _disposeRunner: RunOnceScheduler; + + constructor(authority: string, connectionInfo: TPromise) { + this._authority = authority; + this._client = connectionInfo.then(({ host, port }) => { + return connectRemoteAgentManagement(authority, host, port, `main`); + }); + this._disposeRunner = new RunOnceScheduler(() => this._dispose(), 5000); + } + + private _dispose(): void { + this._disposeRunner.dispose(); + connectionPool.delete(this._authority); + this._client.then((connection) => { + connection.dispose(); + }); + } + + public getClient(): TPromise> { + this._disposeRunner.schedule(); + return this._client; + } + } + + protocol.registerBufferProtocol(REMOTE_HOST_SCHEME, async (request, callback) => { + if (request.method !== 'GET') { + return callback(null); + } + const uri = URI.parse(request.url); + + let activeConnection: ActiveConnection = null; + if (connectionPool.has(uri.authority)) { + activeConnection = connectionPool.get(uri.authority); + } else { + if (this.sharedProcessClient) { + const remoteAuthorityResolverChannel = getDelayedChannel(this.sharedProcessClient.then(c => c.getChannel('remoteAuthorityResolver'))); + const remoteAuthorityResolverChannelClient = new RemoteAuthorityResolverChannelClient(remoteAuthorityResolverChannel); + activeConnection = new ActiveConnection(uri.authority, remoteAuthorityResolverChannelClient.resolveAuthority(uri.authority)); + connectionPool.set(uri.authority, activeConnection); + } + } + try { + const rawClient = await activeConnection.getClient(); + if (connectionPool.has(uri.authority)) { // not disposed in the meantime + const channel = rawClient.getChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME); + + // TODO@alex don't use call directly, wrap it around a `RemoteExtensionsFileSystemProvider` + const fileContents = await channel.call('readFile', [uri]); + callback(Buffer.from(fileContents)); + } else { + callback(null); + } + } catch (err) { + errors.onUnexpectedError(err); + callback(null); + } + }); + let macOpenFileURIs: URI[] = []; let runningTimeout: any = null; app.on('open-file', (event: Event, path: string) => { @@ -426,26 +495,30 @@ export class CodeApplication { if (process.platform === 'win32') { services.set(IUpdateService, new SyncDescriptor(Win32UpdateService)); } else if (process.platform === 'linux') { - services.set(IUpdateService, new SyncDescriptor(LinuxUpdateService)); + if (process.env.SNAP && process.env.SNAP_REVISION) { + services.set(IUpdateService, new SyncDescriptor(SnapUpdateService)); + } else { + services.set(IUpdateService, new SyncDescriptor(LinuxUpdateService)); + } } else if (process.platform === 'darwin') { services.set(IUpdateService, new SyncDescriptor(DarwinUpdateService)); } - services.set(IWindowsMainService, new SyncDescriptor(WindowsManager, machineId)); - services.set(IWindowsService, new SyncDescriptor(WindowsService, this.sharedProcess)); + services.set(IWindowsMainService, new SyncDescriptor(WindowsManager, [machineId])); + services.set(IWindowsService, new SyncDescriptor(WindowsService, [this.sharedProcess])); services.set(ILaunchService, new SyncDescriptor(LaunchService)); - services.set(IIssueService, new SyncDescriptor(IssueService, machineId, this.userEnv)); + services.set(IIssueService, new SyncDescriptor(IssueService, [machineId, this.userEnv])); services.set(IMenubarService, new SyncDescriptor(MenubarService)); // Telemtry if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) { - const channel = getDelayedChannel(this.sharedProcessClient.then(c => c.getChannel('telemetryAppender'))); + const channel = getDelayedChannel(this.sharedProcessClient.then(c => c.getChannel('telemetryAppender'))); const appender = combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(this.logService)); const commonProperties = resolveCommonProperties(product.commit, pkg.version, machineId, this.environmentService.installSourcePath); const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath]; const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths }; - services.set(ITelemetryService, new SyncDescriptor(TelemetryService, config)); + services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config])); } else { services.set(ITelemetryService, NullTelemetryService); } @@ -502,8 +575,8 @@ export class CodeApplication { // Create a URL handler which forwards to the last active window const activeWindowManager = new ActiveWindowManager(windowsService); - const route = () => activeWindowManager.getActiveClientId(); - const urlHandlerChannel = this.electronIpcServer.getChannel('urlHandler', { routeCall: route, routeEvent: route }); + const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id)); + const urlHandlerChannel = this.electronIpcServer.getChannel('urlHandler', activeWindowRouter); const multiplexURLHandler = new URLHandlerChannelClient(urlHandlerChannel); // On Mac, Code can be running without any open windows, so we must create a window to handle urls, diff --git a/src/vs/code/electron-main/logUploader.ts b/src/vs/code/electron-main/logUploader.ts index 7bc22e237..ed1a038a8 100644 --- a/src/vs/code/electron-main/logUploader.ts +++ b/src/vs/code/electron-main/logUploader.ts @@ -9,12 +9,12 @@ import * as fs from 'fs'; import * as path from 'path'; import { localize } from 'vs/nls'; -import { ILaunchChannel } from 'vs/platform/launch/electron-main/launchService'; import product from 'vs/platform/node/product'; import { IRequestService } from 'vs/platform/request/node/request'; import { IRequestContext } from 'vs/base/node/request'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { ILaunchService } from 'vs/platform/launch/electron-main/launchService'; interface PostResult { readonly blob_id: string; @@ -32,7 +32,7 @@ class Endpoint { } export async function uploadLogs( - channel: ILaunchChannel, + launchService: ILaunchService, requestService: IRequestService, environmentService: IEnvironmentService ): Promise { @@ -42,7 +42,7 @@ export async function uploadLogs( return; } - const logsPath = await channel.call('get-logs-path', null); + const logsPath = await launchService.getLogsPath(); if (await promptUserToConfirmLogUpload(logsPath, environmentService)) { console.log(localize('beginUploading', 'Uploading...')); diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 7fe3d9b96..b6c7093f0 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -15,7 +15,7 @@ import { validatePaths } from 'vs/code/node/paths'; import { LifecycleService, ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; import { Server, serve, connect } from 'vs/base/parts/ipc/node/ipc.net'; import { TPromise } from 'vs/base/common/winjs.base'; -import { ILaunchChannel, LaunchChannelClient } from 'vs/platform/launch/electron-main/launchService'; +import { LaunchChannelClient } from 'vs/platform/launch/electron-main/launchService'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { InstantiationService } from 'vs/platform/instantiation/node/instantiationService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; @@ -49,6 +49,7 @@ import { setUnexpectedErrorHandler } from 'vs/base/common/errors'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { CommandLineDialogService } from 'vs/platform/dialogs/node/dialogService'; import { ILabelService, LabelService } from 'vs/platform/label/common/label'; +import { createWaitMarkerFile } from 'vs/code/node/wait'; function createServices(args: ParsedArgs, bufferLogService: BufferLogService): IInstantiationService { const services = new ServiceCollection(); @@ -56,7 +57,7 @@ function createServices(args: ParsedArgs, bufferLogService: BufferLogService): I const environmentService = new EnvironmentService(args, process.execPath); const consoleLogService = new ConsoleLogMainService(getLogLevel(environmentService)); const logService = new MultiplexLogService([consoleLogService, bufferLogService]); - const labelService = new LabelService(environmentService, undefined); + const labelService = new LabelService(environmentService, undefined, undefined); process.once('exit', () => logService.dispose()); @@ -99,6 +100,7 @@ function createPaths(environmentService: IEnvironmentService): TPromise { environmentService.extensionsPath, environmentService.nodeCachedDataDir, environmentService.logsPath, + environmentService.globalStorageHome, environmentService.workspaceStorageHome ]; @@ -195,7 +197,7 @@ function setupIPC(accessor: ServicesAccessor): Thenable { }, 10000); } - const channel = client.getChannel('launch'); + const channel = client.getChannel('launch'); const service = new LaunchChannelClient(channel); // Process Info @@ -207,7 +209,7 @@ function setupIPC(accessor: ServicesAccessor): Thenable { // Log uploader if (typeof environmentService.args['upload-logs'] !== 'undefined') { - return uploadLogs(channel, requestService, environmentService) + return uploadLogs(service, requestService, environmentService) .then(() => Promise.reject(new ExpectedError())); } @@ -293,23 +295,7 @@ function quit(accessor: ServicesAccessor, reason?: ExpectedError | Error): void lifecycleService.kill(exitCode); } -function main() { - - // Set the error handler early enough so that we are not getting the - // default electron error dialog popping up - setUnexpectedErrorHandler(err => console.error(err)); - - let args: ParsedArgs; - try { - args = parseMainProcessArgv(process.argv); - args = validatePaths(args); - } catch (err) { - console.error(err.message); - app.exit(1); - - return void 0; - } - +function startup(args: ParsedArgs): void { // We need to buffer the spdlog logs until we are sure // we are the only instance running, otherwise we'll have concurrent // log file access on Windows @@ -317,7 +303,7 @@ function main() { const bufferLogService = new BufferLogService(); const instantiationService = createServices(args, bufferLogService); - return instantiationService.invokeFunction(accessor => { + instantiationService.invokeFunction(accessor => { // Patch `process.env` with the instance's environment const environmentService = accessor.get(IEnvironmentService); @@ -343,4 +329,46 @@ function main() { }).then(null, err => instantiationService.invokeFunction(quit, err)); } +function main(): void { + + // Set the error handler early enough so that we are not getting the + // default electron error dialog popping up + setUnexpectedErrorHandler(err => console.error(err)); + + // Parse arguments + let args: ParsedArgs; + try { + args = parseMainProcessArgv(process.argv); + args = validatePaths(args); + } catch (err) { + console.error(err.message); + app.exit(1); + + return void 0; + } + + // If we are started with --wait create a random temporary file + // and pass it over to the starting instance. We can use this file + // to wait for it to be deleted to monitor that the edited file + // is closed and then exit the waiting process. + // + // Note: we are not doing this if the wait marker has been already + // added as argument. This can happen if Code was started from CLI. + if (args.wait && !args.waitMarkerFilePath) { + createWaitMarkerFile(args.verbose).then(waitMarkerFilePath => { + if (waitMarkerFilePath) { + process.argv.push('--waitMarkerFilePath', waitMarkerFilePath); + args.waitMarkerFilePath = waitMarkerFilePath; + } + + startup(args); + }); + } + + // Otherwise just startup normally + else { + startup(args); + } +} + main(); diff --git a/src/vs/code/electron-main/sharedProcess.ts b/src/vs/code/electron-main/sharedProcess.ts index 6584009cd..01358983f 100644 --- a/src/vs/code/electron-main/sharedProcess.ts +++ b/src/vs/code/electron-main/sharedProcess.ts @@ -6,7 +6,6 @@ import { assign } from 'vs/base/common/objects'; import { memoize } from 'vs/base/common/decorators'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { BrowserWindow, ipcMain } from 'electron'; import { ISharedProcess } from 'vs/platform/windows/electron-main/windows'; @@ -15,12 +14,13 @@ import { ILogService } from 'vs/platform/log/common/log'; import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; import { IStateService } from 'vs/platform/state/common/state'; import { getBackgroundColor } from 'vs/code/electron-main/theme'; +import { dispose, toDisposable, IDisposable } from 'vs/base/common/lifecycle'; export class SharedProcess implements ISharedProcess { private barrier = new Barrier(); - private window: Electron.BrowserWindow; + private window: Electron.BrowserWindow | null; constructor( private readonly machineId: string, @@ -29,11 +29,10 @@ export class SharedProcess implements ISharedProcess { @ILifecycleService private readonly lifecycleService: ILifecycleService, @IStateService private readonly stateService: IStateService, @ILogService private readonly logService: ILogService - ) { - } + ) { } @memoize - private get _whenReady(): TPromise { + private get _whenReady(): Promise { this.window = new BrowserWindow({ show: false, backgroundColor: getBackgroundColor(this.stateService), @@ -62,26 +61,34 @@ export class SharedProcess implements ISharedProcess { e.preventDefault(); // Still hide the window though if visible - if (this.window.isVisible()) { + if (this.window && this.window.isVisible()) { this.window.hide(); } }; this.window.on('close', onClose); + const disposables: IDisposable[] = []; + this.lifecycleService.onShutdown(() => { + dispose(disposables); + // Shut the shared process down when we are quitting // // Note: because we veto the window close, we must first remove our veto. // Otherwise the application would never quit because the shared process // window is refusing to close! // - this.window.removeListener('close', onClose); + if (this.window) { + this.window.removeListener('close', onClose); + } // Electron seems to crash on Windows without this setTimeout :| setTimeout(() => { try { - this.window.close(); + if (this.window) { + this.window.close(); + } } catch (err) { // ignore, as electron is already shutting down } @@ -90,7 +97,7 @@ export class SharedProcess implements ISharedProcess { }, 0); }); - return new TPromise((c, e) => { + return new Promise(c => { ipcMain.once('handshake:hello', ({ sender }: { sender: any }) => { sender.send('handshake:hey there', { sharedIPCHandle: this.environmentService.sharedIPCHandle, @@ -98,6 +105,7 @@ export class SharedProcess implements ISharedProcess { logLevel: this.logService.getLevel() }); + disposables.push(toDisposable(() => sender.send('handshake:goodbye'))); ipcMain.once('handshake:im ready', () => c(void 0)); }); }); @@ -107,12 +115,13 @@ export class SharedProcess implements ISharedProcess { this.barrier.open(); } - whenReady(): TPromise { - return this.barrier.wait().then(() => this._whenReady); + async whenReady(): Promise { + await this.barrier.wait(); + await this._whenReady; } toggle(): void { - if (this.window.isVisible()) { + if (!this.window || this.window.isVisible()) { this.hide(); } else { this.show(); @@ -120,12 +129,16 @@ export class SharedProcess implements ISharedProcess { } show(): void { - this.window.show(); - this.window.webContents.openDevTools(); + if (this.window) { + this.window.show(); + this.window.webContents.openDevTools(); + } } hide(): void { - this.window.webContents.closeDevTools(); - this.window.hide(); + if (this.window) { + this.window.webContents.closeDevTools(); + this.window.hide(); + } } } diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index c47f23854..b54e0b670 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -15,7 +15,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { parseArgs } from 'vs/platform/environment/node/argv'; import product from 'vs/platform/node/product'; -import { IWindowSettings, MenuBarVisibility, IWindowConfiguration, ReadyState, IRunActionInWindowRequest } from 'vs/platform/windows/common/windows'; +import { IWindowSettings, MenuBarVisibility, IWindowConfiguration, ReadyState, IRunActionInWindowRequest, getTitleBarStyle } from 'vs/platform/windows/common/windows'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; import { ICodeWindow, IWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows'; @@ -143,6 +143,10 @@ export class CodeWindow implements ICodeWindow { 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 @@ -151,32 +155,11 @@ export class CodeWindow implements ICodeWindow { } } - let useNativeTabs = false; if (isMacintosh && windowConfig && windowConfig.nativeTabs === true) { options.tabbingIdentifier = product.nameShort; // this opts in to sierra tabs - useNativeTabs = true; - } - - let useCustomTitleStyle = false; - if (isMacintosh) { - useCustomTitleStyle = !windowConfig || !windowConfig.titleBarStyle || windowConfig.titleBarStyle === 'custom'; // Default to custom on macOS - - const isDev = !this.environmentService.isBuilt || !!config.extensionDevelopmentPath; - if (isDev) { - useCustomTitleStyle = false; // not enabled when developing due to https://github.com/electron/electron/issues/3647 - } - } else { - if (isLinux) { - useCustomTitleStyle = windowConfig && windowConfig.titleBarStyle === 'custom'; - } else { - useCustomTitleStyle = !windowConfig || !windowConfig.titleBarStyle || windowConfig.titleBarStyle === 'custom'; // Default to custom on Windows - } - } - - if (useNativeTabs) { - useCustomTitleStyle = false; // native tabs on sierra do not work with custom title style } + const useCustomTitleStyle = getTitleBarStyle(this.configurationService, this.environmentService) === 'custom'; if (useCustomTitleStyle) { options.titleBarStyle = 'hidden'; this.hiddenTitleBarStyle = true; @@ -197,7 +180,7 @@ export class CodeWindow implements ICodeWindow { this._win.maximize(); if (this.windowState.mode === WindowMode.Fullscreen) { - this._win.setFullScreen(true); + this.setFullScreen(true); } if (!this._win.isVisible()) { @@ -280,6 +263,10 @@ export class CodeWindow implements ICodeWindow { return this.currentConfig ? this.currentConfig.folderUri : void 0; } + get remoteAuthority(): string { + return this.currentConfig ? this.currentConfig.remoteAuthority : void 0; + } + setReady(): void { this._readyState = ReadyState.READY; @@ -372,6 +359,14 @@ export class CodeWindow implements ICodeWindow { this._lastFocusTime = Date.now(); }); + // Simple fullscreen doesn't resize automatically when the resolution changes + screen.on('display-metrics-changed', () => { + if (isMacintosh && this.isFullScreen() && !this.useNativeFullScreen()) { + this.setFullScreen(false); + this.setFullScreen(true); + } + }); + // Window (Un)Maximize this._win.on('maximize', (e) => { if (this.currentConfig) { @@ -408,6 +403,34 @@ export class CodeWindow implements ICodeWindow { // Handle Workspace events this.toDispose.push(this.workspacesMainService.onUntitledWorkspaceDeleted(e => this.onUntitledWorkspaceDeleted(e))); + + // TODO@Ben workaround for https://github.com/Microsoft/vscode/issues/13612 + // It looks like smooth scrolling disappears as soon as the window is minimized + // and maximized again. Touching some window properties "fixes" it, like toggling + // the visibility of the menu. + if (isWindows) { + const windowConfig = this.configurationService.getValue('window'); + if (windowConfig && windowConfig.smoothScrollingWorkaround === true) { + let minimized = false; + + const restoreSmoothScrolling = () => { + if (minimized) { + const visibility = this.getMenuBarVisibility(); + const temporaryVisibility: MenuBarVisibility = (visibility === 'hidden' || visibility === 'toggle') ? 'default' : 'hidden'; + setTimeout(() => { + this.doSetMenuBarVisibility(temporaryVisibility); + this.doSetMenuBarVisibility(visibility); + }, 0); + } + + minimized = false; + }; + + this._win.on('minimize', () => minimized = true); + this._win.on('restore', () => restoreSmoothScrolling()); + this._win.on('maximize', () => restoreSmoothScrolling()); + } + } } private onUntitledWorkspaceDeleted(workspace: IWorkspaceIdentifier): void { @@ -558,7 +581,7 @@ export class CodeWindow implements ICodeWindow { } // Set fullscreen state - windowConfiguration.fullscreen = this._win.isFullScreen(); + windowConfiguration.fullscreen = this.isFullScreen(); // Set Accessibility Config let autoDetectHighContrast = true; @@ -612,7 +635,7 @@ export class CodeWindow implements ICodeWindow { } // fullscreen gets special treatment - if (this._win.isFullScreen()) { + if (this.isFullScreen()) { const display = screen.getDisplayMatching(this.getBounds()); const defaultState = defaultWindowState(); @@ -778,15 +801,55 @@ export class CodeWindow implements ICodeWindow { } toggleFullScreen(): void { - const willBeFullScreen = !this._win.isFullScreen(); + this.setFullScreen(!this.isFullScreen()); + } + + private setFullScreen(fullscreen: boolean): void { + + // Set fullscreen state + if (this.useNativeFullScreen()) { + this.setNativeFullScreen(fullscreen); + } else { + this.setSimpleFullScreen(fullscreen); + } - // set fullscreen flag on window - this._win.setFullScreen(willBeFullScreen); + // Events + this.sendWhenReady(fullscreen ? 'vscode:enterFullScreen' : 'vscode:leaveFullScreen'); - // respect configured menu bar visibility or default to toggle if not set + // Respect configured menu bar visibility or default to toggle if not set this.setMenuBarVisibility(this.currentMenuBarVisibility, false); } + isFullScreen(): boolean { + return this._win.isFullScreen() || this._win.isSimpleFullScreen(); + } + + private setNativeFullScreen(fullscreen: boolean): void { + if (this._win.isSimpleFullScreen()) { + this._win.setSimpleFullScreen(false); + } + + this._win.setFullScreen(fullscreen); + } + + private setSimpleFullScreen(fullscreen: boolean): void { + if (this._win.isFullScreen()) { + this._win.setFullScreen(false); + } + + this._win.setSimpleFullScreen(fullscreen); + this._win.webContents.focus(); // workaround issue where focus is not going into window + } + + private useNativeFullScreen(): boolean { + const windowConfig = this.configurationService.getValue('window'); + if (!windowConfig || typeof windowConfig.nativeFullScreen !== 'boolean') { + return true; // default + } + + return windowConfig.nativeFullScreen !== false; + } + private getMenuBarVisibility(): MenuBarVisibility { const windowConfig = this.configurationService.getValue('window'); if (!windowConfig || !windowConfig.menuBarVisibility) { @@ -827,7 +890,7 @@ export class CodeWindow implements ICodeWindow { } private doSetMenuBarVisibility(visibility: MenuBarVisibility): void { - const isFullscreen = this._win.isFullScreen(); + const isFullscreen = this.isFullScreen(); switch (visibility) { case ('default'): diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 1b954752d..82677e209 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -37,10 +37,11 @@ import { Queue, timeout } from 'vs/base/common/async'; import { exists } from 'vs/base/node/pfs'; import { getComparisonKey, isEqual, normalizePath } from 'vs/base/common/resources'; import { endsWith } from 'vs/base/common/strings'; +import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; -enum WindowError { - UNRESPONSIVE, - CRASHED +const enum WindowError { + UNRESPONSIVE = 1, + CRASHED = 2 } interface INewWindowState extends ISingleWindowState { @@ -51,6 +52,7 @@ interface IWindowState { workspace?: IWorkspaceIdentifier; folderUri?: URI; backupPath: string; + remoteAuthority?: string; uiState: ISingleWindowState; } @@ -73,6 +75,8 @@ interface IOpenBrowserWindowOptions { workspace?: IWorkspaceIdentifier; folderUri?: URI; + remoteAuthority: string; + initialStartup?: boolean; fileInputs?: IFileInputs; @@ -88,6 +92,7 @@ interface IPathParseOptions { ignoreFileNotFound?: boolean; gotoLineMode?: boolean; forceOpenWorkspaceAsFile?: boolean; + remoteAuthority?: string; } interface IFileInputs { @@ -95,6 +100,7 @@ interface IFileInputs { filesToCreate: IPath[]; filesToDiff: IPath[]; filesToWait?: IPathsToWaitFor; + remoteAuthority?: string; } interface IPathToOpen extends IPath { @@ -108,6 +114,9 @@ interface IPathToOpen extends IPath { // the backup path for a Code instance to use backupPath?: string; + // the remote authority for the Code instance to open. Undefined if not remote. + remoteAuthority?: string; + // indicator to create the file path in the Code instance createFilePath?: boolean; } @@ -153,7 +162,7 @@ export class WindowsManager implements IWindowsMainService { @IEnvironmentService private environmentService: IEnvironmentService, @ILifecycleService private lifecycleService: ILifecycleService, @IBackupMainService private backupMainService: IBackupMainService, - @ITelemetryService telemetryService: ITelemetryService, + @ITelemetryService private telemetryService: ITelemetryService, @IConfigurationService private configurationService: IConfigurationService, @IHistoryMainService private historyMainService: IHistoryMainService, @IWorkspacesMainService private workspacesMainService: IWorkspacesMainService, @@ -358,6 +367,7 @@ export class WindowsManager implements IWindowsMainService { workspace: win.openedWorkspace, folderUri: win.openedFolderUri, backupPath: win.backupPath, + remoteAuthority: win.remoteAuthority, uiState: win.serializeWindowState() }; } @@ -381,7 +391,7 @@ export class WindowsManager implements IWindowsMainService { for (const path of pathsToOpen) { if (path.fileUri) { if (!fileInputs) { - fileInputs = { filesToCreate: [], filesToOpen: [], filesToDiff: [] }; + fileInputs = { filesToCreate: [], filesToOpen: [], filesToDiff: [], remoteAuthority: path.remoteAuthority }; } if (!path.createFilePath) { fileInputs.filesToOpen.push(path); @@ -427,7 +437,7 @@ export class WindowsManager implements IWindowsMainService { workspacesToRestore.push(...this.workspacesMainService.getUntitledWorkspacesSync()); // collect from previous window session emptyToRestore = this.backupMainService.getEmptyWindowBackupPaths(); - emptyToRestore.push(...pathsToOpen.filter(w => !w.workspace && !w.folderUri && w.backupPath).map(w => ({ backupFolder: basename(w.backupPath) }))); // add empty windows with backupPath + emptyToRestore.push(...pathsToOpen.filter(w => !w.workspace && !w.folderUri && w.backupPath).map(w => ({ backupFolder: basename(w.backupPath), remoteAuthority: w.remoteAuthority }))); // add empty windows with backupPath emptyToRestore = arrays.distinct(emptyToRestore, info => info.backupFolder); // prevent duplicates } @@ -461,9 +471,9 @@ export class WindowsManager implements IWindowsMainService { for (let i = usedWindows.length - 1; i >= 0; i--) { const usedWindow = usedWindows[i]; if ( - (usedWindow.openedWorkspace && workspacesToRestore.some(workspace => workspace.id === usedWindow.openedWorkspace.id)) || // skip over restored workspace - (usedWindow.openedFolderUri && foldersToRestore.some(folder => isEqual(folder, 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 => workspace.id === usedWindow.openedWorkspace.id)) || // skip over restored workspace + (usedWindow.openedFolderUri && foldersToRestore.some(folder => isEqual(folder, usedWindow.openedFolderUri))) || // skip over restored folder + (usedWindow.backupPath && emptyToRestore.some(empty => empty.backupFolder === basename(usedWindow.backupPath))) // skip over restored empty window ) { continue; } @@ -537,7 +547,8 @@ export class WindowsManager implements IWindowsMainService { // Handle folders to add by looking for the last active workspace (not on initial startup) if (!openConfig.initialStartup && foldersToAdd.length > 0) { - const lastActiveWindow = this.getLastActiveWindow(); + const authority = getRemoteAuthority(foldersToAdd[0]); + const lastActiveWindow = this.getLastActiveWindowForAuthority(authority); if (lastActiveWindow) { usedWindows.push(this.doAddFoldersToExistingWindow(lastActiveWindow, foldersToAdd)); } @@ -552,7 +563,8 @@ export class WindowsManager implements IWindowsMainService { // Find suitable window or folder path to open files in const fileToCheck = fileInputs.filesToOpen[0] || fileInputs.filesToCreate[0] || fileInputs.filesToDiff[0]; - const windows = WindowsManager.WINDOWS; + // only look at the windows with correct authority + const windows = WindowsManager.WINDOWS.filter(w => w.remoteAuthority === fileInputs.remoteAuthority); let bestWindowOrFolder = findBestWindowOrFolderForFile({ windows, @@ -595,6 +607,7 @@ export class WindowsManager implements IWindowsMainService { initialStartup: openConfig.initialStartup, fileInputs, forceNewWindow: true, + remoteAuthority: fileInputs.remoteAuthority, forceNewTabbedWindow: openConfig.forceNewTabbedWindow })); @@ -611,7 +624,7 @@ export class WindowsManager implements IWindowsMainService { const windowsOnWorkspace = arrays.coalesce(allWorkspacesToOpen.map(workspaceToOpen => findWindowOnWorkspace(WindowsManager.WINDOWS, workspaceToOpen))); if (windowsOnWorkspace.length > 0) { const windowOnWorkspace = windowsOnWorkspace[0]; - const fileInputsForWindow = fileInputs; + const fileInputsForWindow = (fileInputs && fileInputs.remoteAuthority === windowOnWorkspace.remoteAuthority) ? fileInputs : void 0; // Do open files usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, windowOnWorkspace, fileInputsForWindow)); @@ -630,7 +643,7 @@ export class WindowsManager implements IWindowsMainService { return; // ignore folders that are already open } - const fileInputsForWindow = fileInputs; + const fileInputsForWindow = (fileInputs && !fileInputs.remoteAuthority) ? fileInputs : void 0; // Do open folder usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, { workspace: workspaceToOpen }, openFolderInNewWindow, fileInputsForWindow)); @@ -653,7 +666,7 @@ export class WindowsManager implements IWindowsMainService { const windowsOnFolderPath = arrays.coalesce(allFoldersToOpen.map(folderToOpen => findWindowOnWorkspace(WindowsManager.WINDOWS, folderToOpen))); if (windowsOnFolderPath.length > 0) { const windowOnFolderPath = windowsOnFolderPath[0]; - const fileInputsForWindow = fileInputs; + const fileInputsForWindow = fileInputs && fileInputs.remoteAuthority === windowOnFolderPath.remoteAuthority ? fileInputs : void 0; // Do open files usedWindows.push(this.doOpenFilesInExistingWindow(openConfig, windowOnFolderPath, fileInputsForWindow)); @@ -673,10 +686,11 @@ export class WindowsManager implements IWindowsMainService { return; // ignore folders that are already open } - const fileInputsForWindow = fileInputs; + const remoteAuthority = getRemoteAuthority(folderToOpen); + const fileInputsForWindow = (fileInputs && fileInputs.remoteAuthority === remoteAuthority) ? fileInputs : void 0; // Do open folder - usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, { folderUri: folderToOpen }, openFolderInNewWindow, fileInputsForWindow)); + usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, { folderUri: folderToOpen, remoteAuthority }, openFolderInNewWindow, fileInputsForWindow)); // Reset these because we handled them if (fileInputsForWindow) { @@ -690,13 +704,15 @@ export class WindowsManager implements IWindowsMainService { // Handle empty to restore if (emptyToRestore.length > 0) { emptyToRestore.forEach(emptyWindowBackupInfo => { - const fileInputsForWindow = fileInputs; + const remoteAuthority = emptyWindowBackupInfo.remoteAuthority; + const fileInputsForWindow = (fileInputs && fileInputs.remoteAuthority === remoteAuthority) ? fileInputs : void 0; usedWindows.push(this.openInBrowserWindow({ userEnv: openConfig.userEnv, cli: openConfig.cli, initialStartup: openConfig.initialStartup, fileInputs: fileInputsForWindow, + remoteAuthority, forceNewWindow: true, forceNewTabbedWindow: openConfig.forceNewTabbedWindow, emptyWindowBackupInfo @@ -716,11 +732,13 @@ export class WindowsManager implements IWindowsMainService { if (fileInputs && !emptyToOpen) { emptyToOpen++; } + const remoteAuthority = fileInputs ? fileInputs.remoteAuthority : (openConfig.cli && openConfig.cli.remote || void 0); for (let i = 0; i < emptyToOpen; i++) { usedWindows.push(this.openInBrowserWindow({ userEnv: openConfig.userEnv, cli: openConfig.cli, initialStartup: openConfig.initialStartup, + remoteAuthority, forceNewWindow: openFolderInNewWindow, forceNewTabbedWindow: openConfig.forceNewTabbedWindow, fileInputs @@ -777,6 +795,7 @@ export class WindowsManager implements IWindowsMainService { workspace: folderOrWorkspace.workspace, folderUri: folderOrWorkspace.folderUri, fileInputs, + remoteAuthority: folderOrWorkspace.remoteAuthority, forceNewWindow, forceNewTabbedWindow: openConfig.forceNewTabbedWindow, windowToUse @@ -821,7 +840,7 @@ export class WindowsManager implements IWindowsMainService { const workspace = this.workspacesMainService.createWorkspaceSync(foldersToOpen.map(folder => ({ uri: folder.folderUri }))); // Add workspace and remove folders thereby - windowsToOpen.push({ workspace }); + windowsToOpen.push({ workspace, remoteAuthority: foldersToOpen[0].remoteAuthority }); windowsToOpen = windowsToOpen.filter(path => !path.folderUri); } } @@ -869,7 +888,7 @@ export class WindowsManager implements IWindowsMainService { private doExtractPathsFromCLI(cli: ParsedArgs): IPath[] { const pathsToOpen: IPathToOpen[] = []; - const parseOptions: IPathParseOptions = { ignoreFileNotFound: true, gotoLineMode: cli.goto }; + const parseOptions: IPathParseOptions = { ignoreFileNotFound: true, gotoLineMode: cli.goto, remoteAuthority: cli.remote || void 0 }; // folder uris const folderUris = asArray(cli['folder-uri']); @@ -932,17 +951,17 @@ export class WindowsManager implements IWindowsMainService { const windowsToOpen: IPathToOpen[] = []; for (const openedWindow of openedWindows) { if (openedWindow.workspace) { // Workspaces - const pathToOpen = this.parsePath(openedWindow.workspace.configPath); + const pathToOpen = this.parsePath(openedWindow.workspace.configPath, { remoteAuthority: openedWindow.remoteAuthority }); if (pathToOpen && pathToOpen.workspace) { windowsToOpen.push(pathToOpen); } } else if (openedWindow.folderUri) { // Folders - const pathToOpen = this.parseUri(openedWindow.folderUri, false); + const pathToOpen = this.parseUri(openedWindow.folderUri, false, { remoteAuthority: openedWindow.remoteAuthority }); if (pathToOpen && pathToOpen.folderUri) { windowsToOpen.push(pathToOpen); } } else if (restoreWindows !== 'folders' && openedWindow.backupPath) { // Windows that were Empty - windowsToOpen.push({ backupPath: openedWindow.backupPath }); + windowsToOpen.push({ backupPath: openedWindow.backupPath, remoteAuthority: openedWindow.remoteAuthority }); } } @@ -994,6 +1013,10 @@ export class WindowsManager implements IWindowsMainService { if (uri.scheme === Schemas.file) { return this.parsePath(uri.fsPath, options); } + + // open remote if either specified in the cli or if it's a remotehost URI + const remoteAuthority = options && options.remoteAuthority || getRemoteAuthority(uri); + // normalize URI uri = normalizePath(uri); if (endsWith(uri.path, '/')) { @@ -1005,15 +1028,18 @@ export class WindowsManager implements IWindowsMainService { return { fileUri: uri.with({ path: parsedPath.path }), lineNumber: parsedPath.line, - columnNumber: parsedPath.column + columnNumber: parsedPath.column, + remoteAuthority }; } return { - fileUri: uri + fileUri: uri, + remoteAuthority }; } return { - folderUri: uri + folderUri: uri, + remoteAuthority }; } @@ -1030,6 +1056,9 @@ export class WindowsManager implements IWindowsMainService { anyPath = parsedPath.path; } + // open remote if either specified in the cli even if it is a local file. TODO: Future idea: resolve in remote host context. + const remoteAuthority = options && options.remoteAuthority; + const candidate = normalize(anyPath); try { const candidateStat = fs.statSync(candidate); @@ -1040,7 +1069,7 @@ export class WindowsManager implements IWindowsMainService { if (!options || !options.forceOpenWorkspaceAsFile) { const workspace = this.workspacesMainService.resolveWorkspaceSync(candidate); if (workspace) { - return { workspace: { id: workspace.id, configPath: workspace.configPath } }; + return { workspace: { id: workspace.id, configPath: workspace.configPath }, remoteAuthority }; } } @@ -1048,7 +1077,8 @@ export class WindowsManager implements IWindowsMainService { return { fileUri: URI.file(candidate), lineNumber: gotoLineMode ? parsedPath.line : void 0, - columnNumber: gotoLineMode ? parsedPath.column : void 0 + columnNumber: gotoLineMode ? parsedPath.column : void 0, + remoteAuthority }; } @@ -1057,7 +1087,8 @@ export class WindowsManager implements IWindowsMainService { // over to us) else if (candidateStat.isDirectory()) { return { - folderUri: URI.file(candidate) + folderUri: URI.file(candidate), + remoteAuthority }; } } @@ -1066,7 +1097,7 @@ export class WindowsManager implements IWindowsMainService { this.historyMainService.removeFromRecentlyOpened([fileUri]); // since file does not seem to exist anymore, remove from recent if (options && options.ignoreFileNotFound) { - return { fileUri, createFilePath: true }; // assume this is a file that does not yet exist + return { fileUri, createFilePath: true, remoteAuthority }; // assume this is a file that does not yet exist } } @@ -1181,6 +1212,7 @@ export class WindowsManager implements IWindowsMainService { configuration.isInitialStartup = options.initialStartup; configuration.workspace = options.workspace; configuration.folderUri = options.folderUri; + configuration.remoteAuthority = options.remoteAuthority; const fileInputs = options.fileInputs; if (fileInputs) { @@ -1286,7 +1318,7 @@ export class WindowsManager implements IWindowsMainService { configuration.backupPath = this.backupMainService.registerFolderBackupSync(configuration.folderUri); } else { const backupFolder = options.emptyWindowBackupInfo && options.emptyWindowBackupInfo.backupFolder; - configuration.backupPath = this.backupMainService.registerEmptyWindowBackupSync({ backupFolder }); + configuration.backupPath = this.backupMainService.registerEmptyWindowBackupSync({ backupFolder, remoteAuthority: configuration.remoteAuthority }); } } @@ -1444,7 +1476,8 @@ export class WindowsManager implements IWindowsMainService { closeWorkspace(win: ICodeWindow): void { this.openInBrowserWindow({ cli: this.environmentService.args, - windowToUse: win + windowToUse: win, + remoteAuthority: win.remoteAuthority }); } @@ -1528,8 +1561,16 @@ export class WindowsManager implements IWindowsMainService { return getLastActiveWindow(WindowsManager.WINDOWS); } + getLastActiveWindowForAuthority(remoteAuthority: string): ICodeWindow { + return getLastActiveWindow(WindowsManager.WINDOWS.filter(w => w.remoteAuthority === remoteAuthority)); + } + openNewWindow(context: OpenContext, options?: INewWindowOptions): ICodeWindow[] { let cli = this.environmentService.args; + let remote = options && options.remoteAuthority || void 0; + if (cli && (cli.remote !== remote)) { + cli = { ...cli, remote }; + } return this.open({ context, cli, forceNewWindow: true, forceEmpty: true }); } @@ -1600,6 +1641,13 @@ export class WindowsManager implements IWindowsMainService { private onWindowError(window: ICodeWindow, error: WindowError): void { this.logService.error(error === WindowError.CRASHED ? '[VS Code]: render process crashed!' : '[VS Code]: detected unresponsive'); + /* __GDPR__ + "windowerror" : { + "type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + } + */ + this.telemetryService.publicLog('windowerror', { type: error }); + // Unresponsive if (error === WindowError.UNRESPONSIVE) { this.dialogs.showMessageBox({ diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 2b1c0bcda..9154f6813 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { spawn, ChildProcess } from 'child_process'; -import { TPromise } from 'vs/base/common/winjs.base'; import { assign } from 'vs/base/common/objects'; import { parseCLIProcessArgv, buildHelpMessage } from 'vs/platform/environment/node/argv'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; @@ -19,7 +18,8 @@ import { resolveTerminalEncoding } from 'vs/base/node/encoding'; import * as iconv from 'iconv-lite'; import { writeFileAndFlushSync } from 'vs/base/node/extfs'; import { isWindows } from 'vs/base/common/platform'; -import { ProfilingSession } from 'v8-inspect-profiler'; +import { ProfilingSession, Target } from 'v8-inspect-profiler'; +import { createWaitMarkerFile } from 'vs/code/node/wait'; function shouldSpawnCliProcess(argv: ParsedArgs): boolean { return !!argv['install-source'] @@ -29,7 +29,7 @@ function shouldSpawnCliProcess(argv: ParsedArgs): boolean { } interface IMainCli { - main: (argv: ParsedArgs) => TPromise; + main: (argv: ParsedArgs) => Thenable; } export async function main(argv: string[]): Promise { @@ -39,7 +39,7 @@ export async function main(argv: string[]): Promise { args = parseCLIProcessArgv(argv); } catch (err) { console.error(err.message); - return TPromise.as(null); + return; } // Help @@ -54,8 +54,9 @@ export async function main(argv: string[]): Promise { // Extensions Management else if (shouldSpawnCliProcess(args)) { - const mainCli = new TPromise(c => require(['vs/code/node/cliProcessMain'], c)); - return mainCli.then(cli => cli.main(args)); + const cli = await new Promise((c, e) => require(['vs/code/node/cliProcessMain'], c, e)); + await cli.main(args); + return; } // Write File @@ -70,13 +71,13 @@ export async function main(argv: string[]): Promise { !fs.existsSync(source) || !fs.statSync(source).isFile() || // make sure source exists as file !fs.existsSync(target) || !fs.statSync(target).isFile() // make sure target exists as file ) { - return TPromise.wrapError(new Error('Using --file-write with invalid arguments.')); + throw new Error('Using --file-write with invalid arguments.'); } try { // Check for readonly status and chmod if so if we are told so - let targetMode: number; + let targetMode: number = 0; let restoreMode = false; if (!!args['file-chmod']) { targetMode = fs.statSync(target).mode; @@ -106,10 +107,9 @@ export async function main(argv: string[]): Promise { fs.chmodSync(target, targetMode); } } catch (error) { - return TPromise.wrapError(new Error(`Using --file-write resulted in an error: ${error}`)); + error.message = `Error using --file-write: ${error.message}`; + throw error; } - - return TPromise.as(null); } // Just Code @@ -127,15 +127,15 @@ export async function main(argv: string[]): Promise { if (verbose) { env['ELECTRON_ENABLE_LOGGING'] = '1'; - processCallbacks.push(child => { + processCallbacks.push(async child => { child.stdout.on('data', (data: Buffer) => console.log(data.toString('utf8').trim())); child.stderr.on('data', (data: Buffer) => console.log(data.toString('utf8').trim())); - return new TPromise(c => child.once('exit', () => c(null))); + await new Promise(c => child.once('exit', () => c())); }); } - let stdinWithoutTty: boolean; + let stdinWithoutTty: boolean = false; try { stdinWithoutTty = !process.stdin.isTTY; // Via https://twitter.com/MylesBorins/status/782009479382626304 } catch (error) { @@ -161,7 +161,7 @@ export async function main(argv: string[]): Promise { stdinFilePath = paths.join(os.tmpdir(), `code-stdin-${Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 3)}.txt`); // open tmp file for writing - let stdinFileError: Error; + let stdinFileError: Error | undefined; let stdinFileStream: fs.WriteStream; try { stdinFileStream = fs.createWriteStream(stdinFilePath); @@ -198,7 +198,7 @@ export async function main(argv: string[]): Promise { // If the user pipes data via stdin but forgot to add the "-" argument, help by printing a message // if we detect that data flows into via stdin after a certain timeout. else if (args._.length === 0) { - processCallbacks.push(child => new TPromise(c => { + processCallbacks.push(child => new Promise(c => { const dataListener = () => { if (isWindows) { console.log(`Run with '${product.applicationName} -' to read output from another program (e.g. 'echo Hello World | ${product.applicationName} -').`); @@ -226,24 +226,11 @@ export async function main(argv: string[]): Promise { // and pass it over to the starting instance. We can use this file // to wait for it to be deleted to monitor that the edited file // is closed and then exit the waiting process. - let waitMarkerFilePath: string; + let waitMarkerFilePath: string | undefined; if (args.wait) { - let waitMarkerError: Error; - const randomTmpFile = paths.join(os.tmpdir(), Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10)); - try { - fs.writeFileSync(randomTmpFile, ''); - waitMarkerFilePath = randomTmpFile; + waitMarkerFilePath = await createWaitMarkerFile(verbose); + if (waitMarkerFilePath) { argv.push('--waitMarkerFilePath', waitMarkerFilePath); - } catch (error) { - waitMarkerError = error; - } - - if (verbose) { - if (waitMarkerError) { - console.error(`Failed to create marker file for --wait: ${waitMarkerError.toString()}`); - } else { - console.log(`Marker file for --wait created: ${waitMarkerFilePath}`); - } } } @@ -274,7 +261,7 @@ export async function main(argv: string[]): Promise { processCallbacks.push(async _child => { class Profiler { - static async start(name: string, filenamePrefix: string, opts: { port: number, tries?: number, chooseTab?: Function }) { + static async start(name: string, filenamePrefix: string, opts: { port: number, tries?: number, target?: (targets: Target[]) => Target }) { const profiler = await import('v8-inspect-profiler'); let session: ProfilingSession; @@ -313,8 +300,8 @@ export async function main(argv: string[]): Promise { const rendererProfileRequest = Profiler.start('renderer', filenamePrefix, { port: portRenderer, tries: 200, - chooseTab: function (targets) { - return targets.find(target => { + target: function (targets) { + return targets.filter(target => { if (!target.webSocketDebuggerUrl) { return false; } @@ -323,7 +310,7 @@ export async function main(argv: string[]): Promise { } else { return true; } - }); + })[0]; } }); @@ -370,13 +357,13 @@ export async function main(argv: string[]): Promise { const child = spawn(process.execPath, argv.slice(2), options); if (args.wait && waitMarkerFilePath) { - return new TPromise(c => { + return new Promise(c => { // Complete when process exits - child.once('exit', () => c(null)); + child.once('exit', () => c(void 0)); // Complete when wait marker file is deleted - whenDeleted(waitMarkerFilePath).then(c, c); + whenDeleted(waitMarkerFilePath!).then(c, c); }).then(() => { // Make sure to delete the tmp stdin file if we have any @@ -386,10 +373,8 @@ export async function main(argv: string[]): Promise { }); } - return TPromise.join(processCallbacks.map(callback => callback(child))); + return Promise.all(processCallbacks.map(callback => callback(child))); } - - return TPromise.as(null); } function eventuallyExit(code: number): void { diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 084ebab7f..6ef613ffe 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -19,7 +19,7 @@ import { InstantiationService } from 'vs/platform/instantiation/common/instantia import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IExtensionManagementService, IExtensionGalleryService, IExtensionManifest, IGalleryExtension, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { ExtensionManagementService, validateLocalExtension } from 'vs/platform/extensionManagement/node/extensionManagementService'; +import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { combinedAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; @@ -42,6 +42,7 @@ import { CommandLineDialogService } from 'vs/platform/dialogs/node/dialogService import { areSameExtensions, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; +import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; const notFound = (id: string) => localize('notFound', "Extension '{0}' not found.", id); const notInstalled = (id: string) => localize('notInstalled', "Extension '{0}' is not installed.", id); @@ -192,7 +193,7 @@ class Main { } const zipPath = path.isAbsolute(extensionDescription) ? extensionDescription : path.join(process.cwd(), extensionDescription); - const manifest = await validateLocalExtension(zipPath); + const manifest = await getManifest(zipPath); return getId(manifest); } @@ -259,7 +260,7 @@ export function main(argv: ParsedArgs): TPromise { piiPaths: [appRoot, extensionsPath] }; - services.set(ITelemetryService, new SyncDescriptor(TelemetryService, config)); + services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config])); } else { services.set(ITelemetryService, NullTelemetryService); } diff --git a/src/vs/code/node/paths.ts b/src/vs/code/node/paths.ts index 5774c6b62..f322145cb 100644 --- a/src/vs/code/node/paths.ts +++ b/src/vs/code/node/paths.ts @@ -35,7 +35,7 @@ function doValidatePaths(args: string[], gotoLineMode?: boolean): string[] { const result = args.map(arg => { let pathCandidate = String(arg); - let parsedPath: IPathWithLineAndColumn; + let parsedPath: IPathWithLineAndColumn | undefined = undefined; if (gotoLineMode) { parsedPath = parseLineAndColumnAware(pathCandidate); pathCandidate = parsedPath.path; @@ -52,7 +52,7 @@ function doValidatePaths(args: string[], gotoLineMode?: boolean): string[] { return null; // do not allow invalid file names } - if (gotoLineMode) { + if (gotoLineMode && parsedPath) { parsedPath.path = sanitizedFilePath; return toPath(parsedPath); @@ -62,7 +62,7 @@ function doValidatePaths(args: string[], gotoLineMode?: boolean): string[] { }); const caseInsensitive = platform.isWindows || platform.isMacintosh; - const distinct = arrays.distinct(result, e => e && caseInsensitive ? e.toLowerCase() : e); + const distinct = arrays.distinct(result, e => e && caseInsensitive ? e.toLowerCase() : (e || '')); return arrays.coalesce(distinct); } @@ -98,7 +98,7 @@ export interface IPathWithLineAndColumn { export function parseLineAndColumnAware(rawPath: string): IPathWithLineAndColumn { const segments = rawPath.split(':'); // C:\file.txt:: - let path: string; + let path: string | null = null; let line: number | null = null; let column: number | null = null; diff --git a/src/vs/code/node/wait.ts b/src/vs/code/node/wait.ts new file mode 100644 index 000000000..06705d72b --- /dev/null +++ b/src/vs/code/node/wait.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { join } from 'path'; +import { tmpdir } from 'os'; +import { writeFile } from 'vs/base/node/pfs'; + +export function createWaitMarkerFile(verbose?: boolean): Promise { + const randomWaitMarkerPath = join(tmpdir(), Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10)); + + return writeFile(randomWaitMarkerPath, '').then(() => { + if (verbose) { + console.log(`Marker file for --wait created: ${randomWaitMarkerPath}`); + } + + return randomWaitMarkerPath; + }, error => { + if (verbose) { + console.error(`Failed to create marker file for --wait: ${error}`); + } + + return Promise.resolve(void 0); + }); +} \ No newline at end of file diff --git a/src/vs/code/node/windowsFinder.ts b/src/vs/code/node/windowsFinder.ts index 02ee9c401..8541b69db 100644 --- a/src/vs/code/node/windowsFinder.ts +++ b/src/vs/code/node/windowsFinder.ts @@ -29,7 +29,7 @@ export interface IBestWindowOrFolderOptions { workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace; } -export function findBestWindowOrFolderForFile({ windows, newWindow, reuseWindow, context, fileUri, workspaceResolver }: IBestWindowOrFolderOptions): W { +export function findBestWindowOrFolderForFile({ windows, newWindow, reuseWindow, context, fileUri, workspaceResolver }: IBestWindowOrFolderOptions): W | null { if (!newWindow && fileUri && (context === OpenContext.DESKTOP || context === OpenContext.CLI || context === OpenContext.DOCK)) { const windowOnFilePath = findWindowOnFilePath(windows, fileUri, workspaceResolver); if (windowOnFilePath) { @@ -39,13 +39,13 @@ export function findBestWindowOrFolderForFile({ windows return !newWindow ? getLastActiveWindow(windows) : null; } -function findWindowOnFilePath(windows: W[], fileUri: URI, workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace): W { +function findWindowOnFilePath(windows: W[], fileUri: URI, workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace): W | null { // First check for windows with workspaces that have a parent folder of the provided path opened const workspaceWindows = windows.filter(window => !!window.openedWorkspace); for (let i = 0; i < workspaceWindows.length; i++) { const window = workspaceWindows[i]; - const resolvedWorkspace = workspaceResolver(window.openedWorkspace); + const resolvedWorkspace = workspaceResolver(window.openedWorkspace!); if (resolvedWorkspace && resolvedWorkspace.folders.some(folder => isEqualOrParent(fileUri, folder.uri))) { return window; } @@ -54,7 +54,7 @@ function findWindowOnFilePath(windows: W[], fileUri: UR // Then go with single folder windows that are parent of the provided file path const singleFolderWindowsOnFilePath = windows.filter(window => window.openedFolderUri && isEqualOrParent(fileUri, window.openedFolderUri)); if (singleFolderWindowsOnFilePath.length) { - return singleFolderWindowsOnFilePath.sort((a, b) => -(a.openedFolderUri.path.length - b.openedFolderUri.path.length))[0]; + return singleFolderWindowsOnFilePath.sort((a, b) => -(a.openedFolderUri!.path.length - b.openedFolderUri!.path.length))[0]; } return null; @@ -66,7 +66,7 @@ export function getLastActiveWindow(windows: W[]): W { return windows.filter(window => window.lastFocusTime === lastFocusedDate)[0]; } -export function findWindowOnWorkspace(windows: W[], workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)): W { +export function findWindowOnWorkspace(windows: W[], workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)): W | null { if (isSingleFolderWorkspaceIdentifier(workspace)) { for (const window of windows) { // match on folder @@ -87,17 +87,17 @@ export function findWindowOnWorkspace(windows: W[], wor return null; } -export function findWindowOnExtensionDevelopmentPath(windows: W[], extensionDevelopmentPath: string): W { +export function findWindowOnExtensionDevelopmentPath(windows: W[], extensionDevelopmentPath: string): W | null { for (const window of windows) { // match on extension development path. The path can be a path or uri string, using paths.isEqual is not 100% correct but good enough - if (paths.isEqual(window.extensionDevelopmentPath, extensionDevelopmentPath, !platform.isLinux /* ignorecase */)) { + if (window.extensionDevelopmentPath && paths.isEqual(window.extensionDevelopmentPath, extensionDevelopmentPath, !platform.isLinux /* ignorecase */)) { return window; } } return null; } -export function findWindowOnWorkspaceOrFolderUri(windows: W[], uri: URI): W { +export function findWindowOnWorkspaceOrFolderUri(windows: W[], uri: URI): W | null { if (!uri) { return null; } diff --git a/src/vs/css.build.js b/src/vs/css.build.js index 4a617c57e..146ebe332 100644 --- a/src/vs/css.build.js +++ b/src/vs/css.build.js @@ -319,7 +319,7 @@ var CSSBuildLoaderPlugin; global.cssInlinedResources = global.cssInlinedResources || []; var normalizedFSPath = fsPath.replace(/\\/g, '/'); if (global.cssInlinedResources.indexOf(normalizedFSPath) >= 0) { - console.warn('CSS INLINING IMAGE AT ' + fsPath + ' MORE THAN ONCE. CONSIDER CONSOLIDATING CSS RULES'); + // console.warn('CSS INLINING IMAGE AT ' + fsPath + ' MORE THAN ONCE. CONSIDER CONSOLIDATING CSS RULES'); } global.cssInlinedResources.push(normalizedFSPath); var MIME = /\.svg$/.test(url) ? 'image/svg+xml' : 'image/png'; diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index d21577afb..1a8af3537 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -5,7 +5,7 @@ import * as browser from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; -import { StandardMouseWheelEvent } from 'vs/base/browser/mouseEvent'; +import { StandardWheelEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { RunOnceScheduler, TimeoutTimer } from 'vs/base/common/async'; import { Disposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; @@ -108,11 +108,11 @@ export class MouseHandler extends ViewEventHandler { this._register(mouseEvents.onMouseDown(this.viewHelper.viewDomNode, (e) => this._onMouseDown(e))); - let onMouseWheel = (browserEvent: MouseWheelEvent) => { + let onMouseWheel = (browserEvent: IMouseWheelEvent) => { if (!this._context.configuration.editor.viewInfo.mouseWheelZoom) { return; } - let e = new StandardMouseWheelEvent(browserEvent); + let e = new StandardWheelEvent(browserEvent); if (e.browserEvent!.ctrlKey || e.browserEvent!.metaKey) { let zoomLevel: number = EditorZoom.getZoomLevel(); let delta = e.deltaY > 0 ? 1 : -1; @@ -122,7 +122,6 @@ export class MouseHandler extends ViewEventHandler { } }; this._register(dom.addDisposableListener(this.viewHelper.viewDomNode, 'mousewheel', onMouseWheel, true)); - this._register(dom.addDisposableListener(this.viewHelper.viewDomNode, 'DOMMouseScroll', onMouseWheel, true)); this._context.addEventHandler(this); } diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 02f01afea..991a44798 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -609,7 +609,7 @@ export interface ICodeEditor extends editorCommon.IEditor { executeEdits(source: string, edits: IIdentifiedSingleEditOperation[], endCursorState?: Selection[]): boolean; /** - * Execute multiple (concommitent) commands on the editor. + * Execute multiple (concomitant) commands on the editor. * @param source The source of the call. * @param command The commands to execute */ @@ -695,7 +695,7 @@ export interface ICodeEditor extends editorCommon.IEditor { addContentWidget(widget: IContentWidget): void; /** * Layout/Reposition a content widget. This is a ping to the editor to call widget.getPosition() - * and update appropiately. + * and update appropriately. */ layoutContentWidget(widget: IContentWidget): void; /** @@ -709,7 +709,7 @@ export interface ICodeEditor extends editorCommon.IEditor { addOverlayWidget(widget: IOverlayWidget): void; /** * Layout/Reposition an overlay widget. This is a ping to the editor to call widget.getPosition() - * and update appropiately. + * and update appropriately. */ layoutOverlayWidget(widget: IOverlayWidget): void; /** @@ -747,7 +747,7 @@ export interface ICodeEditor extends editorCommon.IEditor { * The result position takes scrolling into account and is relative to the top left corner of the editor. * Explanation 1: the results of this method will change for the same `position` if the user scrolls the editor. * Explanation 2: the results of this method will not change if the container of the editor gets repositioned. - * Warning: the results of this method are innacurate for positions that are outside the current editor viewport. + * Warning: the results of this method are inaccurate for positions that are outside the current editor viewport. */ getScrolledVisiblePosition(position: IPosition): { top: number; left: number; height: number; } | null; @@ -812,7 +812,7 @@ export interface IActiveCodeEditor extends ICodeEditor { * The result position takes scrolling into account and is relative to the top left corner of the editor. * Explanation 1: the results of this method will change for the same `position` if the user scrolls the editor. * Explanation 2: the results of this method will not change if the container of the editor gets repositioned. - * Warning: the results of this method are innacurate for positions that are outside the current editor viewport. + * Warning: the results of this method are inaccurate for positions that are outside the current editor viewport. */ getScrolledVisiblePosition(position: IPosition): { top: number; left: number; height: number; }; } diff --git a/src/vs/editor/browser/services/codeEditorServiceImpl.ts b/src/vs/editor/browser/services/codeEditorServiceImpl.ts index f832af4fc..7ddea5230 100644 --- a/src/vs/editor/browser/services/codeEditorServiceImpl.ts +++ b/src/vs/editor/browser/services/codeEditorServiceImpl.ts @@ -141,18 +141,18 @@ class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { this.refCount = 0; this._disposables = []; - let createCSSRules = (type: ModelDecorationCSSRuleType) => { - let rules = new DecorationCSSRules(type, providerArgs, themeService); + const createCSSRules = (type: ModelDecorationCSSRuleType) => { + const rules = new DecorationCSSRules(type, providerArgs, themeService); + this._disposables.push(rules); if (rules.hasContent) { - this._disposables.push(rules); return rules.className; } return void 0; }; - let createInlineCSSRules = (type: ModelDecorationCSSRuleType) => { - let rules = new DecorationCSSRules(type, providerArgs, themeService); + const createInlineCSSRules = (type: ModelDecorationCSSRuleType) => { + const rules = new DecorationCSSRules(type, providerArgs, themeService); + this._disposables.push(rules); if (rules.hasContent) { - this._disposables.push(rules); return { className: rules.className, hasLetterSpacing: rules.hasLetterSpacing }; } return null; @@ -401,11 +401,7 @@ class DecorationCSSRules { if (typeof opts !== 'undefined') { this.collectBorderSettingsCSSText(opts, cssTextArr); if (typeof opts.contentIconPath !== 'undefined') { - if (typeof opts.contentIconPath === 'string') { - cssTextArr.push(strings.format(_CSS_MAP.contentIconPath, URI.file(opts.contentIconPath).toString().replace(/'/g, '%27'))); - } else { - cssTextArr.push(strings.format(_CSS_MAP.contentIconPath, URI.revive(opts.contentIconPath).toString(true).replace(/'/g, '%27'))); - } + cssTextArr.push(strings.format(_CSS_MAP.contentIconPath, URI.revive(opts.contentIconPath).toString(true).replace(/'/g, '%27'))); } if (typeof opts.contentText === 'string') { const truncated = opts.contentText.match(/^.*$/m)![0]; // only take first line @@ -432,11 +428,7 @@ class DecorationCSSRules { let cssTextArr: string[] = []; if (typeof opts.gutterIconPath !== 'undefined') { - if (typeof opts.gutterIconPath === 'string') { - cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, URI.file(opts.gutterIconPath).toString())); - } else { - cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, URI.revive(opts.gutterIconPath).toString(true).replace(/'/g, '%27'))); - } + cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, URI.revive(opts.gutterIconPath).toString(true).replace(/'/g, '%27'))); if (typeof opts.gutterIconSize !== 'undefined') { cssTextArr.push(strings.format(_CSS_MAP.gutterIconSize, opts.gutterIconSize)); } diff --git a/src/vs/editor/browser/viewParts/lines/viewLines.ts b/src/vs/editor/browser/viewParts/lines/viewLines.ts index 18402d003..4b325d52b 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLines.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLines.ts @@ -57,7 +57,7 @@ class HorizontalRevealRequest { export class ViewLines extends ViewPart implements IVisibleLinesHost, IViewLines { /** - * Adds this ammount of pixels to the right of lines (no-one wants to type near the edge of the viewport) + * Adds this amount of pixels to the right of lines (no-one wants to type near the edge of the viewport) */ private static readonly HORIZONTAL_EXTRA_PX = 30; diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css index a0ecc2197..d77870c24 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.css @@ -13,6 +13,11 @@ overflow: hidden; } +/* -- smooth-caret-animation -- */ +.monaco-editor .cursors-layer.cursor-smooth-caret-animation > .cursor { + transition: 80ms; +} + /* -- block-outline-style -- */ .monaco-editor .cursors-layer.cursor-block-outline-style > .cursor { box-sizing: border-box; diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts index b654ed133..a39d17089 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts @@ -23,6 +23,7 @@ export class ViewCursors extends ViewPart { private _readOnly: boolean; private _cursorBlinking: TextEditorCursorBlinkingStyle; private _cursorStyle: TextEditorCursorStyle; + private _cursorSmoothCaretAnimation: boolean; private _selectionIsEmpty: boolean; private _isVisible: boolean; @@ -45,6 +46,7 @@ export class ViewCursors extends ViewPart { this._readOnly = this._context.configuration.editor.readOnly; this._cursorBlinking = this._context.configuration.editor.viewInfo.cursorBlinking; this._cursorStyle = this._context.configuration.editor.viewInfo.cursorStyle; + this._cursorSmoothCaretAnimation = this._context.configuration.editor.viewInfo.cursorSmoothCaretAnimation; this._selectionIsEmpty = true; this._primaryCursor = new ViewCursor(this._context); @@ -295,6 +297,9 @@ export class ViewCursors extends ViewPart { } else { result += ' cursor-solid'; } + if (this._cursorSmoothCaretAnimation) { + result += ' cursor-smooth-caret-animation'; + } return result; } diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 15cef1d52..31225b97d 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -15,7 +15,6 @@ import { Emitter, Event } from 'vs/base/common/event'; import { hash } from 'vs/base/common/hash'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; -import { mark } from 'vs/base/common/performance'; import { Configuration } from 'vs/editor/browser/config/configuration'; import { CoreEditorCommand } from 'vs/editor/browser/controller/coreCommands'; import * as editorBrowser from 'vs/editor/browser/editorBrowser'; @@ -286,7 +285,6 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._contentWidgets = {}; this._overlayWidgets = {}; - mark('editor/start/contrib'); let contributions: IEditorContributionCtor[]; if (Array.isArray(codeEditorWidgetOptions.contributions)) { contributions = codeEditorWidgetOptions.contributions; @@ -302,7 +300,6 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE onUnexpectedError(err); } } - mark('editor/end/contrib'); EditorExtensionsRegistry.getEditorActions().forEach((action) => { const internalAction = new InternalEditorAction( @@ -1639,6 +1636,7 @@ export class EditorModeContext extends Disposable { private _hasCodeActionsProvider: IContextKey; private _hasCodeLensProvider: IContextKey; private _hasDefinitionProvider: IContextKey; + private _hasDeclarationProvider: IContextKey; private _hasImplementationProvider: IContextKey; private _hasTypeDefinitionProvider: IContextKey; private _hasHoverProvider: IContextKey; @@ -1663,6 +1661,7 @@ export class EditorModeContext extends Disposable { this._hasCodeActionsProvider = EditorContextKeys.hasCodeActionsProvider.bindTo(contextKeyService); this._hasCodeLensProvider = EditorContextKeys.hasCodeLensProvider.bindTo(contextKeyService); this._hasDefinitionProvider = EditorContextKeys.hasDefinitionProvider.bindTo(contextKeyService); + this._hasDeclarationProvider = EditorContextKeys.hasDeclarationProvider.bindTo(contextKeyService); this._hasImplementationProvider = EditorContextKeys.hasImplementationProvider.bindTo(contextKeyService); this._hasTypeDefinitionProvider = EditorContextKeys.hasTypeDefinitionProvider.bindTo(contextKeyService); this._hasHoverProvider = EditorContextKeys.hasHoverProvider.bindTo(contextKeyService); @@ -1686,6 +1685,7 @@ export class EditorModeContext extends Disposable { this._register(modes.CodeActionProviderRegistry.onDidChange(update)); this._register(modes.CodeLensProviderRegistry.onDidChange(update)); this._register(modes.DefinitionProviderRegistry.onDidChange(update)); + this._register(modes.DeclarationProviderRegistry.onDidChange(update)); this._register(modes.ImplementationProviderRegistry.onDidChange(update)); this._register(modes.TypeDefinitionProviderRegistry.onDidChange(update)); this._register(modes.HoverProviderRegistry.onDidChange(update)); @@ -1710,6 +1710,7 @@ export class EditorModeContext extends Disposable { this._hasCodeActionsProvider.reset(); this._hasCodeLensProvider.reset(); this._hasDefinitionProvider.reset(); + this._hasDeclarationProvider.reset(); this._hasImplementationProvider.reset(); this._hasTypeDefinitionProvider.reset(); this._hasHoverProvider.reset(); @@ -1734,6 +1735,7 @@ export class EditorModeContext extends Disposable { this._hasCodeActionsProvider.set(modes.CodeActionProviderRegistry.has(model)); this._hasCodeLensProvider.set(modes.CodeLensProviderRegistry.has(model)); this._hasDefinitionProvider.set(modes.DefinitionProviderRegistry.has(model)); + this._hasDeclarationProvider.set(modes.DeclarationProviderRegistry.has(model)); this._hasImplementationProvider.set(modes.ImplementationProviderRegistry.has(model)); this._hasTypeDefinitionProvider.set(modes.TypeDefinitionProviderRegistry.has(model)); this._hasHoverProvider.set(modes.HoverProviderRegistry.has(model)); diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index d12fe3214..190862e8b 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -675,6 +675,11 @@ const editorConfiguration: IConfigurationNode = { 'default': EDITOR_DEFAULTS.viewInfo.mouseWheelZoom, 'markdownDescription': nls.localize('mouseWheelZoom', "Zoom the font of the editor when using mouse wheel and holding `Ctrl`.") }, + 'editor.cursorSmoothCaretAnimation': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.viewInfo.cursorSmoothCaretAnimation, + 'description': nls.localize('cursorSmoothCaretAnimation', "Controls whether the smooth caret animation should be enabled.") + }, 'editor.cursorStyle': { 'type': 'string', 'enum': ['block', 'block-outline', 'line', 'line-thin', 'underline', 'underline-thin'], diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 3ab37ff92..cc3971d0b 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -23,16 +23,14 @@ export interface IEditorScrollbarOptions { arrowSize?: number; /** * Render vertical scrollbar. - * Accepted values: 'auto', 'visible', 'hidden'. * Defaults to 'auto'. */ - vertical?: string; + vertical?: 'auto' | 'visible' | 'hidden'; /** * Render horizontal scrollbar. - * Accepted values: 'auto', 'visible', 'hidden'. * Defaults to 'auto'. */ - horizontal?: string; + horizontal?: 'auto' | 'visible' | 'hidden'; /** * Cast horizontal and vertical shadows when the content is scrolled. * Defaults to true. @@ -326,6 +324,11 @@ export interface IEditorOptions { * @internal */ mouseStyle?: 'text' | 'default' | 'copy'; + /** + * Enable smooth caret animation. + * Defaults to false. + */ + cursorSmoothCaretAnimation?: boolean; /** * Control the cursor style, either 'block' or 'line'. * Defaults to 'line'. @@ -939,6 +942,7 @@ export interface InternalEditorViewOptions { readonly overviewRulerBorder: boolean; readonly cursorBlinking: TextEditorCursorBlinkingStyle; readonly mouseWheelZoom: boolean; + readonly cursorSmoothCaretAnimation: boolean; readonly cursorStyle: TextEditorCursorStyle; readonly cursorWidth: number; readonly hideCursorInOverviewRuler: boolean; @@ -1245,6 +1249,7 @@ export class InternalEditorOptions { && a.overviewRulerBorder === b.overviewRulerBorder && a.cursorBlinking === b.cursorBlinking && a.mouseWheelZoom === b.mouseWheelZoom + && a.cursorSmoothCaretAnimation === b.cursorSmoothCaretAnimation && a.cursorStyle === b.cursorStyle && a.cursorWidth === b.cursorWidth && a.hideCursorInOverviewRuler === b.hideCursorInOverviewRuler @@ -1970,6 +1975,7 @@ export class EditorOptionsValidator { overviewRulerBorder: _boolean(opts.overviewRulerBorder, defaults.overviewRulerBorder), cursorBlinking: _cursorBlinkingStyleFromString(opts.cursorBlinking, defaults.cursorBlinking), mouseWheelZoom: _boolean(opts.mouseWheelZoom, defaults.mouseWheelZoom), + cursorSmoothCaretAnimation: _boolean(opts.cursorSmoothCaretAnimation, defaults.cursorSmoothCaretAnimation), cursorStyle: _cursorStyleFromString(opts.cursorStyle, defaults.cursorStyle), cursorWidth: _clampedInt(opts.cursorWidth, defaults.cursorWidth, 0, Number.MAX_VALUE), hideCursorInOverviewRuler: _boolean(opts.hideCursorInOverviewRuler, defaults.hideCursorInOverviewRuler), @@ -2089,6 +2095,7 @@ export class InternalEditorOptionsFactory { overviewRulerBorder: opts.viewInfo.overviewRulerBorder, cursorBlinking: opts.viewInfo.cursorBlinking, mouseWheelZoom: opts.viewInfo.mouseWheelZoom, + cursorSmoothCaretAnimation: opts.viewInfo.cursorSmoothCaretAnimation, cursorStyle: opts.viewInfo.cursorStyle, cursorWidth: opts.viewInfo.cursorWidth, hideCursorInOverviewRuler: opts.viewInfo.hideCursorInOverviewRuler, @@ -2550,6 +2557,7 @@ export const EDITOR_DEFAULTS: IValidatedEditorOptions = { overviewRulerBorder: true, cursorBlinking: TextEditorCursorBlinkingStyle.Blink, mouseWheelZoom: false, + cursorSmoothCaretAnimation: false, cursorStyle: TextEditorCursorStyle.Line, cursorWidth: 0, hideCursorInOverviewRuler: false, diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index d670846d0..106fcdfa9 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -21,7 +21,11 @@ import { IElectricAction } from 'vs/editor/common/modes/supports/electricCharact export class TypeOperations { - public static indent(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): ICommand[] { + public static indent(config: CursorConfiguration, model: ICursorSimpleModel | null, selections: Selection[] | null): ICommand[] { + if (model === null || selections === null) { + return []; + } + let commands: ICommand[] = []; for (let i = 0, len = selections.length; i < len; i++) { commands[i] = new ShiftCommand(selections[i], { @@ -885,7 +889,11 @@ export class TypeOperations { }); } - public static lineInsertBefore(config: CursorConfiguration, model: ITextModel, selections: Selection[]): ICommand[] { + public static lineInsertBefore(config: CursorConfiguration, model: ITextModel | null, selections: Selection[] | null): ICommand[] { + if (model === null || selections === null) { + return []; + } + let commands: ICommand[] = []; for (let i = 0, len = selections.length; i < len; i++) { let lineNumber = selections[i].positionLineNumber; @@ -902,7 +910,11 @@ export class TypeOperations { return commands; } - public static lineInsertAfter(config: CursorConfiguration, model: ITextModel, selections: Selection[]): ICommand[] { + public static lineInsertAfter(config: CursorConfiguration, model: ITextModel | null, selections: Selection[] | null): ICommand[] { + if (model === null || selections === null) { + return []; + } + let commands: ICommand[] = []; for (let i = 0, len = selections.length; i < len; i++) { const lineNumber = selections[i].positionLineNumber; diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index da16e9105..909f6fb1e 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -39,7 +39,7 @@ export interface IEditOperationBuilder { * @param selection The selection to track. * @param trackPreviousOnEmpty If set, and the selection is empty, indicates whether the selection * should clamp to the previous or the next character. - * @return A unique identifer. + * @return A unique identifier. */ trackSelection(selection: Selection, trackPreviousOnEmpty?: boolean): string; } @@ -80,7 +80,7 @@ export interface ICommand { /** * Compute the cursor state after the edit operations were applied. - * @param model The model the commad has executed on. + * @param model The model the command has executed on. * @param helper A helper to get inverse edit operations and to get previously tracked selections. * @return The cursor state after the command executed. */ @@ -534,10 +534,10 @@ export interface IThemeDecorationRenderOptions { textDecoration?: string; cursor?: string; color?: string | ThemeColor; - opacity?: number; + opacity?: string; letterSpacing?: string; - gutterIconPath?: string | UriComponents; + gutterIconPath?: UriComponents; gutterIconSize?: string; overviewRulerColor?: string | ThemeColor; @@ -551,7 +551,7 @@ export interface IThemeDecorationRenderOptions { */ export interface IContentDecorationRenderOptions { contentText?: string; - contentIconPath?: string | UriComponents; + contentIconPath?: UriComponents; border?: string; borderColor?: string | ThemeColor; diff --git a/src/vs/editor/common/editorContextKeys.ts b/src/vs/editor/common/editorContextKeys.ts index a4f010814..629f65333 100644 --- a/src/vs/editor/common/editorContextKeys.ts +++ b/src/vs/editor/common/editorContextKeys.ts @@ -38,6 +38,7 @@ export namespace EditorContextKeys { export const hasCodeActionsProvider = new RawContextKey('editorHasCodeActionsProvider', false); export const hasCodeLensProvider = new RawContextKey('editorHasCodeLensProvider', false); export const hasDefinitionProvider = new RawContextKey('editorHasDefinitionProvider', false); + export const hasDeclarationProvider = new RawContextKey('editorHasDeclarationProvider', false); export const hasImplementationProvider = new RawContextKey('editorHasImplementationProvider', false); export const hasTypeDefinitionProvider = new RawContextKey('editorHasTypeDefinitionProvider', false); export const hasHoverProvider = new RawContextKey('editorHasHoverProvider', false); diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index 848c2b17c..9912616ba 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -145,7 +145,7 @@ export interface IModelDecoration { */ readonly id: string; /** - * Identifier for a decoration's owener. + * Identifier for a decoration's owner. */ readonly ownerId: number; /** @@ -188,7 +188,7 @@ export interface IModelDecorationsChangeAccessor { */ removeDecoration(id: string): void; /** - * Perform a minimum ammount of operations, in order to transform the decorations + * Perform a minimum amount of operations, in order to transform the decorations * identified by `oldDecorations` to the decorations described by `newDecorations` * and returns the new identifiers associated with the resulting decorations. * @@ -623,13 +623,13 @@ export interface ITextModel { validatePosition(position: IPosition): Position; /** - * Advances the given position by the given offest (negative offsets are also accepted) + * Advances the given position by the given offset (negative offsets are also accepted) * and returns it as a new valid position. * * If the offset and position are such that their combination goes beyond the beginning or * end of the model, throws an exception. * - * If the ofsset is such that the new position would be in the middle of a multi-byte + * If the offset is such that the new position would be in the middle of a multi-byte * line terminator, throws an exception. */ modifyPosition(position: IPosition, offset: number): Position; @@ -851,7 +851,7 @@ export interface ITextModel { changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T, ownerId?: number): T | null; /** - * Perform a minimum ammount of operations, in order to transform the decorations + * Perform a minimum amount of operations, in order to transform the decorations * identified by `oldDecorations` to the decorations described by `newDecorations` * and returns the new identifiers associated with the resulting decorations. * @@ -903,7 +903,7 @@ export interface ITextModel { getLinesDecorations(startLineNumber: number, endLineNumber: number, ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; /** - * Gets all the deocorations in a range as an array. Only `startLineNumber` and `endLineNumber` from `range` are used for filtering. + * Gets all the decorations in a range as an array. Only `startLineNumber` and `endLineNumber` from `range` are used for filtering. * So for now it returns all the decorations on the same line as `range`. * @param range The range to search in * @param ownerId If set, it will ignore decorations belonging to other owners. @@ -970,7 +970,7 @@ export interface ITextModel { /** * Push edit operations, basically editing the model. This is the preferred way * of editing the model. The edit operations will land on the undo stack. - * @param beforeCursorState The cursor state before the edit operaions. This cursor state will be returned when `undo` or `redo` are invoked. + * @param beforeCursorState The cursor state before the edit operations. This cursor state will be returned when `undo` or `redo` are invoked. * @param editOperations The edit operations. * @param cursorStateComputer A callback that can compute the resulting cursors state after the edit operations have been executed. * @return The cursor state returned by the `cursorStateComputer`. diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts index c17b24714..1030643a1 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts @@ -312,7 +312,7 @@ export class PieceTreeTextBuffer implements ITextBuffer { } // At one point, due to how events are emitted and how each operation is handled, - // some operations can trigger a high ammount of temporary string allocations, + // some operations can trigger a high amount of temporary string allocations, // that will immediately get edited again. // e.g. a formatter inserting ridiculous ammounts of \n on a model with a single line // Therefore, the strategy is to collapse all the operations into a huge single edit operation diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 6793fd1a0..bb1af92f9 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -415,7 +415,7 @@ export interface CompletionItem { preselect?: boolean; /** * A string or snippet that should be inserted in a document when selecting - * this completion. When `falsy` the [label](#CompletionItem.label) + * this completion. * is used. */ insertText: string; @@ -618,12 +618,13 @@ export interface SignatureHelp { export enum SignatureHelpTriggerReason { Invoke = 1, TriggerCharacter = 2, - Retrigger = 3, + ContentChange = 3, } export interface SignatureHelpContext { - triggerReason: SignatureHelpTriggerReason; - triggerCharacter?: string; + readonly triggerReason: SignatureHelpTriggerReason; + readonly triggerCharacter?: string; + readonly isRetrigger: boolean; } /** @@ -632,8 +633,8 @@ export interface SignatureHelpContext { */ export interface SignatureHelpProvider { - readonly signatureHelpTriggerCharacters: ReadonlyArray; - readonly signatureHelpRetriggerCharacters: ReadonlyArray; + readonly signatureHelpTriggerCharacters?: ReadonlyArray; + readonly signatureHelpRetriggerCharacters?: ReadonlyArray; /** * Provide help for the signature at the given position and document. @@ -746,6 +747,18 @@ export interface DefinitionProvider { provideDefinition(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; } +/** + * The definition provider interface defines the contract between extensions and + * the [go to definition](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-definition) + * and peek definition features. + */ +export interface DeclarationProvider { + /** + * Provide the declaration of the symbol at the given position and document. + */ + provideDeclaration(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; +} + /** * The implementation provider interface defines the contract between extensions and * the go to implementation feature. @@ -863,7 +876,7 @@ export interface DocumentSymbolProvider { provideDocumentSymbols(model: model.ITextModel, token: CancellationToken): ProviderResult; } -export type TextEdit = { range: IRange; text: string; eol?: model.EndOfLineSequence; } | { range: undefined; text: undefined; eol: model.EndOfLineSequence; }; +export type TextEdit = { range: IRange; text: string; eol?: model.EndOfLineSequence; }; /** * Interface used to format a model @@ -1129,7 +1142,6 @@ export interface Command { * @internal */ export interface CommentInfo { - owner: number; threads: CommentThread[]; commentingRanges?: IRange[]; reply?: Command; @@ -1186,7 +1198,6 @@ export interface Comment { * @internal */ export interface CommentThreadChangedEvent { - readonly owner: number; /** * Added comment threads. */ @@ -1276,6 +1287,11 @@ export const DocumentHighlightProviderRegistry = new LanguageFeatureRegistry(); +/** + * @internal + */ +export const DeclarationProviderRegistry = new LanguageFeatureRegistry(); + /** * @internal */ diff --git a/src/vs/editor/common/modes/languageConfiguration.ts b/src/vs/editor/common/modes/languageConfiguration.ts index d12a89f0c..b73b42a29 100644 --- a/src/vs/editor/common/modes/languageConfiguration.ts +++ b/src/vs/editor/common/modes/languageConfiguration.ts @@ -86,7 +86,7 @@ export interface LanguageConfiguration { */ export interface IndentationRule { /** - * If a line matches this pattern, then all the lines after it should be unindendented once (until another rule matches). + * If a line matches this pattern, then all the lines after it should be unindented once (until another rule matches). */ decreaseIndentPattern: RegExp; /** @@ -120,7 +120,7 @@ export interface FoldingMarkers { */ export interface FoldingRules { /** - * Used by the indentation based strategy to decide wheter empty lines belong to the previous or the next block. + * Used by the indentation based strategy to decide whether empty lines belong to the previous or the next block. * A language adheres to the off-side rule if blocks in that language are expressed by their indentation. * See [wikipedia](https://en.wikipedia.org/wiki/Off-side_rule) for more information. * If not set, `false` is used and empty lines belong to the previous block. @@ -220,10 +220,6 @@ export interface EnterAction { * Describe what to do with the indentation. */ indentAction: IndentAction; - /** - * Describe whether to outdent current line. - */ - outdentCurrentLine?: boolean; /** * Describes text to be appended after the new line and after the indentation. */ diff --git a/src/vs/editor/common/modes/linkComputer.ts b/src/vs/editor/common/modes/linkComputer.ts index 513f1a138..0510feb72 100644 --- a/src/vs/editor/common/modes/linkComputer.ts +++ b/src/vs/editor/common/modes/linkComputer.ts @@ -13,7 +13,7 @@ export interface ILinkComputerTarget { getLineContent(lineNumber: number): string; } -const enum State { +export const enum State { Invalid = 0, Start = 1, H = 2, @@ -27,12 +27,13 @@ const enum State { AfterColon = 10, AlmostThere = 11, End = 12, - Accept = 13 + Accept = 13, + LastKnownState = 14 // marker, custom states may follow } -type Edge = [State, number, State]; +export type Edge = [State, number, State]; -class StateMachine { +export class StateMachine { private _states: Uint8Matrix; private _maxCharCode: number; @@ -141,7 +142,7 @@ function getClassifier(): CharacterClassifier { return _classifier; } -class LinkComputer { +export class LinkComputer { private static _createLink(classifier: CharacterClassifier, line: string, lineNumber: number, linkBeginIndex: number, linkEndIndex: number): ILink { // Do not allow to end link in certain characters... @@ -183,8 +184,7 @@ class LinkComputer { }; } - public static computeLinks(model: ILinkComputerTarget): ILink[] { - const stateMachine = getStateMachine(); + public static computeLinks(model: ILinkComputerTarget, stateMachine: StateMachine = getStateMachine()): ILink[] { const classifier = getClassifier(); let result: ILink[] = []; @@ -249,7 +249,15 @@ class LinkComputer { resetStateMachine = true; } } else if (state === State.End) { - const chClass = classifier.get(chCode); + + let chClass: CharacterClass; + if (chCode === CharCode.OpenSquareBracket) { + // Allow for the authority part to contain ipv6 addresses which contain [ and ] + hasOpenSquareBracket = true; + chClass = CharacterClass.None; + } else { + chClass = classifier.get(chCode); + } // Check if character terminates link if (chClass === CharacterClass.ForceTermination) { diff --git a/src/vs/editor/common/services/editorSimpleWorker.ts b/src/vs/editor/common/services/editorSimpleWorker.ts index 84590d69a..ed3a564f5 100644 --- a/src/vs/editor/common/services/editorSimpleWorker.ts +++ b/src/vs/editor/common/services/editorSimpleWorker.ts @@ -425,8 +425,8 @@ export abstract class BaseEditorSimpleWorker { lastEol = eol; } - if (!range) { - // eol-change only + if (Range.isEmpty(range) && !text) { + // empty change continue; } @@ -463,7 +463,7 @@ export abstract class BaseEditorSimpleWorker { } if (typeof lastEol === 'number') { - result.push({ eol: lastEol, text: undefined, range: undefined }); + result.push({ eol: lastEol, text: '', range: { startLineNumber: 0, startColumn: 0, endLineNumber: 0, endColumn: 0 } }); } return Promise.resolve(result); diff --git a/src/vs/editor/common/services/getIconClasses.ts b/src/vs/editor/common/services/getIconClasses.ts index d3be6057d..7861390e0 100644 --- a/src/vs/editor/common/services/getIconClasses.ts +++ b/src/vs/editor/common/services/getIconClasses.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import { Schemas } from 'vs/base/common/network'; import { DataUri, basenameOrAuthority } from 'vs/base/common/resources'; import { URI as uri } from 'vs/base/common/uri'; diff --git a/src/vs/editor/common/services/languagesRegistry.ts b/src/vs/editor/common/services/languagesRegistry.ts index 3287988b9..6b4aef605 100644 --- a/src/vs/editor/common/services/languagesRegistry.ts +++ b/src/vs/editor/common/services/languagesRegistry.ts @@ -4,6 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { onUnexpectedError } from 'vs/base/common/errors'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; import * as mime from 'vs/base/common/mime'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; @@ -26,7 +28,10 @@ export interface IResolvedLanguage { configurationFiles: URI[]; } -export class LanguagesRegistry { +export class LanguagesRegistry extends Disposable { + + private readonly _onDidChange: Emitter = this._register(new Emitter()); + public readonly onDidChange: Event = this._onDidChange.event; private _nextLanguageId: number; private _languages: { [id: string]: IResolvedLanguage; }; @@ -39,6 +44,7 @@ export class LanguagesRegistry { private _warnOnOverwrite: boolean; constructor(useModesRegistry = true, warnOnOverwrite = false) { + super(); this._nextLanguageId = 1; this._languages = {}; this._mimeTypesMap = {}; @@ -49,7 +55,7 @@ export class LanguagesRegistry { if (useModesRegistry) { this._registerLanguages(ModesRegistry.getLanguages()); - ModesRegistry.onDidAddLanguages((m) => this._registerLanguages(m)); + this._register(ModesRegistry.onDidAddLanguages((m) => this._registerLanguages(m))); } } @@ -80,6 +86,8 @@ export class LanguagesRegistry { }); Registry.as(Extensions.Configuration).registerOverrideIdentifiers(ModesRegistry.getLanguages().map(language => language.id)); + + this._onDidChange.fire(); } private _registerLanguage(lang: ILanguageExtensionPoint): void { diff --git a/src/vs/editor/common/services/modeService.ts b/src/vs/editor/common/services/modeService.ts index 687045df6..2c456c578 100644 --- a/src/vs/editor/common/services/modeService.ts +++ b/src/vs/editor/common/services/modeService.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IMode, LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -21,6 +22,11 @@ export interface ILanguageExtensionPoint { configuration?: URI; } +export interface ILanguageSelection extends IDisposable { + readonly languageIdentifier: LanguageIdentifier; + readonly onDidChange: Event; +} + export interface IModeService { _serviceBrand: any; @@ -41,8 +47,9 @@ export interface IModeService { getConfigurationFiles(modeId: string): URI[]; // --- instantiation - getMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): IMode | null; - getOrCreateMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): Promise; - getOrCreateModeByLanguageName(languageName: string): Promise; - getOrCreateModeByFilepathOrFirstLine(filepath: string, firstLine?: string): Promise; + create(commaSeparatedMimetypesOrCommaSeparatedIds: string): ILanguageSelection; + createByLanguageName(languageName: string): ILanguageSelection; + createByFilepathOrFirstLine(filepath: string, firstLine?: string): ILanguageSelection; + + triggerMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): void; } diff --git a/src/vs/editor/common/services/modeServiceImpl.ts b/src/vs/editor/common/services/modeServiceImpl.ts index e8082e186..615c4b979 100644 --- a/src/vs/editor/common/services/modeServiceImpl.ts +++ b/src/vs/editor/common/services/modeServiceImpl.ts @@ -3,14 +3,41 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IMode, LanguageId, LanguageIdentifier } from 'vs/editor/common/modes'; import { FrankensteinMode } from 'vs/editor/common/modes/abstractMode'; import { NULL_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/nullMode'; import { LanguagesRegistry } from 'vs/editor/common/services/languagesRegistry'; -import { IModeService } from 'vs/editor/common/services/modeService'; +import { ILanguageSelection, IModeService } from 'vs/editor/common/services/modeService'; + +class LanguageSelection extends Disposable implements ILanguageSelection { + + public languageIdentifier: LanguageIdentifier; + + private readonly _selector: () => LanguageIdentifier; + + private readonly _onDidChange: Emitter = this._register(new Emitter()); + public readonly onDidChange: Event = this._onDidChange.event; + + constructor(onLanguagesMaybeChanged: Event, selector: () => LanguageIdentifier) { + super(); + this._selector = selector; + this.languageIdentifier = this._selector(); + this._register(onLanguagesMaybeChanged(() => this._evaluate())); + } + + private _evaluate(): void { + let languageIdentifier = this._selector(); + if (languageIdentifier.id === this.languageIdentifier.id) { + // no change + return; + } + this.languageIdentifier = languageIdentifier; + this._onDidChange.fire(this.languageIdentifier); + } +} export class ModeServiceImpl implements IModeService { public _serviceBrand: any; @@ -21,10 +48,14 @@ export class ModeServiceImpl implements IModeService { private readonly _onDidCreateMode: Emitter = new Emitter(); public readonly onDidCreateMode: Event = this._onDidCreateMode.event; + protected readonly _onLanguagesMaybeChanged: Emitter = new Emitter(); + private readonly onLanguagesMaybeChanged: Event = this._onLanguagesMaybeChanged.event; + constructor(warnOnOverwrite = false) { this._instantiatedModes = {}; this._registry = new LanguagesRegistry(true, warnOnOverwrite); + this._registry.onDidChange(() => this._onLanguagesMaybeChanged.fire()); } protected _onReady(): Promise { @@ -93,44 +124,44 @@ export class ModeServiceImpl implements IModeService { // --- instantiation - public getMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): IMode | null { - const modeIds = this._registry.extractModeIds(commaSeparatedMimetypesOrCommaSeparatedIds); - - let isPlainText = false; - for (let i = 0; i < modeIds.length; i++) { - if (this._instantiatedModes.hasOwnProperty(modeIds[i])) { - return this._instantiatedModes[modeIds[i]]; - } - isPlainText = isPlainText || (modeIds[i] === 'plaintext'); - } - - if (isPlainText) { - // Try to do it synchronously - let r: IMode | null = null; - this.getOrCreateMode(commaSeparatedMimetypesOrCommaSeparatedIds).then((mode) => { - r = mode; - }, onUnexpectedError); - return r; - } - return null; - } - - public getOrCreateMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): Promise { - return this._onReady().then(() => { + public create(commaSeparatedMimetypesOrCommaSeparatedIds: string): ILanguageSelection { + return new LanguageSelection(this.onLanguagesMaybeChanged, () => { const modeId = this.getModeId(commaSeparatedMimetypesOrCommaSeparatedIds); - // Fall back to plain text if no mode was found - return this._getOrCreateMode(modeId || 'plaintext'); + return this._createModeAndGetLanguageIdentifier(modeId); }); } - public getOrCreateModeByLanguageName(languageName: string): Promise { - return this._onReady().then(() => { + public createByLanguageName(languageName: string): ILanguageSelection { + return new LanguageSelection(this.onLanguagesMaybeChanged, () => { const modeId = this._getModeIdByLanguageName(languageName); - // Fall back to plain text if no mode was found - return this._getOrCreateMode(modeId || 'plaintext'); + return this._createModeAndGetLanguageIdentifier(modeId); + }); + } + + public createByFilepathOrFirstLine(filepath: string, firstLine?: string): ILanguageSelection { + return new LanguageSelection(this.onLanguagesMaybeChanged, () => { + const modeId = this.getModeIdByFilepathOrFirstLine(filepath, firstLine); + return this._createModeAndGetLanguageIdentifier(modeId); }); } + private _createModeAndGetLanguageIdentifier(modeId: string | null): LanguageIdentifier { + // Fall back to plain text if no mode was found + const languageIdentifier = this.getLanguageIdentifier(modeId || 'plaintext') || NULL_LANGUAGE_IDENTIFIER; + this._getOrCreateMode(languageIdentifier.language); + return languageIdentifier; + } + + public triggerMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): void { + const modeId = this.getModeId(commaSeparatedMimetypesOrCommaSeparatedIds); + // Fall back to plain text if no mode was found + this._getOrCreateMode(modeId || 'plaintext'); + } + + public waitForLanguageRegistration(): Promise { + return this._onReady().then(() => { }); + } + private _getModeIdByLanguageName(languageName: string): string | null { const modeIds = this._registry.getModeIdsFromLanguageName(languageName); @@ -141,14 +172,6 @@ export class ModeServiceImpl implements IModeService { return null; } - public getOrCreateModeByFilepathOrFirstLine(filepath: string, firstLine?: string): Promise { - return this._onReady().then(() => { - const modeId = this.getModeIdByFilepathOrFirstLine(filepath, firstLine); - // Fall back to plain text if no mode was found - return this._getOrCreateMode(modeId || 'plaintext'); - }); - } - private _getOrCreateMode(modeId: string): IMode { if (!this._instantiatedModes.hasOwnProperty(modeId)) { let languageIdentifier = this.getLanguageIdentifier(modeId) || NULL_LANGUAGE_IDENTIFIER; diff --git a/src/vs/editor/common/services/modelService.ts b/src/vs/editor/common/services/modelService.ts index 13f72ee51..a75af8a1f 100644 --- a/src/vs/editor/common/services/modelService.ts +++ b/src/vs/editor/common/services/modelService.ts @@ -6,7 +6,7 @@ import { Event } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { ITextBufferFactory, ITextModel, ITextModelCreationOptions } from 'vs/editor/common/model'; -import { IMode } from 'vs/editor/common/modes'; +import { ILanguageSelection } from 'vs/editor/common/services/modeService'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const IModelService = createDecorator('modelService'); @@ -14,11 +14,11 @@ export const IModelService = createDecorator('modelService'); export interface IModelService { _serviceBrand: any; - createModel(value: string | ITextBufferFactory, modeOrPromise: Promise | IMode, resource: URI | undefined, isForSimpleWidget?: boolean): ITextModel; + createModel(value: string | ITextBufferFactory, languageSelection: ILanguageSelection | null, resource: URI, isForSimpleWidget?: boolean): ITextModel; updateModel(model: ITextModel, value: string | ITextBufferFactory): void; - setMode(model: ITextModel, modeOrPromise: Promise | IMode): void; + setMode(model: ITextModel, languageSelection: ILanguageSelection): void; destroyModel(resource: URI): void; diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index e68601b9a..f506eef2f 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -3,11 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; -import { isFalsyOrEmpty } from 'vs/base/common/arrays'; -import { isThenable } from 'vs/base/common/async'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; import { Emitter, Event } from 'vs/base/common/event'; import { MarkdownString } from 'vs/base/common/htmlContent'; +import { escape } from 'vs/base/common/strings'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import * as network from 'vs/base/common/network'; import { basename } from 'vs/base/common/paths'; @@ -20,8 +19,9 @@ import { DefaultEndOfLine, EndOfLinePreference, EndOfLineSequence, IIdentifiedSi import { ClassName } from 'vs/editor/common/model/intervalTree'; import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel'; import { IModelLanguageChangedEvent } from 'vs/editor/common/model/textModelEvents'; -import { IMode, LanguageIdentifier } from 'vs/editor/common/modes'; +import { LanguageIdentifier } from 'vs/editor/common/modes'; import { PLAINTEXT_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/modesRegistry'; +import { ILanguageSelection } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; import { overviewRulerError, overviewRulerInfo, overviewRulerWarning } from 'vs/editor/common/view/editorColorRegistry'; @@ -34,7 +34,10 @@ function MODEL_ID(resource: URI): string { } class ModelData implements IDisposable { - model: ITextModel; + public readonly model: ITextModel; + + private _languageSelection: ILanguageSelection | null; + private _languageSelectionListener: IDisposable | null; private _markerDecorations: string[]; private _modelEventListeners: IDisposable[]; @@ -46,6 +49,9 @@ class ModelData implements IDisposable { ) { this.model = model; + this._languageSelection = null; + this._languageSelectionListener = null; + this._markerDecorations = []; this._modelEventListeners = []; @@ -53,14 +59,33 @@ class ModelData implements IDisposable { this._modelEventListeners.push(model.onDidChangeLanguage((e) => onDidChangeLanguage(model, e))); } + private _disposeLanguageSelection(): void { + if (this._languageSelectionListener) { + this._languageSelectionListener.dispose(); + this._languageSelectionListener = null; + } + if (this._languageSelection) { + this._languageSelection.dispose(); + this._languageSelection = null; + } + } + public dispose(): void { this._markerDecorations = this.model.deltaDecorations(this._markerDecorations, []); this._modelEventListeners = dispose(this._modelEventListeners); + this._disposeLanguageSelection(); } public acceptMarkerDecorations(newDecorations: IModelDeltaDecoration[]): void { this._markerDecorations = this.model.deltaDecorations(this._markerDecorations, newDecorations); } + + public setLanguage(languageSelection: ILanguageSelection): void { + this._disposeLanguageSelection(); + this._languageSelection = languageSelection; + this._languageSelectionListener = this._languageSelection.onDidChange(() => this.model.setMode(languageSelection.languageIdentifier)); + this.model.setMode(languageSelection.languageIdentifier); + } } class ModelMarkerHandler { @@ -167,36 +192,49 @@ class ModelMarkerHandler { let { message, source, relatedInformation, code } = marker; if (typeof message === 'string') { - message = message.trim(); + hoverMessage = new MarkdownString(); + // Disable markdown renderer sanitize to allow html + // Hence, escape all input strings + hoverMessage.sanitize = false; if (source) { - if (/\n/g.test(message)) { - if (code) { - message = nls.localize('diagAndSourceAndCodeMultiline', "[{0}]\n{1} [{2}]", source, message, code); - } else { - message = nls.localize('diagAndSourceMultiline', "[{0}]\n{1}", source, message); - } - } else { - if (code) { - message = nls.localize('diagAndSourceAndCode', "[{0}] {1} [{2}]", source, message, code); - } else { - message = nls.localize('diagAndSource', "[{0}] {1}", source, message); - } + hoverMessage.appendMarkdown(`[${escape(source)}]`); + hoverMessage.appendText(' '); + } + + hoverMessage.appendMarkdown(``); + message = escape(message.trim()); + const lines = message.split(/\r\n|\r|\n/g); + if (lines.length > 1) { + if (source) { + hoverMessage.appendMarkdown(`
`); } + for (const line of lines) { + hoverMessage.appendText(line); + hoverMessage.appendMarkdown(`
`); + } + } else { + hoverMessage.appendText(message); } + hoverMessage.appendMarkdown(`
`); - hoverMessage = new MarkdownString().appendCodeblock('_', message); + if (code) { + if (lines.length === 1) { + hoverMessage.appendText(' '); + } + hoverMessage.appendMarkdown(`[${escape(code)}]`); + } - if (!isFalsyOrEmpty(relatedInformation)) { - hoverMessage.appendMarkdown('\n'); - for (const { message, resource, startLineNumber, startColumn } of relatedInformation!) { + if (isNonEmptyArray(relatedInformation)) { + hoverMessage.appendMarkdown(`\n`); + for (const { message, resource, startLineNumber, startColumn } of relatedInformation) { hoverMessage.appendMarkdown( - `* [${basename(resource.path)}(${startLineNumber}, ${startColumn})](${resource.toString(false)}#${startLineNumber},${startColumn}): ` + escape(`* [${basename(resource.path)}(${startLineNumber}, ${startColumn})](${resource.toString(false)}#${startLineNumber},${startColumn}): `) ); - hoverMessage.appendText(`${message}`); - hoverMessage.appendMarkdown('\n'); + hoverMessage.appendText(`${escape(message)}`); + hoverMessage.appendMarkdown(`\n`); } - hoverMessage.appendMarkdown('\n'); + hoverMessage.appendMarkdown(`\n`); } } @@ -508,14 +546,14 @@ export class ModelServiceImpl extends Disposable implements IModelService { return [EditOperation.replaceMove(oldRange, textBuffer.getValueInRange(newRange, EndOfLinePreference.TextDefined))]; } - public createModel(value: string | ITextBufferFactory, modeOrPromise: Promise | IMode, resource: URI, isForSimpleWidget: boolean = false): ITextModel { + public createModel(value: string | ITextBufferFactory, languageSelection: ILanguageSelection | null, resource: URI, isForSimpleWidget: boolean = false): ITextModel { let modelData: ModelData; - if (!modeOrPromise || isThenable(modeOrPromise)) { - modelData = this._createModelData(value, PLAINTEXT_LANGUAGE_IDENTIFIER, resource, isForSimpleWidget); - this.setMode(modelData.model, modeOrPromise); + if (languageSelection) { + modelData = this._createModelData(value, languageSelection.languageIdentifier, resource, isForSimpleWidget); + this.setMode(modelData.model, languageSelection); } else { - modelData = this._createModelData(value, modeOrPromise.getLanguageIdentifier(), resource, isForSimpleWidget); + modelData = this._createModelData(value, PLAINTEXT_LANGUAGE_IDENTIFIER, resource, isForSimpleWidget); } // handle markers (marker service => model) @@ -528,19 +566,15 @@ export class ModelServiceImpl extends Disposable implements IModelService { return modelData.model; } - public setMode(model: ITextModel, modeOrPromise: Promise | IMode): void { - if (!modeOrPromise) { + public setMode(model: ITextModel, languageSelection: ILanguageSelection): void { + if (!languageSelection) { return; } - if (isThenable(modeOrPromise)) { - modeOrPromise.then((mode) => { - if (!model.isDisposed()) { - model.setMode(mode.getLanguageIdentifier()); - } - }); - } else { - model.setMode(modeOrPromise.getLanguageIdentifier()); + let modelData = this._models[MODEL_ID(model.uri)]; + if (!modelData) { + return; } + modelData.setLanguage(languageSelection); } public destroyModel(resource: URI): void { diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 265e12824..8e5409b2a 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -597,7 +597,7 @@ export enum CompletionTriggerKind { export enum SignatureHelpTriggerReason { Invoke = 1, TriggerCharacter = 2, - Retrigger = 3 + ContentChange = 3 } /** diff --git a/src/vs/editor/contrib/clipboard/clipboard.ts b/src/vs/editor/contrib/clipboard/clipboard.ts index 94eefa7c0..5e43f1b39 100644 --- a/src/vs/editor/contrib/clipboard/clipboard.ts +++ b/src/vs/editor/contrib/clipboard/clipboard.ts @@ -148,6 +148,12 @@ class ExecCommandCopyAction extends ExecCommandAction { if (!emptySelectionClipboard && editor.getSelection().isEmpty()) { return; } + // Prevent copying an empty line by accident + if (editor.getSelections().length === 1 && editor.getSelection().isEmpty()) { + if (editor.getModel().getLineFirstNonWhitespaceColumn(editor.getSelection().positionLineNumber) === 0) { + return; + } + } super.run(accessor, editor); } diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index 98d345070..ee8ecbb90 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { flatten, isFalsyOrEmpty, mergeSort } from 'vs/base/common/arrays'; +import { flatten, mergeSort, isNonEmptyArray } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { illegalArgument, isPromiseCanceledError, onUnexpectedExternalError } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; @@ -23,8 +23,25 @@ export function getCodeActions(model: ITextModel, rangeOrSelection: Range | Sele const promises = CodeActionProviderRegistry.all(model) .filter(provider => { + if (!provider.providedCodeActionKinds) { + return true; + } + // Avoid calling providers that we know will not return code actions of interest - return !provider.providedCodeActionKinds || provider.providedCodeActionKinds.some(providedKind => isValidActionKind(trigger && trigger.filter, providedKind)); + return provider.providedCodeActionKinds.some(providedKind => { + // Filter out actions by kind + // The provided kind can be either a subset of a superset of the filtered kind + if (trigger && trigger.filter && trigger.filter.kind && !(trigger.filter.kind.contains(providedKind) || new CodeActionKind(providedKind).contains(trigger.filter.kind.value))) { + return false; + } + + // Don't return source actions unless they are explicitly requested + if (trigger && CodeActionKind.Source.contains(providedKind) && (!trigger.filter || !trigger.filter.includeSourceActions)) { + return false; + } + + return true; + }); }) .map(support => { return Promise.resolve(support.provideCodeActions(model, rangeOrSelection, codeActionContext, token)).then(providedCodeActions => { @@ -66,15 +83,13 @@ function isValidActionKind(filter: CodeActionFilter | undefined, kind: string | } function codeActionsComparator(a: CodeAction, b: CodeAction): number { - const aHasDiags = !isFalsyOrEmpty(a.diagnostics); - const bHasDiags = !isFalsyOrEmpty(b.diagnostics); - if (aHasDiags) { - if (bHasDiags) { - return a.diagnostics![0].message.localeCompare(b.diagnostics![0].message); + if (isNonEmptyArray(a.diagnostics)) { + if (isNonEmptyArray(b.diagnostics)) { + return a.diagnostics[0].message.localeCompare(b.diagnostics[0].message); } else { return -1; } - } else if (bHasDiags) { + } else if (isNonEmptyArray(b.diagnostics)) { return 1; } else { return 0; // both have no diagnostics diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 5e5cdec2b..34ee776f0 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -93,7 +93,7 @@ export class QuickFixController implements IEditorContribution { // Triggered for specific scope // Apply if we only have one action or requested autoApply, otherwise show menu e.actions.then(fixes => { - if (e.trigger.autoApply === CodeActionAutoApply.First || (e.trigger.autoApply === CodeActionAutoApply.IfSingle && fixes.length === 1)) { + if (fixes.length > 0 && e.trigger.autoApply === CodeActionAutoApply.First || (e.trigger.autoApply === CodeActionAutoApply.IfSingle && fixes.length === 1)) { this._onApplyCodeAction(fixes[0]); } else { this._codeActionContextMenu.show(e.actions, e.position); diff --git a/src/vs/editor/contrib/codeAction/codeActionModel.ts b/src/vs/editor/contrib/codeAction/codeActionModel.ts index dc821a5a8..3c4c4b90c 100644 --- a/src/vs/editor/contrib/codeAction/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/codeActionModel.ts @@ -81,9 +81,12 @@ export class CodeActionOracle { } private _getRangeOfSelectionUnlessWhitespaceEnclosed(trigger: CodeActionTrigger): Selection | undefined { + if (!this._editor.hasModel()) { + return undefined; + } const model = this._editor.getModel(); const selection = this._editor.getSelection(); - if (model && selection && selection.isEmpty() && !(trigger.filter && trigger.filter.includeSourceActions)) { + if (selection.isEmpty() && trigger.type === 'auto') { const { lineNumber, column } = selection.getPosition(); const line = model.getLineContent(lineNumber); if (line.length === 0) { diff --git a/src/vs/editor/contrib/codeAction/codeActionWidget.ts b/src/vs/editor/contrib/codeAction/codeActionWidget.ts index aaead454e..a4af30a65 100644 --- a/src/vs/editor/contrib/codeAction/codeActionWidget.ts +++ b/src/vs/editor/contrib/codeAction/codeActionWidget.ts @@ -29,7 +29,7 @@ export class CodeActionContextMenu { show(fixes: Thenable, at: { x: number; y: number } | Position) { - const actions = fixes ? fixes.then(value => { + const actionsPromise = fixes ? fixes.then(value => { return value.map(action => { return new Action(action.command ? action.command.id : action.title, action.title, undefined, true, () => { return always( @@ -45,19 +45,21 @@ export class CodeActionContextMenu { return actions; }) : Promise.resolve([] as Action[]); - this._contextMenuService.showContextMenu({ - getAnchor: () => { - if (Position.isIPosition(at)) { - at = this._toCoords(at); - } - return at; - }, - getActions: () => actions, - onHide: () => { - this._visible = false; - this._editor.focus(); - }, - autoSelectFirstItem: true + actionsPromise.then(actions => { + this._contextMenuService.showContextMenu({ + getAnchor: () => { + if (Position.isIPosition(at)) { + at = this._toCoords(at); + } + return at; + }, + getActions: () => actions, + onHide: () => { + this._visible = false; + this._editor.focus(); + }, + autoSelectFirstItem: true + }); }); } diff --git a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts index 4f919a400..ce8ef441c 100644 --- a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts +++ b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts @@ -11,7 +11,6 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import 'vs/css!./lightBulbWidget'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { TextModel } from 'vs/editor/common/model/textModel'; -import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger'; import { CodeActionsComputeEvent } from './codeActionModel'; export class LightBulbWidget implements IDisposable, IContentWidget { @@ -122,12 +121,8 @@ export class LightBulbWidget implements IDisposable, IContentWidget { const selection = this._model.rangeOrSelection; this._model.actions.then(fixes => { - if (!token.isCancellationRequested && fixes && fixes.length > 0) { - if (!selection || selection.isEmpty() && fixes.every(fix => !!(fix.kind && CodeActionKind.Refactor.contains(fix.kind)))) { - this.hide(); - } else { - this._show(); - } + if (!token.isCancellationRequested && fixes && fixes.length > 0 && selection) { + this._show(); } else { this.hide(); } diff --git a/src/vs/editor/contrib/codelens/codelens.ts b/src/vs/editor/contrib/codelens/codelens.ts index eecd765ec..6dccdfd13 100644 --- a/src/vs/editor/contrib/codelens/codelens.ts +++ b/src/vs/editor/contrib/codelens/codelens.ts @@ -73,8 +73,8 @@ registerLanguageCommand('_executeCodeLensProvider', function (accessor, args) { for (const item of value) { if (typeof itemResolveCount === 'undefined' || Boolean(item.symbol.command)) { result.push(item.symbol); - } else if (itemResolveCount-- > 0) { - resolve.push(Promise.resolve(item.provider.resolveCodeLens(model, item.symbol, CancellationToken.None)).then(symbol => result.push(symbol))); + } else if (itemResolveCount-- > 0 && item.provider.resolveCodeLens) { + resolve.push(Promise.resolve(item.provider.resolveCodeLens(model, item.symbol, CancellationToken.None)).then(symbol => result.push(symbol || item.symbol))); } } diff --git a/src/vs/editor/contrib/codelens/codelensWidget.ts b/src/vs/editor/contrib/codelens/codelensWidget.ts index 847f99f66..c2d6fb737 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/codelensWidget.ts @@ -111,6 +111,7 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget { this._domNode.style.height = `${Math.round(lineHeight * 1.1)}px`; this._domNode.style.lineHeight = `${lineHeight}px`; this._domNode.style.fontSize = `${Math.round(fontInfo.fontSize * .9)}px`; + this._domNode.style.paddingRight = `${Math.round(fontInfo.fontSize * .45)}px`; this._domNode.innerHTML = ' '; } diff --git a/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts index 5b4ba4eec..30536ea19 100644 --- a/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts @@ -126,7 +126,7 @@ class SaturationBox extends Disposable { private width: number; private height: number; - private monitor: GlobalMouseMoveMonitor; + private monitor: GlobalMouseMoveMonitor | null; private _onDidChange = new Emitter<{ s: number, v: number }>(); readonly onDidChange: Event<{ s: number, v: number }> = this._onDidChange.event; @@ -168,8 +168,10 @@ class SaturationBox extends Disposable { const mouseUpListener = dom.addDisposableListener(document, dom.EventType.MOUSE_UP, () => { this._onColorFlushed.fire(); mouseUpListener.dispose(); - this.monitor.stopMonitoring(true); - this.monitor = null; + if (this.monitor) { + this.monitor.stopMonitoring(true); + this.monitor = null; + } }, true); } @@ -195,7 +197,7 @@ class SaturationBox extends Disposable { private paint(): void { const hsva = this.model.color.hsva; const saturatedColor = new Color(new HSVA(hsva.h, 1, 1, 1)); - const ctx = this.canvas.getContext('2d'); + const ctx = this.canvas.getContext('2d')!; const whiteGradient = ctx.createLinearGradient(0, 0, this.canvas.width, 0); whiteGradient.addColorStop(0, 'rgba(255, 255, 255, 1)'); @@ -207,7 +209,7 @@ class SaturationBox extends Disposable { blackGradient.addColorStop(1, 'rgba(0, 0, 0, 1)'); ctx.rect(0, 0, this.canvas.width, this.canvas.height); - ctx.fillStyle = Color.Format.CSS.format(saturatedColor); + ctx.fillStyle = Color.Format.CSS.format(saturatedColor)!; ctx.fill(); ctx.fillStyle = whiteGradient; ctx.fill(); diff --git a/src/vs/editor/contrib/contextmenu/contextmenu.ts b/src/vs/editor/contrib/contextmenu/contextmenu.ts index af631099f..debaba390 100644 --- a/src/vs/editor/contrib/contextmenu/contextmenu.ts +++ b/src/vs/editor/contrib/contextmenu/contextmenu.ts @@ -21,11 +21,6 @@ import { IContextMenuService, IContextViewService } from 'vs/platform/contextvie import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -export interface IPosition { - x: number; - y: number; -} - export class ContextMenuController implements IEditorContribution { private static readonly ID = 'editor.contrib.contextmenu'; @@ -93,16 +88,16 @@ export class ContextMenuController implements IEditorContribution { } // Unless the user triggerd the context menu through Shift+F10, use the mouse position as menu position - let forcedPosition: IPosition; + let anchor: IAnchor; if (e.target.type !== MouseTargetType.TEXTAREA) { - forcedPosition = { x: e.event.posx, y: e.event.posy + 1 }; + anchor = { x: e.event.posx - 1, width: 2, y: e.event.posy - 1, height: 2 }; } // Show the context menu - this.showContextMenu(forcedPosition); + this.showContextMenu(anchor); } - public showContextMenu(forcedPosition?: IPosition): void { + public showContextMenu(anchor?: IAnchor): void { if (!this._editor.getConfiguration().contribInfo.contextmenu) { return; // Context menu is turned off through configuration } @@ -117,7 +112,7 @@ export class ContextMenuController implements IEditorContribution { // Show menu if we have actions to show if (menuActions.length > 0) { - this._doShowContextMenu(menuActions, forcedPosition); + this._doShowContextMenu(menuActions, anchor); } } @@ -137,7 +132,7 @@ export class ContextMenuController implements IEditorContribution { return result; } - private _doShowContextMenu(actions: IAction[], forcedPosition: IPosition | null = null): void { + private _doShowContextMenu(actions: IAction[], anchor: IAnchor | null = null): void { // Disable hover const oldHoverSetting = this._editor.getConfiguration().contribInfo.hover; @@ -147,7 +142,6 @@ export class ContextMenuController implements IEditorContribution { } }); - let anchor: IAnchor = forcedPosition; if (!anchor) { // Ensure selection is visible this._editor.revealPosition(this._editor.getPosition(), ScrollType.Immediate); @@ -163,18 +157,12 @@ export class ContextMenuController implements IEditorContribution { anchor = { x: posx, y: posy }; } - // prevent menu from appearing right below the cursor - anchor.height = 1; - anchor.width = 2; - // Show menu this._contextMenuIsBeingShownCount++; this._contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => { - return Promise.resolve(actions); - }, + getActions: () => actions, getActionItem: (action) => { const keybinding = this._keybindingFor(action); diff --git a/src/vs/editor/contrib/documentSymbols/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/outlineModel.ts index 347c430e8..a6181f433 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineModel.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { binarySearch, isFalsyOrEmpty, coalesceInPlace } from 'vs/base/common/arrays'; +import { binarySearch, coalesceInPlace } from 'vs/base/common/arrays'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { first, forEach, size } from 'vs/base/common/collections'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; @@ -301,10 +301,8 @@ export class OutlineModel extends TreeElement { let group = new OutlineGroup(id, result, provider, index); return Promise.resolve(provider.provideDocumentSymbols(result.textModel, token)).then(result => { - if (!isFalsyOrEmpty(result)) { - for (const info of result) { - OutlineModel._makeOutlineElement(info, group); - } + for (const info of result || []) { + OutlineModel._makeOutlineElement(info, group); } return group; }, err => { diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree.ts b/src/vs/editor/contrib/documentSymbols/outlineTree.ts index bbbc4df60..98b50239d 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineTree.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineTree.ts @@ -146,13 +146,13 @@ export class OutlineRenderer implements IRenderer { const decoration = dom.$('.outline-element-decoration'); dom.addClass(container, 'outline-element'); dom.append(container, icon, labelContainer, detail, decoration); - return { icon, labelContainer, label: new HighlightedLabel(labelContainer), detail, decoration }; + return { icon, labelContainer, label: new HighlightedLabel(labelContainer, true), detail, decoration }; } if (templateId === 'outline-group') { const labelContainer = dom.$('.outline-element-label'); dom.addClass(container, 'outline-element'); dom.append(container, labelContainer); - return { labelContainer, label: new HighlightedLabel(labelContainer) }; + return { labelContainer, label: new HighlightedLabel(labelContainer, true) }; } throw new Error(templateId); diff --git a/src/vs/editor/contrib/find/findWidget.css b/src/vs/editor/contrib/find/findWidget.css index faf3f7efe..35ece46a3 100644 --- a/src/vs/editor/contrib/find/findWidget.css +++ b/src/vs/editor/contrib/find/findWidget.css @@ -258,10 +258,6 @@ display:none; } -.monaco-editor .find-widget.collapsed-find-widget > .find-part .monaco-inputbox > .wrapper > .input { - padding-right: 0px; -} - .monaco-editor .findMatch { -webkit-animation-duration: 0; -webkit-animation-name: inherit !important; diff --git a/src/vs/editor/contrib/format/format.ts b/src/vs/editor/contrib/format/format.ts index c87f02388..0f510fde9 100644 --- a/src/vs/editor/contrib/format/format.ts +++ b/src/vs/editor/contrib/format/format.ts @@ -5,7 +5,7 @@ import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; -import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; import { registerDefaultLanguageCommand, registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; @@ -22,11 +22,13 @@ export class NoProviderError extends Error { constructor(message?: string) { super(); this.name = NoProviderError.Name; - this.message = message; + if (message) { + this.message = message; + } } } -export function getDocumentRangeFormattingEdits(model: ITextModel, range: Range, options: FormattingOptions, token: CancellationToken): Promise { +export function getDocumentRangeFormattingEdits(model: ITextModel, range: Range, options: FormattingOptions, token: CancellationToken): Promise { const providers = DocumentRangeFormattingEditProviderRegistry.ordered(model); @@ -37,10 +39,10 @@ export function getDocumentRangeFormattingEdits(model: ITextModel, range: Range, return first(providers.map(provider => () => { return Promise.resolve(provider.provideDocumentRangeFormattingEdits(model, range, options, token)) .then(undefined, onUnexpectedExternalError); - }), result => !isFalsyOrEmpty(result)); + }), isNonEmptyArray); } -export function getDocumentFormattingEdits(model: ITextModel, options: FormattingOptions, token: CancellationToken): Promise { +export function getDocumentFormattingEdits(model: ITextModel, options: FormattingOptions, token: CancellationToken): Promise { const providers = DocumentFormattingEditProviderRegistry.ordered(model); // try range formatters when no document formatter is registered @@ -51,10 +53,10 @@ export function getDocumentFormattingEdits(model: ITextModel, options: Formattin return first(providers.map(provider => () => { return Promise.resolve(provider.provideDocumentFormattingEdits(model, options, token)) .then(undefined, onUnexpectedExternalError); - }), result => !isFalsyOrEmpty(result)); + }), isNonEmptyArray); } -export function getOnTypeFormattingEdits(model: ITextModel, position: Position, ch: string, options: FormattingOptions): Promise { +export function getOnTypeFormattingEdits(model: ITextModel, position: Position, ch: string, options: FormattingOptions): Promise { const [support] = OnTypeFormattingEditProviderRegistry.ordered(model); if (!support) { return Promise.resolve(undefined); diff --git a/src/vs/editor/contrib/format/formattingEdit.ts b/src/vs/editor/contrib/format/formattingEdit.ts index b83bb89eb..23bab3f18 100644 --- a/src/vs/editor/contrib/format/formattingEdit.ts +++ b/src/vs/editor/contrib/format/formattingEdit.ts @@ -25,13 +25,18 @@ export class FormattingEdit { } if (typeof newEol === 'number') { - editor.getModel().pushEOL(newEol); + if (editor.hasModel()) { + editor.getModel().pushEOL(newEol); + } } return singleEdits; } private static _isFullModelReplaceEdit(editor: ICodeEditor, edit: ISingleEditOperation): boolean { + if (!editor.hasModel()) { + return false; + } const model = editor.getModel(); const editRange = model.validateRange(edit.range); const fullModelRange = model.getFullModelRange(); diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinition.ts b/src/vs/editor/contrib/goToDefinition/goToDefinition.ts index 077a28ea3..28b14740e 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinition.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinition.ts @@ -9,7 +9,7 @@ import { onUnexpectedExternalError } from 'vs/base/common/errors'; import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; import { ITextModel } from 'vs/editor/common/model'; -import { DefinitionLink, DefinitionProviderRegistry, ImplementationProviderRegistry, TypeDefinitionProviderRegistry } from 'vs/editor/common/modes'; +import { DefinitionLink, DefinitionProviderRegistry, ImplementationProviderRegistry, TypeDefinitionProviderRegistry, DeclarationProviderRegistry } from 'vs/editor/common/modes'; import { LanguageFeatureRegistry } from 'vs/editor/common/modes/languageFeatureRegistry'; @@ -40,6 +40,12 @@ export function getDefinitionsAtPosition(model: ITextModel, position: Position, }); } +export function getDeclarationsAtPosition(model: ITextModel, position: Position, token: CancellationToken): Thenable { + return getDefinitions(model, position, DeclarationProviderRegistry, (provider, model, position) => { + return provider.provideDeclaration(model, position, token); + }); +} + export function getImplementationsAtPosition(model: ITextModel, position: Position, token: CancellationToken): Thenable { return getDefinitions(model, position, ImplementationProviderRegistry, (provider, model, position) => { return provider.provideImplementation(model, position, token); @@ -53,5 +59,6 @@ export function getTypeDefinitionsAtPosition(model: ITextModel, position: Positi } registerDefaultLanguageCommand('_executeDefinitionProvider', (model, position) => getDefinitionsAtPosition(model, position, CancellationToken.None)); +registerDefaultLanguageCommand('_executeDeclarationProvider', (model, position) => getDeclarationsAtPosition(model, position, CancellationToken.None)); registerDefaultLanguageCommand('_executeImplementationProvider', (model, position) => getImplementationsAtPosition(model, position, CancellationToken.None)); registerDefaultLanguageCommand('_executeTypeDefinitionProvider', (model, position) => getTypeDefinitionsAtPosition(model, position, CancellationToken.None)); diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts index d88c33d1a..21bff97b8 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts @@ -26,7 +26,7 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IProgressService } from 'vs/platform/progress/common/progress'; -import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition } from './goToDefinition'; +import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition, getDeclarationsAtPosition } from './goToDefinition'; export class DefinitionActionConfig { @@ -58,7 +58,7 @@ export class DefinitionAction extends EditorAction { const model = editor.getModel(); const pos = editor.getPosition(); - const definitionPromise = this._getDeclarationsAtPosition(model, pos, CancellationToken.None).then(references => { + const definitionPromise = this._getTargetLocationForPosition(model, pos, CancellationToken.None).then(references => { if (model.isDisposed() || editor.getModel() !== model) { // new model, no more model @@ -113,7 +113,7 @@ export class DefinitionAction extends EditorAction { return definitionPromise; } - protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position, token: CancellationToken): Thenable { + protected _getTargetLocationForPosition(model: ITextModel, position: corePosition.Position, token: CancellationToken): Thenable { return getDefinitionsAtPosition(model, position, token); } @@ -249,8 +249,84 @@ export class PeekDefinitionAction extends DefinitionAction { } } +export class DeclarationAction extends DefinitionAction { + + protected _getTargetLocationForPosition(model: ITextModel, position: corePosition.Position, token: CancellationToken): Thenable { + return getDeclarationsAtPosition(model, position, token); + } + + protected _getNoResultFoundMessage(info?: IWordAtPosition): string { + return info && info.word + ? nls.localize('decl.noResultWord', "No declaration found for '{0}'", info.word) + : nls.localize('decl.generic.noResults', "No declaration found"); + } + + protected _getMetaTitle(model: ReferencesModel): string { + return model.references.length > 1 && nls.localize('decl.meta.title', " – {0} declarations", model.references.length); + } +} + +export class GoToDeclarationAction extends DeclarationAction { + + public static readonly ID = 'editor.action.goToRealDeclaration'; + + constructor() { + super(new DefinitionActionConfig(), { + id: GoToDeclarationAction.ID, + label: nls.localize('actions.goToDeclaration.label', "Go to Declaration"), + alias: 'Go to Declaration', + precondition: ContextKeyExpr.and( + EditorContextKeys.hasDeclarationProvider, + EditorContextKeys.isInEmbeddedEditor.toNegated()), + kbOpts: { + kbExpr: EditorContextKeys.editorTextFocus, + primary: KeyMod.CtrlCmd | KeyCode.F12, + weight: KeybindingWeight.EditorContrib + }, + menuOpts: { + group: 'navigation', + order: 1.3 + } + }); + } + + protected _getNoResultFoundMessage(info?: IWordAtPosition): string { + return info && info.word + ? nls.localize('decl.noResultWord', "No declaration found for '{0}'", info.word) + : nls.localize('decl.generic.noResults', "No declaration found"); + } + + protected _getMetaTitle(model: ReferencesModel): string { + return model.references.length > 1 && nls.localize('decl.meta.title', " – {0} declarations", model.references.length); + } +} + +export class PeekDeclarationAction extends DeclarationAction { + constructor() { + super(new DefinitionActionConfig(void 0, true, false), { + id: 'editor.action.peekDeclaration', + label: nls.localize('actions.peekDecl.label', "Peek Declaration"), + alias: 'Peek Declaration', + precondition: ContextKeyExpr.and( + EditorContextKeys.hasDeclarationProvider, + PeekContext.notInPeekEditor, + EditorContextKeys.isInEmbeddedEditor.toNegated()), + kbOpts: { + kbExpr: EditorContextKeys.editorTextFocus, + primary: KeyMod.Alt | KeyCode.F12, + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F10 }, + weight: KeybindingWeight.EditorContrib + }, + menuOpts: { + group: 'navigation', + order: 1.31 + } + }); + } +} + export class ImplementationAction extends DefinitionAction { - protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position, token: CancellationToken): Thenable { + protected _getTargetLocationForPosition(model: ITextModel, position: corePosition.Position, token: CancellationToken): Thenable { return getImplementationsAtPosition(model, position, token); } @@ -308,7 +384,7 @@ export class PeekImplementationAction extends ImplementationAction { } export class TypeDefinitionAction extends DefinitionAction { - protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position, token: CancellationToken): Thenable { + protected _getTargetLocationForPosition(model: ITextModel, position: corePosition.Position, token: CancellationToken): Thenable { return getTypeDefinitionsAtPosition(model, position, token); } @@ -372,6 +448,8 @@ export class PeekTypeDefinitionAction extends TypeDefinitionAction { registerEditorAction(GoToDefinitionAction); registerEditorAction(OpenDefinitionToSideAction); registerEditorAction(PeekDefinitionAction); +registerEditorAction(GoToDeclarationAction); +registerEditorAction(PeekDeclarationAction); registerEditorAction(GoToImplementationAction); registerEditorAction(PeekImplementationAction); registerEditorAction(GoToTypeDefinitionAction); diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts index 5a85fbe40..7118ce9d8 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts @@ -73,6 +73,12 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC } private startFindDefinition(mouseEvent: ClickLinkMouseEvent, withKey?: ClickLinkKeyboardEvent): void { + + // check if we are active and on a content widget + if (mouseEvent.target.type === MouseTargetType.CONTENT_WIDGET && this.decorations.length > 0) { + return; + } + if (!this.isEnabled(mouseEvent, withKey)) { this.currentWordUnderMouse = null; this.removeDecorations(); @@ -270,7 +276,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC private isEnabled(mouseEvent: ClickLinkMouseEvent, withKey?: ClickLinkKeyboardEvent): boolean { return this.editor.getModel() && mouseEvent.isNoneOrSingleMouseDown && - mouseEvent.target.type === MouseTargetType.CONTENT_TEXT && + (mouseEvent.target.type === MouseTargetType.CONTENT_TEXT) && (mouseEvent.hasTriggerModifier || (withKey && withKey.keyCodeIsTriggerKey)) && DefinitionProviderRegistry.has(this.editor.getModel()); } diff --git a/src/vs/editor/contrib/gotoError/gotoError.ts b/src/vs/editor/contrib/gotoError/gotoError.ts index bf78d4f6e..8551d1fb9 100644 --- a/src/vs/editor/contrib/gotoError/gotoError.ts +++ b/src/vs/editor/contrib/gotoError/gotoError.ts @@ -36,7 +36,7 @@ class MarkerModel { constructor(editor: ICodeEditor, markers: IMarker[]) { this._editor = editor; - this._markers = null; + this._markers = []; this._nextIdx = -1; this._toUnbind = []; this._ignoreSelectionChange = false; @@ -50,7 +50,7 @@ class MarkerModel { if (this._ignoreSelectionChange) { return; } - if (this.currentMarker && Range.containsPosition(this.currentMarker, this._editor.getPosition())) { + if (this.currentMarker && this._editor.getPosition() && Range.containsPosition(this.currentMarker, this._editor.getPosition()!)) { return; } this._nextIdx = -1; @@ -93,14 +93,14 @@ class MarkerModel { for (let i = 0; i < this._markers.length; i++) { let range = Range.lift(this._markers[i]); - if (range.isEmpty()) { - const word = this._editor.getModel().getWordAtPosition(range.getStartPosition()); + if (range.isEmpty() && this._editor.getModel()) { + const word = this._editor.getModel()!.getWordAtPosition(range.getStartPosition()); if (word) { range = new Range(range.startLineNumber, word.startColumn, range.startLineNumber, word.endColumn); } } - if (range.containsPosition(position) || position.isBeforeOrEqual(range.getStartPosition())) { + if (position && (range.containsPosition(position) || position.isBeforeOrEqual(range.getStartPosition()))) { this._nextIdx = i; found = true; break; @@ -115,7 +115,7 @@ class MarkerModel { } } - get currentMarker(): IMarker { + get currentMarker(): IMarker | undefined { return this.canNavigate() ? this._markers[this._nextIdx] : undefined; } @@ -158,7 +158,7 @@ class MarkerModel { return this._markers.length > 0; } - public findMarkerAtPosition(pos: Position): IMarker { + public findMarkerAtPosition(pos: Position): IMarker | undefined { for (const marker of this._markers) { if (Range.containsPosition(marker, pos)) { return marker; @@ -189,8 +189,8 @@ class MarkerController implements editorCommon.IEditorContribution { } private _editor: ICodeEditor; - private _model: MarkerModel; - private _widget: MarkerNavigationWidget; + private _model: MarkerModel | null; + private _widget: MarkerNavigationWidget | null; private _widgetVisible: IContextKey; private _disposeOnClose: IDisposable[] = []; @@ -245,15 +245,22 @@ class MarkerController implements editorCommon.IEditorContribution { this._disposeOnClose.push(this._editor.onDidChangeModel(() => this._cleanUp())); this._disposeOnClose.push(this._model.onCurrentMarkerChanged(marker => { - if (!marker) { + if (!marker || !this._model) { this._cleanUp(); } else { this._model.withoutWatchingEditorPosition(() => { + if (!this._widget || !this._model) { + return; + } this._widget.showAtMarker(marker, this._model.indexOf(marker), this._model.total); }); } })); this._disposeOnClose.push(this._model.onMarkerSetChanged(() => { + if (!this._widget || !this._widget.position || !this._model) { + return; + } + const marker = this._model.findMarkerAtPosition(this._widget.position); if (marker) { this._widget.updateMarker(marker); @@ -273,15 +280,29 @@ class MarkerController implements editorCommon.IEditorContribution { } private _onMarkerChanged(changedResources: URI[]): void { - if (!changedResources.some(r => this._editor.getModel().uri.toString() === r.toString())) { + let editorModel = this._editor.getModel(); + if (!editorModel) { + return; + } + + if (!this._model) { + return; + } + + if (!changedResources.some(r => editorModel!.uri.toString() === r.toString())) { return; } this._model.setMarkers(this._getMarkers()); } private _getMarkers(): IMarker[] { + let model = this._editor.getModel(); + if (!model) { + return []; + } + return this._markerService.read({ - resource: this._editor.getModel().uri, + resource: model.uri, severities: MarkerSeverity.Error | MarkerSeverity.Warning | MarkerSeverity.Info }); } @@ -305,22 +326,27 @@ class MarkerNavigationAction extends EditorAction { const editorService = accessor.get(ICodeEditorService); const controller = MarkerController.get(editor); if (!controller) { - return undefined; + return Promise.resolve(void 0); } const model = controller.getOrCreateModel(); const atEdge = model.move(this._isNext, !this._multiFile); if (!atEdge || !this._multiFile) { - return undefined; + return Promise.resolve(void 0); } // try with the next/prev file let markers = markerService.read({ severities: MarkerSeverity.Error | MarkerSeverity.Warning | MarkerSeverity.Info }).sort(MarkerNavigationAction.compareMarker); if (markers.length === 0) { - return undefined; + return Promise.resolve(void 0); + } + + let editorModel = editor.getModel(); + if (!editorModel) { + return Promise.resolve(void 0); } - let oldMarker = model.currentMarker || { resource: editor.getModel().uri, severity: MarkerSeverity.Error, startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }; + let oldMarker = model.currentMarker || { resource: editorModel!.uri, severity: MarkerSeverity.Error, startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }; let idx = binarySearch(markers, oldMarker, MarkerNavigationAction.compareMarker); if (idx < 0) { // find best match... @@ -333,11 +359,11 @@ class MarkerNavigationAction extends EditorAction { } let newMarker = markers[idx]; - if (newMarker.resource.toString() === editor.getModel().uri.toString()) { + if (newMarker.resource.toString() === editorModel!.uri.toString()) { // the next `resource` is this resource which // means we cycle within this file model.move(this._isNext, true); - return undefined; + return Promise.resolve(void 0); } // close the widget for this editor-instance, open the resource diff --git a/src/vs/editor/contrib/gotoError/gotoErrorWidget.css b/src/vs/editor/contrib/gotoError/gotoErrorWidget.css index 6de672d1f..f5f1f3832 100644 --- a/src/vs/editor/contrib/gotoError/gotoErrorWidget.css +++ b/src/vs/editor/contrib/gotoError/gotoErrorWidget.css @@ -33,7 +33,16 @@ user-select: text; } +.monaco-editor .marker-widget .descriptioncontainer .message { + display: flex; +} + +.monaco-editor .marker-widget .descriptioncontainer .message .source, +.monaco-editor .marker-widget .descriptioncontainer .message .code, .monaco-editor .marker-widget .descriptioncontainer .filename { - cursor: pointer; opacity: 0.6; } + +.monaco-editor .marker-widget .descriptioncontainer .filename { + cursor: pointer; +} diff --git a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts index 694533f44..cba65d3fa 100644 --- a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts @@ -21,7 +21,7 @@ import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElemen import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { getBaseLabel, getPathLabel } from 'vs/base/common/labels'; -import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; import { Event, Emitter } from 'vs/base/common/event'; class MessageWidget { @@ -45,6 +45,7 @@ class MessageWidget { domNode.setAttribute('role', 'alert'); this._messageBlock = document.createElement('div'); + dom.addClass(this._messageBlock, 'message'); domNode.appendChild(this._messageBlock); this._relatedBlock = document.createElement('div'); @@ -80,34 +81,20 @@ class MessageWidget { update({ source, message, relatedInformation, code }: IMarker): void { if (source) { - this._lines = 0; - this._longestLineLength = 0; - const indent = new Array(source.length + 3 + 1).join(' '); const lines = message.split(/\r\n|\r|\n/g); - for (let i = 0; i < lines.length; i++) { - let line = lines[i]; - this._lines += 1; - if (code && i === lines.length - 1) { - line += ` [${code}]`; - } + this._lines = lines.length; + this._longestLineLength = 0; + for (const line of lines) { this._longestLineLength = Math.max(line.length, this._longestLineLength); - if (i === 0) { - message = `[${source}] ${line}`; - } else { - message += `\n${indent}${line}`; - } } } else { this._lines = 1; - if (code) { - message += ` [${code}]`; - } this._longestLineLength = message.length; } dom.clearNode(this._relatedBlock); - if (!isFalsyOrEmpty(relatedInformation)) { + if (isNonEmptyArray(relatedInformation)) { this._relatedBlock.style.paddingTop = `${Math.floor(this._editor.getConfiguration().lineHeight * .66)}px`; this._lines += 1; @@ -133,8 +120,26 @@ class MessageWidget { } } - this._messageBlock.innerText = message; - this._editor.applyFontInfo(this._messageBlock); + dom.clearNode(this._messageBlock); + if (source) { + const sourceElement = document.createElement('div'); + sourceElement.innerText = `[${source}] `; + dom.addClass(sourceElement, 'source'); + this._editor.applyFontInfo(sourceElement); + this._messageBlock.appendChild(sourceElement); + } + const messageElement = document.createElement('div'); + messageElement.innerText = message; + this._editor.applyFontInfo(messageElement); + this._messageBlock.appendChild(messageElement); + if (code) { + const codeElement = document.createElement('div'); + codeElement.innerText = ` [${code}]`; + dom.addClass(codeElement, 'code'); + this._editor.applyFontInfo(codeElement); + this._messageBlock.appendChild(codeElement); + } + const fontInfo = this._editor.getConfiguration().fontInfo; const scrollWidth = Math.ceil(fontInfo.typicalFullwidthCharacterWidth * this._longestLineLength * 0.75); const scrollHeight = fontInfo.lineHeight * this._lines; @@ -159,7 +164,7 @@ export class MarkerNavigationWidget extends ZoneWidget { private _message: MessageWidget; private _callOnDispose: IDisposable[] = []; private _severity: MarkerSeverity; - private _backgroundColor: Color; + private _backgroundColor: Color | null; private _onDidSelectRelatedInformation = new Emitter(); readonly onDidSelectRelatedInformation: Event = this._onDidSelectRelatedInformation.event; @@ -195,7 +200,7 @@ export class MarkerNavigationWidget extends ZoneWidget { protected _applyStyles(): void { if (this._parentContainer) { - this._parentContainer.style.backgroundColor = this._backgroundColor.toString(); + this._parentContainer.style.backgroundColor = this._backgroundColor ? this._backgroundColor.toString() : ''; } super._applyStyles(); } @@ -244,7 +249,8 @@ export class MarkerNavigationWidget extends ZoneWidget { // show let range = Range.lift(marker); - let position = range.containsPosition(this.editor.getPosition()) ? this.editor.getPosition() : range.getStartPosition(); + const editorPosition = this.editor.getPosition(); + let position = editorPosition && range.containsPosition(editorPosition) ? editorPosition : range.getStartPosition(); super.show(position, this.computeRequiredHeight()); this.editor.revealPositionInCenter(position, ScrollType.Smooth); diff --git a/src/vs/editor/contrib/hover/hoverOperation.ts b/src/vs/editor/contrib/hover/hoverOperation.ts index 0371b56a9..5add5dff3 100644 --- a/src/vs/editor/contrib/hover/hoverOperation.ts +++ b/src/vs/editor/contrib/hover/hoverOperation.ts @@ -47,8 +47,6 @@ export const enum HoverStartMode { export class HoverOperation { - static HOVER_TIME = 300; - private _computer: IHoverComputer; private _state: ComputeHoverOperationState; private _hoverTime: number; @@ -63,10 +61,10 @@ export class HoverOperation { private _errorCallback?: (err: any) => void; private _progressCallback: (progress: any) => void; - constructor(computer: IHoverComputer, success: (r: Result) => void, error: undefined | ((err: any) => void), progress: (progress: any) => void) { + constructor(computer: IHoverComputer, success: (r: Result) => void, error: undefined | ((err: any) => void), progress: (progress: any) => void, hoverTime: number) { this._computer = computer; this._state = ComputeHoverOperationState.IDLE; - this._hoverTime = HoverOperation.HOVER_TIME; + this._hoverTime = hoverTime; this._firstWaitScheduler = new RunOnceScheduler(() => this._triggerAsyncComputation(), 0); this._secondWaitScheduler = new RunOnceScheduler(() => this._triggerSyncComputation(), 0); diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index cae0f3420..5a9b0656d 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -186,7 +186,8 @@ export class ModesContentHoverWidget extends ContentHoverWidget { this._computer, result => this._withResult(result, true), null, - result => this._withResult(result, false) + result => this._withResult(result, false), + this._editor.getConfiguration().contribInfo.hover.delay ); this._register(dom.addStandardDisposableListener(this.getDomNode(), dom.EventType.FOCUS, () => { diff --git a/src/vs/editor/contrib/hover/modesGlyphHover.ts b/src/vs/editor/contrib/hover/modesGlyphHover.ts index 0c35acdfb..9b1260a6c 100644 --- a/src/vs/editor/contrib/hover/modesGlyphHover.ts +++ b/src/vs/editor/contrib/hover/modesGlyphHover.ts @@ -109,7 +109,8 @@ export class ModesGlyphHoverWidget extends GlyphHoverWidget { this._computer, (result: IHoverMessage[]) => this._withResult(result), undefined, - (result: any) => this._withResult(result) + (result: any) => this._withResult(result), + 300 ); } diff --git a/src/vs/editor/contrib/inPlaceReplace/inPlaceReplace.ts b/src/vs/editor/contrib/inPlaceReplace/inPlaceReplace.ts index 1ef5a49d4..2ef007b74 100644 --- a/src/vs/editor/contrib/inPlaceReplace/inPlaceReplace.ts +++ b/src/vs/editor/contrib/inPlaceReplace/inPlaceReplace.ts @@ -37,7 +37,7 @@ class InPlaceReplaceController implements IEditorContribution { private readonly editor: ICodeEditor; private readonly editorWorkerService: IEditorWorkerService; private decorationIds: string[] = []; - private currentRequest: CancelablePromise; + private currentRequest: CancelablePromise; private decorationRemover: CancelablePromise; constructor( @@ -55,29 +55,31 @@ class InPlaceReplaceController implements IEditorContribution { return InPlaceReplaceController.ID; } - public run(source: string, up: boolean): Promise { + public run(source: string, up: boolean): Promise | undefined { // cancel any pending request if (this.currentRequest) { this.currentRequest.cancel(); } - let selection = this.editor.getSelection(); + const editorSelection = this.editor.getSelection(); const model = this.editor.getModel(); - const modelURI = model.uri; - + if (!model || !editorSelection) { + return undefined; + } + let selection = editorSelection; if (selection.startLineNumber !== selection.endLineNumber) { // Can't accept multiline selection - return null; + return undefined; } const state = new EditorState(this.editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position); - + const modelURI = model.uri; if (!this.editorWorkerService.canNavigateValueSet(modelURI)) { - return undefined; + return Promise.resolve(void 0); } - this.currentRequest = createCancelablePromise(token => this.editorWorkerService.navigateValueSet(modelURI, selection, up)); + this.currentRequest = createCancelablePromise(token => this.editorWorkerService.navigateValueSet(modelURI, selection!, up)); return this.currentRequest.then(result => { @@ -94,7 +96,7 @@ class InPlaceReplaceController implements IEditorContribution { // Selection let editRange = Range.lift(result.range); let highlightRange = result.range; - let diff = result.value.length - (selection.endColumn - selection.startColumn); + let diff = result.value.length - (selection!.endColumn - selection!.startColumn); // highlight highlightRange = { @@ -104,11 +106,11 @@ class InPlaceReplaceController implements IEditorContribution { endColumn: highlightRange.startColumn + result.value.length }; if (diff > 1) { - selection = new Selection(selection.startLineNumber, selection.startColumn, selection.endLineNumber, selection.endColumn + diff - 1); + selection = new Selection(selection!.startLineNumber, selection!.startColumn, selection!.endLineNumber, selection!.endColumn + diff - 1); } // Insert new text - const command = new InPlaceReplaceCommand(editRange, selection, result.value); + const command = new InPlaceReplaceCommand(editRange, selection!, result.value); this.editor.pushUndoStop(); this.editor.executeCommand(source, command); @@ -147,10 +149,10 @@ class InPlaceReplaceUp extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { - let controller = InPlaceReplaceController.get(editor); + public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise | undefined { + const controller = InPlaceReplaceController.get(editor); if (!controller) { - return undefined; + return Promise.resolve(void 0); } return controller.run(this.id, true); } @@ -172,10 +174,10 @@ class InPlaceReplaceDown extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { - let controller = InPlaceReplaceController.get(editor); + public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise | undefined { + const controller = InPlaceReplaceController.get(editor); if (!controller) { - return undefined; + return Promise.resolve(void 0); } return controller.run(this.id, false); } diff --git a/src/vs/editor/contrib/indentation/indentation.ts b/src/vs/editor/contrib/indentation/indentation.ts index ca4ac2590..859632608 100644 --- a/src/vs/editor/contrib/indentation/indentation.ts +++ b/src/vs/editor/contrib/indentation/indentation.ts @@ -10,11 +10,11 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, IActionOptions, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand'; import { EditOperation } from 'vs/editor/common/core/editOperation'; -import { Range } from 'vs/editor/common/core/range'; +import { Range, IRange } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder, IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; +import { IIdentifiedSingleEditOperation, ITextModel, EndOfLineSequence } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; import { StandardTokenType, TextEdit } from 'vs/editor/common/modes'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; @@ -48,12 +48,12 @@ export function unshiftIndent(tabSize: number, indentation: string, count?: numb export function getReindentEditOperations(model: ITextModel, startLineNumber: number, endLineNumber: number, inheritedIndent?: string): IIdentifiedSingleEditOperation[] { if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) { // Model is empty - return undefined; + return []; } let indentationRules = LanguageConfigurationRegistry.getIndentationRules(model.getLanguageIdentifier().id); if (!indentationRules) { - return undefined; + return []; } endLineNumber = Math.min(endLineNumber, model.getLineCount()); @@ -73,7 +73,7 @@ export function getReindentEditOperations(model: ITextModel, startLineNumber: nu } if (startLineNumber > endLineNumber - 1) { - return undefined; + return []; } let { tabSize, insertSpaces } = model.getOptions(); @@ -167,7 +167,11 @@ export class IndentationToSpacesAction extends EditorAction { return; } let modelOpts = model.getOptions(); - const command = new IndentationToSpacesCommand(editor.getSelection(), modelOpts.tabSize); + let selection = editor.getSelection(); + if (!selection) { + return; + } + const command = new IndentationToSpacesCommand(selection, modelOpts.tabSize); editor.pushUndoStop(); editor.executeCommands(this.id, [command]); @@ -197,7 +201,11 @@ export class IndentationToTabsAction extends EditorAction { return; } let modelOpts = model.getOptions(); - const command = new IndentationToTabsCommand(editor.getSelection(), modelOpts.tabSize); + let selection = editor.getSelection(); + if (!selection) { + return; + } + const command = new IndentationToTabsCommand(selection, modelOpts.tabSize); editor.pushUndoStop(); editor.executeCommands(this.id, [command]); @@ -229,7 +237,7 @@ export class ChangeIndentationSizeAction extends EditorAction { id: n.toString(), label: n.toString(), // add description for tabSize value set in the configuration - description: n === creationOpts.tabSize ? nls.localize('configuredTabSize', "Configured Tab Size") : null + description: n === creationOpts.tabSize ? nls.localize('configuredTabSize', "Configured Tab Size") : undefined })); // auto focus the tabSize set for the current editor @@ -238,10 +246,12 @@ export class ChangeIndentationSizeAction extends EditorAction { setTimeout(() => { quickInputService.pick(picks, { placeHolder: nls.localize({ key: 'selectTabWidth', comment: ['Tab corresponds to the tab key'] }, "Select Tab Size for Current File"), activeItem: picks[autoFocusIndex] }).then(pick => { if (pick) { - model.updateOptions({ - tabSize: parseInt(pick.label, 10), - insertSpaces: this.insertSpaces - }); + if (model && !model.isDisposed()) { + model.updateOptions({ + tabSize: parseInt(pick.label, 10), + insertSpaces: this.insertSpaces + }); + } } }); }, 50/* quick open is sensitive to being opened so soon after another */); @@ -318,7 +328,7 @@ export class ReindentLinesAction extends EditorAction { return; } let edits = getReindentEditOperations(model, 1, model.getLineCount()); - if (edits) { + if (edits.length > 0) { editor.pushUndoStop(); editor.executeEdits(this.id, edits); editor.pushUndoStop(); @@ -342,9 +352,14 @@ export class ReindentSelectedLinesAction extends EditorAction { return; } + let selections = editor.getSelections(); + if (selections === null) { + return; + } + let edits: IIdentifiedSingleEditOperation[] = []; - for (let selection of editor.getSelections()) { + for (let selection of selections) { let startLineNumber = selection.startLineNumber; let endLineNumber = selection.endLineNumber; @@ -360,7 +375,7 @@ export class ReindentSelectedLinesAction extends EditorAction { startLineNumber--; } - let editOperations = getReindentEditOperations(model, startLineNumber, endLineNumber) || []; + let editOperations = getReindentEditOperations(model, startLineNumber, endLineNumber); edits.push(...editOperations); } @@ -374,7 +389,7 @@ export class ReindentSelectedLinesAction extends EditorAction { export class AutoIndentOnPasteCommand implements ICommand { - private _edits: TextEdit[]; + private _edits: { range: IRange; text: string; eol?: EndOfLineSequence; }[]; private _initialSelection: Selection; private _selectionId: string; @@ -385,7 +400,7 @@ export class AutoIndentOnPasteCommand implements ICommand { for (let edit of edits) { if (edit.range && typeof edit.text === 'string') { - this._edits.push(edit); + this._edits.push(edit as { range: IRange; text: string; eol?: EndOfLineSequence; }); } } } @@ -456,11 +471,16 @@ export class AutoIndentOnPaste implements IEditorContribution { } private trigger(range: Range): void { - if (this.editor.getSelections().length > 1) { + let selections = this.editor.getSelections(); + if (selections === null || selections.length > 1) { return; } const model = this.editor.getModel(); + if (!model) { + return; + } + if (!model.isCheapToTokenize(range.getStartPosition().lineNumber)) { return; } @@ -588,7 +608,7 @@ export class AutoIndentOnPaste implements IEditorContribution { } } - let cmd = new AutoIndentOnPasteCommand(textEdits, this.editor.getSelection()); + let cmd = new AutoIndentOnPasteCommand(textEdits, this.editor.getSelection()!); this.editor.executeCommand('autoIndentOnPaste', cmd); this.editor.pushUndoStop(); } diff --git a/src/vs/editor/contrib/linesOperations/linesOperations.ts b/src/vs/editor/contrib/linesOperations/linesOperations.ts index b8d09dcc0..abb9612ed 100644 --- a/src/vs/editor/contrib/linesOperations/linesOperations.ts +++ b/src/vs/editor/contrib/linesOperations/linesOperations.ts @@ -249,7 +249,12 @@ export class TrimTrailingWhitespaceAction extends EditorAction { cursors = (editor.getSelections() || []).map(s => new Position(s.positionLineNumber, s.positionColumn)); } - let command = new TrimTrailingWhitespaceCommand(editor.getSelection(), cursors); + let selection = editor.getSelection(); + if (selection === null) { + return; + } + + let command = new TrimTrailingWhitespaceCommand(selection, cursors); editor.pushUndoStop(); editor.executeCommands(this.id, [command]); @@ -265,7 +270,7 @@ interface IDeleteLinesOperation { positionColumn: number; } -class DeleteLinesAction extends EditorAction { +export class DeleteLinesAction extends EditorAction { constructor() { super({ @@ -297,7 +302,11 @@ class DeleteLinesAction extends EditorAction { private _getLinesToRemove(editor: ICodeEditor): IDeleteLinesOperation[] { // Construct delete operations - let operations: IDeleteLinesOperation[] = editor.getSelections().map((s) => { + let selections = editor.getSelections(); + if (selections === null) { + return []; + } + let operations: IDeleteLinesOperation[] = selections.map((s) => { let endLineNumber = s.endLineNumber; if (s.startLineNumber < s.endLineNumber && s.endColumn === 1) { @@ -313,14 +322,17 @@ class DeleteLinesAction extends EditorAction { // Sort delete operations operations.sort((a, b) => { + if (a.startLineNumber === b.startLineNumber) { + return a.endLineNumber - b.endLineNumber; + } return a.startLineNumber - b.startLineNumber; }); - // Merge delete operations on consecutive lines + // Merge delete operations which are adjacent or overlapping let mergedOperations: IDeleteLinesOperation[] = []; let previousOperation = operations[0]; for (let i = 1; i < operations.length; i++) { - if (previousOperation.endLineNumber + 1 === operations[i].startLineNumber) { + if (previousOperation.endLineNumber + 1 >= operations[i].startLineNumber) { // Merge current operations into the previous one previousOperation.endLineNumber = operations[i].endLineNumber; } else { @@ -378,7 +390,7 @@ class OutdentLinesAction extends EditorAction { } public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { - CoreEditingCommands.Outdent.runEditorCommand(null, editor, null); + CoreEditingCommands.Outdent.runEditorCommand(_accessor, editor, null); } } @@ -435,6 +447,10 @@ export class InsertLineAfterAction extends EditorAction { export abstract class AbstractDeleteAllToBoundaryAction extends EditorAction { public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { const primaryCursor = editor.getSelection(); + if (primaryCursor === null) { + return; + } + let rangesToDelete = this._getRangesToDelete(editor); // merge overlapping selections let effectiveRanges: Range[] = []; @@ -488,7 +504,7 @@ export class DeleteAllLeftAction extends AbstractDeleteAllToBoundaryAction { } _getEndCursorState(primaryCursor: Range, rangesToDelete: Range[]): Selection[] { - let endPrimaryCursor: Selection; + let endPrimaryCursor: Selection | null = null; let endCursorState: Selection[] = []; let deletedLines = 0; @@ -518,15 +534,24 @@ export class DeleteAllLeftAction extends AbstractDeleteAllToBoundaryAction { } _getRangesToDelete(editor: ICodeEditor): Range[] { - let rangesToDelete: Range[] = editor.getSelections(); + let selections = editor.getSelections(); + if (selections === null) { + return []; + } + + let rangesToDelete: Range[] = selections; let model = editor.getModel(); + if (model === null) { + return []; + } + rangesToDelete.sort(Range.compareRangesUsingStarts); rangesToDelete = rangesToDelete.map(selection => { if (selection.isEmpty()) { if (selection.startColumn === 1) { let deleteFromLine = Math.max(1, selection.startLineNumber - 1); - let deleteFromColumn = selection.startLineNumber === 1 ? 1 : model.getLineContent(deleteFromLine).length + 1; + let deleteFromColumn = selection.startLineNumber === 1 ? 1 : model!.getLineContent(deleteFromLine).length + 1; return new Range(deleteFromLine, deleteFromColumn, selection.startLineNumber, 1); } else { return new Range(selection.startLineNumber, 1, selection.startLineNumber, selection.startColumn); @@ -557,7 +582,7 @@ export class DeleteAllRightAction extends AbstractDeleteAllToBoundaryAction { } _getEndCursorState(primaryCursor: Range, rangesToDelete: Range[]): Selection[] { - let endPrimaryCursor: Selection; + let endPrimaryCursor: Selection | null = null; let endCursorState: Selection[] = []; for (let i = 0, len = rangesToDelete.length, offset = 0; i < len; i++) { let range = rangesToDelete[i]; @@ -579,10 +604,19 @@ export class DeleteAllRightAction extends AbstractDeleteAllToBoundaryAction { _getRangesToDelete(editor: ICodeEditor): Range[] { let model = editor.getModel(); + if (model === null) { + return []; + } - let rangesToDelete: Range[] = editor.getSelections().map((sel) => { + let selections = editor.getSelections(); + + if (selections === null) { + return []; + } + + let rangesToDelete: Range[] = selections.map((sel) => { if (sel.isEmpty()) { - const maxColumn = model.getLineMaxColumn(sel.startLineNumber); + const maxColumn = model!.getLineMaxColumn(sel.startLineNumber); if (sel.startColumn === maxColumn) { return new Range(sel.startLineNumber, sel.startColumn, sel.startLineNumber + 1, 1); @@ -616,7 +650,14 @@ export class JoinLinesAction extends EditorAction { public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { let selections = editor.getSelections(); + if (selections === null) { + return; + } + let primaryCursor = editor.getSelection(); + if (primaryCursor === null) { + return; + } selections.sort(Range.compareRangesUsingStarts); let reducedSelections: Selection[] = []; @@ -624,7 +665,7 @@ export class JoinLinesAction extends EditorAction { let lastSelection = selections.reduce((previousValue, currentValue) => { if (previousValue.isEmpty()) { if (previousValue.endLineNumber === currentValue.startLineNumber) { - if (primaryCursor.equalsSelection(previousValue)) { + if (primaryCursor!.equalsSelection(previousValue)) { primaryCursor = currentValue; } return currentValue; @@ -649,6 +690,10 @@ export class JoinLinesAction extends EditorAction { reducedSelections.push(lastSelection); let model = editor.getModel(); + if (model === null) { + return; + } + let edits: IIdentifiedSingleEditOperation[] = []; let endCursorState: Selection[] = []; let endPrimaryCursor = primaryCursor; @@ -759,7 +804,15 @@ export class TransposeAction extends EditorAction { public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { let selections = editor.getSelections(); + if (selections === null) { + return; + } + let model = editor.getModel(); + if (model === null) { + return; + } + let commands: ICommand[] = []; for (let i = 0, len = selections.length; i < len; i++) { @@ -800,7 +853,15 @@ export class TransposeAction extends EditorAction { export abstract class AbstractCaseAction extends EditorAction { public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { let selections = editor.getSelections(); + if (selections === null) { + return; + } + let model = editor.getModel(); + if (model === null) { + return; + } + let commands: ICommand[] = []; for (let i = 0, len = selections.length; i < len; i++) { diff --git a/src/vs/editor/contrib/linesOperations/sortLinesCommand.ts b/src/vs/editor/contrib/linesOperations/sortLinesCommand.ts index 421a67e9c..779062027 100644 --- a/src/vs/editor/contrib/linesOperations/sortLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/sortLinesCommand.ts @@ -33,7 +33,11 @@ export class SortLinesCommand implements editorCommon.ICommand { return helper.getTrackedSelection(this.selectionId); } - public static canRun(model: ITextModel, selection: Selection, descending: boolean): boolean { + public static canRun(model: ITextModel | null, selection: Selection, descending: boolean): boolean { + if (model === null) { + return false; + } + let data = getSortData(model, selection, descending); if (!data) { diff --git a/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts b/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts index 1c256e783..793029d38 100644 --- a/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts @@ -9,7 +9,7 @@ import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; import { Handler } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -import { DeleteAllLeftAction, DeleteAllRightAction, IndentLinesAction, InsertLineAfterAction, InsertLineBeforeAction, JoinLinesAction, LowerCaseAction, SortLinesAscendingAction, SortLinesDescendingAction, TransposeAction, UpperCaseAction } from 'vs/editor/contrib/linesOperations/linesOperations'; +import { DeleteAllLeftAction, DeleteAllRightAction, IndentLinesAction, InsertLineAfterAction, InsertLineBeforeAction, JoinLinesAction, LowerCaseAction, SortLinesAscendingAction, SortLinesDescendingAction, TransposeAction, UpperCaseAction, DeleteLinesAction } from 'vs/editor/contrib/linesOperations/linesOperations'; import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; @@ -879,4 +879,24 @@ suite('Editor Contrib - Line Operations', () => { model.dispose(); }); + + test('issue #62112: Delete line does not work properly when multiple cursors are on line', () => { + const TEXT = [ + 'a', + 'foo boo', + 'too', + 'c', + ]; + withTestCodeEditor(TEXT, {}, (editor, cursor) => { + editor.setSelections([ + new Selection(2, 4, 2, 4), + new Selection(2, 8, 2, 8), + new Selection(3, 4, 3, 4), + ]); + const deleteLinesAction = new DeleteLinesAction(); + deleteLinesAction.run(null, editor); + + assert.equal(editor.getValue(), 'a\nc'); + }); + }); }); diff --git a/src/vs/editor/contrib/markdown/markdownRenderer.ts b/src/vs/editor/contrib/markdown/markdownRenderer.ts index b9f7970cb..62208620c 100644 --- a/src/vs/editor/contrib/markdown/markdownRenderer.ts +++ b/src/vs/editor/contrib/markdown/markdownRenderer.ts @@ -48,7 +48,8 @@ export class MarkdownRenderer { } } - return this._modeService.getOrCreateMode(modeId || '').then(_ => { + this._modeService.triggerMode(modeId || ''); + return Promise.resolve(true).then(_ => { const promise = TokenizationRegistry.getPromise(modeId || ''); if (promise) { return promise.then(support => tokenizeToString(value, support)); diff --git a/src/vs/editor/contrib/multicursor/multicursor.ts b/src/vs/editor/contrib/multicursor/multicursor.ts index 1408b4932..13f3554c6 100644 --- a/src/vs/editor/contrib/multicursor/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/multicursor.ts @@ -165,6 +165,57 @@ class InsertCursorAtEndOfEachLineSelected extends EditorAction { } } +class InsertCursorAtEndOfLineSelected extends EditorAction { + + constructor() { + super({ + id: 'editor.action.addCursorsToBottom', + label: nls.localize('mutlicursor.addCursorsToBottom', "Add Cursors To Bottom"), + alias: 'Add Cursors To Bottom', + precondition: null + }); + } + + public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + const selections = editor.getSelections(); + const lineCount = editor.getModel().getLineCount(); + + let newSelections = []; + for (let i = selections[0].startLineNumber; i <= lineCount; i++) { + newSelections.push(new Selection(i, selections[0].startColumn, i, selections[0].endColumn)); + } + + if (newSelections.length > 0) { + editor.setSelections(newSelections); + } + } +} + +class InsertCursorAtTopOfLineSelected extends EditorAction { + + constructor() { + super({ + id: 'editor.action.addCursorsToTop', + label: nls.localize('mutlicursor.addCursorsToTop', "Add Cursors To Top"), + alias: 'Add Cursors To Top', + precondition: null + }); + } + + public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + const selections = editor.getSelections(); + + let newSelections = []; + for (let i = selections[0].startLineNumber; i >= 1; i--) { + newSelections.push(new Selection(i, selections[0].startColumn, i, selections[0].endColumn)); + } + + if (newSelections.length > 0) { + editor.setSelections(newSelections); + } + } +} + export class MultiCursorSessionResult { constructor( public readonly selections: Selection[], @@ -943,3 +994,5 @@ registerEditorAction(MoveSelectionToNextFindMatchAction); registerEditorAction(MoveSelectionToPreviousFindMatchAction); registerEditorAction(SelectHighlightsAction); registerEditorAction(CompatChangeAll); +registerEditorAction(InsertCursorAtEndOfLineSelected); +registerEditorAction(InsertCursorAtTopOfLineSelected); \ No newline at end of file diff --git a/src/vs/editor/contrib/parameterHints/parameterHints.ts b/src/vs/editor/contrib/parameterHints/parameterHints.ts index f85e2604b..6a3e1c555 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHints.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHints.ts @@ -12,7 +12,7 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { ParameterHintsWidget } from './parameterHintsWidget'; +import { ParameterHintsWidget, TriggerContext } from './parameterHintsWidget'; import { Context } from 'vs/editor/contrib/parameterHints/provideSignatureHelp'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import * as modes from 'vs/editor/common/modes'; @@ -25,8 +25,8 @@ class ParameterHintsController implements IEditorContribution { return editor.getContribution(ParameterHintsController.ID); } - private editor: ICodeEditor; - private widget: ParameterHintsWidget; + private readonly editor: ICodeEditor; + private readonly widget: ParameterHintsWidget; constructor(editor: ICodeEditor, @IInstantiationService instantiationService: IInstantiationService) { this.editor = editor; @@ -49,12 +49,12 @@ class ParameterHintsController implements IEditorContribution { this.widget.next(); } - trigger(context: modes.SignatureHelpContext): void { + trigger(context: TriggerContext): void { this.widget.trigger(context); } dispose(): void { - this.widget = dispose(this.widget); + dispose(this.widget); } } @@ -77,7 +77,9 @@ export class TriggerParameterHintsAction extends EditorAction { public run(accessor: ServicesAccessor, editor: ICodeEditor): void { let controller = ParameterHintsController.get(editor); if (controller) { - controller.trigger({ triggerReason: modes.SignatureHelpTriggerReason.Invoke }); + controller.trigger({ + triggerReason: modes.SignatureHelpTriggerReason.Invoke + }); } } } diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index 1849c8d7a..16eb86f95 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -10,7 +10,7 @@ import * as dom from 'vs/base/browser/dom'; import * as aria from 'vs/base/browser/ui/aria/aria'; import * as modes from 'vs/editor/common/modes'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; -import { RunOnceScheduler, createCancelablePromise, CancelablePromise } from 'vs/base/common/async'; +import { createCancelablePromise, CancelablePromise, Delayer } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Event, Emitter, chain } from 'vs/base/common/event'; import { domEvent, stop } from 'vs/base/browser/event'; @@ -28,6 +28,11 @@ import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer'; const $ = dom.$; +export interface TriggerContext { + readonly triggerReason: modes.SignatureHelpTriggerReason; + readonly triggerCharacter?: string; +} + export interface IHintEvent { hints: modes.SignatureHelp; } @@ -50,9 +55,8 @@ export class ParameterHintsModel extends Disposable { private triggerChars = new CharacterSet(); private retriggerChars = new CharacterSet(); - private triggerContext: modes.SignatureHelpContext | undefined; - private throttledDelayer: RunOnceScheduler; - private provideSignatureHelpRequest?: CancelablePromise; + private throttledDelayer: Delayer; + private provideSignatureHelpRequest?: CancelablePromise; constructor( editor: ICodeEditor, @@ -64,7 +68,7 @@ export class ParameterHintsModel extends Disposable { this.enabled = false; this.triggerCharactersListeners = []; - this.throttledDelayer = new RunOnceScheduler(() => this.doTrigger(), delay); + this.throttledDelayer = new Delayer(delay); this._register(this.editor.onDidChangeConfiguration(() => this.onEditorConfigurationChange())); this._register(this.editor.onDidChangeModel(e => this.onModelChanged())); @@ -81,7 +85,6 @@ export class ParameterHintsModel extends Disposable { cancel(silent: boolean = false): void { this.active = false; this.pending = false; - this.triggerContext = undefined; this.throttledDelayer.cancel(); @@ -95,30 +98,37 @@ export class ParameterHintsModel extends Disposable { } } - trigger(context: modes.SignatureHelpContext, delay?: number): void { - if (!modes.SignatureHelpProviderRegistry.has(this.editor.getModel())) { + trigger(context: TriggerContext, delay?: number): void { + + const model = this.editor.getModel(); + if (model === null || !modes.SignatureHelpProviderRegistry.has(model)) { return; } - this.cancel(true); - this.triggerContext = context; - return this.throttledDelayer.schedule(delay); + this.throttledDelayer.trigger( + () => this.doTrigger({ + triggerReason: context.triggerReason, + triggerCharacter: context.triggerCharacter, + isRetrigger: this.isTriggered, + }), delay).then(undefined, onUnexpectedError); } - private doTrigger(): void { - if (this.provideSignatureHelpRequest) { - this.provideSignatureHelpRequest.cancel(); + private doTrigger(triggerContext: modes.SignatureHelpContext): Promise { + this.cancel(true); + + if (!this.editor.hasModel()) { + return Promise.resolve(false); } - this.pending = true; + const model = this.editor.getModel(); + const position = this.editor.getPosition(); - const triggerContext = this.triggerContext || { triggerReason: modes.SignatureHelpTriggerReason.Invoke }; - this.triggerContext = undefined; + this.pending = true; this.provideSignatureHelpRequest = createCancelablePromise(token => - provideSignatureHelp(this.editor.getModel(), this.editor.getPosition(), triggerContext, token)); + provideSignatureHelp(model!, position!, triggerContext, token)); - this.provideSignatureHelpRequest.then(result => { + return this.provideSignatureHelpRequest.then(result => { this.pending = false; if (!result || !result.signatures || result.signatures.length === 0) { @@ -135,11 +145,12 @@ export class ParameterHintsModel extends Disposable { }).catch(error => { this.pending = false; onUnexpectedError(error); + return false; }); } private get isTriggered(): boolean { - return this.active || this.pending || this.throttledDelayer.isScheduled(); + return this.active || this.pending || this.throttledDelayer.isTriggered(); } private onModelChanged(): void { @@ -180,15 +191,10 @@ export class ParameterHintsModel extends Disposable { const lastCharIndex = text.length - 1; const triggerCharCode = text.charCodeAt(lastCharIndex); - if (this.isTriggered && this.retriggerChars.has(triggerCharCode)) { - this.trigger({ - triggerReason: modes.SignatureHelpTriggerReason.Retrigger, - triggerCharacter: text.charAt(lastCharIndex) - }); - } else if (this.triggerChars.has(triggerCharCode)) { + if (this.triggerChars.has(triggerCharCode) || this.isTriggered && this.retriggerChars.has(triggerCharCode)) { this.trigger({ triggerReason: modes.SignatureHelpTriggerReason.TriggerCharacter, - triggerCharacter: text.charAt(lastCharIndex) + triggerCharacter: text.charAt(lastCharIndex), }); } } @@ -197,13 +203,13 @@ export class ParameterHintsModel extends Disposable { if (e.source === 'mouse') { this.cancel(); } else if (this.isTriggered) { - this.trigger({ triggerReason: modes.SignatureHelpTriggerReason.Retrigger }); + this.trigger({ triggerReason: modes.SignatureHelpTriggerReason.ContentChange }); } } private onModelContentChange(): void { if (this.isTriggered) { - this.trigger({ triggerReason: modes.SignatureHelpTriggerReason.Retrigger }); + this.trigger({ triggerReason: modes.SignatureHelpTriggerReason.ContentChange }); } } @@ -229,7 +235,7 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { private readonly markdownRenderer: MarkdownRenderer; private renderDisposeables: IDisposable[]; - private model: ParameterHintsModel; + private model: ParameterHintsModel | null; private readonly keyVisible: IContextKey; private readonly keyMultipleSignatures: IContextKey; private element: HTMLElement; @@ -238,8 +244,8 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { private overloads: HTMLElement; private currentSignature: number; private visible: boolean; - private hints: modes.SignatureHelp; - private announcedLabel: string; + private hints: modes.SignatureHelp | null; + private announcedLabel: string | null; private scrollbar: DomScrollableElement; private disposables: IDisposable[]; @@ -355,7 +361,7 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { this.editor.layoutContentWidget(this); } - getPosition(): IContentWidgetPosition { + getPosition(): IContentWidgetPosition | null { if (this.visible) { return { position: this.editor.getPosition(), @@ -366,6 +372,10 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { } private render(): void { + if (!this.hints) { + return; + } + const multiple = this.hints.signatures.length > 1; dom.toggleClass(this.element, 'multiple', multiple); this.keyMultipleSignatures.set(multiple); @@ -409,13 +419,14 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { this.renderDisposeables.push(renderedContents); documentation.appendChild(renderedContents.element); } - dom.append(this.docs, $('p', null, documentation)); + dom.append(this.docs, $('p', {}, documentation)); } dom.toggleClass(this.signature, 'has-docs', !!signature.documentation); - if (typeof signature.documentation === 'string') { - dom.append(this.docs, $('p', null, signature.documentation)); + if (signature.documentation === undefined) { /** no op */ } + else if (typeof signature.documentation === 'string') { + dom.append(this.docs, $('p', {}, signature.documentation)); } else { const renderedContents = this.markdownRenderer.render(signature.documentation); dom.addClass(renderedContents.element, 'markdown-docs'); @@ -518,6 +529,10 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { // } next(): boolean { + if (!this.hints) { + return false; + } + const length = this.hints.signatures.length; const last = (this.currentSignature % length) === (length - 1); const cycle = this.editor.getConfiguration().contribInfo.parameterHints.cycle; @@ -539,6 +554,10 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { } previous(): boolean { + if (!this.hints) { + return false; + } + const length = this.hints.signatures.length; const first = this.currentSignature === 0; const cycle = this.editor.getConfiguration().contribInfo.parameterHints.cycle; @@ -560,7 +579,9 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { } cancel(): void { - this.model.cancel(); + if (this.model) { + this.model.cancel(); + } } getDomNode(): HTMLElement { @@ -571,8 +592,10 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { return ParameterHintsWidget.ID; } - trigger(context: modes.SignatureHelpContext): void { - this.model.trigger(context, 0); + trigger(context: TriggerContext): void { + if (this.model) { + this.model.trigger(context, 0); + } } private updateMaxHeight(): void { diff --git a/src/vs/editor/contrib/parameterHints/provideSignatureHelp.ts b/src/vs/editor/contrib/parameterHints/provideSignatureHelp.ts index 99290fb5f..8f29af942 100644 --- a/src/vs/editor/contrib/parameterHints/provideSignatureHelp.ts +++ b/src/vs/editor/contrib/parameterHints/provideSignatureHelp.ts @@ -27,4 +27,7 @@ export function provideSignatureHelp(model: ITextModel, position: Position, cont } registerDefaultLanguageCommand('_executeSignatureHelpProvider', (model, position) => - provideSignatureHelp(model, position, { triggerReason: modes.SignatureHelpTriggerReason.Invoke }, CancellationToken.None)); + provideSignatureHelp(model, position, { + triggerReason: modes.SignatureHelpTriggerReason.Invoke, + isRetrigger: false + }, CancellationToken.None)); diff --git a/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts b/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts index 586e20631..5145f5565 100644 --- a/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts +++ b/src/vs/editor/contrib/parameterHints/test/parameterHintsModel.test.ts @@ -14,7 +14,7 @@ import { TextModel } from 'vs/editor/common/model/textModel'; import * as modes from 'vs/editor/common/modes'; import { createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { IStorageService, NullStorageService } from 'vs/platform/storage/common/storage'; +import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { ParameterHintsModel } from '../parameterHintsWidget'; @@ -44,7 +44,7 @@ suite('ParameterHintsModel', () => { model: textModel, serviceCollection: new ServiceCollection( [ITelemetryService, NullTelemetryService], - [IStorageService, NullStorageService] + [IStorageService, InMemoryStorageService] ) }); disposables.push(textModel); @@ -93,7 +93,8 @@ suite('ParameterHintsModel', () => { editor.trigger('keyboard', Handler.Type, { text: triggerChar }); } else { assert.strictEqual(invokeCount, 2); - assert.strictEqual(context.triggerReason, modes.SignatureHelpTriggerReason.Retrigger); + assert.strictEqual(context.triggerReason, modes.SignatureHelpTriggerReason.TriggerCharacter); + assert.ok(context.isRetrigger); assert.strictEqual(context.triggerCharacter, triggerChar); done(); } @@ -149,7 +150,9 @@ suite('ParameterHintsModel', () => { provideSignatureHelp(_model: ITextModel, _position: Position, _token: CancellationToken, context: modes.SignatureHelpContext): modes.SignatureHelp | Thenable { ++invokeCount; - assert.strictEqual(context.triggerReason, modes.SignatureHelpTriggerReason.Retrigger); + + assert.strictEqual(context.triggerReason, modes.SignatureHelpTriggerReason.TriggerCharacter); + assert.strictEqual(context.isRetrigger, false); assert.strictEqual(context.triggerCharacter, 'c'); // Give some time to allow for later triggers @@ -185,7 +188,8 @@ suite('ParameterHintsModel', () => { // retrigger after delay for widget to show up setTimeout(() => editor.trigger('keyboard', Handler.Type, { text: 'b' }), 50); } else if (invokeCount === 2) { - assert.strictEqual(context.triggerReason, modes.SignatureHelpTriggerReason.Retrigger); + assert.strictEqual(context.triggerReason, modes.SignatureHelpTriggerReason.TriggerCharacter); + assert.ok(context.isRetrigger); assert.strictEqual(context.triggerCharacter, 'b'); done(); } else { @@ -272,7 +276,8 @@ suite('ParameterHintsModel', () => { // retrigger after delay for widget to show up setTimeout(() => editor.trigger('keyboard', Handler.Type, { text: retriggerChar }), 50); } else if (invokeCount === 2) { - assert.strictEqual(context.triggerReason, modes.SignatureHelpTriggerReason.Retrigger); + assert.strictEqual(context.triggerReason, modes.SignatureHelpTriggerReason.TriggerCharacter); + assert.ok(context.isRetrigger); assert.strictEqual(context.triggerCharacter, retriggerChar); done(); } else { diff --git a/src/vs/editor/contrib/referenceSearch/referenceSearch.ts b/src/vs/editor/contrib/referenceSearch/referenceSearch.ts index 5c872b26f..539d96078 100644 --- a/src/vs/editor/contrib/referenceSearch/referenceSearch.ts +++ b/src/vs/editor/contrib/referenceSearch/referenceSearch.ts @@ -60,8 +60,8 @@ export class ReferenceAction extends EditorAction { constructor() { super({ id: 'editor.action.referenceSearch.trigger', - label: nls.localize('references.action.label', "Find All References"), - alias: 'Find All References', + label: nls.localize('references.action.label', "Peek References"), + alias: 'Find All References', // leave the alias? precondition: ContextKeyExpr.and( EditorContextKeys.hasReferenceProvider, PeekContext.notInPeekEditor, diff --git a/src/vs/editor/contrib/smartSelect/smartSelect.ts b/src/vs/editor/contrib/smartSelect/smartSelect.ts index 3305a1cfa..d7c9b6002 100644 --- a/src/vs/editor/contrib/smartSelect/smartSelect.ts +++ b/src/vs/editor/contrib/smartSelect/smartSelect.ts @@ -22,14 +22,12 @@ import { MenuId } from 'vs/platform/actions/common/actions'; class State { public editor: ICodeEditor; - public next: State; - public previous: State; - public selection: Range; + public next?: State; + public previous?: State; + public selection: Range | null; constructor(editor: ICodeEditor) { this.editor = editor; - this.next = null; - this.previous = null; this.selection = editor.getSelection(); } } @@ -45,7 +43,7 @@ class SmartSelectController implements IEditorContribution { } private _tokenSelectionSupport: TokenSelectionSupport; - private _state: State; + private _state?: State; private _ignoreSelection: boolean; constructor( @@ -53,7 +51,6 @@ class SmartSelectController implements IEditorContribution { @IInstantiationService instantiationService: IInstantiationService ) { this._tokenSelectionSupport = instantiationService.createInstance(TokenSelectionSupport); - this._state = null; this._ignoreSelection = false; } @@ -65,6 +62,9 @@ class SmartSelectController implements IEditorContribution { } public run(forward: boolean): Promise { + if (!this.editor.hasModel()) { + return Promise.resolve(void 0); + } const selection = this.editor.getSelection(); const model = this.editor.getModel(); @@ -72,11 +72,11 @@ class SmartSelectController implements IEditorContribution { // forget about current state if (this._state) { if (this._state.editor !== this.editor) { - this._state = null; + this._state = undefined; } } - let promise: Promise = Promise.resolve(null); + let promise: Promise = Promise.resolve(void 0); if (!this._state) { promise = Promise.resolve(this._tokenSelectionSupport.getRangesToPositionSync(model.uri, selection.getStartPosition())).then((elements: ILogicalSelectionEntry[]) => { @@ -84,8 +84,11 @@ class SmartSelectController implements IEditorContribution { return; } - let lastState: State; + let lastState: State | undefined; elements.filter((element) => { + if (!this.editor.hasModel()) { + return false; + } // filter ranges inside the selection const selection = this.editor.getSelection(); const range = new Range(element.range.startLineNumber, element.range.startColumn, element.range.endLineNumber, element.range.endColumn); @@ -116,7 +119,7 @@ class SmartSelectController implements IEditorContribution { if (this._ignoreSelection) { return; } - this._state = null; + this._state = undefined; unhook.dispose(); }); }); @@ -135,7 +138,9 @@ class SmartSelectController implements IEditorContribution { this._ignoreSelection = true; try { - this.editor.setSelection(this._state.selection); + if (this._state.selection) { + this.editor.setSelection(this._state.selection); + } } finally { this._ignoreSelection = false; } @@ -154,7 +159,7 @@ abstract class AbstractSmartSelect extends EditorAction { this._forward = forward; } - public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { + public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise | undefined { let controller = SmartSelectController.get(editor); if (controller) { return controller.run(this._forward); diff --git a/src/vs/editor/contrib/smartSelect/test/tokenSelectionSupport.test.ts b/src/vs/editor/contrib/smartSelect/test/tokenSelectionSupport.test.ts index 633ce5031..bab374e26 100644 --- a/src/vs/editor/contrib/smartSelect/test/tokenSelectionSupport.test.ts +++ b/src/vs/editor/contrib/smartSelect/test/tokenSelectionSupport.test.ts @@ -8,7 +8,7 @@ import { Range } from 'vs/editor/common/core/range'; import { Position } from 'vs/editor/common/core/position'; import { LanguageIdentifier } from 'vs/editor/common/modes'; import { TokenSelectionSupport } from 'vs/editor/contrib/smartSelect/tokenSelectionSupport'; -import { MockMode } from 'vs/editor/test/common/mocks/mockMode'; +import { MockMode, StaticLanguageSelector } from 'vs/editor/test/common/mocks/mockMode'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; @@ -56,7 +56,7 @@ suite('TokenSelectionSupport', () => { function assertGetRangesToPosition(text: string[], lineNumber: number, column: number, ranges: Range[]): void { let uri = URI.file('test.js'); - modelService.createModel(text.join('\n'), mode, uri); + modelService.createModel(text.join('\n'), new StaticLanguageSelector(mode.getLanguageIdentifier()), uri); let actual = tokenSelectionSupport.getRangesToPositionSync(uri, new Position(lineNumber, column)); diff --git a/src/vs/editor/contrib/smartSelect/tokenSelectionSupport.ts b/src/vs/editor/contrib/smartSelect/tokenSelectionSupport.ts index 8fca91c77..04400c569 100644 --- a/src/vs/editor/contrib/smartSelect/tokenSelectionSupport.ts +++ b/src/vs/editor/contrib/smartSelect/tokenSelectionSupport.ts @@ -7,14 +7,14 @@ import { URI } from 'vs/base/common/uri'; import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { Node, build, find } from './tokenTree'; +import { build, find } from './tokenTree'; import { Position } from 'vs/editor/common/core/position'; /** * Interface used to compute a hierachry of logical ranges. */ export interface ILogicalSelectionEntry { - type: string; + type?: string; range: Range; } @@ -45,10 +45,9 @@ export class TokenSelectionSupport { private _doGetRangesToPosition(model: ITextModel, position: Position): Range[] { let tree = build(model); - let node: Node; - let lastRange: Range; + let lastRange: Range | undefined; - node = find(tree, position); + let node = find(tree, position); let ranges: Range[] = []; while (node) { if (!lastRange || !Range.equalsRange(lastRange, node.range)) { diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index 9928428a6..17bf8b96b 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -17,7 +17,7 @@ import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { optional } from 'vs/platform/instantiation/common/instantiation'; import { Choice, Placeholder, SnippetParser, Text, TextmateSnippet } from './snippetParser'; -import { ClipboardBasedVariableResolver, CompositeSnippetVariableResolver, ModelBasedVariableResolver, SelectionBasedVariableResolver, TimeBasedVariableResolver } from './snippetVariables'; +import { ClipboardBasedVariableResolver, CompositeSnippetVariableResolver, ModelBasedVariableResolver, SelectionBasedVariableResolver, TimeBasedVariableResolver, CommentBasedVariableResolver } from './snippetVariables'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import * as colors from 'vs/platform/theme/common/colorRegistry'; @@ -402,6 +402,7 @@ export class SnippetSession { modelBasedVariableResolver, new ClipboardBasedVariableResolver(clipboardService, idx, indexedSelections.length), 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 8713e370e..92b27d62f 100644 --- a/src/vs/editor/contrib/snippet/snippetVariables.ts +++ b/src/vs/editor/contrib/snippet/snippetVariables.ts @@ -8,6 +8,7 @@ import { basename, dirname } from 'vs/base/common/paths'; import { ITextModel } from 'vs/editor/common/model'; import { Selection } from 'vs/editor/common/core/selection'; import { VariableResolver, Variable, Text } from 'vs/editor/contrib/snippet/snippetParser'; +import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { getLeadingWhitespace, commonPrefixLength, isFalsyOrWhitespace, pad } from 'vs/base/common/strings'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; @@ -34,6 +35,9 @@ export const KnownSnippetVariableNames = Object.freeze({ 'TM_FILENAME_BASE': true, 'TM_DIRECTORY': true, 'TM_FILEPATH': true, + 'BLOCK_COMMENT_START': true, + 'BLOCK_COMMENT_END': true, + 'LINE_COMMENT': true, }); export class CompositeSnippetVariableResolver implements VariableResolver { @@ -68,7 +72,7 @@ export class SelectionBasedVariableResolver implements VariableResolver { if (name === 'SELECTION' || name === 'TM_SELECTED_TEXT') { let value = this._model.getValueInRange(this._selection) || undefined; - if (value && this._selection.startLineNumber !== this._selection.endLineNumber) { + if (value && this._selection.startLineNumber !== this._selection.endLineNumber && variable.snippet) { // Selection is a multiline string which we indentation we now // need to adjust. We compare the indentation of this variable // with the indentation at the editor position and add potential @@ -83,7 +87,7 @@ export class SelectionBasedVariableResolver implements VariableResolver { return false; } if (marker instanceof Text) { - varLeadingWhitespace = getLeadingWhitespace(marker.value.split(/\r\n|\r|\n/).pop()); + varLeadingWhitespace = getLeadingWhitespace(marker.value.split(/\r\n|\r|\n/).pop()!); } return true; }); @@ -180,7 +184,29 @@ export class ClipboardBasedVariableResolver implements VariableResolver { } } } - +export class CommentBasedVariableResolver implements VariableResolver { + constructor( + private readonly _model: ITextModel + ) { + // + } + resolve(variable: Variable): string | undefined { + const { name } = variable; + const language = this._model.getLanguageIdentifier(); + const config = LanguageConfigurationRegistry.getComments(language.id); + if (!config) { + return undefined; + } + if (name === 'LINE_COMMENT') { + return config.lineCommentToken || undefined; + } else if (name === 'BLOCK_COMMENT_START') { + return config.blockCommentStartToken || undefined; + } else if (name === 'BLOCK_COMMENT_END') { + return config.blockCommentEndToken || undefined; + } + return undefined; + } +} export class TimeBasedVariableResolver implements VariableResolver { private static readonly dayNames = [nls.localize('Sunday', "Sunday"), nls.localize('Monday', "Monday"), nls.localize('Tuesday', "Tuesday"), nls.localize('Wednesday', "Wednesday"), nls.localize('Thursday', "Thursday"), nls.localize('Friday', "Friday"), nls.localize('Saturday', "Saturday")]; diff --git a/src/vs/editor/contrib/suggest/media/suggest.css b/src/vs/editor/contrib/suggest/media/suggest.css index 384bcb782..36cfab86b 100644 --- a/src/vs/editor/contrib/suggest/media/suggest.css +++ b/src/vs/editor/contrib/suggest/media/suggest.css @@ -123,6 +123,7 @@ overflow: hidden; text-overflow: ellipsis; opacity: 0.7; + white-space: nowrap; } .monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .type-label > .monaco-tokenized-source { @@ -172,6 +173,7 @@ background-image: url('Misc_16x.svg'); background-repeat: no-repeat; background-position: center; + background-size: contain; } .monaco-editor .suggest-widget .monaco-list .monaco-list-row .suggest-icon.method::before, @@ -221,7 +223,7 @@ } .monaco-editor .suggest-widget.docs-below .details { - border-top-width: 0px; + border-top-width: 0; } .monaco-editor .suggest-widget .details > .monaco-scrollable-element { diff --git a/src/vs/editor/contrib/suggest/suggest.ts b/src/vs/editor/contrib/suggest/suggest.ts index f26191571..7a74211a8 100644 --- a/src/vs/editor/contrib/suggest/suggest.ts +++ b/src/vs/editor/contrib/suggest/suggest.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { first } from 'vs/base/common/async'; -import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; import { assign } from 'vs/base/common/objects'; import { onUnexpectedExternalError, canceled } from 'vs/base/common/errors'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; @@ -80,7 +80,7 @@ export function provideSuggestionItems( // for each support in the group ask for suggestions return Promise.all(supports.map(support => { - if (!isFalsyOrEmpty(onlyFrom) && onlyFrom!.indexOf(support) < 0) { + if (isNonEmptyArray(onlyFrom) && onlyFrom.indexOf(support) < 0) { return undefined; } @@ -88,8 +88,8 @@ export function provideSuggestionItems( const len = allSuggestions.length; - if (container && !isFalsyOrEmpty(container.suggestions)) { - for (let suggestion of container.suggestions) { + if (container) { + for (let suggestion of container.suggestions || []) { if (acceptSuggestion(suggestion)) { // fill in default range when missing diff --git a/src/vs/editor/contrib/suggest/suggestModel.ts b/src/vs/editor/contrib/suggest/suggestModel.ts index c37df1178..dff805107 100644 --- a/src/vs/editor/contrib/suggest/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/suggestModel.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; import { TimeoutTimer } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; @@ -193,10 +193,7 @@ export class SuggestModel implements IDisposable { const supportsByTriggerCharacter: { [ch: string]: Set } = Object.create(null); for (const support of CompletionProviderRegistry.all(this._editor.getModel())) { - if (isFalsyOrEmpty(support.triggerCharacters)) { - continue; - } - for (const ch of support.triggerCharacters) { + for (const ch of support.triggerCharacters || []) { let set = supportsByTriggerCharacter[ch]; if (!set) { set = supportsByTriggerCharacter[ch] = new Set(); @@ -386,6 +383,8 @@ export class SuggestModel implements IDisposable { this._requestToken = new CancellationTokenSource(); + // TODO: Remove this workaround - https://github.com/Microsoft/vscode/issues/61917 + // let wordDistance = Promise.resolve().then(() => WordDistance.create(this._editorWorker, this._editor)); let wordDistance = WordDistance.create(this._editorWorker, this._editor); let items = provideSuggestionItems( @@ -409,7 +408,7 @@ export class SuggestModel implements IDisposable { return; } - if (!isFalsyOrEmpty(existingItems)) { + if (isNonEmptyArray(existingItems)) { const cmpFn = getSuggestionComparator(this._editor.getConfiguration().contribInfo.suggest.snippets); items = items.concat(existingItems).sort(cmpFn); } diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 3dc19b691..fabad1d6b 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -765,7 +765,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate(orderedByScore.map(provider => () => { return Promise.resolve(provider.provideDocumentHighlights(model, position, token)) .then(undefined, onUnexpectedExternalError); - }), result => !arrays.isFalsyOrEmpty(result)); + }), arrays.isNonEmptyArray); } interface IOccurenceAtPositionRequest { diff --git a/src/vs/editor/contrib/zoneWidget/zoneWidget.ts b/src/vs/editor/contrib/zoneWidget/zoneWidget.ts index e2f37b85c..a627626bf 100644 --- a/src/vs/editor/contrib/zoneWidget/zoneWidget.ts +++ b/src/vs/editor/contrib/zoneWidget/zoneWidget.ts @@ -31,8 +31,8 @@ export interface IOptions { } export interface IStyles { - frameColor?: Color; - arrowColor?: Color; + frameColor?: Color | null; + arrowColor?: Color | null; } const defaultColor = new Color(new RGBA(0, 122, 204)); diff --git a/src/vs/editor/standalone/browser/colorizer.ts b/src/vs/editor/standalone/browser/colorizer.ts index 621861e26..e085b2e80 100644 --- a/src/vs/editor/standalone/browser/colorizer.ts +++ b/src/vs/editor/standalone/browser/colorizer.ts @@ -60,7 +60,7 @@ export class Colorizer { } // Send out the event to create the mode - modeService.getOrCreateMode(language); + modeService.triggerMode(language); let tokenizationSupport = TokenizationRegistry.get(language); if (tokenizationSupport) { diff --git a/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts b/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts index 0b84763f0..9b77523fe 100644 --- a/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts +++ b/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts @@ -22,11 +22,13 @@ export class EditorActionCommandEntry extends QuickOpenEntryGroup { private key: string; private action: IEditorAction; private editor: IEditor; + private keyAriaLabel: string; - constructor(key: string, highlights: IHighlight[], action: IEditorAction, editor: IEditor) { + constructor(key: string, keyAriaLabel: string, highlights: IHighlight[], action: IEditorAction, editor: IEditor) { super(); this.key = key; + this.keyAriaLabel = keyAriaLabel; this.setHighlights(highlights); this.action = action; this.editor = editor; @@ -37,6 +39,10 @@ export class EditorActionCommandEntry extends QuickOpenEntryGroup { } public getAriaLabel(): string { + if (this.keyAriaLabel) { + return nls.localize('ariaLabelEntryWithKey', "{0}, {1}, commands", this.getLabel(), this.keyAriaLabel); + } + return nls.localize('ariaLabelEntry', "{0}, commands", this.getLabel()); } @@ -119,12 +125,12 @@ export class QuickCommandAction extends BaseEditorQuickOpenAction { for (let i = 0; i < actions.length; i++) { let action = actions[i]; - let keybind = keybindingService.lookupKeybinding(action.id); + let keybinding = keybindingService.lookupKeybinding(action.id); if (action.label) { let highlights = matchesFuzzy(searchValue, action.label); if (highlights) { - entries.push(new EditorActionCommandEntry(keybind ? keybind.getLabel() : '', highlights, action, editor)); + entries.push(new EditorActionCommandEntry(keybinding ? keybinding.getLabel() : '', keybinding ? keybinding.getAriaLabel() : '', highlights, action, editor)); } } } diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 2d2a285fd..f1e31e1af 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -12,7 +12,6 @@ import { IDisposable, IReference, ImmortalReference, combinedDisposable, toDispo import { OS, isLinux, isMacintosh } from 'vs/base/common/platform'; import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ICodeEditor, IDiffEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IBulkEditOptions, IBulkEditResult, IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { isDiffEditorConfigurationKey, isEditorConfigurationKey } from 'vs/editor/common/config/commonEditorConfig'; @@ -25,8 +24,6 @@ import { TextEdit, WorkspaceEdit, isResourceTextEdit } from 'vs/editor/common/mo import { IModelService } from 'vs/editor/common/services/modelService'; import { ITextEditorModel, ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService'; import { ITextResourceConfigurationService, ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; -import { IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; -import { Menu } from 'vs/platform/actions/common/menu'; import { CommandsRegistry, ICommand, ICommandEvent, ICommandHandler, ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationChangeEvent, IConfigurationData, IConfigurationOverrides, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Configuration, ConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; @@ -60,8 +57,8 @@ export class SimpleModel implements ITextEditorModel { return this._onDispose.event; } - public load(): TPromise { - return TPromise.as(this); + public load(): Promise { + return Promise.resolve(this); } public get textEditorModel(): ITextModel { @@ -100,7 +97,7 @@ export class SimpleEditorModelResolverService implements ITextModelService { this.editor = editor; } - public createModelReference(resource: URI): TPromise> { + public createModelReference(resource: URI): Promise> { let model: ITextModel; model = withTypedEditor(this.editor, @@ -109,10 +106,10 @@ export class SimpleEditorModelResolverService implements ITextModelService { ); if (!model) { - return TPromise.as(new ImmortalReference(null)); + return Promise.resolve(new ImmortalReference(null)); } - return TPromise.as(new ImmortalReference(new SimpleModel(model))); + return Promise.resolve(new ImmortalReference(new SimpleModel(model))); } public registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): IDisposable { @@ -479,31 +476,16 @@ export class SimpleResourcePropertiesService implements ITextResourcePropertiesS } } -export class SimpleMenuService implements IMenuService { - - _serviceBrand: any; - - private readonly _commandService: ICommandService; - - constructor(commandService: ICommandService) { - this._commandService = commandService; - } - - public createMenu(id: MenuId, contextKeyService: IContextKeyService): IMenu { - return new Menu(id, Promise.resolve(true), this._commandService, contextKeyService); - } -} - export class StandaloneTelemetryService implements ITelemetryService { _serviceBrand: void; public isOptedIn = false; - public publicLog(eventName: string, data?: any): TPromise { - return TPromise.wrap(null); + public publicLog(eventName: string, data?: any): Promise { + return Promise.resolve(null); } - public getTelemetryInfo(): TPromise { + public getTelemetryInfo(): Promise { return null; } } @@ -635,4 +617,8 @@ export class SimpleUriLabelService implements ILabelService { public registerFormatter(selector: string, formatter: LabelRules): IDisposable { throw new Error('Not implemented'); } + + public getHostLabel(): string { + return ''; + } } diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index 32a6d0727..385af45f8 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -69,7 +69,7 @@ export interface IActionDescriptor { contextMenuOrder?: number; /** * Method that will be executed when the action is triggered. - * @param editor The editor instance is passed in as a convinience + * @param editor The editor instance is passed in as a convenience */ run(editor: ICodeEditor): void | Promise; } diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index 5802e10b9..acace4115 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -18,6 +18,7 @@ import { FindMatch, ITextModel, TextModelResolvedOptions } from 'vs/editor/commo import * as modes from 'vs/editor/common/modes'; import { NULL_STATE, nullTokenize } from 'vs/editor/common/modes/nullMode'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { ILanguageSelection } from 'vs/editor/common/services/modeService'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IWebWorkerOptions, MonacoWebWorker, createWebWorker as actualCreateWebWorker } from 'vs/editor/common/services/webWorker'; import * as standaloneEnums from 'vs/editor/common/standalone/standaloneEnums'; @@ -134,8 +135,8 @@ export function createDiffNavigator(diffEditor: IStandaloneDiffEditor, opts?: ID return new DiffNavigator(diffEditor, opts); } -function doCreateModel(value: string, mode: Promise, uri?: URI): ITextModel { - return StaticServices.modelService.get().createModel(value, mode, uri); +function doCreateModel(value: string, languageSelection: ILanguageSelection, uri?: URI): ITextModel { + return StaticServices.modelService.get().createModel(value, languageSelection, uri); } /** @@ -154,16 +155,16 @@ export function createModel(value: string, language?: string, uri?: URI): ITextM firstLine = value.substring(0, firstLF); } - return doCreateModel(value, StaticServices.modeService.get().getOrCreateModeByFilepathOrFirstLine(path, firstLine), uri); + return doCreateModel(value, StaticServices.modeService.get().createByFilepathOrFirstLine(path, firstLine), uri); } - return doCreateModel(value, StaticServices.modeService.get().getOrCreateMode(language), uri); + return doCreateModel(value, StaticServices.modeService.get().create(language), uri); } /** * Change the language for a model. */ export function setModelLanguage(model: ITextModel, languageId: string): void { - StaticServices.modelService.get().setMode(model, StaticServices.modeService.get().getOrCreateMode(languageId)); + StaticServices.modelService.get().setMode(model, StaticServices.modeService.get().create(languageId)); } /** @@ -277,7 +278,7 @@ function getSafeTokenizationSupport(language: string): modes.ITokenizationSuppor export function tokenize(text: string, languageId: string): Token[][] { let modeService = StaticServices.modeService.get(); // Needed in order to get the mode registered for subsequent look-ups - modeService.getOrCreateMode(languageId); + modeService.triggerMode(languageId); let tokenizationSupport = getSafeTokenizationSupport(languageId); let lines = text.split(/\r\n|\r|\n/); @@ -294,7 +295,7 @@ export function tokenize(text: string, languageId: string): Token[][] { } /** - * Define a new theme or updte an existing theme. + * Define a new theme or update an existing theme. */ export function defineTheme(themeName: string, themeData: IStandaloneThemeData): void { StaticServices.standaloneThemeService.get().defineTheme(themeName, themeData); diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index cc62e166b..5d3be561d 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -334,7 +334,7 @@ export function registerRenameProvider(languageId: string, provider: modes.Renam } /** - * Register a signature help provider (used by e.g. paremeter hints). + * Register a signature help provider (used by e.g. parameter hints). */ export function registerSignatureHelpProvider(languageId: string, provider: modes.SignatureHelpProvider): IDisposable { return modes.SignatureHelpProviderRegistry.register(languageId, provider); diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index 3e6bd77a8..c37d0812b 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -13,7 +13,7 @@ import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { ITextResourceConfigurationService, ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; -import { SimpleBulkEditService, SimpleConfigurationService, SimpleDialogService, SimpleMenuService, SimpleNotificationService, SimpleProgressService, SimpleResourceConfigurationService, SimpleResourcePropertiesService, SimpleUriLabelService, SimpleWorkspaceContextService, StandaloneCommandService, StandaloneKeybindingService, StandaloneTelemetryService } from 'vs/editor/standalone/browser/simpleServices'; +import { SimpleBulkEditService, SimpleConfigurationService, SimpleDialogService, SimpleNotificationService, SimpleProgressService, SimpleResourceConfigurationService, SimpleResourcePropertiesService, SimpleUriLabelService, SimpleWorkspaceContextService, StandaloneCommandService, StandaloneKeybindingService, StandaloneTelemetryService } from 'vs/editor/standalone/browser/simpleServices'; import { StandaloneCodeEditorServiceImpl } from 'vs/editor/standalone/browser/standaloneCodeServiceImpl'; import { StandaloneThemeServiceImpl } from 'vs/editor/standalone/browser/standaloneThemeServiceImpl'; import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneThemeService'; @@ -37,10 +37,11 @@ import { MarkerService } from 'vs/platform/markers/common/markerService'; import { IMarkerService } from 'vs/platform/markers/common/markers'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IProgressService } from 'vs/platform/progress/common/progress'; -import { IStorageService, NullStorageService } from 'vs/platform/storage/common/storage'; +import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { MenuService } from 'vs/platform/actions/common/menuService'; export interface IEditorOverrideServices { [index: string]: any; @@ -142,7 +143,7 @@ export module StaticServices { export const progressService = define(IProgressService, () => new SimpleProgressService()); - export const storageService = define(IStorageService, () => NullStorageService); + export const storageService = define(IStorageService, () => InMemoryStorageService); export const logService = define(ILogService, () => new NullLogService()); @@ -189,7 +190,7 @@ export class DynamicStandaloneServices extends Disposable { ensure(IContextMenuService, () => this._register(new ContextMenuService(domElement, telemetryService, notificationService, contextViewService, keybindingService, themeService))); - ensure(IMenuService, () => new SimpleMenuService(commandService)); + ensure(IMenuService, () => new MenuService(commandService)); ensure(IBulkEditService, () => new SimpleBulkEditService(StaticServices.modelService.get(IModelService))); } diff --git a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts index 32ef8b200..50633da83 100644 --- a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts +++ b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts @@ -216,6 +216,10 @@ export class StandaloneThemeServiceImpl implements IStandaloneThemeService { } else { theme = this._knownThemes.get(VS_THEME_NAME); } + if (this._theme === theme) { + // Nothing to do + return theme.id; + } this._theme = theme; let cssRules: string[] = []; diff --git a/src/vs/editor/standalone/common/monarch/monarchLexer.ts b/src/vs/editor/standalone/common/monarch/monarchLexer.ts index 7142c905f..35c9e5b1c 100644 --- a/src/vs/editor/standalone/common/monarch/monarchLexer.ts +++ b/src/vs/editor/standalone/common/monarch/monarchLexer.ts @@ -787,38 +787,31 @@ class MonarchTokenizer implements modes.ITokenizationSupport { } private _getNestedEmbeddedModeData(mimetypeOrModeId: string): EmbeddedModeData { - let nestedMode = this._locateMode(mimetypeOrModeId); - if (nestedMode) { - let tokenizationSupport = modes.TokenizationRegistry.get(nestedMode.getId()); + let nestedModeId = this._locateMode(mimetypeOrModeId); + if (nestedModeId) { + let tokenizationSupport = modes.TokenizationRegistry.get(nestedModeId); if (tokenizationSupport) { - return new EmbeddedModeData(nestedMode.getId(), tokenizationSupport.getInitialState()); + return new EmbeddedModeData(nestedModeId, tokenizationSupport.getInitialState()); } } - let nestedModeId = nestedMode ? nestedMode.getId() : NULL_MODE_ID; - return new EmbeddedModeData(nestedModeId, NULL_STATE); + return new EmbeddedModeData(nestedModeId || NULL_MODE_ID, NULL_STATE); } - private _locateMode(mimetypeOrModeId: string): modes.IMode { + private _locateMode(mimetypeOrModeId: string): string | null { if (!mimetypeOrModeId || !this._modeService.isRegisteredMode(mimetypeOrModeId)) { return null; } let modeId = this._modeService.getModeId(mimetypeOrModeId); - // Fire mode loading event - this._modeService.getOrCreateMode(modeId); - - let mode = this._modeService.getMode(modeId); - if (mode) { - // Re-emit tokenizationSupport change events from all modes that I ever embedded + if (modeId) { + // Fire mode loading event + this._modeService.triggerMode(modeId); this._embeddedModes[modeId] = true; - return mode; } - this._embeddedModes[modeId] = true; - - return null; + return modeId; } } diff --git a/src/vs/editor/standalone/common/monarch/monarchTypes.ts b/src/vs/editor/standalone/common/monarch/monarchTypes.ts index c254f8158..34f42d7a5 100644 --- a/src/vs/editor/standalone/common/monarch/monarchTypes.ts +++ b/src/vs/editor/standalone/common/monarch/monarchTypes.ts @@ -105,7 +105,7 @@ export interface IExpandedMonarchLanguageAction { */ bracket?: string; /** - * switch to embedded language (useing the mimetype) or get out using "@pop" + * switch to embedded language (using the mimetype) or get out using "@pop" */ nextEmbedded?: string; /** @@ -128,7 +128,7 @@ export interface IMonarchLanguageBracket { */ open: string; /** - * closeing bracket + * closing bracket */ close: string; /** diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index 323ccc2bc..c69813039 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -1105,14 +1105,13 @@ class SurroundingMode extends MockMode { class OnEnterMode extends MockMode { private static readonly _id = new LanguageIdentifier('onEnterMode', 3); - constructor(indentAction: IndentAction, outdentCurrentLine?: boolean) { + constructor(indentAction: IndentAction) { super(OnEnterMode._id); this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { onEnterRules: [{ beforeText: /.*/, action: { - indentAction: indentAction, - outdentCurrentLine: outdentCurrentLine + indentAction: indentAction } }] })); diff --git a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts index e72956f57..feb3a465c 100644 --- a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts +++ b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts @@ -134,7 +134,7 @@ suite('Decoration Render Options', () => { // unix file path (used as string) let s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet); - s.registerDecorationType('example', { gutterIconPath: '/Users/foo/bar.png' }); + s.registerDecorationType('example', { gutterIconPath: URI.file('/Users/foo/bar.png') }); let sheet = readStyleSheet(styleSheet);//.innerHTML || styleSheet.sheet.toString(); assert( sheet.indexOf('background: url(\'file:///Users/foo/bar.png\') center center no-repeat;') > 0 @@ -143,7 +143,7 @@ suite('Decoration Render Options', () => { // windows file path (used as string) s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet); - s.registerDecorationType('example', { gutterIconPath: 'c:\\files\\miles\\more.png' }); + s.registerDecorationType('example', { gutterIconPath: URI.file('c:\\files\\miles\\more.png') }); sheet = readStyleSheet(styleSheet); // TODO@Alex test fails // assert( @@ -162,7 +162,7 @@ suite('Decoration Render Options', () => { // single quote must always be escaped/encoded s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet); - s.registerDecorationType('example', { gutterIconPath: '/Users/foo/b\'ar.png' }); + s.registerDecorationType('example', { gutterIconPath: URI.file('/Users/foo/b\'ar.png') }); sheet = readStyleSheet(styleSheet); assert( sheet.indexOf('background: url(\'file:///Users/foo/b%27ar.png\') center center no-repeat;') > 0 diff --git a/src/vs/editor/test/browser/testCommand.ts b/src/vs/editor/test/browser/testCommand.ts index c145216dd..7bc2f7974 100644 --- a/src/vs/editor/test/browser/testCommand.ts +++ b/src/vs/editor/test/browser/testCommand.ts @@ -61,7 +61,7 @@ export function getEditOperation(model: ITextModel, command: editorCommon.IComma trackSelection: (selection: Selection) => { - return null; + return ''; } }; command.getEditOperations(model, editOperationBuilder); diff --git a/src/vs/editor/test/common/mocks/mockMode.ts b/src/vs/editor/test/common/mocks/mockMode.ts index 7d5ce73d9..bf24cc35c 100644 --- a/src/vs/editor/test/common/mocks/mockMode.ts +++ b/src/vs/editor/test/common/mocks/mockMode.ts @@ -3,8 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IMode, LanguageIdentifier } from 'vs/editor/common/modes'; +import { ILanguageSelection } from 'vs/editor/common/services/modeService'; export class MockMode extends Disposable implements IMode { private _languageIdentifier: LanguageIdentifier; @@ -22,3 +24,9 @@ export class MockMode extends Disposable implements IMode { return this._languageIdentifier; } } + +export class StaticLanguageSelector implements ILanguageSelection { + readonly onDidChange: Event = Event.None; + constructor(public readonly languageIdentifier: LanguageIdentifier) { } + public dispose(): void { } +} diff --git a/src/vs/editor/test/common/modes/linkComputer.test.ts b/src/vs/editor/test/common/modes/linkComputer.test.ts index 70daaefe1..b4f3b76f6 100644 --- a/src/vs/editor/test/common/modes/linkComputer.test.ts +++ b/src/vs/editor/test/common/modes/linkComputer.test.ts @@ -195,4 +195,11 @@ suite('Editor Modes - Link Computer', () => { ' https://msdn.microsoft.com/en-us/library/windows/desktop/ms687414(v=vs.85).aspx ' ); }); + + test('issue #62278: "Ctrl + click to follow link" for IPv6 URLs', () => { + assertLink( + 'let x = "http://[::1]:5000/connect/token"', + ' http://[::1]:5000/connect/token ' + ); + }); }); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 3ed51a54a..d6ea545dd 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -151,7 +151,7 @@ declare namespace monaco { * * @param value A string which represents an Uri (see `Uri#toString`). */ - static parse(value: string): Uri; + static parse(value: string, _strict?: boolean): Uri; /** * Creates a new Uri from a file system path, e.g. `c:\my\files`, * `/usr/home`, or `\\server\share\some\path`. @@ -392,9 +392,13 @@ declare namespace monaco { static readonly WinCtrl: number; static chord(firstPart: number, secondPart: number): number; } + export interface IMarkdownString { value: string; isTrusted?: boolean; + uris?: { + [href: string]: UriComponents; + }; } export interface IKeyboardEvent { @@ -931,7 +935,7 @@ declare namespace monaco.editor { export function tokenize(text: string, languageId: string): Token[][]; /** - * Define a new theme or updte an existing theme. + * Define a new theme or update an existing theme. */ export function defineTheme(themeName: string, themeData: IStandaloneThemeData): void; @@ -1036,7 +1040,7 @@ declare namespace monaco.editor { contextMenuOrder?: number; /** * Method that will be executed when the action is triggered. - * @param editor The editor instance is passed in as a convinience + * @param editor The editor instance is passed in as a convenience */ run(editor: ICodeEditor): void | Promise; } @@ -1296,7 +1300,7 @@ declare namespace monaco.editor { */ readonly id: string; /** - * Identifier for a decoration's owener. + * Identifier for a decoration's owner. */ readonly ownerId: number; /** @@ -1567,13 +1571,13 @@ declare namespace monaco.editor { */ validatePosition(position: IPosition): Position; /** - * Advances the given position by the given offest (negative offsets are also accepted) + * Advances the given position by the given offset (negative offsets are also accepted) * and returns it as a new valid position. * * If the offset and position are such that their combination goes beyond the beginning or * end of the model, throws an exception. * - * If the ofsset is such that the new position would be in the middle of a multi-byte + * If the offset is such that the new position would be in the middle of a multi-byte * line terminator, throws an exception. */ modifyPosition(position: IPosition, offset: number): Position; @@ -1668,7 +1672,7 @@ declare namespace monaco.editor { */ getWordUntilPosition(position: IPosition): IWordAtPosition; /** - * Perform a minimum ammount of operations, in order to transform the decorations + * Perform a minimum amount of operations, in order to transform the decorations * identified by `oldDecorations` to the decorations described by `newDecorations` * and returns the new identifiers associated with the resulting decorations. * @@ -1708,7 +1712,7 @@ declare namespace monaco.editor { */ getLinesDecorations(startLineNumber: number, endLineNumber: number, ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; /** - * Gets all the deocorations in a range as an array. Only `startLineNumber` and `endLineNumber` from `range` are used for filtering. + * Gets all the decorations in a range as an array. Only `startLineNumber` and `endLineNumber` from `range` are used for filtering. * So for now it returns all the decorations on the same line as `range`. * @param range The range to search in * @param ownerId If set, it will ignore decorations belonging to other owners. @@ -1753,7 +1757,7 @@ declare namespace monaco.editor { /** * Push edit operations, basically editing the model. This is the preferred way * of editing the model. The edit operations will land on the undo stack. - * @param beforeCursorState The cursor state before the edit operaions. This cursor state will be returned when `undo` or `redo` are invoked. + * @param beforeCursorState The cursor state before the edit operations. This cursor state will be returned when `undo` or `redo` are invoked. * @param editOperations The edit operations. * @param cursorStateComputer A callback that can compute the resulting cursors state after the edit operations have been executed. * @return The cursor state returned by the `cursorStateComputer`. @@ -1837,7 +1841,7 @@ declare namespace monaco.editor { * @param selection The selection to track. * @param trackPreviousOnEmpty If set, and the selection is empty, indicates whether the selection * should clamp to the previous or the next character. - * @return A unique identifer. + * @return A unique identifier. */ trackSelection(selection: Selection, trackPreviousOnEmpty?: boolean): string; } @@ -1870,7 +1874,7 @@ declare namespace monaco.editor { getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void; /** * Compute the cursor state after the edit operations were applied. - * @param model The model the commad has executed on. + * @param model The model the command has executed on. * @param helper A helper to get inverse edit operations and to get previously tracked selections. * @return The cursor state after the command executed. */ @@ -2388,16 +2392,14 @@ declare namespace monaco.editor { arrowSize?: number; /** * Render vertical scrollbar. - * Accepted values: 'auto', 'visible', 'hidden'. * Defaults to 'auto'. */ - vertical?: string; + vertical?: 'auto' | 'visible' | 'hidden'; /** * Render horizontal scrollbar. - * Accepted values: 'auto', 'visible', 'hidden'. * Defaults to 'auto'. */ - horizontal?: string; + horizontal?: 'auto' | 'visible' | 'hidden'; /** * Cast horizontal and vertical shadows when the content is scrolled. * Defaults to true. @@ -2675,6 +2677,11 @@ declare namespace monaco.editor { * Defaults to false. */ mouseWheelZoom?: boolean; + /** + * Enable smooth caret animation. + * Defaults to false. + */ + cursorSmoothCaretAnimation?: boolean; /** * Control the cursor style, either 'block' or 'line'. * Defaults to 'line'. @@ -3228,6 +3235,7 @@ declare namespace monaco.editor { readonly overviewRulerBorder: boolean; readonly cursorBlinking: TextEditorCursorBlinkingStyle; readonly mouseWheelZoom: boolean; + readonly cursorSmoothCaretAnimation: boolean; readonly cursorStyle: TextEditorCursorStyle; readonly cursorWidth: number; readonly hideCursorInOverviewRuler: boolean; @@ -3971,7 +3979,7 @@ declare namespace monaco.editor { */ executeEdits(source: string, edits: IIdentifiedSingleEditOperation[], endCursorState?: Selection[]): boolean; /** - * Execute multiple (concommitent) commands on the editor. + * Execute multiple (concomitant) commands on the editor. * @param source The source of the call. * @param command The commands to execute */ @@ -4012,7 +4020,7 @@ declare namespace monaco.editor { addContentWidget(widget: IContentWidget): void; /** * Layout/Reposition a content widget. This is a ping to the editor to call widget.getPosition() - * and update appropiately. + * and update appropriately. */ layoutContentWidget(widget: IContentWidget): void; /** @@ -4025,7 +4033,7 @@ declare namespace monaco.editor { addOverlayWidget(widget: IOverlayWidget): void; /** * Layout/Reposition an overlay widget. This is a ping to the editor to call widget.getPosition() - * and update appropiately. + * and update appropriately. */ layoutOverlayWidget(widget: IOverlayWidget): void; /** @@ -4058,7 +4066,7 @@ declare namespace monaco.editor { * The result position takes scrolling into account and is relative to the top left corner of the editor. * Explanation 1: the results of this method will change for the same `position` if the user scrolls the editor. * Explanation 2: the results of this method will not change if the container of the editor gets repositioned. - * Warning: the results of this method are innacurate for positions that are outside the current editor viewport. + * Warning: the results of this method are inaccurate for positions that are outside the current editor viewport. */ getScrolledVisiblePosition(position: IPosition): { top: number; @@ -4291,7 +4299,7 @@ declare namespace monaco.languages { export function registerRenameProvider(languageId: string, provider: RenameProvider): IDisposable; /** - * Register a signature help provider (used by e.g. paremeter hints). + * Register a signature help provider (used by e.g. parameter hints). */ export function registerSignatureHelpProvider(languageId: string, provider: SignatureHelpProvider): IDisposable; @@ -4476,7 +4484,7 @@ declare namespace monaco.languages { */ export interface IndentationRule { /** - * If a line matches this pattern, then all the lines after it should be unindendented once (until another rule matches). + * If a line matches this pattern, then all the lines after it should be unindented once (until another rule matches). */ decreaseIndentPattern: RegExp; /** @@ -4509,7 +4517,7 @@ declare namespace monaco.languages { */ export interface FoldingRules { /** - * Used by the indentation based strategy to decide wheter empty lines belong to the previous or the next block. + * Used by the indentation based strategy to decide whether empty lines belong to the previous or the next block. * A language adheres to the off-side rule if blocks in that language are expressed by their indentation. * See [wikipedia](https://en.wikipedia.org/wiki/Off-side_rule) for more information. * If not set, `false` is used and empty lines belong to the previous block. @@ -4608,10 +4616,6 @@ declare namespace monaco.languages { * Describe what to do with the indentation. */ indentAction: IndentAction; - /** - * Describe whether to outdent current line. - */ - outdentCurrentLine?: boolean; /** * Describes text to be appended after the new line and after the indentation. */ @@ -4756,7 +4760,7 @@ declare namespace monaco.languages { preselect?: boolean; /** * A string or snippet that should be inserted in a document when selecting - * this completion. When `falsy` the [label](#CompletionItem.label) + * this completion. * is used. */ insertText: string; @@ -4921,12 +4925,13 @@ declare namespace monaco.languages { export enum SignatureHelpTriggerReason { Invoke = 1, TriggerCharacter = 2, - Retrigger = 3 + ContentChange = 3 } export interface SignatureHelpContext { - triggerReason: SignatureHelpTriggerReason; - triggerCharacter?: string; + readonly triggerReason: SignatureHelpTriggerReason; + readonly triggerCharacter?: string; + readonly isRetrigger: boolean; } /** @@ -4934,8 +4939,8 @@ declare namespace monaco.languages { * the [parameter hints](https://code.visualstudio.com/docs/editor/intellisense)-feature. */ export interface SignatureHelpProvider { - readonly signatureHelpTriggerCharacters: ReadonlyArray; - readonly signatureHelpRetriggerCharacters: ReadonlyArray; + readonly signatureHelpTriggerCharacters?: ReadonlyArray; + readonly signatureHelpRetriggerCharacters?: ReadonlyArray; /** * Provide help for the signature at the given position and document. */ @@ -5051,6 +5056,18 @@ declare namespace monaco.languages { provideDefinition(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult; } + /** + * The definition provider interface defines the contract between extensions and + * the [go to definition](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-definition) + * and peek definition features. + */ + export interface DeclarationProvider { + /** + * Provide the declaration of the symbol at the given position and document. + */ + provideDeclaration(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult; + } + /** * The implementation provider interface defines the contract between extensions and * the go to implementation feature. @@ -5131,10 +5148,6 @@ declare namespace monaco.languages { range: IRange; text: string; eol?: editor.EndOfLineSequence; - } | { - range: undefined; - text: undefined; - eol: editor.EndOfLineSequence; }; /** @@ -5494,7 +5507,7 @@ declare namespace monaco.languages { */ bracket?: string; /** - * switch to embedded language (useing the mimetype) or get out using "@pop" + * switch to embedded language (using the mimetype) or get out using "@pop" */ nextEmbedded?: string; /** @@ -5514,7 +5527,7 @@ declare namespace monaco.languages { */ open: string; /** - * closeing bracket + * closing bracket */ close: string; /** @@ -5542,3 +5555,5 @@ declare namespace monaco.worker { } } + +//dtsv=2 \ No newline at end of file diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 9d981e148..90b6e588e 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -10,7 +10,7 @@ import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry' import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { Event } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { URI, UriComponents } from 'vs/base/common/uri'; export interface ILocalizedString { @@ -58,49 +58,44 @@ export function isISubmenuItem(item: IMenuItem | ISubmenuItem): item is ISubmenu return (item as ISubmenuItem).submenu !== undefined; } -export class MenuId { - - private static ID = 1; - - static readonly EditorTitle = new MenuId(); - static readonly EditorTitleContext = new MenuId(); - static readonly EditorContext = new MenuId(); - static readonly EmptyEditorGroupContext = new MenuId(); - static readonly ExplorerContext = new MenuId(); - static readonly OpenEditorsContext = new MenuId(); - static readonly ProblemsPanelContext = new MenuId(); - static readonly DebugVariablesContext = new MenuId(); - static readonly DebugWatchContext = new MenuId(); - static readonly DebugCallStackContext = new MenuId(); - static readonly DebugBreakpointsContext = new MenuId(); - static readonly DebugConsoleContext = new MenuId(); - static readonly SCMTitle = new MenuId(); - static readonly SCMSourceControl = new MenuId(); - static readonly SCMResourceGroupContext = new MenuId(); - static readonly SCMResourceContext = new MenuId(); - static readonly SCMChangeContext = new MenuId(); - static readonly CommandPalette = new MenuId(); - static readonly ViewTitle = new MenuId(); - static readonly ViewItemContext = new MenuId(); - static readonly TouchBarContext = new MenuId(); - static readonly SearchContext = new MenuId(); - static readonly MenubarFileMenu = new MenuId(); - static readonly MenubarEditMenu = new MenuId(); - static readonly MenubarRecentMenu = new MenuId(); - static readonly MenubarSelectionMenu = new MenuId(); - static readonly MenubarViewMenu = new MenuId(); - static readonly MenubarAppearanceMenu = new MenuId(); - static readonly MenubarLayoutMenu = new MenuId(); - static readonly MenubarGoMenu = new MenuId(); - static readonly MenubarSwitchEditorMenu = new MenuId(); - static readonly MenubarSwitchGroupMenu = new MenuId(); - static readonly MenubarDebugMenu = new MenuId(); - static readonly MenubarNewBreakpointMenu = new MenuId(); - static readonly MenubarPreferencesMenu = new MenuId(); - static readonly MenubarHelpMenu = new MenuId(); - static readonly MenubarTerminalMenu = new MenuId(); - - readonly id: string = String(MenuId.ID++); +export const enum MenuId { + CommandPalette, + DebugBreakpointsContext, + DebugCallStackContext, + DebugConsoleContext, + DebugVariablesContext, + DebugWatchContext, + EditorContext, + EditorTitle, + EditorTitleContext, + EmptyEditorGroupContext, + ExplorerContext, + MenubarAppearanceMenu, + MenubarDebugMenu, + MenubarEditMenu, + MenubarFileMenu, + MenubarGoMenu, + MenubarHelpMenu, + MenubarLayoutMenu, + MenubarNewBreakpointMenu, + MenubarPreferencesMenu, + MenubarRecentMenu, + MenubarSelectionMenu, + MenubarSwitchEditorMenu, + MenubarSwitchGroupMenu, + MenubarTerminalMenu, + MenubarViewMenu, + OpenEditorsContext, + ProblemsPanelContext, + SCMChangeContext, + SCMResourceContext, + SCMResourceGroupContext, + SCMSourceControl, + SCMTitle, + SearchContext, + TouchBarContext, + ViewItemContext, + ViewTitle, } export interface IMenuActionOptions { @@ -128,6 +123,7 @@ export interface IMenuRegistry { getCommands(): ICommandsMap; appendMenuItem(menu: MenuId, item: IMenuItem | ISubmenuItem): IDisposable; getMenuItems(loc: MenuId): (IMenuItem | ISubmenuItem)[]; + onDidChangeMenu: Event; } export interface ICommandsMap { @@ -136,9 +132,11 @@ export interface ICommandsMap { export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry { - private _commands: { [id: string]: ICommandAction } = Object.create(null); + private readonly _commands: { [id: string]: ICommandAction } = Object.create(null); + private readonly _menuItems: { [loc: string]: (IMenuItem | ISubmenuItem)[] } = Object.create(null); + private readonly _onDidChangeMenu = new Emitter(); - private _menuItems: { [loc: string]: (IMenuItem | ISubmenuItem)[] } = Object.create(null); + readonly onDidChangeMenu: Event = this._onDidChangeMenu.event; addCommand(command: ICommandAction): boolean { const old = this._commands[command.id]; @@ -158,27 +156,29 @@ export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry { return result; } - appendMenuItem({ id }: MenuId, item: IMenuItem | ISubmenuItem): IDisposable { + appendMenuItem(id: MenuId, item: IMenuItem | ISubmenuItem): IDisposable { let array = this._menuItems[id]; if (!array) { this._menuItems[id] = array = [item]; } else { array.push(item); } + this._onDidChangeMenu.fire(id); return { - dispose() { + dispose: () => { const idx = array.indexOf(item); if (idx >= 0) { array.splice(idx, 1); + this._onDidChangeMenu.fire(id); } } }; } - getMenuItems({ id }: MenuId): (IMenuItem | ISubmenuItem)[] { + getMenuItems(id: MenuId): (IMenuItem | ISubmenuItem)[] { const result = this._menuItems[id] || []; - if (id === MenuId.CommandPalette.id) { + if (id === MenuId.CommandPalette) { // CommandPalette is special because it shows // all commands by default this._appendImplicitItems(result); diff --git a/src/vs/platform/actions/common/menu.ts b/src/vs/platform/actions/common/menu.ts index 9b7212efc..72013076f 100644 --- a/src/vs/platform/actions/common/menu.ts +++ b/src/vs/platform/actions/common/menu.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Emitter } from 'vs/base/common/event'; +import { Event, Emitter, filterEvent, debounceEvent } from 'vs/base/common/event'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { MenuId, MenuRegistry, MenuItemAction, IMenu, IMenuItem, IMenuActionOptions, ISubmenuItem, SubmenuItemAction, isIMenuItem } from 'vs/platform/actions/common/actions'; @@ -13,59 +13,74 @@ type MenuItemGroup = [string, (IMenuItem | ISubmenuItem)[]]; export class Menu implements IMenu { - private _menuGroups: MenuItemGroup[] = []; - private _disposables: IDisposable[] = []; - private _onDidChange = new Emitter(); + private readonly _onDidChange = new Emitter(); + private readonly _disposables: IDisposable[] = []; + + private _menuGroups: MenuItemGroup[]; + private _contextKeys: Set; constructor( - id: MenuId, - startupSignal: Thenable, + private readonly _id: MenuId, @ICommandService private readonly _commandService: ICommandService, @IContextKeyService private readonly _contextKeyService: IContextKeyService ) { - startupSignal.then(_ => { - const menuItems = MenuRegistry.getMenuItems(id); - const keysFilter = new Set(); - - let group: MenuItemGroup | undefined; - menuItems.sort(Menu._compareMenuItems); - - for (let item of menuItems) { - // group by groupId - const groupName = item.group; - if (!group || group[0] !== groupName) { - group = [groupName || '', []]; - this._menuGroups.push(group); - } - group![1].push(item); + this._build(); + + // rebuild this menu whenever the menu registry reports an + // event for this MenuId + debounceEvent( + filterEvent(MenuRegistry.onDidChangeMenu, menuId => menuId === this._id), + () => { }, + 100 + )(this._build, this, this._disposables); + + // when context keys change we need to change if the menu also + // has changed + this._contextKeyService.onDidChangeContext(event => { + if (event.affectsSome(this._contextKeys)) { + this._onDidChange.fire(); + } + }, this, this._disposables); + } - // keep keys for eventing - Menu._fillInKbExprKeys(item.when, keysFilter); + private _build(): void { - // keep precondition keys for event if applicable - if (isIMenuItem(item) && item.command.precondition) { - Menu._fillInKbExprKeys(item.command.precondition, keysFilter); - } + // reset + this._menuGroups = []; + this._contextKeys = new Set(); - // keep toggled keys for event if applicable - if (isIMenuItem(item) && item.command.toggled) { - Menu._fillInKbExprKeys(item.command.toggled, keysFilter); - } + const menuItems = MenuRegistry.getMenuItems(this._id); + + let group: MenuItemGroup | undefined; + menuItems.sort(Menu._compareMenuItems); + + for (let item of menuItems) { + // group by groupId + const groupName = item.group || ''; + if (!group || group[0] !== groupName) { + group = [groupName, []]; + this._menuGroups.push(group); } + group![1].push(item); - // subscribe to context changes - this._disposables.push(this._contextKeyService.onDidChangeContext(event => { - if (event.affectsSome(keysFilter)) { - this._onDidChange.fire(); - } - })); + // keep keys for eventing + Menu._fillInKbExprKeys(item.when, this._contextKeys); + + // keep precondition keys for event if applicable + if (isIMenuItem(item) && item.command.precondition) { + Menu._fillInKbExprKeys(item.command.precondition, this._contextKeys); + } - this._onDidChange.fire(this); - }); + // keep toggled keys for event if applicable + if (isIMenuItem(item) && item.command.toggled) { + Menu._fillInKbExprKeys(item.command.toggled, this._contextKeys); + } + } + this._onDidChange.fire(this); } dispose() { - this._disposables = dispose(this._disposables); + dispose(this._disposables); this._onDidChange.dispose(); } diff --git a/src/vs/workbench/services/actions/common/menuService.ts b/src/vs/platform/actions/common/menuService.ts similarity index 75% rename from src/vs/workbench/services/actions/common/menuService.ts rename to src/vs/platform/actions/common/menuService.ts index 9bdef6d4c..e5013e185 100644 --- a/src/vs/workbench/services/actions/common/menuService.ts +++ b/src/vs/platform/actions/common/menuService.ts @@ -6,7 +6,6 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { MenuId, IMenu, IMenuService } from 'vs/platform/actions/common/actions'; import { Menu } from 'vs/platform/actions/common/menu'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ICommandService } from 'vs/platform/commands/common/commands'; export class MenuService implements IMenuService { @@ -14,13 +13,12 @@ export class MenuService implements IMenuService { _serviceBrand: any; constructor( - @IExtensionService private readonly _extensionService: IExtensionService, @ICommandService private readonly _commandService: ICommandService ) { // } createMenu(id: MenuId, contextKeyService: IContextKeyService): IMenu { - return new Menu(id, this._extensionService.whenInstalledExtensionsRegistered(), this._commandService, contextKeyService); + return new Menu(id, this._commandService, contextKeyService); } } diff --git a/src/vs/workbench/services/actions/test/common/menuService.test.ts b/src/vs/platform/actions/test/common/menuService.test.ts similarity index 94% rename from src/vs/workbench/services/actions/test/common/menuService.test.ts rename to src/vs/platform/actions/test/common/menuService.test.ts index 27d45ad36..28ec720b8 100644 --- a/src/vs/workbench/services/actions/test/common/menuService.test.ts +++ b/src/vs/platform/actions/test/common/menuService.test.ts @@ -2,18 +2,16 @@ * 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 { MenuRegistry, MenuId, isIMenuItem } from 'vs/platform/actions/common/actions'; -import { MenuService } from 'vs/workbench/services/actions/common/menuService'; +import { MenuService } from 'vs/platform/actions/common/menuService'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { NullCommandService } from 'vs/platform/commands/common/commands'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; -import { TestExtensionService } from 'vs/workbench/test/workbenchTestServices'; // --- service instances -const extensionService = new TestExtensionService(); - const contextKeyService = new class extends MockContextKeyService { contextMatchesRules() { return true; @@ -29,8 +27,8 @@ suite('MenuService', function () { let testMenuId: MenuId; setup(function () { - menuService = new MenuService(extensionService, NullCommandService); - testMenuId = new MenuId(); + menuService = new MenuService(NullCommandService); + testMenuId = Math.PI; disposables = []; }); diff --git a/src/vs/platform/backup/common/backup.ts b/src/vs/platform/backup/common/backup.ts index 201e8f429..e1e5392e7 100644 --- a/src/vs/platform/backup/common/backup.ts +++ b/src/vs/platform/backup/common/backup.ts @@ -21,6 +21,7 @@ export const IBackupMainService = createDecorator('backupMai export interface IEmptyWindowBackupInfo { backupFolder: string; + remoteAuthority?: string; } export interface IBackupMainService { diff --git a/src/vs/platform/backup/electron-main/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts index 4b647b934..e34e8728d 100644 --- a/src/vs/platform/backup/electron-main/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -139,12 +139,13 @@ export class BackupMainService implements IBackupMainService { public registerEmptyWindowBackupSync(backupInfo: IEmptyWindowBackupInfo): string { let backupFolder = backupInfo.backupFolder; + let remoteAuthority = backupInfo.remoteAuthority; // Generate a new folder if this is a new empty workspace if (!backupFolder) { backupFolder = this.getRandomEmptyWindowId(); } if (!this.emptyWorkspaces.some(w => isEqual(w.backupFolder, backupFolder, !platform.isLinux))) { - this.emptyWorkspaces.push({ backupFolder }); + this.emptyWorkspaces.push({ backupFolder, remoteAuthority }); this.saveSync(); } return this.getBackupPath(backupFolder); @@ -181,7 +182,7 @@ export class BackupMainService implements IBackupMainService { this.rootWorkspaces = this.validateWorkspaces(backups.rootWorkspaces); // read folder backups - let workspaceFolders: URI[]; + let workspaceFolders: URI[] = []; try { if (Array.isArray(backups.folderURIWorkspaces)) { workspaceFolders = backups.folderURIWorkspaces.map(f => URI.parse(f)); diff --git a/src/vs/platform/commands/common/commands.ts b/src/vs/platform/commands/common/commands.ts index 7e115e474..f2a0df759 100644 --- a/src/vs/platform/commands/common/commands.ts +++ b/src/vs/platform/commands/common/commands.ts @@ -6,7 +6,7 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { TypeConstraint, validateConstraints } from 'vs/base/common/types'; import { ServicesAccessor, createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { Event } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { LinkedList } from 'vs/base/common/linkedList'; export const ICommandService = createDecorator('commandService'); @@ -42,6 +42,7 @@ export interface ICommandHandlerDescription { } export interface ICommandRegistry { + onDidRegisterCommand: Event; registerCommand(id: string, command: ICommandHandler): IDisposable; registerCommand(command: ICommand): IDisposable; registerCommandAlias(oldId: string, newId: string): IDisposable; @@ -51,7 +52,10 @@ export interface ICommandRegistry { export const CommandsRegistry: ICommandRegistry = new class implements ICommandRegistry { - private _commands = new Map>(); + private readonly _commands = new Map>(); + + private readonly _onDidRegisterCommand = new Emitter(); + readonly onDidRegisterCommand: Event = this._onDidRegisterCommand.event; registerCommand(idOrCommand: string | ICommand, handler?: ICommandHandler): IDisposable { @@ -90,12 +94,17 @@ export const CommandsRegistry: ICommandRegistry = new class implements ICommandR let removeFn = commands.unshift(idOrCommand); - return toDisposable(() => { + let ret = toDisposable(() => { removeFn(); if (this._commands.get(id).isEmpty()) { this._commands.delete(id); } }); + + // tell the world about this command + this._onDidRegisterCommand.fire(id); + + return ret; } registerCommandAlias(oldId: string, newId: string): IDisposable { diff --git a/src/vs/platform/configuration/node/configuration.ts b/src/vs/platform/configuration/node/configuration.ts index 63c84fd20..b84aa645c 100644 --- a/src/vs/platform/configuration/node/configuration.ts +++ b/src/vs/platform/configuration/node/configuration.ts @@ -12,32 +12,42 @@ import { Event, Emitter } from 'vs/base/common/event'; export class UserConfiguration extends Disposable { private userConfigModelWatcher: ConfigWatcher; + private initializePromise: Promise; private readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; - constructor(settingsPath: string) { + constructor(private settingsPath: string) { super(); - this.userConfigModelWatcher = new ConfigWatcher(settingsPath, { - changeBufferDelay: 300, onError: error => onUnexpectedError(error), defaultConfig: new ConfigurationModelParser(settingsPath), parse: (content: string, parseErrors: any[]) => { - const userConfigModelParser = new ConfigurationModelParser(settingsPath); - userConfigModelParser.parse(content); - parseErrors = [...userConfigModelParser.errors]; - return userConfigModelParser; - } - }); - this._register(this.userConfigModelWatcher); - - // Listeners - this._register(this.userConfigModelWatcher.onDidUpdateConfiguration(() => this._onDidChangeConfiguration.fire(this.configurationModel))); } - get configurationModel(): ConfigurationModel { + initialize(): Promise { + if (!this.initializePromise) { + this.initializePromise = new Promise((c, e) => { + this.userConfigModelWatcher = new ConfigWatcher(this.settingsPath, { + changeBufferDelay: 300, onError: error => onUnexpectedError(error), defaultConfig: new ConfigurationModelParser(this.settingsPath), parse: (content: string, parseErrors: any[]) => { + const userConfigModelParser = new ConfigurationModelParser(this.settingsPath); + userConfigModelParser.parse(content); + parseErrors = [...userConfigModelParser.errors]; + return userConfigModelParser; + }, initCallback: () => c(null) + }); + this._register(this.userConfigModelWatcher); + + // Listeners + this._register(this.userConfigModelWatcher.onDidUpdateConfiguration(() => this._onDidChangeConfiguration.fire(this.userConfigModelWatcher.getConfig().configurationModel))); + }); + } + return this.initializePromise.then(() => this.userConfigModelWatcher.getConfig().configurationModel); + } + + initializeSync(): ConfigurationModel { + this.initialize(); return this.userConfigModelWatcher.getConfig().configurationModel; } - reload(): Promise { - return new Promise(c => this.userConfigModelWatcher.reload(() => c(null))); + reload(): Promise { + return this.initialize().then(() => new Promise(c => this.userConfigModelWatcher.reload(userConfigModelParser => c(userConfigModelParser.configurationModel)))); } } \ No newline at end of file diff --git a/src/vs/platform/configuration/node/configurationService.ts b/src/vs/platform/configuration/node/configurationService.ts index ff137c9d8..fcd894e27 100644 --- a/src/vs/platform/configuration/node/configurationService.ts +++ b/src/vs/platform/configuration/node/configurationService.ts @@ -7,7 +7,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { IConfigurationService, IConfigurationChangeEvent, IConfigurationOverrides, ConfigurationTarget, compare, isConfigurationOverrides, IConfigurationData } from 'vs/platform/configuration/common/configuration'; -import { DefaultConfigurationModel, Configuration, ConfigurationChangeEvent } from 'vs/platform/configuration/common/configurationModels'; +import { DefaultConfigurationModel, Configuration, ConfigurationChangeEvent, ConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { Event, Emitter } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { equals } from 'vs/base/common/objects'; @@ -31,10 +31,13 @@ export class ConfigurationService extends Disposable implements IConfigurationSe this.userConfiguration = this._register(new UserConfiguration(environmentService.appSettingsPath)); - this.reset(); + // Initialize + const defaults = new DefaultConfigurationModel(); + const user = this.userConfiguration.initializeSync(); + this._configuration = new Configuration(defaults, user); // Listeners - this._register(this.userConfiguration.onDidChangeConfiguration(() => this.onDidChangeUserConfiguration())); + this._register(this.userConfiguration.onDidChangeConfiguration(userConfigurationModel => this.onDidChangeUserConfiguration(userConfigurationModel))); this._register(Registry.as(Extensions.Configuration).onDidRegisterConfiguration(configurationProperties => this.onDidRegisterConfiguration(configurationProperties))); } @@ -85,16 +88,16 @@ export class ConfigurationService extends Disposable implements IConfigurationSe reloadConfiguration(folder?: IWorkspaceFolder): Promise { return folder ? Promise.resolve(null) : - this.userConfiguration.reload().then(() => this.onDidChangeUserConfiguration()); + this.userConfiguration.reload().then(userConfigurationModel => this.onDidChangeUserConfiguration(userConfigurationModel)); } - private onDidChangeUserConfiguration(): void { + private onDidChangeUserConfiguration(userConfigurationModel: ConfigurationModel): void { let changedKeys: string[] = []; - const { added, updated, removed } = compare(this._configuration.user, this.userConfiguration.configurationModel); + const { added, updated, removed } = compare(this._configuration.user, userConfigurationModel); changedKeys = [...added, ...updated, ...removed]; if (changedKeys.length) { const oldConfiguartion = this._configuration; - this.reset(); + this._configuration.updateUserConfiguration(userConfigurationModel); changedKeys = changedKeys.filter(key => !equals(oldConfiguartion.getValue(key, {}, null), this._configuration.getValue(key, {}, null))); if (changedKeys.length) { this.trigger(changedKeys, ConfigurationTarget.USER); @@ -103,16 +106,10 @@ export class ConfigurationService extends Disposable implements IConfigurationSe } private onDidRegisterConfiguration(keys: string[]): void { - this.reset(); // reset our caches + this._configuration.updateDefaultConfiguration(new DefaultConfigurationModel()); this.trigger(keys, ConfigurationTarget.DEFAULT); } - private reset(): void { - const defaults = new DefaultConfigurationModel(); - const user = this.userConfiguration.configurationModel; - this._configuration = new Configuration(defaults, user); - } - private trigger(keys: string[], source: ConfigurationTarget): void { this._onDidChangeConfiguration.fire(new ConfigurationChangeEvent().change(keys).telemetryData(source, this.getTargetConfiguration(source))); } diff --git a/src/vs/platform/contextview/browser/contextMenuHandler.ts b/src/vs/platform/contextview/browser/contextMenuHandler.ts index e77179c4b..989638f72 100644 --- a/src/vs/platform/contextview/browser/contextMenuHandler.ts +++ b/src/vs/platform/contextview/browser/contextMenuHandler.ts @@ -7,7 +7,7 @@ import 'vs/css!./contextMenuHandler'; import { combinedDisposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { ActionRunner, IAction, IRunEvent } from 'vs/base/common/actions'; +import { ActionRunner, IRunEvent } from 'vs/base/common/actions'; import { Menu } from 'vs/base/browser/ui/menu/menu'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; @@ -50,58 +50,62 @@ export class ContextMenuHandler { } showContextMenu(delegate: IContextMenuDelegate): void { - delegate.getActions().then((actions: IAction[]) => { - if (!actions.length) { - return; // Don't render an empty context menu - } + const actions = delegate.getActions(); + if (!actions.length) { + return; // Don't render an empty context menu + } - this.focusToReturn = document.activeElement as HTMLElement; + this.focusToReturn = document.activeElement as HTMLElement; - this.contextViewService.showContextView({ - getAnchor: () => delegate.getAnchor(), - canRelayout: false, + let menu: Menu | undefined; - render: (container) => { - this.menuContainerElement = container; + this.contextViewService.showContextView({ + getAnchor: () => delegate.getAnchor(), + canRelayout: false, + anchorAlignment: delegate.anchorAlignment, - let className = delegate.getMenuClassName ? delegate.getMenuClassName() : ''; + render: (container) => { + this.menuContainerElement = container; - if (className) { - container.className += ' ' + className; - } + let className = delegate.getMenuClassName ? delegate.getMenuClassName() : ''; - const menuDisposables: IDisposable[] = []; + if (className) { + container.className += ' ' + className; + } - const actionRunner = delegate.actionRunner || new ActionRunner(); - actionRunner.onDidBeforeRun(this.onActionRun, this, menuDisposables); - actionRunner.onDidRun(this.onDidActionRun, this, menuDisposables); + const menuDisposables: IDisposable[] = []; - const menu = new Menu(container, actions, { - actionItemProvider: delegate.getActionItem, - context: delegate.getActionsContext ? delegate.getActionsContext() : null, - actionRunner, - getKeyBinding: delegate.getKeyBinding ? delegate.getKeyBinding : action => this.keybindingService.lookupKeybinding(action.id) - }); + const actionRunner = delegate.actionRunner || new ActionRunner(); + actionRunner.onDidBeforeRun(this.onActionRun, this, menuDisposables); + actionRunner.onDidRun(this.onDidActionRun, this, menuDisposables); - menuDisposables.push(attachMenuStyler(menu, this.themeService)); + menu = new Menu(container, actions, { + actionItemProvider: delegate.getActionItem, + context: delegate.getActionsContext ? delegate.getActionsContext() : null, + actionRunner, + getKeyBinding: delegate.getKeyBinding ? delegate.getKeyBinding : action => this.keybindingService.lookupKeybinding(action.id) + }); - menu.onDidCancel(() => this.contextViewService.hideContextView(true), null, menuDisposables); - menu.onDidBlur(() => this.contextViewService.hideContextView(true), null, menuDisposables); - domEvent(window, EventType.BLUR)(() => { this.contextViewService.hideContextView(true); }, null, menuDisposables); + menuDisposables.push(attachMenuStyler(menu, this.themeService)); - menu.focus(!!delegate.autoSelectFirstItem); + menu.onDidCancel(() => this.contextViewService.hideContextView(true), null, menuDisposables); + menu.onDidBlur(() => this.contextViewService.hideContextView(true), null, menuDisposables); + domEvent(window, EventType.BLUR)(() => { this.contextViewService.hideContextView(true); }, null, menuDisposables); - return combinedDisposable([...menuDisposables, menu]); - }, + return combinedDisposable([...menuDisposables, menu]); + }, - onHide: (didCancel?: boolean) => { - if (delegate.onHide) { - delegate.onHide(didCancel); - } + focus: () => { + menu.focus(!!delegate.autoSelectFirstItem); + }, - this.menuContainerElement = null; + onHide: (didCancel?: boolean) => { + if (delegate.onHide) { + delegate.onHide(didCancel); } - }); + + this.menuContainerElement = null; + } }); } @@ -152,4 +156,4 @@ export class ContextMenuHandler { dispose(): void { this.setContainer(null); } -} \ No newline at end of file +} diff --git a/src/vs/platform/contextview/browser/contextView.ts b/src/vs/platform/contextview/browser/contextView.ts index 9acbb8061..df51bcf7d 100644 --- a/src/vs/platform/contextview/browser/contextView.ts +++ b/src/vs/platform/contextview/browser/contextView.ts @@ -7,6 +7,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IContextMenuDelegate } from 'vs/base/browser/contextmenu'; +import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; export const IContextViewService = createDecorator('contextViewService'); @@ -17,6 +18,7 @@ export interface IContextViewService { showContextView(delegate: IContextViewDelegate): void; hideContextView(data?: any): void; layout(): void; + anchorAlignment?: AnchorAlignment; } export interface IContextViewDelegate { @@ -27,6 +29,8 @@ export interface IContextViewDelegate { render(container: HTMLElement): IDisposable; onDOMEvent?(e: any, activeElement: HTMLElement): void; onHide?(data?: any): void; + focus?(): void; + anchorAlignment?: AnchorAlignment; } export const IContextMenuService = createDecorator('contextMenuService'); diff --git a/src/vs/platform/dialogs/node/dialogIpc.ts b/src/vs/platform/dialogs/node/dialogIpc.ts index 035f714fd..c4011630a 100644 --- a/src/vs/platform/dialogs/node/dialogIpc.ts +++ b/src/vs/platform/dialogs/node/dialogIpc.ts @@ -4,29 +4,23 @@ *--------------------------------------------------------------------------------------------*/ import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; import { IDialogService, IConfirmation, IConfirmationResult } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; import { Event } from 'vs/base/common/event'; -export interface IDialogChannel extends IChannel { - call(command: 'show'): Thenable; - call(command: 'confirm'): Thenable; - call(command: string, arg?: any): Thenable; -} - -export class DialogChannel implements IDialogChannel { +export class DialogChannel implements IServerChannel { constructor(@IDialogService private dialogService: IDialogService) { } - listen(event: string): Event { - throw new Error('No event found'); + listen(_, event: string): Event { + throw new Error(`Event not found: ${event}`); } - call(command: string, args?: any[]): Thenable { + call(_, command: string, args?: any[]): Thenable { switch (command) { - case 'show': return this.dialogService.show(args[0], args[1], args[2]); - case 'confirm': return this.dialogService.confirm(args[0]); + case 'show': return this.dialogService.show(args![0], args![1], args![2]); + case 'confirm': return this.dialogService.confirm(args![0]); } return TPromise.wrapError(new Error('invalid command')); } @@ -36,7 +30,7 @@ export class DialogChannelClient implements IDialogService { _serviceBrand: any; - constructor(private channel: IDialogChannel) { } + constructor(private channel: IChannel) { } show(severity: Severity, message: string, options: string[]): TPromise { return TPromise.wrap(this.channel.call('show', [severity, message, options])); diff --git a/src/vs/platform/dialogs/node/dialogService.ts b/src/vs/platform/dialogs/node/dialogService.ts index be93063cc..cf04c7608 100644 --- a/src/vs/platform/dialogs/node/dialogService.ts +++ b/src/vs/platform/dialogs/node/dialogService.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as readline from 'readline'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IDialogService, IConfirmation, IConfirmationResult } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; import { localize } from 'vs/nls'; @@ -14,8 +13,8 @@ export class CommandLineDialogService implements IDialogService { _serviceBrand: any; - show(severity: Severity, message: string, options: string[]): TPromise { - const promise = new TPromise((c, e) => { + show(severity: Severity, message: string, options: string[]): Promise { + const promise = new Promise((c, e) => { const rl = readline.createInterface({ input: process.stdin, output: process.stdout, @@ -58,7 +57,7 @@ export class CommandLineDialogService implements IDialogService { return -1; } - confirm(confirmation: IConfirmation): TPromise { + confirm(confirmation: IConfirmation): Promise { return this.show(Severity.Info, confirmation.message, [confirmation.primaryButton || localize('ok', "Ok"), confirmation.secondaryButton || localize('cancel', "Cancel")]).then(index => { return { confirmed: index === 0 diff --git a/src/vs/platform/download/node/downloadIpc.ts b/src/vs/platform/download/node/downloadIpc.ts new file mode 100644 index 000000000..8d082d30f --- /dev/null +++ b/src/vs/platform/download/node/downloadIpc.ts @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI } from 'vs/base/common/uri'; +import * as path from 'path'; +import * as fs from 'fs'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { Event, Emitter, buffer } from 'vs/base/common/event'; +import { IDownloadService } from 'vs/platform/download/common/download'; +import { mkdirp } from 'vs/base/node/pfs'; +import { IURITransformer } from 'vs/base/common/uriIpc'; + +export type UploadResponse = Buffer | string | undefined; + +export function upload(uri: URI): Event { + const stream = new Emitter(); + const readstream = fs.createReadStream(uri.fsPath); + readstream.on('data', data => stream.fire(data)); + readstream.on('error', error => stream.fire(error.toString())); + readstream.on('close', () => stream.fire()); + return stream.event; +} + +export class DownloadServiceChannel implements IServerChannel { + + constructor() { } + + listen(_, event: string, arg?: any): Event { + switch (event) { + case 'upload': return buffer(upload(URI.revive(arg))); + } + + throw new Error(`Event not found: ${event}`); + } + + call(_, command: string): TPromise { + throw new Error(`Call not found: ${command}`); + } +} + +export class DownloadServiceChannelClient implements IDownloadService { + + _serviceBrand: any; + + constructor(private channel: IChannel, private getUriTransformer: () => IURITransformer) { } + + download(from: URI, to: string): Promise { + from = this.getUriTransformer().transformOutgoing(from); + const dirName = path.dirname(to); + let out: fs.WriteStream; + return new Promise((c, e) => { + return mkdirp(dirName) + .then(() => { + out = fs.createWriteStream(to); + out.once('close', () => c()); + out.once('error', e); + const uploadStream = this.channel.listen('upload', from); + const disposable = uploadStream(result => { + if (result === void 0) { + disposable.dispose(); + out.end(c); + } else if (Buffer.isBuffer(result)) { + out.write(result); + } else if (typeof result === 'string') { + disposable.dispose(); + out.end(() => e(result)); + } + }); + }); + }); + } +} \ No newline at end of file diff --git a/src/vs/platform/driver/electron-main/driver.ts b/src/vs/platform/driver/electron-main/driver.ts index ff392b246..e7d9049de 100644 --- a/src/vs/platform/driver/electron-main/driver.ts +++ b/src/vs/platform/driver/electron-main/driver.ts @@ -3,13 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; -import { IDriver, DriverChannel, IElement, IWindowDriverChannel, WindowDriverChannelClient, IWindowDriverRegistry, WindowDriverRegistryChannel, IWindowDriver, IDriverOptions } from 'vs/platform/driver/node/driver'; +import { IDriver, DriverChannel, IElement, WindowDriverChannelClient, IWindowDriverRegistry, WindowDriverRegistryChannel, IWindowDriver, IDriverOptions } from 'vs/platform/driver/node/driver'; import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; import { serve as serveNet } from 'vs/base/parts/ipc/node/ipc.net'; import { combinedDisposable, IDisposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IPCServer, IClientRouter } from 'vs/base/parts/ipc/node/ipc'; +import { IPCServer, StaticRouter } from 'vs/base/parts/ipc/node/ipc'; import { SimpleKeybinding, KeyCode } from 'vs/base/common/keyCodes'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; import { OS } from 'vs/base/common/platform'; @@ -19,19 +18,6 @@ import { ScanCodeBinding } from 'vs/base/common/scanCode'; import { KeybindingParser } from 'vs/base/common/keybindingParser'; import { timeout } from 'vs/base/common/async'; -class WindowRouter implements IClientRouter { - - constructor(private windowId: number) { } - - routeCall(): TPromise { - return TPromise.as(`window:${this.windowId}`); - } - - routeEvent(): TPromise { - return TPromise.as(`window:${this.windowId}`); - } -} - function isSilentKeyCode(keyCode: KeyCode) { return keyCode < KeyCode.KEY_0; } @@ -50,57 +36,60 @@ export class Driver implements IDriver, IWindowDriverRegistry { @IWindowsMainService private windowsService: IWindowsMainService ) { } - registerWindowDriver(windowId: number): TPromise { + async registerWindowDriver(windowId: number): Promise { this.registeredWindowIds.add(windowId); this.reloadingWindowIds.delete(windowId); this.onDidReloadingChange.fire(); - return TPromise.as(this.options); + return this.options; } - reloadWindowDriver(windowId: number): TPromise { + async reloadWindowDriver(windowId: number): Promise { this.reloadingWindowIds.add(windowId); - return TPromise.as(null); } - getWindowIds(): TPromise { - return TPromise.as(this.windowsService.getWindows() + async getWindowIds(): Promise { + return this.windowsService.getWindows() .map(w => w.id) - .filter(id => this.registeredWindowIds.has(id) && !this.reloadingWindowIds.has(id))); + .filter(id => this.registeredWindowIds.has(id) && !this.reloadingWindowIds.has(id)); } - capturePage(windowId: number): TPromise { - return this.whenUnfrozen(windowId).then(() => { - const window = this.windowsService.getWindowById(windowId); - const webContents = window.win.webContents; - return new TPromise(c => webContents.capturePage(image => c(image.toPNG().toString('base64')))); - }); + async capturePage(windowId: number): Promise { + await this.whenUnfrozen(windowId); + + const window = this.windowsService.getWindowById(windowId); + const webContents = window.win.webContents; + const image = await new Promise(c => webContents.capturePage(c)); + + return image.toPNG().toString('base64'); } - reloadWindow(windowId: number): TPromise { - return this.whenUnfrozen(windowId).then(() => { - const window = this.windowsService.getWindowById(windowId); - this.reloadingWindowIds.add(windowId); - this.windowsService.reload(window); - }); + async reloadWindow(windowId: number): Promise { + await this.whenUnfrozen(windowId); + + const window = this.windowsService.getWindowById(windowId); + this.reloadingWindowIds.add(windowId); + this.windowsService.reload(window); } - dispatchKeybinding(windowId: number, keybinding: string): TPromise { - return this.whenUnfrozen(windowId).then(() => { - const [first, second] = KeybindingParser.parseUserBinding(keybinding); + async dispatchKeybinding(windowId: number, keybinding: string): Promise { + await this.whenUnfrozen(windowId); + + const [first, second] = KeybindingParser.parseUserBinding(keybinding); - return this._dispatchKeybinding(windowId, first).then(() => { - if (second) { - return this._dispatchKeybinding(windowId, second); - } else { - return TPromise.as(null); - } - }); - }); + if (!first) { + return; + } + + await this._dispatchKeybinding(windowId, first); + + if (second) { + await this._dispatchKeybinding(windowId, second); + } } - private _dispatchKeybinding(windowId: number, keybinding: SimpleKeybinding | ScanCodeBinding): TPromise { + private async _dispatchKeybinding(windowId: number, keybinding: SimpleKeybinding | ScanCodeBinding): Promise { if (keybinding instanceof ScanCodeBinding) { - return TPromise.wrapError(new Error('ScanCodeBindings not supported')); + throw new Error('ScanCodeBindings not supported'); } const window = this.windowsService.getWindowById(windowId); @@ -135,76 +124,64 @@ export class Driver implements IDriver, IWindowDriverRegistry { webContents.sendInputEvent({ type: 'keyUp', keyCode, modifiers } as any); - return TPromise.wrap(timeout(100)); + await timeout(100); } - click(windowId: number, selector: string, xoffset?: number, yoffset?: number): TPromise { - return this.getWindowDriver(windowId).then(windowDriver => { - return windowDriver.click(selector, xoffset, yoffset); - }); + async click(windowId: number, selector: string, xoffset?: number, yoffset?: number): Promise { + const windowDriver = await this.getWindowDriver(windowId); + await windowDriver.click(selector, xoffset, yoffset); } - doubleClick(windowId: number, selector: string): TPromise { - return this.getWindowDriver(windowId).then(windowDriver => { - return windowDriver.doubleClick(selector); - }); + async doubleClick(windowId: number, selector: string): Promise { + const windowDriver = await this.getWindowDriver(windowId); + await windowDriver.doubleClick(selector); } - setValue(windowId: number, selector: string, text: string): TPromise { - return this.getWindowDriver(windowId).then(windowDriver => { - return windowDriver.setValue(selector, text); - }); + async setValue(windowId: number, selector: string, text: string): Promise { + const windowDriver = await this.getWindowDriver(windowId); + await windowDriver.setValue(selector, text); } - getTitle(windowId: number): TPromise { - return this.getWindowDriver(windowId).then(windowDriver => { - return windowDriver.getTitle(); - }); + async getTitle(windowId: number): Promise { + const windowDriver = await this.getWindowDriver(windowId); + return await windowDriver.getTitle(); } - isActiveElement(windowId: number, selector: string): TPromise { - return this.getWindowDriver(windowId).then(windowDriver => { - return windowDriver.isActiveElement(selector); - }); + async isActiveElement(windowId: number, selector: string): Promise { + const windowDriver = await this.getWindowDriver(windowId); + return await windowDriver.isActiveElement(selector); } - getElements(windowId: number, selector: string, recursive: boolean): TPromise { - return this.getWindowDriver(windowId).then(windowDriver => { - return windowDriver.getElements(selector, recursive); - }); + async getElements(windowId: number, selector: string, recursive: boolean): Promise { + const windowDriver = await this.getWindowDriver(windowId); + return await windowDriver.getElements(selector, recursive); } - typeInEditor(windowId: number, selector: string, text: string): TPromise { - return this.getWindowDriver(windowId).then(windowDriver => { - return windowDriver.typeInEditor(selector, text); - }); + async typeInEditor(windowId: number, selector: string, text: string): Promise { + const windowDriver = await this.getWindowDriver(windowId); + await windowDriver.typeInEditor(selector, text); } - getTerminalBuffer(windowId: number, selector: string): TPromise { - return this.getWindowDriver(windowId).then(windowDriver => { - return windowDriver.getTerminalBuffer(selector); - }); + async getTerminalBuffer(windowId: number, selector: string): Promise { + const windowDriver = await this.getWindowDriver(windowId); + return await windowDriver.getTerminalBuffer(selector); } - writeInTerminal(windowId: number, selector: string, text: string): TPromise { - return this.getWindowDriver(windowId).then(windowDriver => { - return windowDriver.writeInTerminal(selector, text); - }); + async writeInTerminal(windowId: number, selector: string, text: string): Promise { + const windowDriver = await this.getWindowDriver(windowId); + await windowDriver.writeInTerminal(selector, text); } - private getWindowDriver(windowId: number): TPromise { - return this.whenUnfrozen(windowId).then(() => { - const router = new WindowRouter(windowId); - const windowDriverChannel = this.windowServer.getChannel('windowDriver', router); - return new WindowDriverChannelClient(windowDriverChannel); - }); - } + private async getWindowDriver(windowId: number): Promise { + await this.whenUnfrozen(windowId); - private whenUnfrozen(windowId: number): TPromise { - return TPromise.wrap(this._whenUnfrozen(windowId)); + const id = `window:${windowId}`; + const router = new StaticRouter(ctx => ctx === id); + const windowDriverChannel = this.windowServer.getChannel('windowDriver', router); + return new WindowDriverChannelClient(windowDriverChannel); } - private async _whenUnfrozen(windowId: number): Promise { + private async whenUnfrozen(windowId: number): Promise { while (this.reloadingWindowIds.has(windowId)) { await toPromise(this.onDidReloadingChange.event); } diff --git a/src/vs/platform/driver/node/driver.ts b/src/vs/platform/driver/node/driver.ts index d8e213518..2337f164a 100644 --- a/src/vs/platform/driver/node/driver.ts +++ b/src/vs/platform/driver/node/driver.ts @@ -6,7 +6,7 @@ import { connect as connectNet, Client } from 'vs/base/parts/ipc/node/ipc.net'; import { TPromise } from 'vs/base/common/winjs.base'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; import { Event } from 'vs/base/common/event'; export const ID = 'driverService'; @@ -44,32 +44,15 @@ export interface IDriver { } //*END -export interface IDriverChannel extends IChannel { - call(command: 'getWindowIds'): Thenable; - call(command: 'capturePage'): Thenable; - call(command: 'reloadWindow', arg: number): Thenable; - call(command: 'dispatchKeybinding', arg: [number, string]): Thenable; - call(command: 'click', arg: [number, string, number | undefined, number | undefined]): Thenable; - call(command: 'doubleClick', arg: [number, string]): Thenable; - call(command: 'setValue', arg: [number, string, string]): Thenable; - call(command: 'getTitle', arg: [number]): Thenable; - call(command: 'isActiveElement', arg: [number, string]): Thenable; - call(command: 'getElements', arg: [number, string, boolean]): Thenable; - call(command: 'typeInEditor', arg: [number, string, string]): Thenable; - call(command: 'getTerminalBuffer', arg: [number, string]): Thenable; - call(command: 'writeInTerminal', arg: [number, string, string]): Thenable; - call(command: string, arg: any): Thenable; -} - -export class DriverChannel implements IDriverChannel { +export class DriverChannel implements IServerChannel { constructor(private driver: IDriver) { } - listen(event: string): Event { + listen(_, event: string): Event { throw new Error('No event found'); } - call(command: string, arg?: any): TPromise { + call(_, command: string, arg?: any): TPromise { switch (command) { case 'getWindowIds': return this.driver.getWindowIds(); case 'capturePage': return this.driver.capturePage(arg); @@ -86,7 +69,7 @@ export class DriverChannel implements IDriverChannel { case 'writeInTerminal': return this.driver.writeInTerminal(arg[0], arg[1], arg[2]); } - return undefined; + throw new Error(`Call not found: ${command}`); } } @@ -94,7 +77,7 @@ export class DriverChannelClient implements IDriver { _serviceBrand: any; - constructor(private channel: IDriverChannel) { } + constructor(private channel: IChannel) { } getWindowIds(): TPromise { return TPromise.wrap(this.channel.call('getWindowIds')); @@ -158,27 +141,21 @@ export interface IWindowDriverRegistry { reloadWindowDriver(windowId: number): TPromise; } -export interface IWindowDriverRegistryChannel extends IChannel { - call(command: 'registerWindowDriver', arg: number): Thenable; - call(command: 'reloadWindowDriver', arg: number): Thenable; - call(command: string, arg: any): Thenable; -} - -export class WindowDriverRegistryChannel implements IWindowDriverRegistryChannel { +export class WindowDriverRegistryChannel implements IServerChannel { constructor(private registry: IWindowDriverRegistry) { } - listen(event: string): Event { - throw new Error('No event found'); + listen(_, event: string): Event { + throw new Error(`Event not found: ${event}`); } - call(command: string, arg?: any): Thenable { + call(_, command: string, arg?: any): Thenable { switch (command) { case 'registerWindowDriver': return this.registry.registerWindowDriver(arg); case 'reloadWindowDriver': return this.registry.reloadWindowDriver(arg); } - return undefined; + throw new Error(`Call not found: ${command}`); } } @@ -186,7 +163,7 @@ export class WindowDriverRegistryChannelClient implements IWindowDriverRegistry _serviceBrand: any; - constructor(private channel: IWindowDriverRegistryChannel) { } + constructor(private channel: IChannel) { } registerWindowDriver(windowId: number): TPromise { return TPromise.wrap(this.channel.call('registerWindowDriver', windowId)); @@ -209,28 +186,15 @@ export interface IWindowDriver { writeInTerminal(selector: string, text: string): TPromise; } -export interface IWindowDriverChannel extends IChannel { - call(command: 'click', arg: [string, number | undefined, number | undefined]): Thenable; - call(command: 'doubleClick', arg: string): Thenable; - call(command: 'setValue', arg: [string, string]): Thenable; - call(command: 'getTitle'): Thenable; - call(command: 'isActiveElement', arg: string): Thenable; - call(command: 'getElements', arg: [string, boolean]): Thenable; - call(command: 'typeInEditor', arg: [string, string]): Thenable; - call(command: 'getTerminalBuffer', arg: string): Thenable; - call(command: 'writeInTerminal', arg: [string, string]): Thenable; - call(command: string, arg: any): Thenable; -} - -export class WindowDriverChannel implements IWindowDriverChannel { +export class WindowDriverChannel implements IServerChannel { constructor(private driver: IWindowDriver) { } - listen(event: string): Event { - throw new Error('No event found'); + listen(_, event: string): Event { + throw new Error(`No event found: ${event}`); } - call(command: string, arg?: any): Thenable { + call(_, command: string, arg?: any): Thenable { switch (command) { case 'click': return this.driver.click(arg[0], arg[1], arg[2]); case 'doubleClick': return this.driver.doubleClick(arg); @@ -243,7 +207,7 @@ export class WindowDriverChannel implements IWindowDriverChannel { case 'writeInTerminal': return this.driver.writeInTerminal(arg[0], arg[1]); } - return undefined; + throw new Error(`Call not found: ${command}`); } } @@ -251,7 +215,7 @@ export class WindowDriverChannelClient implements IWindowDriver { _serviceBrand: any; - constructor(private channel: IWindowDriverChannel) { } + constructor(private channel: IChannel) { } click(selector: string, xoffset?: number, yoffset?: number): TPromise { return TPromise.wrap(this.channel.call('click', [selector, xoffset, yoffset])); diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 357c8f692..87c7e26a3 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -35,7 +35,6 @@ export interface ParsedArgs { 'trace-options'?: string; log?: string; logExtensionHostCommunication?: boolean; - logStorage?: boolean; 'extensions-dir'?: string; 'builtin-extensions-dir'?: string; extensionDevelopmentPath?: string; @@ -69,17 +68,18 @@ export interface ParsedArgs { 'upload-logs'?: string; 'driver'?: string; 'driver-verbose'?: boolean; + remote?: string; } export const IEnvironmentService = createDecorator('environmentService'); export interface IDebugParams { - port: number; + port: number | null; break: boolean; } export interface IExtensionHostDebugParams extends IDebugParams { - debugId: string; + debugId?: string; } export interface IEnvironmentService { @@ -95,14 +95,15 @@ export interface IEnvironmentService { userDataPath: string; appNameLong: string; - appQuality: string; + appQuality?: string; appSettingsHome: string; appSettingsPath: string; appKeybindingsPath: string; - settingsSearchBuildId: number; - settingsSearchUrl: string; + settingsSearchBuildId?: number; + settingsSearchUrl?: string; + globalStorageHome: string; workspaceStorageHome: string; backupHome: string; @@ -114,14 +115,13 @@ export interface IEnvironmentService { disableExtensions: boolean | string[]; builtinExtensionsPath: string; extensionsPath: string; - extensionDevelopmentLocationURI: URI; - extensionTestsPath: string; + extensionDevelopmentLocationURI?: URI; + extensionTestsPath?: string; debugExtensionHost: IExtensionHostDebugParams; debugSearch: IDebugParams; logExtensionHostCommunication: boolean; - logStorage: boolean; isBuilt: boolean; wait: boolean; @@ -129,7 +129,7 @@ export interface IEnvironmentService { performance: boolean; // logging - log: string; + log?: string; logsPath: string; verbose: boolean; @@ -141,12 +141,12 @@ export interface IEnvironmentService { mainIPCHandle: string; sharedIPCHandle: string; - nodeCachedDataDir: string; + nodeCachedDataDir?: string; installSourcePath: string; disableUpdates: boolean; disableCrashReporter: boolean; - driverHandle: string; + driverHandle?: string; driverVerbose: boolean; } diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 3618a0b25..f61de1599 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -20,6 +20,7 @@ const options: minimist.Opts = { 'extensions-dir', 'folder-uri', 'file-uri', + 'remote', 'extensionDevelopmentPath', 'extensionTestsPath', 'install-extension', @@ -54,7 +55,6 @@ const options: minimist.Opts = { 'prof-startup', 'verbose', 'logExtensionHostCommunication', - 'logStorage', 'disable-extensions', 'list-extensions', 'show-versions', diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index 56c5ba1c1..1cbd2aad8 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -92,8 +92,9 @@ export class EnvironmentService implements IEnvironmentService { @memoize get userDataPath(): string { - if (process.env['VSCODE_PORTABLE']) { - return path.join(process.env['VSCODE_PORTABLE'], 'user-data'); + const vscodePortable = process.env['VSCODE_PORTABLE']; + if (vscodePortable) { + return path.join(vscodePortable, 'user-data'); } return parseUserDataDir(this._args, process); @@ -101,7 +102,7 @@ export class EnvironmentService implements IEnvironmentService { get appNameLong(): string { return product.nameLong; } - get appQuality(): string { return product.quality; } + get appQuality(): string | undefined { return product.quality; } @memoize get appSettingsHome(): string { return path.join(this.userDataPath, 'User'); } @@ -109,14 +110,17 @@ export class EnvironmentService implements IEnvironmentService { @memoize get appSettingsPath(): string { return path.join(this.appSettingsHome, 'settings.json'); } + @memoize + get globalStorageHome(): string { return path.join(this.appSettingsHome, 'globalStorage'); } + @memoize get workspaceStorageHome(): string { return path.join(this.appSettingsHome, 'workspaceStorage'); } @memoize - get settingsSearchBuildId(): number { return product.settingsSearchBuildId; } + get settingsSearchBuildId(): number | undefined { return product.settingsSearchBuildId; } @memoize - get settingsSearchUrl(): string { return product.settingsSearchUrl; } + get settingsSearchUrl(): string | undefined { return product.settingsSearchUrl; } @memoize get appKeybindingsPath(): string { return path.join(this.appSettingsHome, 'keybindings.json'); } @@ -152,17 +156,23 @@ export class EnvironmentService implements IEnvironmentService { if (fromArgs) { return fromArgs; - } else if (process.env['VSCODE_EXTENSIONS']) { - return process.env['VSCODE_EXTENSIONS']; - } else if (process.env['VSCODE_PORTABLE']) { - return path.join(process.env['VSCODE_PORTABLE'], 'extensions'); - } else { - return path.join(this.userHome, product.dataFolderName, 'extensions'); } + + const vscodeExtensions = process.env['VSCODE_EXTENSIONS']; + if (vscodeExtensions) { + return vscodeExtensions; + } + + const vscodePortable = process.env['VSCODE_PORTABLE']; + if (vscodePortable) { + return path.join(vscodePortable, 'extensions'); + } + + return path.join(this.userHome, product.dataFolderName, 'extensions'); } @memoize - get extensionDevelopmentLocationURI(): URI { + get extensionDevelopmentLocationURI(): URI | undefined { const s = this._args.extensionDevelopmentPath; if (s) { if (/^[^:/?#]+?:\/\//.test(s)) { @@ -174,13 +184,13 @@ export class EnvironmentService implements IEnvironmentService { } @memoize - get extensionTestsPath(): string { return this._args.extensionTestsPath ? path.normalize(this._args.extensionTestsPath) : this._args.extensionTestsPath; } + get extensionTestsPath(): string | undefined { return this._args.extensionTestsPath ? path.normalize(this._args.extensionTestsPath) : this._args.extensionTestsPath; } get disableExtensions(): boolean | string[] { if (this._args['disable-extensions']) { return true; } - const disableExtensions: string | string[] = this._args['disable-extension']; + const disableExtensions = this._args['disable-extension']; if (disableExtensions) { if (typeof disableExtensions === 'string') { return [disableExtensions]; @@ -192,11 +202,11 @@ export class EnvironmentService implements IEnvironmentService { return false; } - get skipGettingStarted(): boolean { return this._args['skip-getting-started']; } + get skipGettingStarted(): boolean { return !!this._args['skip-getting-started']; } - get skipReleaseNotes(): boolean { return this._args['skip-release-notes']; } + get skipReleaseNotes(): boolean { return !!this._args['skip-release-notes']; } - get skipAddToRecentlyOpened(): boolean { return this._args['skip-add-to-recently-opened']; } + get skipAddToRecentlyOpened(): boolean { return !!this._args['skip-add-to-recently-opened']; } @memoize get debugExtensionHost(): IExtensionHostDebugParams { return parseExtensionHostPort(this._args, this.isBuilt); } @@ -205,16 +215,15 @@ export class EnvironmentService implements IEnvironmentService { get debugSearch(): IDebugParams { return parseSearchPort(this._args, this.isBuilt); } get isBuilt(): boolean { return !process.env['VSCODE_DEV']; } - get verbose(): boolean { return this._args.verbose; } - get log(): string { return this._args.log; } + get verbose(): boolean { return !!this._args.verbose; } + get log(): string | undefined { return this._args.log; } - get wait(): boolean { return this._args.wait; } + get wait(): boolean { return !!this._args.wait; } - get logExtensionHostCommunication(): boolean { return this._args.logExtensionHostCommunication; } - get logStorage(): boolean { return this._args.logStorage; } + get logExtensionHostCommunication(): boolean { return !!this._args.logExtensionHostCommunication; } - get performance(): boolean { return this._args.performance; } - get status(): boolean { return this._args.status; } + get performance(): boolean { return !!this._args.performance; } + get status(): boolean { return !!this._args.status; } @memoize get mainIPCHandle(): string { return getIPCHandle(this.userDataPath, 'main'); } @@ -223,13 +232,13 @@ export class EnvironmentService implements IEnvironmentService { get sharedIPCHandle(): string { return getIPCHandle(this.userDataPath, 'shared'); } @memoize - get nodeCachedDataDir(): string { return process.env['VSCODE_NODE_CACHED_DATA_DIR'] || undefined; } + get nodeCachedDataDir(): string | undefined { return process.env['VSCODE_NODE_CACHED_DATA_DIR'] || undefined; } get disableUpdates(): boolean { return !!this._args['disable-updates']; } get disableCrashReporter(): boolean { return !!this._args['disable-crash-reporter']; } - get driverHandle(): string { return this._args['driver']; } - get driverVerbose(): boolean { return this._args['driver-verbose']; } + get driverHandle(): string | undefined { return this._args['driver']; } + get driverVerbose(): boolean { return !!this._args['driver-verbose']; } constructor(private _args: ParsedArgs, private _execPath: string) { if (!process.env['VSCODE_LOGS']) { @@ -237,7 +246,7 @@ export class EnvironmentService implements IEnvironmentService { process.env['VSCODE_LOGS'] = path.join(this.userDataPath, 'logs', key); } - this.logsPath = process.env['VSCODE_LOGS']; + this.logsPath = process.env['VSCODE_LOGS']!; } } @@ -249,14 +258,14 @@ export function parseSearchPort(args: ParsedArgs, isBuild: boolean): IDebugParam return parseDebugPort(args.debugSearch, args.debugBrkSearch, 5876, isBuild); } -export function parseDebugPort(debugArg: string, debugBrkArg: string, defaultBuildPort: number, isBuild: boolean, debugId?: string): IExtensionHostDebugParams { +export function parseDebugPort(debugArg: string | undefined, debugBrkArg: string | undefined, defaultBuildPort: number, isBuild: boolean, debugId?: string): IExtensionHostDebugParams { const portStr = debugBrkArg || debugArg; const port = Number(portStr) || (!isBuild ? defaultBuildPort : null); const brk = port ? Boolean(!!debugBrkArg) : false; return { port, break: brk, debugId }; } -function parsePathArg(arg: string, process: NodeJS.Process): string { +function parsePathArg(arg: string | undefined, process: NodeJS.Process): string | undefined { if (!arg) { return undefined; } diff --git a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts index 5330fff2c..1e82bac6c 100644 --- a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts +++ b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts @@ -6,7 +6,7 @@ import { localize } from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IExtensionManagementService, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, ILocalExtension, isIExtensionIdentifier, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementService, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, ILocalExtension, isIExtensionIdentifier, LocalExtensionType, DidInstallExtensionEvent, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getIdFromLocalExtensionId, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -30,6 +30,7 @@ export class ExtensionEnablementService implements IExtensionEnablementService { @IEnvironmentService private environmentService: IEnvironmentService, @IExtensionManagementService private extensionManagementService: IExtensionManagementService ) { + extensionManagementService.onDidInstallExtension(this._onDidInstallExtension, this, this.disposables); extensionManagementService.onDidUninstallExtension(this._onDidUninstallExtension, this, this.disposables); } @@ -287,18 +288,32 @@ export class ExtensionEnablementService implements IExtensionEnablementService { } } + private _onDidInstallExtension(event: DidInstallExtensionEvent): void { + if (event.local && event.operation === InstallOperation.Install) { + const wasDisabled = !this.isEnabled(event.local); + this._reset(event.local.galleryIdentifier); + if (wasDisabled) { + this._onEnablementChanged.fire(event.local.galleryIdentifier); + } + } + } + private _onDidUninstallExtension({ identifier, error }: DidUninstallExtensionEvent): void { if (!error) { const id = getIdFromLocalExtensionId(identifier.id); if (id) { const extension = { id, uuid: identifier.uuid }; - this._removeFromDisabledExtensions(extension, StorageScope.WORKSPACE); - this._removeFromEnabledExtensions(extension, StorageScope.WORKSPACE); - this._removeFromDisabledExtensions(extension, StorageScope.GLOBAL); + this._reset(extension); } } } + private _reset(extension: IExtensionIdentifier) { + this._removeFromDisabledExtensions(extension, StorageScope.WORKSPACE); + this._removeFromEnabledExtensions(extension, StorageScope.WORKSPACE); + this._removeFromDisabledExtensions(extension, StorageScope.GLOBAL); + } + dispose(): void { this.disposables = dispose(this.disposables); } diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 7e7e490f0..517dbb619 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -11,6 +11,7 @@ import { ILocalization } from 'vs/platform/localizations/common/localizations'; import { URI } from 'vs/base/common/uri'; import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { IRemoteAuthorityResolver } from 'vs/platform/remote/common/remoteAuthorityResolver'; export const EXTENSION_IDENTIFIER_PATTERN = '^([a-z0-9A-Z][a-z0-9\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\-A-Z]*)$'; export const EXTENSION_IDENTIFIER_REGEX = new RegExp(EXTENSION_IDENTIFIER_PATTERN); @@ -108,8 +109,11 @@ export interface IExtensionContributions { views?: { [location: string]: IView[] }; colors?: IColor[]; localizations?: ILocalization[]; + remoteAuthorityResolvers?: IRemoteAuthorityResolver[]; } +export type ExtensionKind = 'ui' | 'workspace'; + export interface IExtensionManifest { name: string; publisher: string; @@ -124,6 +128,7 @@ export interface IExtensionManifest { activationEvents?: string[]; extensionDependencies?: string[]; extensionPack?: string[]; + extensionKind?: ExtensionKind; contributes?: IExtensionContributions; repository?: { url: string; @@ -300,6 +305,9 @@ export interface DidUninstallExtensionEvent { error?: string; } +export const INSTALL_ERROR_MALICIOUS = 'malicious'; +export const INSTALL_ERROR_INCOMPATIBLE = 'incompatible'; + export interface IExtensionManagementService { _serviceBrand: any; @@ -330,9 +338,9 @@ export interface IExtensionManagementServer { export interface IExtensionManagementServerService { _serviceBrand: any; - readonly extensionManagementServers: IExtensionManagementServer[]; - getLocalExtensionManagementServer(): IExtensionManagementServer; - getExtensionManagementServer(location: URI): IExtensionManagementServer; + readonly localExtensionManagementServer: IExtensionManagementServer | null; + readonly remoteExtensionManagementServer: IExtensionManagementServer | null; + getExtensionManagementServer(location: URI): IExtensionManagementServer | null; } export const enum EnablementState { @@ -418,8 +426,6 @@ export interface IExtensionTipsService { getOtherRecommendations(): Promise; getWorkspaceRecommendations(): Promise; getKeymapRecommendations(): IExtensionRecommendation[]; - getAllRecommendations(): Promise; - getKeywordsForExtension(extension: string): string[]; toggleIgnoredRecommendation(extensionId: string, shouldIgnore: boolean): void; getAllIgnoredRecommendations(): { global: string[], workspace: string[] }; onRecommendationChange: Event; diff --git a/src/vs/platform/extensionManagement/node/extensionLifecycle.ts b/src/vs/platform/extensionManagement/node/extensionLifecycle.ts index 64cd0d545..4e69db51e 100644 --- a/src/vs/platform/extensionManagement/node/extensionLifecycle.ts +++ b/src/vs/platform/extensionManagement/node/extensionLifecycle.ts @@ -11,44 +11,57 @@ import { toErrorMessage } from 'vs/base/common/errorMessage'; import { posix } from 'path'; import { Limiter } from 'vs/base/common/async'; import { fromNodeEventEmitter, anyEvent, mapEvent, debounceEvent } from 'vs/base/common/event'; +import * as objects from 'vs/base/common/objects'; import { Schemas } from 'vs/base/common/network'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; export class ExtensionsLifecycle extends Disposable { private processesLimiter: Limiter = new Limiter(5); // Run max 5 processes in parallel constructor( - @ILogService private logService: ILogService + private environmentService: IEnvironmentService, + private logService: ILogService ) { super(); } - async uninstall(extension: ILocalExtension): Promise { - const uninstallScript = this.parseUninstallScript(extension); - if (uninstallScript) { - this.logService.info(extension.identifier.id, 'Running Uninstall hook'); + postUninstall(extension: ILocalExtension): Promise { + return this.parseAndRun(extension, 'uninstall'); + } + + postInstall(extension: ILocalExtension): Promise { + return this.parseAndRun(extension, 'install'); + } + + private async parseAndRun(extension: ILocalExtension, type: string): Promise { + const script = this.parseScript(extension, type); + if (script) { + this.logService.info(extension.identifier.id, `Running ${type} hook`); await this.processesLimiter.queue(() => - this.runUninstallHook(uninstallScript.uninstallHook, uninstallScript.args, extension) - .then(() => this.logService.info(extension.identifier.id, 'Finished running uninstall hook'), err => this.logService.error(extension.identifier.id, `Failed to run uninstall hook: ${err}`))); + this.runLifecycleHook(script.script, script.args, extension) + .then(() => this.logService.info(extension.identifier.id, `Finished running ${type} hook`), err => this.logService.error(extension.identifier.id, `Failed to run ${type} hook: ${err}`))); } } - private parseUninstallScript(extension: ILocalExtension): { uninstallHook: string, args: string[] } | null { - if (extension.location.scheme === Schemas.file && extension.manifest && extension.manifest['scripts'] && typeof extension.manifest['scripts']['vscode:uninstall'] === 'string') { - const uninstallScript = (extension.manifest['scripts']['vscode:uninstall']).split(' '); - if (uninstallScript.length < 2 || uninstallScript[0] !== 'node' || !uninstallScript[1]) { - this.logService.warn(extension.identifier.id, 'Uninstall script should be a node script'); + private parseScript(extension: ILocalExtension, type: string): { script: string, args: string[] } | null { + const scriptKey = `vscode:${type}`; + if (extension.location.scheme === Schemas.file && extension.manifest && extension.manifest['scripts'] && typeof extension.manifest['scripts'][scriptKey] === 'string') { + const script = (extension.manifest['scripts'][scriptKey]).split(' '); + if (script.length < 2 || script[0] !== 'node' || !script[1]) { + this.logService.warn(extension.identifier.id, `${scriptKey} should be a node script`); return null; } - return { uninstallHook: posix.join(extension.location.fsPath, uninstallScript[1]), args: uninstallScript.slice(2) || [] }; + return { script: posix.join(extension.location.fsPath, script[1]), args: script.slice(2) || [] }; } return null; } - private runUninstallHook(lifecycleHook: string, args: string[], extension: ILocalExtension): Promise { - return new Promise((c, e) => { + private runLifecycleHook(lifecycleHook: string, args: string[], extension: ILocalExtension): Thenable { + const extensionStoragePath = posix.join(this.environmentService.globalStorageHome, extension.identifier.id.toLocaleLowerCase()); + return new Promise((c, e) => { - const extensionLifecycleProcess = this.start(lifecycleHook, args, extension); + const extensionLifecycleProcess = this.start(lifecycleHook, args, extension, extensionStoragePath); let timeoutHandler; const onexit = (error?: string) => { @@ -84,10 +97,13 @@ export class ExtensionsLifecycle extends Disposable { }); } - private start(uninstallHook: string, args: string[], extension: ILocalExtension): ChildProcess { + private start(uninstallHook: string, args: string[], extension: ILocalExtension, extensionStoragePath: string): ChildProcess { const opts = { silent: true, - execArgv: undefined + execArgv: undefined, + env: objects.mixin(objects.deepClone(process.env), { + VSCODE_EXTENSION_STORAGE_LOCATION: extensionStoragePath + }) }; const extensionUninstallProcess = fork(uninstallHook, ['--type=extensionUninstall', ...args], opts); diff --git a/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts index 0bce6d8d0..8c2303550 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts @@ -3,47 +3,31 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension, LocalExtensionType, DidUninstallExtensionEvent, IExtensionIdentifier, IGalleryMetadata, IReportedExtension } from '../common/extensionManagement'; import { Event, buffer, mapEvent } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { IURITransformer } from 'vs/base/common/uriIpc'; -export interface IExtensionManagementChannel extends IChannel { - listen(event: 'onInstallExtension'): Event; - listen(event: 'onDidInstallExtension'): Event; - listen(event: 'onUninstallExtension'): Event; - listen(event: 'onDidUninstallExtension'): Event; - - call(command: 'zip', args: [ILocalExtension]): Thenable; - call(command: 'unzip', args: [URI, LocalExtensionType]): Thenable; - call(command: 'install', args: [URI]): Thenable; - call(command: 'installFromGallery', args: [IGalleryExtension]): Thenable; - call(command: 'uninstall', args: [ILocalExtension, boolean]): Thenable; - call(command: 'reinstallFromGallery', args: [ILocalExtension]): Thenable; - call(command: 'getInstalled', args: [LocalExtensionType | null]): Thenable; - call(command: 'getExtensionsReport'): Thenable; - call(command: 'updateMetadata', args: [ILocalExtension, IGalleryMetadata]): Thenable; -} - -export class ExtensionManagementChannel implements IExtensionManagementChannel { +export class ExtensionManagementChannel implements IServerChannel { onInstallExtension: Event; onDidInstallExtension: Event; onUninstallExtension: Event; onDidUninstallExtension: Event; - constructor(private service: IExtensionManagementService) { + constructor(private service: IExtensionManagementService, private getUriTransformer: (requestContext: any) => IURITransformer) { this.onInstallExtension = buffer(service.onInstallExtension, true); this.onDidInstallExtension = buffer(service.onDidInstallExtension, true); this.onUninstallExtension = buffer(service.onUninstallExtension, true); this.onDidUninstallExtension = buffer(service.onDidUninstallExtension, true); } - listen(event: string): Event { + listen(context, event: string): Event { + const uriTransformer = this.getUriTransformer(context); switch (event) { case 'onInstallExtension': return this.onInstallExtension; - case 'onDidInstallExtension': return this.onDidInstallExtension; + case 'onDidInstallExtension': return mapEvent(this.onDidInstallExtension, i => ({ ...i, local: this._transformOutgoing(i.local, uriTransformer) })); case 'onUninstallExtension': return this.onUninstallExtension; case 'onDidUninstallExtension': return this.onDidUninstallExtension; } @@ -51,24 +35,31 @@ export class ExtensionManagementChannel implements IExtensionManagementChannel { throw new Error('Invalid listen'); } - call(command: string, args?: any): Thenable { + call(context, command: string, args?: any): Thenable { + const uriTransformer = this.getUriTransformer(context); switch (command) { - case 'zip': return this.service.zip(this._transform(args[0])); - case 'unzip': return this.service.unzip(URI.revive(args[0]), args[1]); - case 'install': return this.service.install(URI.revive(args[0])); + case 'zip': return this.service.zip(this._transformIncoming(args[0], uriTransformer)).then(uri => uriTransformer.transformOutgoing(uri)); + case 'unzip': return this.service.unzip(URI.revive(uriTransformer.transformIncoming(args[0])), args[1]); + case 'install': return this.service.install(URI.revive(uriTransformer.transformIncoming(args[0]))); case 'installFromGallery': return this.service.installFromGallery(args[0]); - case 'uninstall': return this.service.uninstall(this._transform(args[0]), args[1]); - case 'reinstallFromGallery': return this.service.reinstallFromGallery(this._transform(args[0])); - case 'getInstalled': return this.service.getInstalled(args[0]); - case 'updateMetadata': return this.service.updateMetadata(this._transform(args[0]), args[1]); + case 'uninstall': return this.service.uninstall(this._transformIncoming(args[0], uriTransformer), args[1]); + case 'reinstallFromGallery': return this.service.reinstallFromGallery(this._transformIncoming(args[0], uriTransformer)); + case 'getInstalled': return this.service.getInstalled(args[0]).then(extensions => extensions.map(e => this._transformOutgoing(e, uriTransformer))); + case 'updateMetadata': return this.service.updateMetadata(this._transformIncoming(args[0], uriTransformer), args[1]).then(e => this._transformOutgoing(e, uriTransformer)); case 'getExtensionsReport': return this.service.getExtensionsReport(); } throw new Error('Invalid call'); } - private _transform(extension: ILocalExtension): ILocalExtension { - return extension ? { ...extension, ...{ location: URI.revive(extension.location) } } : extension; + private _transformIncoming(extension: ILocalExtension, uriTransformer: IURITransformer): ILocalExtension { + return extension ? { ...extension, ...{ location: URI.revive(uriTransformer.transformIncoming(extension.location)) } } : extension; + } + + private _transformOutgoing(extension: ILocalExtension, uriTransformer: IURITransformer): ILocalExtension; + private _transformOutgoing(extension: ILocalExtension | undefined, uriTransformer: IURITransformer): ILocalExtension | undefined; + private _transformOutgoing(extension: ILocalExtension | undefined, uriTransformer: IURITransformer): ILocalExtension | undefined { + return extension ? { ...extension, ...{ location: uriTransformer.transformOutgoing(extension.location) } } : extension; } } @@ -76,23 +67,23 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer _serviceBrand: any; - constructor(private channel: IExtensionManagementChannel, private uriTransformer: IURITransformer) { } + constructor(private channel: IChannel) { } get onInstallExtension(): Event { return this.channel.listen('onInstallExtension'); } - get onDidInstallExtension(): Event { return mapEvent(this.channel.listen('onDidInstallExtension'), i => ({ ...i, local: this._transformIncoming(i.local) })); } + get onDidInstallExtension(): Event { return mapEvent(this.channel.listen('onDidInstallExtension'), i => ({ ...i, local: this._transformIncoming(i.local) })); } get onUninstallExtension(): Event { return this.channel.listen('onUninstallExtension'); } get onDidUninstallExtension(): Event { return this.channel.listen('onDidUninstallExtension'); } zip(extension: ILocalExtension): Promise { - return Promise.resolve(this.channel.call('zip', [this._transformOutgoing(extension)]).then(result => URI.revive(this.uriTransformer.transformIncoming(result)))); + return Promise.resolve(this.channel.call('zip', [extension]).then(result => URI.revive(result))); } unzip(zipLocation: URI, type: LocalExtensionType): Promise { - return Promise.resolve(this.channel.call('unzip', [this.uriTransformer.transformOutgoing(zipLocation), type])); + return Promise.resolve(this.channel.call('unzip', [zipLocation, type])); } install(vsix: URI): Promise { - return Promise.resolve(this.channel.call('install', [this.uriTransformer.transformOutgoing(vsix)])); + return Promise.resolve(this.channel.call('install', [vsix])); } installFromGallery(extension: IGalleryExtension): Promise { @@ -100,20 +91,20 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer } uninstall(extension: ILocalExtension, force = false): Promise { - return Promise.resolve(this.channel.call('uninstall', [this._transformOutgoing(extension)!, force])); + return Promise.resolve(this.channel.call('uninstall', [extension!, force])); } reinstallFromGallery(extension: ILocalExtension): Promise { - return Promise.resolve(this.channel.call('reinstallFromGallery', [this._transformOutgoing(extension)])); + return Promise.resolve(this.channel.call('reinstallFromGallery', [extension])); } getInstalled(type: LocalExtensionType | null = null): Promise { - return Promise.resolve(this.channel.call('getInstalled', [type])) + return Promise.resolve(this.channel.call('getInstalled', [type])) .then(extensions => extensions.map(extension => this._transformIncoming(extension))); } updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise { - return Promise.resolve(this.channel.call('updateMetadata', [this._transformOutgoing(local), metadata])) + return Promise.resolve(this.channel.call('updateMetadata', [local, metadata])) .then(extension => this._transformIncoming(extension)); } @@ -124,13 +115,6 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer private _transformIncoming(extension: ILocalExtension): ILocalExtension; private _transformIncoming(extension: ILocalExtension | undefined): ILocalExtension | undefined; private _transformIncoming(extension: ILocalExtension | undefined): ILocalExtension | undefined { - return extension ? { ...extension, ...{ location: URI.revive(this.uriTransformer.transformIncoming(extension.location)) } } : extension; - } - - private _transformOutgoing(extension: ILocalExtension): ILocalExtension; - private _transformOutgoing(extension: ILocalExtension | undefined): ILocalExtension | undefined; - private _transformOutgoing(extension: ILocalExtension | undefined): ILocalExtension | undefined { - return extension ? { ...extension, ...{ location: this.uriTransformer.transformOutgoing(extension.location) } } : extension; + return extension ? { ...extension, ...{ location: URI.revive(extension.location) } } : extension; } - } \ No newline at end of file diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index e67fb9492..5b5120e4b 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -10,7 +10,7 @@ import * as errors from 'vs/base/common/errors'; import { assign } from 'vs/base/common/objects'; import { toDisposable, Disposable } from 'vs/base/common/lifecycle'; import { flatten } from 'vs/base/common/arrays'; -import { extract, buffer, ExtractError, zip, IFile } from 'vs/platform/node/zip'; +import { extract, ExtractError, zip, IFile } from 'vs/platform/node/zip'; import { ValueCallback, ErrorCallback } from 'vs/base/common/winjs.base'; import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, @@ -19,12 +19,14 @@ import { StatisticType, IExtensionIdentifier, IReportedExtension, - InstallOperation + InstallOperation, + INSTALL_ERROR_MALICIOUS, + INSTALL_ERROR_INCOMPATIBLE } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getGalleryExtensionIdFromLocal, adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, groupByExtension, getMaliciousExtensionsSet, getLocalExtensionId, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, getIdFromLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { localizeManifest } from '../common/extensionNls'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { Limiter, always, createCancelablePromise, CancelablePromise } from 'vs/base/common/async'; +import { Limiter, always, createCancelablePromise, CancelablePromise, Queue } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; import * as semver from 'semver'; import { URI } from 'vs/base/common/uri'; @@ -44,11 +46,12 @@ import { IDownloadService } from 'vs/platform/download/common/download'; import { optional } from 'vs/platform/instantiation/common/instantiation'; import { Schemas } from 'vs/base/common/network'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem'; const ERROR_SCANNING_USER_EXTENSIONS = 'scanningUser'; const INSTALL_ERROR_UNSET_UNINSTALLED = 'unsetUninstalled'; -const INSTALL_ERROR_INCOMPATIBLE = 'incompatible'; const INSTALL_ERROR_DOWNLOADING = 'downloading'; const INSTALL_ERROR_VALIDATING = 'validating'; const INSTALL_ERROR_GALLERY = 'gallery'; @@ -56,7 +59,6 @@ const INSTALL_ERROR_LOCAL = 'local'; const INSTALL_ERROR_EXTRACTING = 'extracting'; const INSTALL_ERROR_RENAMING = 'renaming'; const INSTALL_ERROR_DELETING = 'deleting'; -const INSTALL_ERROR_MALICIOUS = 'malicious'; const ERROR_UNKNOWN = 'unknown'; export class ExtensionManagementError extends Error { @@ -78,12 +80,6 @@ function parseManifest(raw: string): Promise<{ manifest: IExtensionManifest; met }); } -export function validateLocalExtension(zipPath: string): Promise { - return buffer(zipPath, 'extension/package.json') - .then(buffer => parseManifest(buffer.toString('utf8'))) - .then(({ manifest }) => manifest); -} - function readManifest(extensionPath: string): Promise<{ manifest: IExtensionManifest; metadata: IGalleryMetadata; }> { const promises = [ pfs.readFile(path.join(extensionPath, 'package.json'), 'utf8') @@ -114,7 +110,7 @@ export class ExtensionManagementService extends Disposable implements IExtension private systemExtensionsPath: string; private extensionsPath: string; private uninstalledPath: string; - private uninstalledFileLimiter: Limiter; + private uninstalledFileLimiter: Queue; private reportedExtensions: Promise | undefined; private lastReportTimestamp = 0; private readonly installingExtensions: Map> = new Map>(); @@ -135,7 +131,7 @@ export class ExtensionManagementService extends Disposable implements IExtension onDidUninstallExtension: Event = this._onDidUninstallExtension.event; constructor( - @IEnvironmentService environmentService: IEnvironmentService, + @IEnvironmentService private environmentService: IEnvironmentService, @IDialogService private dialogService: IDialogService, @IExtensionGalleryService private galleryService: IExtensionGalleryService, @ILogService private logService: ILogService, @@ -146,9 +142,9 @@ export class ExtensionManagementService extends Disposable implements IExtension this.systemExtensionsPath = environmentService.builtinExtensionsPath; this.extensionsPath = environmentService.extensionsPath; this.uninstalledPath = path.join(this.extensionsPath, '.obsolete'); - this.uninstalledFileLimiter = new Limiter(1); + this.uninstalledFileLimiter = new Queue(); this.manifestCache = this._register(new ExtensionsManifestCache(environmentService, this)); - this.extensionLifecycle = this._register(new ExtensionsLifecycle(this.logService)); + this.extensionLifecycle = this._register(new ExtensionsLifecycle(environmentService, this.logService)); this._register(toDisposable(() => { this.installingExtensions.forEach(promise => promise.cancel()); @@ -203,11 +199,11 @@ export class ExtensionManagementService extends Disposable implements IExtension .then(downloadLocation => { const zipPath = path.resolve(downloadLocation.fsPath); - return validateLocalExtension(zipPath) + return getManifest(zipPath) .then(manifest => { const identifier = { id: getLocalExtensionIdFromManifest(manifest) }; if (manifest.engines && manifest.engines.vscode && !isEngineValid(manifest.engines.vscode)) { - return Promise.reject(new Error(nls.localize('incompatible', "Unable to install Extension '{0}' as it is not compatible with Code '{1}'.", identifier.id, pkg.version))); + return Promise.reject(new Error(nls.localize('incompatible', "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", identifier.id, pkg.version))); } return this.removeIfExists(identifier.id) .then( @@ -229,7 +225,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } return null; }), - e => Promise.reject(new Error(nls.localize('restartCode', "Please restart Code before reinstalling {0}.", manifest.displayName || manifest.name)))); + e => Promise.reject(new Error(nls.localize('restartCode', "Please restart VS Code before reinstalling {0}.", manifest.displayName || manifest.name)))); }); }); }); @@ -330,6 +326,9 @@ export class ExtensionManagementService extends Disposable implements IExtension this.logService.error(`Failed to install extension:`, extension.identifier.id, error ? error.message : errorCode); this._onDidInstallExtension.fire({ identifier, gallery: extension, operation, error: errorCode }); this.reportTelemetry(this.getTelemetryEvent(operation), telemetryData, new Date().getTime() - startTime, error); + if (error instanceof Error) { + error.name = errorCode; + } errorCallback(error); }); @@ -396,7 +395,7 @@ export class ExtensionManagementService extends Disposable implements IExtension .then( zipPath => { this.logService.info('Downloaded extension:', extension.name, zipPath); - return validateLocalExtension(zipPath) + return getManifest(zipPath) .then( manifest => ({ zipPath, id: getLocalExtensionIdFromManifest(manifest), metadata }), error => Promise.reject(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_VALIDATING)) @@ -404,7 +403,7 @@ export class ExtensionManagementService extends Disposable implements IExtension }, error => Promise.reject(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_DOWNLOADING))); } else { - return Promise.reject(new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Unable to install because, the depending extension '{0}' compatible with current version '{1}' of VS Code is not found.", extension.identifier.id, pkg.version), INSTALL_ERROR_INCOMPATIBLE)); + return Promise.reject(new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Unable to install because, the extension '{0}' compatible with current version '{1}' of VS Code is not found.", extension.identifier.id, pkg.version), INSTALL_ERROR_INCOMPATIBLE)); } }, error => Promise.reject(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_GALLERY))); @@ -450,17 +449,17 @@ export class ExtensionManagementService extends Disposable implements IExtension const extensionPath = path.join(location, id); return pfs.rimraf(extensionPath) .then(() => this.extractAndRename(id, zipPath, tempPath, extensionPath, token), e => Promise.reject(new ExtensionManagementError(nls.localize('errorDeleting', "Unable to delete the existing folder '{0}' while installing the extension '{1}'. Please delete the folder manually and try again", extensionPath, id), INSTALL_ERROR_DELETING))) - .then(() => { - this.logService.info('Installation completed.', id); - return this.scanExtension(id, location, type); - }) - .then(local => { - if (metadata) { - local.metadata = metadata; - return this.saveMetadataForLocalExtension(local); - } - return local; - }); + .then(() => this.scanExtension(id, location, type)) + .then(local => + this.extensionLifecycle.postInstall(local) + .then(() => { + this.logService.info('Installation completed.', id); + if (metadata) { + local.metadata = metadata; + return this.saveMetadataForLocalExtension(local); + } + return local; + }, error => pfs.rimraf(extensionPath).then(() => Promise.reject(error), () => Promise.reject(error)))); } private extractAndRename(id: string, zipPath: string, extractPath: string, renamePath: string, token: CancellationToken): Promise { @@ -729,11 +728,30 @@ export class ExtensionManagementService extends Disposable implements IExtension private scanSystemExtensions(): Promise { this.logService.trace('Started scanning system extensions'); - return this.scanExtensions(this.systemExtensionsPath, LocalExtensionType.System) + const systemExtensionsPromise = this.scanExtensions(this.systemExtensionsPath, LocalExtensionType.System) .then(result => { this.logService.info('Scanned system extensions:', result.length); return result; }); + if (this.environmentService.isBuilt) { + return systemExtensionsPromise; + } + + // Scan other system extensions during development + const devSystemExtensionsPromise = this.getDevSystemExtensionsList() + .then(devSystemExtensionsList => { + if (devSystemExtensionsList.length) { + return this.scanExtensions(this.devSystemExtensionsPath, LocalExtensionType.System) + .then(result => { + this.logService.info('Scanned dev system extensions:', result.length); + return result.filter(r => devSystemExtensionsList.some(id => areSameExtensions(r.galleryIdentifier, { id }))); + }); + } else { + return []; + } + }); + return Promise.all([systemExtensionsPromise, devSystemExtensionsPromise]) + .then(([systemExtensions, devSystemExtensions]) => [...systemExtensions, ...devSystemExtensions]); } private scanUserExtensions(excludeOutdated: boolean): Promise { @@ -751,7 +769,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } private scanExtensions(root: string, type: LocalExtensionType): Promise { - const limiter = new Limiter(10); + const limiter = new Limiter(10); return pfs.readdir(root) .then(extensionsFolders => Promise.all(extensionsFolders.map(extensionFolder => limiter.queue(() => this.scanExtension(extensionFolder, root, type))))) .then(extensions => extensions.filter(e => e && e.identifier)); @@ -792,7 +810,7 @@ export class ExtensionManagementService extends Disposable implements IExtension .then(uninstalled => this.scanExtensions(this.extensionsPath, LocalExtensionType.User) // All user extensions .then(extensions => { const toRemove: ILocalExtension[] = extensions.filter(e => uninstalled[e.identifier.id]); - return Promise.all(toRemove.map(e => this.extensionLifecycle.uninstall(e).then(() => this.removeUninstalledExtension(e)))); + return Promise.all(toRemove.map(e => this.extensionLifecycle.postUninstall(e).then(() => this.removeUninstalledExtension(e)))); }) ).then(() => null); } @@ -893,6 +911,30 @@ export class ExtensionManagementService extends Disposable implements IExtension }); } + private _devSystemExtensionsPath: string | null = null; + private get devSystemExtensionsPath(): string { + if (!this._devSystemExtensionsPath) { + this._devSystemExtensionsPath = path.normalize(path.join(getPathFromAmdModule(require, ''), '..', '.build', 'builtInExtensions')); + } + return this._devSystemExtensionsPath; + } + + private _devSystemExtensionsFilePath: string | null = null; + private get devSystemExtensionsFilePath(): string { + if (!this._devSystemExtensionsFilePath) { + this._devSystemExtensionsFilePath = path.normalize(path.join(getPathFromAmdModule(require, ''), '..', 'build', 'builtInExtensions.json')); + } + return this._devSystemExtensionsFilePath; + } + + private getDevSystemExtensionsList(): Promise { + return pfs.readFile(this.devSystemExtensionsFilePath, 'utf8') + .then(raw => { + const parsed: { name: string }[] = JSON.parse(raw); + return parsed.map(({ name }) => name); + }); + } + private toNonCancellablePromise(promise: Promise): Promise { return new Promise((c, e) => promise.then(result => c(result), error => e(error))); } diff --git a/src/vs/platform/extensionManagement/node/extensionManagementUtil.ts b/src/vs/platform/extensionManagement/node/extensionManagementUtil.ts index 7497c7a3a..74de3958a 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementUtil.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementUtil.ts @@ -5,6 +5,9 @@ import * as semver from 'semver'; import { adoptToGalleryExtensionId, LOCAL_EXTENSION_ID_REGEX } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { IExtensionManifest } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { buffer } from 'vs/platform/node/zip'; +import { localize } from 'vs/nls'; export function getIdAndVersionFromLocalExtensionId(localExtensionId: string): { id: string, version: string | null } { const matches = LOCAL_EXTENSION_ID_REGEX.exec(localExtensionId); @@ -18,4 +21,15 @@ export function getIdAndVersionFromLocalExtensionId(localExtensionId: string): { id: adoptToGalleryExtensionId(localExtensionId), version: null }; +} + +export function getManifest(vsix: string): Promise { + return buffer(vsix, 'extension/package.json') + .then(buffer => { + try { + return JSON.parse(buffer.toString('utf8')); + } catch (err) { + throw new Error(localize('invalidManifest', "VSIX invalid: package.json is not a JSON file.")); + } + }); } \ No newline at end of file diff --git a/src/vs/platform/extensionManagement/node/media/defaultIcon.png b/src/vs/platform/extensionManagement/node/media/defaultIcon.png index 2f36a9434..d8b2dbc3d 100644 Binary files a/src/vs/platform/extensionManagement/node/media/defaultIcon.png and b/src/vs/platform/extensionManagement/node/media/defaultIcon.png differ diff --git a/src/vs/platform/extensionManagement/node/multiExtensionManagement.ts b/src/vs/platform/extensionManagement/node/multiExtensionManagement.ts new file mode 100644 index 000000000..719b8887e --- /dev/null +++ b/src/vs/platform/extensionManagement/node/multiExtensionManagement.ts @@ -0,0 +1,113 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event, EventMultiplexer } from 'vs/base/common/event'; +import { + IExtensionManagementService, ILocalExtension, IGalleryExtension, LocalExtensionType, InstallExtensionEvent, DidInstallExtensionEvent, IExtensionIdentifier, DidUninstallExtensionEvent, IReportedExtension, IGalleryMetadata, + IExtensionManagementServerService, IExtensionManagementServer, IExtensionGalleryService +} from 'vs/platform/extensionManagement/common/extensionManagement'; +import { flatten } from 'vs/base/common/arrays'; +import { isUIExtension } from 'vs/platform/extensions/common/extensions'; +import { URI } from 'vs/base/common/uri'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { IRemoteAuthorityResolverService, IRemoteAuthorityResolver } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; + +export class MulitExtensionManagementService extends Disposable implements IExtensionManagementService { + + _serviceBrand: any; + + readonly onInstallExtension: Event; + readonly onDidInstallExtension: Event; + readonly onUninstallExtension: Event; + readonly onDidUninstallExtension: Event; + + private readonly servers: IExtensionManagementServer[]; + + constructor( + @IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService, + @IExtensionGalleryService private extensionGalleryService: IExtensionGalleryService, + @IConfigurationService private configurationService: IConfigurationService, + @IRemoteAuthorityResolverService private remoteAuthorityResolverService: IRemoteAuthorityResolverService + ) { + super(); + this.servers = this.extensionManagementServerService.remoteExtensionManagementServer ? [this.extensionManagementServerService.localExtensionManagementServer, this.extensionManagementServerService.remoteExtensionManagementServer] : [this.extensionManagementServerService.localExtensionManagementServer]; + + this.onInstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(server.extensionManagementService.onInstallExtension); return emitter; }, new EventMultiplexer())).event; + this.onDidInstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(server.extensionManagementService.onDidInstallExtension); return emitter; }, new EventMultiplexer())).event; + this.onUninstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(server.extensionManagementService.onUninstallExtension); return emitter; }, new EventMultiplexer())).event; + this.onDidUninstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(server.extensionManagementService.onDidUninstallExtension); return emitter; }, new EventMultiplexer())).event; + } + + getInstalled(type?: LocalExtensionType): Promise { + return Promise.all(this.servers.map(({ extensionManagementService }) => extensionManagementService.getInstalled(type))) + .then(result => flatten(result)); + } + + uninstall(extension: ILocalExtension, force?: boolean): Promise { + return this.getServer(extension).extensionManagementService.uninstall(extension, force); + } + + reinstallFromGallery(extension: ILocalExtension): Promise { + return this.getServer(extension).extensionManagementService.reinstallFromGallery(extension); + } + + updateMetadata(extension: ILocalExtension, metadata: IGalleryMetadata): Promise { + return this.getServer(extension).extensionManagementService.updateMetadata(extension, metadata); + } + + zip(extension: ILocalExtension): Promise { + throw new Error('Not Supported'); + } + + unzip(zipLocation: URI, type: LocalExtensionType): Promise { + return Promise.all(this.servers.map(({ extensionManagementService }) => extensionManagementService.unzip(zipLocation, type))).then(() => null); + } + + install(vsix: URI): Promise { + if (!this.extensionManagementServerService.remoteExtensionManagementServer) { + return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.install(vsix); + } + return Promise.all([getManifest(vsix.fsPath), this.hasToSyncExtensions()]) + .then(([manifest, syncExtensions]) => { + const servers = isUIExtension(manifest, this.configurationService) ? [this.extensionManagementServerService.localExtensionManagementServer] : syncExtensions ? this.servers : [this.extensionManagementServerService.remoteExtensionManagementServer]; + return Promise.all(servers.map(server => server.extensionManagementService.install(vsix))) + .then(() => null); + }); + } + + installFromGallery(gallery: IGalleryExtension): Promise { + if (!this.extensionManagementServerService.remoteExtensionManagementServer) { + return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery); + } + return Promise.all([this.extensionGalleryService.getManifest(gallery, CancellationToken.None), this.hasToSyncExtensions()]) + .then(([manifest, syncExtensions]) => { + const servers = isUIExtension(manifest, this.configurationService) ? [this.extensionManagementServerService.localExtensionManagementServer] : syncExtensions ? this.servers : [this.extensionManagementServerService.remoteExtensionManagementServer]; + return Promise.all(servers.map(server => server.extensionManagementService.installFromGallery(gallery))) + .then(() => null); + }); + } + + getExtensionsReport(): Promise { + return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.getExtensionsReport(); + } + + private getServer(extension: ILocalExtension): IExtensionManagementServer { + return this.extensionManagementServerService.getExtensionManagementServer(extension.location); + } + + private _remoteAuthorityResolverPromise: Thenable; + private hasToSyncExtensions(): Thenable { + if (!this.extensionManagementServerService.remoteExtensionManagementServer) { + return Promise.resolve(false); + } + if (!this._remoteAuthorityResolverPromise) { + this._remoteAuthorityResolverPromise = this.remoteAuthorityResolverService.getRemoteAuthorityResolver(this.extensionManagementServerService.remoteExtensionManagementServer.authority); + } + return this._remoteAuthorityResolverPromise.then(({ syncExtensions }) => !!syncExtensions); + } +} \ No newline at end of file diff --git a/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts b/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts index f865ab523..5fa1d7cbb 100644 --- a/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts +++ b/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import * as sinon from 'sinon'; -import { IExtensionManagementService, IExtensionEnablementService, DidUninstallExtensionEvent, EnablementState, IExtensionContributions, ILocalExtension, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementService, IExtensionEnablementService, DidUninstallExtensionEvent, EnablementState, IExtensionContributions, ILocalExtension, LocalExtensionType, DidInstallExtensionEvent, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { Emitter } from 'vs/base/common/event'; @@ -32,7 +32,7 @@ export class TestExtensionEnablementService extends ExtensionEnablementService { super(storageService(instantiationService), instantiationService.get(IWorkspaceContextService), instantiationService.get(IEnvironmentService) || instantiationService.stub(IEnvironmentService, {} as IEnvironmentService), instantiationService.get(IExtensionManagementService) || instantiationService.stub(IExtensionManagementService, - { onDidUninstallExtension: new Emitter().event } as IExtensionManagementService)); + { onDidInstallExtension: new Emitter().event, onDidUninstallExtension: new Emitter().event } as IExtensionManagementService)); } public async reset(): Promise { @@ -46,10 +46,11 @@ suite('ExtensionEnablementService Test', () => { let testObject: IExtensionEnablementService; const didUninstallEvent: Emitter = new Emitter(); + const didInstallEvent: Emitter = new Emitter(); setup(() => { instantiationService = new TestInstantiationService(); - instantiationService.stub(IExtensionManagementService, { onDidUninstallExtension: didUninstallEvent.event, getInstalled: () => Promise.resolve([]) } as IExtensionManagementService); + instantiationService.stub(IExtensionManagementService, { onDidUninstallExtension: didUninstallEvent.event, onDidInstallExtension: didInstallEvent.event, getInstalled: () => Promise.resolve([]) } as IExtensionManagementService); testObject = new TestExtensionEnablementService(instantiationService); }); @@ -294,6 +295,90 @@ suite('ExtensionEnablementService Test', () => { .then(extensions => assert.deepEqual([], extensions)); }); + test('test installing an extension re-eanbles it when disabled globally', async () => { + const local = aLocalExtension('pub.a'); + await testObject.setEnablement(local, EnablementState.Disabled); + didInstallEvent.fire({ local, identifier: local.galleryIdentifier, operation: InstallOperation.Install }); + const extensions = await testObject.getDisabledExtensions(); + assert.deepEqual([], extensions); + }); + + test('test updating an extension does not re-eanbles it when disabled globally', async () => { + const local = aLocalExtension('pub.a'); + await testObject.setEnablement(local, EnablementState.Disabled); + didInstallEvent.fire({ local, identifier: local.galleryIdentifier, operation: InstallOperation.Update }); + const extensions = await testObject.getDisabledExtensions(); + assert.deepEqual([{ id: 'pub.a' }], extensions); + }); + + test('test installing an extension fires enablement change event when disabled globally', async () => { + const local = aLocalExtension('pub.a'); + await testObject.setEnablement(local, EnablementState.Disabled); + return new Promise((c, e) => { + testObject.onEnablementChanged(e => { + if (e.id === local.galleryIdentifier.id) { + c(); + } + }); + didInstallEvent.fire({ local, identifier: local.galleryIdentifier, 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.Disabled); + testObject.onEnablementChanged(target); + didInstallEvent.fire({ local, identifier: local.galleryIdentifier, 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.WorkspaceDisabled); + didInstallEvent.fire({ local, identifier: local.galleryIdentifier, operation: InstallOperation.Install }); + const extensions = await testObject.getDisabledExtensions(); + assert.deepEqual([], extensions); + }); + + test('test updating an extension does not re-eanbles it when workspace disabled', async () => { + const local = aLocalExtension('pub.a'); + await testObject.setEnablement(local, EnablementState.WorkspaceDisabled); + didInstallEvent.fire({ local, identifier: local.galleryIdentifier, operation: InstallOperation.Update }); + const extensions = await testObject.getDisabledExtensions(); + assert.deepEqual([{ id: 'pub.a' }], extensions); + }); + + test('test installing an extension fires enablement change event when workspace disabled', async () => { + const local = aLocalExtension('pub.a'); + await testObject.setEnablement(local, EnablementState.WorkspaceDisabled); + return new Promise((c, e) => { + testObject.onEnablementChanged(e => { + if (e.id === local.galleryIdentifier.id) { + c(); + } + }); + didInstallEvent.fire({ local, identifier: local.galleryIdentifier, 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.WorkspaceDisabled); + testObject.onEnablementChanged(target); + didInstallEvent.fire({ local, identifier: local.galleryIdentifier, 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.galleryIdentifier, operation: InstallOperation.Install }); + assert.ok(!target.called); + }); + test('test remove an extension from disablement list when uninstalled', () => { return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)) @@ -352,7 +437,7 @@ suite('ExtensionEnablementService Test', () => { test('test getDisabledExtensions include extensions disabled in enviroment', () => { instantiationService.stub(IEnvironmentService, { disableExtensions: ['pub.a'] } as IEnvironmentService); - instantiationService.stub(IExtensionManagementService, { onDidUninstallExtension: didUninstallEvent.event, getInstalled: () => Promise.resolve([aLocalExtension('pub.a'), aLocalExtension('pub.b')]) } as IExtensionManagementService); + instantiationService.stub(IExtensionManagementService, { onDidUninstallExtension: didUninstallEvent.event, onDidInstallExtension: didInstallEvent.event, getInstalled: () => Promise.resolve([aLocalExtension('pub.a'), aLocalExtension('pub.b')]) } as IExtensionManagementService); testObject = new TestExtensionEnablementService(instantiationService); return testObject.getDisabledExtensions() .then(actual => { @@ -360,6 +445,7 @@ suite('ExtensionEnablementService Test', () => { assert.equal(actual[0].id, 'pub.a'); }); }); + }); function aLocalExtension(id: string, contributes?: IExtensionContributions): ILocalExtension { diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index 1bae8460f..25998a367 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -3,6 +3,31 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IExtensionManifest } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; + export const MANIFEST_CACHE_FOLDER = 'CachedExtensions'; export const USER_MANIFEST_CACHE_FILE = 'user'; export const BUILTIN_MANIFEST_CACHE_FILE = 'builtin'; + +const uiExtensions = new Set(); +uiExtensions.add('msjsdiag.debugger-for-chrome'); + +export function isUIExtension(manifest: IExtensionManifest, configurationService: IConfigurationService): boolean { + const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name); + const configuredUIExtensions = configurationService.getValue('_workbench.uiExtensions') || []; + if (configuredUIExtensions.length) { + if (configuredUIExtensions.indexOf(extensionId) !== -1) { + return true; + } + if (configuredUIExtensions.indexOf(`-${extensionId}`) !== -1) { + return false; + } + } + switch (manifest.extensionKind) { + case 'ui': return true; + case 'workspace': return false; + default: return uiExtensions.has(extensionId) || !manifest.main; + } +} diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index 41c6352e1..779fa645d 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -46,7 +46,7 @@ export interface IFileService { onDidChangeFileSystemProviderRegistrations: Event; /** - * Registeres a file system provider for a certain scheme. + * Registers a file system provider for a certain scheme. */ registerProvider(scheme: string, provider: IFileSystemProvider): IDisposable; @@ -74,13 +74,13 @@ export interface IFileService { resolveFile(resource: URI, options?: IResolveFileOptions): TPromise; /** - * Same as resolveFile but supports resolving mulitple resources in parallel. + * Same as resolveFile but supports resolving multiple resources in parallel. * If one of the resolve targets fails to resolve returns a fake IFileStat instead of making the whole call fail. */ resolveFiles(toResolve: { resource: URI, options?: IResolveFileOptions }[]): TPromise; /** - *Finds out if a file identified by the resource exists. + * Finds out if a file identified by the resource exists. */ existsFile(resource: URI): TPromise; @@ -125,6 +125,12 @@ export interface IFileService { */ createFile(resource: URI, content?: string, options?: ICreateFileOptions): TPromise; + /** + * Reads a folder's content with the given path. The returned promise + * will have the list of children as a result. + */ + readFolder(resource: URI): TPromise; + /** * Creates a new folder with the given path. The returned promise * will have the stat model object as a result. @@ -198,6 +204,7 @@ export const enum FileSystemProviderCapabilities { export interface IFileSystemProvider { readonly capabilities: FileSystemProviderCapabilities; + onDidChangeCapabilities: Event; onDidChangeFile: Event; watch(resource: URI, opts: IWatchOptions): IDisposable; diff --git a/src/vs/platform/history/electron-main/historyMainService.ts b/src/vs/platform/history/electron-main/historyMainService.ts index fdce961c3..209ed98e2 100644 --- a/src/vs/platform/history/electron-main/historyMainService.ts +++ b/src/vs/platform/history/electron-main/historyMainService.ts @@ -240,7 +240,7 @@ export class HistoryMainService implements IHistoryMainService { // Add currently files to open to the beginning if any if (currentFiles) { - files.unshift(...currentFiles.map(f => f.fileUri)); + files.unshift(...arrays.coalesce(currentFiles.map(f => f.fileUri))); } // Clear those dupes @@ -374,7 +374,8 @@ export class HistoryMainService implements IHistoryMainService { let description; let args; if (isSingleFolderWorkspaceIdentifier(workspace)) { - description = nls.localize('folderDesc', "{0} {1}", getBaseLabel(workspace), this.labelService.getUriLabel(dirname(workspace))); + const parentFolder = dirname(workspace); + description = parentFolder ? nls.localize('folderDesc', "{0} {1}", getBaseLabel(workspace), this.labelService.getUriLabel(parentFolder)) : getBaseLabel(workspace); args = `--folder-uri "${workspace.toString()}"`; } else { description = nls.localize('codeWorkspace', "Code Workspace"); diff --git a/src/vs/platform/instantiation/common/descriptors.ts b/src/vs/platform/instantiation/common/descriptors.ts index 9ad8ad5b4..658330c23 100644 --- a/src/vs/platform/instantiation/common/descriptors.ts +++ b/src/vs/platform/instantiation/common/descriptors.ts @@ -5,15 +5,16 @@ import * as instantiation from './instantiation'; - export class SyncDescriptor { readonly ctor: any; readonly staticArguments: any[]; + readonly supportsDelayedInstantiation: boolean; - constructor(ctor: new (...args: any[]) => T, ..._staticArguments: any[]) { + constructor(ctor: new (...args: any[]) => T, staticArguments: any[] = [], supportsDelayedInstantiation: boolean = false) { this.ctor = ctor; - this.staticArguments = _staticArguments; + this.staticArguments = staticArguments; + this.supportsDelayedInstantiation = supportsDelayedInstantiation; } } @@ -74,7 +75,7 @@ export interface CreateSyncFunc { (ctor: instantiation.IConstructorSignature8, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8): SyncDescriptor0; } export const createSyncDescriptor: CreateSyncFunc = (ctor: any, ...staticArguments: any[]): any => { - return new SyncDescriptor(ctor, ...staticArguments); + return new SyncDescriptor(ctor, staticArguments); }; export interface SyncDescriptor0 { diff --git a/src/vs/platform/instantiation/common/extensions.ts b/src/vs/platform/instantiation/common/extensions.ts index 5ff44b2f2..5ecd792c5 100644 --- a/src/vs/platform/instantiation/common/extensions.ts +++ b/src/vs/platform/instantiation/common/extensions.ts @@ -14,8 +14,8 @@ export interface IServiceContribution { const _registry: IServiceContribution[] = []; -export function registerSingleton(id: ServiceIdentifier, ctor: IConstructorSignature0): void { - _registry.push({ id, descriptor: new SyncDescriptor(ctor) }); +export function registerSingleton(id: ServiceIdentifier, ctor: IConstructorSignature0, supportsDelayedInstantiation?: boolean): void { + _registry.push({ id, descriptor: new SyncDescriptor(ctor, [], supportsDelayedInstantiation) }); } export function getServices(): IServiceContribution[] { diff --git a/src/vs/platform/instantiation/common/instantiationService.ts b/src/vs/platform/instantiation/common/instantiationService.ts index 2a8a4fe60..b1b28925e 100644 --- a/src/vs/platform/instantiation/common/instantiationService.ts +++ b/src/vs/platform/instantiation/common/instantiationService.ts @@ -186,7 +186,7 @@ export class InstantiationService implements IInstantiationService { for (let { data } of roots) { // create instance and overwrite the service collections - const instance = this._createServiceInstanceWithOwner(data.id, data.desc.ctor, data.desc.staticArguments, data._trace); + const instance = this._createServiceInstanceWithOwner(data.id, data.desc.ctor, data.desc.staticArguments, false, data._trace); this._setServiceInstance(data.id, instance); graph.removeNode(data); } @@ -195,17 +195,17 @@ export class InstantiationService implements IInstantiationService { return this._getServiceInstanceOrDescriptor(id); } - private _createServiceInstanceWithOwner(id: ServiceIdentifier, ctor: any, args: any[] = [], _trace: Trace): T { + private _createServiceInstanceWithOwner(id: ServiceIdentifier, ctor: any, args: any[] = [], supportsDelayedInstantiation: boolean, _trace: Trace): T { if (this._services.get(id) instanceof SyncDescriptor) { - return this._createServiceInstance(ctor, args, _trace); + return this._createServiceInstance(ctor, args, supportsDelayedInstantiation, _trace); } else if (this._parent) { - return this._parent._createServiceInstanceWithOwner(id, ctor, args, _trace); + return this._parent._createServiceInstanceWithOwner(id, ctor, args, supportsDelayedInstantiation, _trace); } else { throw new Error('illegalState - creating UNKNOWN service instance'); } } - protected _createServiceInstance(ctor: any, args: any[] = [], _trace: Trace): T { + protected _createServiceInstance(ctor: any, args: any[] = [], supportsDelayedInstantiation: boolean, _trace: Trace): T { return this._createInstance(ctor, args, _trace); } } diff --git a/src/vs/platform/instantiation/node/instantiationService.ts b/src/vs/platform/instantiation/node/instantiationService.ts index a1e1a5ad7..c888491b3 100644 --- a/src/vs/platform/instantiation/node/instantiationService.ts +++ b/src/vs/platform/instantiation/node/instantiationService.ts @@ -16,8 +16,12 @@ export class InstantiationService extends BaseInstantiationService { return new InstantiationService(services, this._strict, this); } - protected _createServiceInstance(ctor: any, args: any[] = [], _trace): T { - return InstantiationService._newIdleProxyService(() => super._createServiceInstance(ctor, args, _trace)); + protected _createServiceInstance(ctor: any, args: any[] = [], supportsDelayedInstantiation: boolean, _trace): T { + if (supportsDelayedInstantiation) { + return InstantiationService._newIdleProxyService(() => super._createServiceInstance(ctor, args, supportsDelayedInstantiation, _trace)); + } else { + return super._createServiceInstance(ctor, args, supportsDelayedInstantiation, _trace); + } } private static _newIdleProxyService(executor: () => T): T { diff --git a/src/vs/platform/instantiation/test/common/instantiationServiceMock.ts b/src/vs/platform/instantiation/test/common/instantiationServiceMock.ts index 94ed67017..0bef39df5 100644 --- a/src/vs/platform/instantiation/test/common/instantiationServiceMock.ts +++ b/src/vs/platform/instantiation/test/common/instantiationServiceMock.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as sinon from 'sinon'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as types from 'vs/base/common/types'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { TPromise } from 'vs/base/common/winjs.base'; interface IServiceMock { id: ServiceIdentifier; diff --git a/src/vs/platform/issue/common/issue.ts b/src/vs/platform/issue/common/issue.ts index 62734a94f..1d8f9d662 100644 --- a/src/vs/platform/issue/common/issue.ts +++ b/src/vs/platform/issue/common/issue.ts @@ -3,9 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; export const IIssueService = createDecorator('issueService'); @@ -43,9 +41,20 @@ export interface IssueReporterStyles extends WindowStyles { sliderActiveColor: string; } +export interface IssueReporterExtensionData { + name: string; + publisher: string; + version: string; + id: string; + isTheme: boolean; + displayName: string | undefined; + repositoryUrl: string | undefined; + bugsUrl: string | undefined; +} + export interface IssueReporterData extends WindowData { styles: IssueReporterStyles; - enabledExtensions: ILocalExtension[]; + enabledExtensions: IssueReporterExtensionData[]; issueType?: IssueType; } @@ -78,6 +87,6 @@ export interface ProcessExplorerData extends WindowData { export interface IIssueService { _serviceBrand: any; - openReporter(data: IssueReporterData): TPromise; - openProcessExplorer(data: ProcessExplorerData): TPromise; + openReporter(data: IssueReporterData): Thenable; + openProcessExplorer(data: ProcessExplorerData): Thenable; } diff --git a/src/vs/platform/issue/electron-main/issueService.ts b/src/vs/platform/issue/electron-main/issueService.ts index 1cb9f6236..a63f2ed19 100644 --- a/src/vs/platform/issue/electron-main/issueService.ts +++ b/src/vs/platform/issue/electron-main/issueService.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise, Promise } from 'vs/base/common/winjs.base'; import { localize } from 'vs/nls'; import * as objects from 'vs/base/common/objects'; import { parseArgs } from 'vs/platform/environment/node/argv'; @@ -15,14 +14,15 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { isMacintosh, IProcessEnvironment } from 'vs/base/common/platform'; import { ILogService } from 'vs/platform/log/common/log'; import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { IWindowState } from 'vs/platform/windows/electron-main/windows'; const DEFAULT_BACKGROUND_COLOR = '#1E1E1E'; export class IssueService implements IIssueService { _serviceBrand: any; - _issueWindow: BrowserWindow; + _issueWindow: BrowserWindow | null; _issueParentWindow: BrowserWindow; - _processExplorerWindow: BrowserWindow; + _processExplorerWindow: BrowserWindow | null; constructor( private machineId: string, @@ -73,102 +73,102 @@ export class IssueService implements IIssueService { } - openReporter(data: IssueReporterData): TPromise { - this._issueParentWindow = BrowserWindow.getFocusedWindow(); - const position = this.getWindowPosition(this._issueParentWindow, 700, 800); - if (!this._issueWindow) { - this._issueWindow = new BrowserWindow({ - width: position.width, - height: position.height, - minWidth: 300, - minHeight: 200, - x: position.x, - y: position.y, - title: localize('issueReporter', "Issue Reporter"), - backgroundColor: data.styles.backgroundColor || DEFAULT_BACKGROUND_COLOR - }); - - this._issueWindow.setMenuBarVisibility(false); // workaround for now, until a menu is implemented - - // Modified when testing UI - const features: IssueReporterFeatures = {}; - - this.logService.trace('issueService#openReporter: opening issue reporter'); - this._issueWindow.loadURL(this.getIssueReporterPath(data, features)); - - this._issueWindow.on('close', () => this._issueWindow = null); - - this._issueParentWindow.on('closed', () => { - if (this._issueWindow) { - this._issueWindow.close(); - this._issueWindow = null; - } - }); - } - - this._issueWindow.focus(); + openReporter(data: IssueReporterData): Promise { + return new Promise(_ => { + this._issueParentWindow = BrowserWindow.getFocusedWindow(); + const position = this.getWindowPosition(this._issueParentWindow, 700, 800); + if (!this._issueWindow) { + this._issueWindow = new BrowserWindow({ + width: position.width, + height: position.height, + minWidth: 300, + minHeight: 200, + x: position.x, + y: position.y, + title: localize('issueReporter', "Issue Reporter"), + backgroundColor: data.styles.backgroundColor || DEFAULT_BACKGROUND_COLOR + }); + + this._issueWindow.setMenuBarVisibility(false); // workaround for now, until a menu is implemented + + // Modified when testing UI + const features: IssueReporterFeatures = {}; + + this.logService.trace('issueService#openReporter: opening issue reporter'); + this._issueWindow.loadURL(this.getIssueReporterPath(data, features)); + + this._issueWindow.on('close', () => this._issueWindow = null); + + this._issueParentWindow.on('closed', () => { + if (this._issueWindow) { + this._issueWindow.close(); + this._issueWindow = null; + } + }); + } - return TPromise.as(null); + this._issueWindow.focus(); + }); } - openProcessExplorer(data: ProcessExplorerData): TPromise { - // Create as singleton - if (!this._processExplorerWindow) { - const parentWindow = BrowserWindow.getFocusedWindow(); - const position = this.getWindowPosition(parentWindow, 800, 300); - this._processExplorerWindow = new BrowserWindow({ - skipTaskbar: true, - resizable: true, - width: position.width, - height: position.height, - minWidth: 300, - minHeight: 200, - x: position.x, - y: position.y, - backgroundColor: data.styles.backgroundColor, - title: localize('processExplorer', "Process Explorer") - }); - - this._processExplorerWindow.setMenuBarVisibility(false); - - const windowConfiguration = { - appRoot: this.environmentService.appRoot, - nodeCachedDataDir: this.environmentService.nodeCachedDataDir, - windowId: this._processExplorerWindow.id, - userEnv: this.userEnv, - machineId: this.machineId, - data - }; - - const environment = parseArgs(process.argv); - const config = objects.assign(environment, windowConfiguration); - for (let key in config) { - if (config[key] === void 0 || config[key] === null || config[key] === '') { - delete config[key]; // only send over properties that have a true value + openProcessExplorer(data: ProcessExplorerData): Promise { + return new Promise(_ => { + // Create as singleton + if (!this._processExplorerWindow) { + const parentWindow = BrowserWindow.getFocusedWindow(); + const position = this.getWindowPosition(parentWindow, 800, 300); + this._processExplorerWindow = new BrowserWindow({ + skipTaskbar: true, + resizable: true, + width: position.width, + height: position.height, + minWidth: 300, + minHeight: 200, + x: position.x, + y: position.y, + backgroundColor: data.styles.backgroundColor, + title: localize('processExplorer', "Process Explorer") + }); + + this._processExplorerWindow.setMenuBarVisibility(false); + + const windowConfiguration = { + appRoot: this.environmentService.appRoot, + nodeCachedDataDir: this.environmentService.nodeCachedDataDir, + windowId: this._processExplorerWindow.id, + userEnv: this.userEnv, + machineId: this.machineId, + data + }; + + const environment = parseArgs(process.argv); + const config = objects.assign(environment, windowConfiguration); + for (let key in config) { + if (config[key] === void 0 || config[key] === null || config[key] === '') { + delete config[key]; // only send over properties that have a true value + } } - } - this._processExplorerWindow.loadURL(`${require.toUrl('vs/code/electron-browser/processExplorer/processExplorer.html')}?config=${encodeURIComponent(JSON.stringify(config))}`); + this._processExplorerWindow.loadURL(`${require.toUrl('vs/code/electron-browser/processExplorer/processExplorer.html')}?config=${encodeURIComponent(JSON.stringify(config))}`); - this._processExplorerWindow.on('close', () => this._processExplorerWindow = void 0); - - parentWindow.on('close', () => { - if (this._processExplorerWindow) { - this._processExplorerWindow.close(); - this._processExplorerWindow = null; - } - }); - } + this._processExplorerWindow.on('close', () => this._processExplorerWindow = null); - // Focus - this._processExplorerWindow.focus(); + parentWindow.on('close', () => { + if (this._processExplorerWindow) { + this._processExplorerWindow.close(); + this._processExplorerWindow = null; + } + }); + } - return TPromise.as(null); + // Focus + this._processExplorerWindow.focus(); + }); } - private getWindowPosition(parentWindow: BrowserWindow, defaultWidth: number, defaultHeight: number) { + private getWindowPosition(parentWindow: BrowserWindow, defaultWidth: number, defaultHeight: number): IWindowState { // We want the new window to open on the same display that the parent is in - let displayToUse: Electron.Display; + let displayToUse: Electron.Display | undefined; const displays = screen.getAllDisplays(); // Single Display @@ -196,16 +196,14 @@ export class IssueService implements IIssueService { } } - let state = { + const state: IWindowState = { width: defaultWidth, - height: defaultHeight, - x: undefined, - y: undefined + height: defaultHeight }; const displayBounds = displayToUse.bounds; - state.x = displayBounds.x + (displayBounds.width / 2) - (state.width / 2); - state.y = displayBounds.y + (displayBounds.height / 2) - (state.height / 2); + state.x = displayBounds.x + (displayBounds.width / 2) - (state.width! / 2); + state.y = displayBounds.y + (displayBounds.height / 2) - (state.height! / 2); if (displayBounds.width > 0 && displayBounds.height > 0 /* Linux X11 sessions sometimes report wrong display bounds */) { if (state.x < displayBounds.x) { @@ -224,11 +222,11 @@ export class IssueService implements IIssueService { state.y = displayBounds.y; // prevent window from falling out of the screen to the bottom } - if (state.width > displayBounds.width) { + if (state.width! > displayBounds.width) { state.width = displayBounds.width; // prevent window from exceeding display bounds width } - if (state.height > displayBounds.height) { + if (state.height! > displayBounds.height) { state.height = displayBounds.height; // prevent window from exceeding display bounds height } } @@ -236,7 +234,7 @@ export class IssueService implements IIssueService { return state; } - private getSystemInformation(): TPromise { + private getSystemInformation(): Promise { return new Promise((resolve, reject) => { this.launchService.getMainProcessInfo().then(info => { resolve(this.diagnosticsService.getSystemInfo(info)); @@ -244,7 +242,7 @@ export class IssueService implements IIssueService { }); } - private getPerformanceInfo(): TPromise { + private getPerformanceInfo(): Promise { return new Promise((resolve, reject) => { this.launchService.getMainProcessInfo().then(info => { this.diagnosticsService.getPerformanceInfo(info) @@ -259,7 +257,11 @@ export class IssueService implements IIssueService { }); } - private getIssueReporterPath(data: IssueReporterData, features: IssueReporterFeatures) { + private getIssueReporterPath(data: IssueReporterData, features: IssueReporterFeatures): string { + if (!this._issueWindow) { + throw new Error('Issue window has been disposed'); + } + const windowConfiguration = { appRoot: this.environmentService.appRoot, nodeCachedDataDir: this.environmentService.nodeCachedDataDir, diff --git a/src/vs/platform/issue/node/issueIpc.ts b/src/vs/platform/issue/node/issueIpc.ts index 9692c6793..c6b2ad7e7 100644 --- a/src/vs/platform/issue/node/issueIpc.ts +++ b/src/vs/platform/issue/node/issueIpc.ts @@ -3,33 +3,27 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; import { IIssueService, IssueReporterData, ProcessExplorerData } from '../common/issue'; import { Event } from 'vs/base/common/event'; -export interface IIssueChannel extends IChannel { - call(command: 'openIssueReporter', arg: IssueReporterData): TPromise; - call(command: 'getStatusInfo'): TPromise; - call(command: string, arg?: any): TPromise; -} - -export class IssueChannel implements IIssueChannel { +export class IssueChannel implements IServerChannel { constructor(private service: IIssueService) { } - listen(event: string): Event { - throw new Error('No event found'); + listen(_, event: string): Event { + throw new Error(`Event not found: ${event}`); } - call(command: string, arg?: any): TPromise { + call(_, command: string, arg?: any): Thenable { switch (command) { case 'openIssueReporter': return this.service.openReporter(arg); case 'openProcessExplorer': return this.service.openProcessExplorer(arg); } - return undefined; + + throw new Error(`Call not found: ${command}`); } } @@ -37,13 +31,13 @@ export class IssueChannelClient implements IIssueService { _serviceBrand: any; - constructor(private channel: IIssueChannel) { } + constructor(private channel: IChannel) { } - openReporter(data: IssueReporterData): TPromise { + openReporter(data: IssueReporterData): Thenable { return this.channel.call('openIssueReporter', data); } - openProcessExplorer(data: ProcessExplorerData): TPromise { + openProcessExplorer(data: ProcessExplorerData): Thenable { return this.channel.call('openProcessExplorer', data); } } \ No newline at end of file diff --git a/src/vs/platform/keybinding/common/keybindingResolver.ts b/src/vs/platform/keybinding/common/keybindingResolver.ts index e09852b56..d549ce8ca 100644 --- a/src/vs/platform/keybinding/common/keybindingResolver.ts +++ b/src/vs/platform/keybinding/common/keybindingResolver.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; import { MenuRegistry } from 'vs/platform/actions/common/actions'; import { CommandsRegistry, ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; import { ContextKeyAndExpr, ContextKeyExpr, IContext } from 'vs/platform/contextkey/common/contextkey'; @@ -322,7 +322,7 @@ export class KeybindingResolver { } const command = CommandsRegistry.getCommand(id); if (command && typeof command.description === 'object' - && !isFalsyOrEmpty((command.description).args)) { // command with args + && isNonEmptyArray((command.description).args)) { // command with args return; } unboundCommands.push(id); diff --git a/src/vs/platform/label/common/label.ts b/src/vs/platform/label/common/label.ts index cc27d1363..abe748d84 100644 --- a/src/vs/platform/label/common/label.ts +++ b/src/vs/platform/label/common/label.ts @@ -18,6 +18,8 @@ import { localize } from 'vs/nls'; import { isParent } from 'vs/platform/files/common/files'; import { basename, dirname, join } from 'vs/base/common/paths'; import { Schemas } from 'vs/base/common/network'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; export interface RegisterFormatterEvent { selector: string; @@ -33,6 +35,7 @@ export interface ILabelService { */ getUriLabel(resource: URI, options?: { relative?: boolean, noPrefix?: boolean }): string; getWorkspaceLabel(workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWorkspace), options?: { verbose: boolean }): string; + getHostLabel(): string; registerFormatter(selector: string, formatter: LabelRules): IDisposable; onDidRegisterFormatter: Event; } @@ -66,7 +69,8 @@ export class LabelService implements ILabelService { constructor( @IEnvironmentService private environmentService: IEnvironmentService, - @IWorkspaceContextService private contextService: IWorkspaceContextService + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IWindowService private windowService: IWindowService ) { } get onDidRegisterFormatter(): Event { @@ -155,6 +159,19 @@ export class LabelService implements ILabelService { return localize('workspaceName', "{0} (Workspace)", workspaceName); } + getHostLabel(): string { + if (this.windowService) { + const authority = this.windowService.getConfiguration().remoteAuthority; + if (authority) { + const formatter = this.findFormatter(URI.from({ scheme: REMOTE_HOST_SCHEME, authority })); + if (formatter && formatter.workspace) { + return formatter.workspace.suffix; + } + } + } + return ''; + } + registerFormatter(selector: string, formatter: LabelRules): IDisposable { this.formatters[selector] = formatter; this._onDidRegisterFormatter.fire({ selector, formatter }); diff --git a/src/vs/platform/label/test/label.test.ts b/src/vs/platform/label/test/label.test.ts index 3e282c237..542e75639 100644 --- a/src/vs/platform/label/test/label.test.ts +++ b/src/vs/platform/label/test/label.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { LabelService } from 'vs/platform/label/common/label'; -import { TestEnvironmentService, TestContextService } from 'vs/workbench/test/workbenchTestServices'; +import { TestEnvironmentService, TestContextService, TestWindowService } from 'vs/workbench/test/workbenchTestServices'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { URI } from 'vs/base/common/uri'; import { nativeSep } from 'vs/base/common/paths'; @@ -16,7 +16,7 @@ suite('URI Label', () => { let labelService: LabelService; setup(() => { - labelService = new LabelService(TestEnvironmentService, new TestContextService()); + labelService = new LabelService(TestEnvironmentService, new TestContextService(), new TestWindowService()); }); test('file scheme', function () { diff --git a/src/vs/platform/launch/electron-main/launchService.ts b/src/vs/platform/launch/electron-main/launchService.ts index e79812506..ebc0c2ee4 100644 --- a/src/vs/platform/launch/electron-main/launchService.ts +++ b/src/vs/platform/launch/electron-main/launchService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; import { ILogService } from 'vs/platform/log/common/log'; import { IURLService } from 'vs/platform/url/common/url'; import { IProcessEnvironment, isMacintosh } from 'vs/base/common/platform'; @@ -19,6 +19,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { BrowserWindow } from 'electron'; import { Event } from 'vs/base/common/event'; import { hasArgs } from 'vs/platform/environment/node/argv'; +import { coalesce } from 'vs/base/common/arrays'; export const ID = 'launchService'; export const ILaunchService = createDecorator(ID); @@ -45,15 +46,14 @@ function parseOpenUrl(args: ParsedArgs): URI[] { if (args['open-url'] && args._urls && args._urls.length > 0) { // --open-url must contain -- followed by the url(s) // process.argv is used over args._ as args._ are resolved to file paths at this point - return args._urls + return coalesce(args._urls .map(url => { try { return URI.parse(url); } catch (err) { return null; } - }) - .filter(uri => !!uri); + })); } return []; @@ -67,23 +67,15 @@ export interface ILaunchService { getLogsPath(): TPromise; } -export interface ILaunchChannel extends IChannel { - call(command: 'start', arg: IStartArguments): TPromise; - call(command: 'get-main-process-id', arg: null): TPromise; - call(command: 'get-main-process-info', arg: null): TPromise; - call(command: 'get-logs-path', arg: null): TPromise; - call(command: string, arg: any): TPromise; -} - -export class LaunchChannel implements ILaunchChannel { +export class LaunchChannel implements IServerChannel { constructor(private service: ILaunchService) { } - listen(event: string): Event { - throw new Error('No event found'); + listen(_, event: string): Event { + throw new Error(`Event not found: ${event}`); } - call(command: string, arg: any): TPromise { + call(_, command: string, arg: any): TPromise { switch (command) { case 'start': const { args, userEnv } = arg as IStartArguments; @@ -99,7 +91,7 @@ export class LaunchChannel implements ILaunchChannel { return this.service.getLogsPath(); } - return undefined; + throw new Error(`Call not found: ${command}`); } } @@ -107,7 +99,7 @@ export class LaunchChannelClient implements ILaunchService { _serviceBrand: any; - constructor(private channel: ILaunchChannel) { } + constructor(private channel: IChannel) { } start(args: ParsedArgs, userEnv: IProcessEnvironment): TPromise { return this.channel.call('start', { args, userEnv }); @@ -161,7 +153,7 @@ export class LaunchService implements ILaunchService { } }); - return TPromise.as(null); + return TPromise.as(void 0); } // Otherwise handle in windows service @@ -170,7 +162,7 @@ export class LaunchService implements ILaunchService { private startOpenWindow(args: ParsedArgs, userEnv: IProcessEnvironment): TPromise { const context = !!userEnv['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP; - let usedWindows: ICodeWindow[]; + let usedWindows: ICodeWindow[] = []; // Special case extension development if (!!args.extensionDevelopmentPath) { @@ -238,7 +230,7 @@ export class LaunchService implements ILaunchService { ]).then(() => void 0, () => void 0); } - return TPromise.as(null); + return TPromise.as(void 0); } getMainProcessId(): TPromise { @@ -279,10 +271,13 @@ export class LaunchService implements ILaunchService { if (window.openedFolderUri) { folderURIs.push(window.openedFolderUri); } else if (window.openedWorkspace) { - const rootFolders = this.workspacesMainService.resolveWorkspaceSync(window.openedWorkspace.configPath).folders; - rootFolders.forEach(root => { - folderURIs.push(root.uri); - }); + const resolvedWorkspace = this.workspacesMainService.resolveWorkspaceSync(window.openedWorkspace.configPath); + if (resolvedWorkspace) { + const rootFolders = resolvedWorkspace.folders; + rootFolders.forEach(root => { + folderURIs.push(root.uri); + }); + } } return this.browserWindowToInfo(window.win, folderURIs); diff --git a/src/vs/platform/lifecycle/common/lifecycle.ts b/src/vs/platform/lifecycle/common/lifecycle.ts index 86d0f0111..17108e70f 100644 --- a/src/vs/platform/lifecycle/common/lifecycle.ts +++ b/src/vs/platform/lifecycle/common/lifecycle.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { isThenable } from 'vs/base/common/async'; @@ -149,9 +148,9 @@ export const NullLifecycleService: ILifecycleService = { }; // Shared veto handling across main and renderer -export function handleVetos(vetos: (boolean | Thenable)[], onError: (error: Error) => void): TPromise { +export function handleVetos(vetos: (boolean | Thenable)[], onError: (error: Error) => void): Promise { if (vetos.length === 0) { - return TPromise.as(false); + return Promise.resolve(false); } const promises: Thenable[] = []; @@ -161,7 +160,7 @@ export function handleVetos(vetos: (boolean | Thenable)[], onError: (er // veto, done if (valueOrPromise === true) { - return TPromise.as(true); + return Promise.resolve(true); } if (isThenable(valueOrPromise)) { @@ -176,5 +175,5 @@ export function handleVetos(vetos: (boolean | Thenable)[], onError: (er } } - return TPromise.join(promises).then(() => lazyValue); + return Promise.all(promises).then(() => lazyValue); } diff --git a/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts b/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts index 2662c2cd4..fb0b87eef 100644 --- a/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts +++ b/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { ILifecycleService, WillShutdownEvent, ShutdownReason, StartupKind, LifecyclePhase, handleVetos, LifecyclePhaseToString, ShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -102,7 +101,7 @@ export class LifecycleService extends Disposable implements ILifecycleService { }); } - private handleWillShutdown(reason: ShutdownReason): TPromise { + private handleWillShutdown(reason: ShutdownReason): Promise { const vetos: (boolean | Thenable)[] = []; this._onWillShutdown.fire({ @@ -130,7 +129,7 @@ export class LifecycleService extends Disposable implements ILifecycleService { reason }); - return TPromise.join(joiners).then(() => void 0, err => { + return Promise.all(joiners).then(() => void 0, err => { this.notificationService.error(toErrorMessage(err)); onUnexpectedError(err); }); diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index abb88e40e..fb5ad01b7 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -594,10 +594,14 @@ export class ObjectTreeResourceNavigator extends Disposable { this._register(this.tree.onDidChangeSelection(e => this.onSelection(e))); } - private onFocus(e: ITreeEvent): void { + private onFocus(e: ITreeEvent): void { const focus = this.tree.getFocus(); this.tree.setSelection(focus, e.browserEvent); + if (!e.browserEvent) { + return; + } + const isMouseEvent = e.browserEvent && e.browserEvent instanceof MouseEvent; if (!isMouseEvent) { @@ -605,7 +609,7 @@ export class ObjectTreeResourceNavigator extends Disposable { } } - private onSelection(e: ITreeEvent): void { + private onSelection(e: ITreeEvent): void { if (!e.browserEvent || !(e.browserEvent instanceof MouseEvent)) { return; } @@ -873,9 +877,6 @@ export class WorkbenchObjectTree, TFilterData = void> private hasDoubleSelection: IContextKey; private hasMultiSelection: IContextKey; - private _openOnSingleClick: boolean; - private _useAltAsMultipleSelectionModifier: boolean; - constructor( container: HTMLElement, delegate: IListVirtualDelegate, @@ -903,9 +904,6 @@ export class WorkbenchObjectTree, TFilterData = void> this.hasDoubleSelection = WorkbenchListDoubleSelection.bindTo(this.contextKeyService); this.hasMultiSelection = WorkbenchListMultiSelection.bindTo(this.contextKeyService); - this._openOnSingleClick = useSingleClickToOpen(configurationService); - this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); - this.disposables.push( this.contextKeyService, (listService as ListService).register(this), @@ -923,30 +921,12 @@ export class WorkbenchObjectTree, TFilterData = void> const focus = this.getFocus(); this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0); - }), - configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(openModeSettingKey)) { - this._openOnSingleClick = useSingleClickToOpen(configurationService); - } - - if (e.affectsConfiguration(multiSelectModifierSettingKey)) { - this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); - } }) ); } - get openOnSingleClick(): boolean { - return this._openOnSingleClick; - } - - get useAltAsMultipleSelectionModifier(): boolean { - return this._useAltAsMultipleSelectionModifier; - } - dispose(): void { super.dispose(); - this.disposables = dispose(this.disposables); } } diff --git a/src/vs/platform/localizations/common/localizations.ts b/src/vs/platform/localizations/common/localizations.ts index d1959f4f1..82a18ef35 100644 --- a/src/vs/platform/localizations/common/localizations.ts +++ b/src/vs/platform/localizations/common/localizations.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Event } from 'vs/base/common/event'; export interface ILocalization { @@ -30,7 +29,7 @@ export interface ILocalizationsService { _serviceBrand: any; readonly onDidLanguagesChange: Event; - getLanguageIds(type?: LanguageType): TPromise; + getLanguageIds(type?: LanguageType): Thenable; } export function isValidLocalization(localization: ILocalization): boolean { diff --git a/src/vs/platform/localizations/node/localizations.ts b/src/vs/platform/localizations/node/localizations.ts index d08a4100a..44ad18775 100644 --- a/src/vs/platform/localizations/node/localizations.ts +++ b/src/vs/platform/localizations/node/localizations.ts @@ -8,8 +8,7 @@ import { createHash } from 'crypto'; import { IExtensionManagementService, ILocalExtension, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; import { Disposable } from 'vs/base/common/lifecycle'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { Limiter } from 'vs/base/common/async'; +import { Queue } from 'vs/base/common/async'; import { areSameExtensions, getGalleryExtensionIdFromLocal, getIdFromLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ILogService } from 'vs/platform/log/common/log'; import { isValidLocalization, ILocalizationsService, LanguageType } from 'vs/platform/localizations/common/localizations'; @@ -56,14 +55,14 @@ export class LocalizationsService extends Disposable implements ILocalizationsSe this.extensionManagementService.getInstalled().then(installed => this.cache.update(installed)); } - getLanguageIds(type: LanguageType): TPromise { + getLanguageIds(type: LanguageType): Thenable { if (type === LanguageType.Core) { - return TPromise.as([...systemLanguages]); + return Promise.resolve([...systemLanguages]); } return this.cache.getLanguagePacks() .then(languagePacks => { const languages = type === LanguageType.Contributed ? Object.keys(languagePacks) : [...systemLanguages, ...Object.keys(languagePacks)]; - return TPromise.as(distinct(languages)); + return distinct(languages); }); } @@ -86,7 +85,7 @@ export class LocalizationsService extends Disposable implements ILocalizationsSe } private update(): void { - TPromise.join([this.cache.getLanguagePacks(), this.extensionManagementService.getInstalled()]) + Promise.all([this.cache.getLanguagePacks(), this.extensionManagementService.getInstalled()]) .then(([current, installed]) => this.cache.update(installed) .then(updated => { if (!equals(Object.keys(current), Object.keys(updated))) { @@ -100,7 +99,7 @@ class LanguagePacksCache extends Disposable { private languagePacks: { [language: string]: ILanguagePack } = {}; private languagePacksFilePath: string; - private languagePacksFileLimiter: Limiter; + private languagePacksFileLimiter: Queue; constructor( @IEnvironmentService environmentService: IEnvironmentService, @@ -108,19 +107,19 @@ class LanguagePacksCache extends Disposable { ) { super(); this.languagePacksFilePath = posix.join(environmentService.userDataPath, 'languagepacks.json'); - this.languagePacksFileLimiter = new Limiter(1); + this.languagePacksFileLimiter = new Queue(); } - getLanguagePacks(): TPromise<{ [language: string]: ILanguagePack }> { + getLanguagePacks(): Thenable<{ [language: string]: ILanguagePack }> { // if queue is not empty, fetch from disk if (this.languagePacksFileLimiter.size) { return this.withLanguagePacks() .then(() => this.languagePacks); } - return TPromise.as(this.languagePacks); + return Promise.resolve(this.languagePacks); } - update(extensions: ILocalExtension[]): TPromise<{ [language: string]: ILanguagePack }> { + update(extensions: ILocalExtension[]): Thenable<{ [language: string]: ILanguagePack }> { return this.withLanguagePacks(languagePacks => { Object.keys(languagePacks).forEach(language => languagePacks[language] = undefined); this.createLanguagePacksFromExtensions(languagePacks, ...extensions); @@ -168,11 +167,11 @@ class LanguagePacksCache extends Disposable { } } - private withLanguagePacks(fn: (languagePacks: { [language: string]: ILanguagePack }) => T = () => null): TPromise { + private withLanguagePacks(fn: (languagePacks: { [language: string]: ILanguagePack }) => T = () => null): Thenable { return this.languagePacksFileLimiter.queue(() => { let result: T | null = null; return pfs.readFile(this.languagePacksFilePath, 'utf8') - .then(null, err => err.code === 'ENOENT' ? TPromise.as('{}') : TPromise.wrapError(err)) + .then(null, err => err.code === 'ENOENT' ? Promise.resolve('{}') : Promise.reject(err)) .then<{ [language: string]: ILanguagePack }>(raw => { try { return JSON.parse(raw); } catch (e) { return {}; } }) .then(languagePacks => { result = fn(languagePacks); return languagePacks; }) .then(languagePacks => { diff --git a/src/vs/platform/localizations/node/localizationsIpc.ts b/src/vs/platform/localizations/node/localizationsIpc.ts index 863bb3f80..e4f968b3f 100644 --- a/src/vs/platform/localizations/node/localizationsIpc.ts +++ b/src/vs/platform/localizations/node/localizationsIpc.ts @@ -3,20 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; import { Event, buffer } from 'vs/base/common/event'; import { ILocalizationsService, LanguageType } from 'vs/platform/localizations/common/localizations'; -export interface ILocalizationsChannel extends IChannel { - listen(event: 'onDidLanguagesChange'): Event; - listen(event: string, arg?: any): Event; - - call(command: 'getLanguageIds'): Thenable; - call(command: string, arg?: any): Thenable; -} - -export class LocalizationsChannel implements ILocalizationsChannel { +export class LocalizationsChannel implements IServerChannel { onDidLanguagesChange: Event; @@ -24,19 +15,20 @@ export class LocalizationsChannel implements ILocalizationsChannel { this.onDidLanguagesChange = buffer(service.onDidLanguagesChange, true); } - listen(event: string): Event { + listen(_, event: string): Event { switch (event) { case 'onDidLanguagesChange': return this.onDidLanguagesChange; } - throw new Error('No event found'); + throw new Error(`Event not found: ${event}`); } - call(command: string, arg?: any): Thenable { + call(_, command: string, arg?: any): Thenable { switch (command) { case 'getLanguageIds': return this.service.getLanguageIds(arg); } - return undefined; + + throw new Error(`Call not found: ${command}`); } } @@ -44,11 +36,11 @@ export class LocalizationsChannelClient implements ILocalizationsService { _serviceBrand: any; - constructor(private channel: ILocalizationsChannel) { } + constructor(private channel: IChannel) { } get onDidLanguagesChange(): Event { return this.channel.listen('onDidLanguagesChange'); } - getLanguageIds(type?: LanguageType): TPromise { - return TPromise.wrap(this.channel.call('getLanguageIds', type)); + getLanguageIds(type?: LanguageType): Thenable { + return this.channel.call('getLanguageIds', type); } } \ No newline at end of file diff --git a/src/vs/platform/log/node/logIpc.ts b/src/vs/platform/log/node/logIpc.ts index ca2cb87d6..9e655ee69 100644 --- a/src/vs/platform/log/node/logIpc.ts +++ b/src/vs/platform/log/node/logIpc.ts @@ -3,20 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; import { LogLevel, ILogService, DelegatedLogService } from 'vs/platform/log/common/log'; import { Event, buffer } from 'vs/base/common/event'; -export interface ILogLevelSetterChannel extends IChannel { - listen(event: 'onDidChangeLogLevel'): Event; - listen(event: string, arg?: any): Event; - - call(command: 'setLevel', logLevel: LogLevel): TPromise; - call(command: string, arg?: any): TPromise; -} - -export class LogLevelSetterChannel implements ILogLevelSetterChannel { +export class LogLevelSetterChannel implements IServerChannel { onDidChangeLogLevel: Event; @@ -24,32 +15,33 @@ export class LogLevelSetterChannel implements ILogLevelSetterChannel { this.onDidChangeLogLevel = buffer(service.onDidChangeLogLevel, true); } - listen(event: string): Event { + listen(_, event: string): Event { switch (event) { case 'onDidChangeLogLevel': return this.onDidChangeLogLevel; } - throw new Error('No event found'); + throw new Error(`Event not found: ${event}`); } - call(command: string, arg?: any): TPromise { + call(_, command: string, arg?: any): Thenable { switch (command) { - case 'setLevel': this.service.setLevel(arg); return TPromise.as(null); + case 'setLevel': this.service.setLevel(arg); } - return undefined; + + throw new Error(`Call not found: ${command}`); } } export class LogLevelSetterChannelClient { - constructor(private channel: ILogLevelSetterChannel) { } + constructor(private channel: IChannel) { } get onDidChangeLogLevel(): Event { return this.channel.listen('onDidChangeLogLevel'); } - setLevel(level: LogLevel): TPromise { - return this.channel.call('setLevel', level); + setLevel(level: LogLevel): void { + this.channel.call('setLevel', level); } } diff --git a/src/vs/platform/markers/common/markerService.ts b/src/vs/platform/markers/common/markerService.ts index a3fd07f77..6781b3c09 100644 --- a/src/vs/platform/markers/common/markerService.ts +++ b/src/vs/platform/markers/common/markerService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { isFalsyOrEmpty, isNonEmptyArray } from 'vs/base/common/arrays'; import { Schemas } from 'vs/base/common/network'; import { IDisposable } from 'vs/base/common/lifecycle'; import { isEmptyObject } from 'vs/base/common/types'; @@ -146,10 +146,8 @@ export class MarkerService implements IMarkerService { } remove(owner: string, resources: URI[]): void { - if (!isFalsyOrEmpty(resources)) { - for (const resource of resources) { - this.changeOne(owner, resource, []); - } + for (const resource of resources || []) { + this.changeOne(owner, resource, []); } } @@ -238,7 +236,7 @@ export class MarkerService implements IMarkerService { } // add new markers - if (!isFalsyOrEmpty(data)) { + if (isNonEmptyArray(data)) { // group by resource const groups: { [resource: string]: IMarker[] } = Object.create(null); diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index af8317230..2ab3a42ee 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -7,7 +7,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 } from 'electron'; -import { OpenContext, IRunActionInWindowRequest } from 'vs/platform/windows/common/windows'; +import { OpenContext, IRunActionInWindowRequest, getTitleBarStyle } from 'vs/platform/windows/common/windows'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUpdateService, StateType } from 'vs/platform/update/common/update'; @@ -70,7 +70,9 @@ export class Menubar { this.menubarMenus = Object.create(null); this.keybindings = Object.create(null); - this.restoreCachedMenubarData(); + if (isMacintosh || getTitleBarStyle(this.configurationService, this.environmentService) === 'native') { + this.restoreCachedMenubarData(); + } this.addFallbackHandlers(); @@ -112,7 +114,7 @@ export class Menubar { // Help Menu Items if (product.twitterUrl) { - this.fallbackMenuHandlers['workbench.action.openRequestFeatureUrl'] = () => this.openUrl(product.twitterUrl, 'openTwitterUrl'); + this.fallbackMenuHandlers['workbench.action.openTwitterUrl'] = () => this.openUrl(product.twitterUrl, 'openTwitterUrl'); } if (product.requestFeatureUrl) { @@ -124,7 +126,7 @@ export class Menubar { } if (product.licenseUrl) { - this.addFallbackHandlers['workbench.action.openLicenseUrl'] = () => { + this.fallbackMenuHandlers['workbench.action.openLicenseUrl'] = () => { if (language) { const queryArgChar = product.licenseUrl.indexOf('?') > 0 ? '&' : '?'; this.openUrl(`${product.licenseUrl}${queryArgChar}lang=${language}`, 'openLicenseUrl'); @@ -177,6 +179,10 @@ export class Menubar { } private get currentEnableNativeTabs(): boolean { + if (!isMacintosh) { + return false; + } + let enableNativeTabs = this.configurationService.getValue('window.nativeTabs'); if (typeof enableNativeTabs !== 'boolean') { enableNativeTabs = false; @@ -392,7 +398,7 @@ export class Menubar { private shouldDrawMenu(menuId: string): boolean { // We need to draw an empty menu to override the electron default - if (!isMacintosh && this.configurationService.getValue('window.titleBarStyle') === 'custom') { + if (!isMacintosh && getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { return false; } diff --git a/src/vs/platform/menubar/node/menubarIpc.ts b/src/vs/platform/menubar/node/menubarIpc.ts index 52eaa9966..a5315dbb9 100644 --- a/src/vs/platform/menubar/node/menubarIpc.ts +++ b/src/vs/platform/menubar/node/menubarIpc.ts @@ -2,29 +2,25 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; import { TPromise } from 'vs/base/common/winjs.base'; import { IMenubarService, IMenubarData } from 'vs/platform/menubar/common/menubar'; import { Event } from 'vs/base/common/event'; -export interface IMenubarChannel extends IChannel { - call(command: 'updateMenubar', arg: [number, IMenubarData]): TPromise; - call(command: string, arg?: any): TPromise; -} - -export class MenubarChannel implements IMenubarChannel { +export class MenubarChannel implements IServerChannel { constructor(private service: IMenubarService) { } - listen(event: string, arg?: any): Event { - throw new Error('No events'); + listen(_, event: string): Event { + throw new Error(`Event not found: ${event}`); } - call(command: string, arg?: any): TPromise { + call(_, command: string, arg?: any): TPromise { switch (command) { case 'updateMenubar': return this.service.updateMenubar(arg[0], arg[1]); } - return undefined; + + throw new Error(`Call not found: ${command}`); } } @@ -32,7 +28,7 @@ export class MenubarChannelClient implements IMenubarService { _serviceBrand: any; - constructor(private channel: IMenubarChannel) { } + constructor(private channel: IChannel) { } updateMenubar(windowId: number, menuData: IMenubarData): TPromise { return this.channel.call('updateMenubar', [windowId, menuData]); diff --git a/src/vs/platform/node/zip.ts b/src/vs/platform/node/zip.ts index 0d8922cc6..fc1a1a87a 100644 --- a/src/vs/platform/node/zip.ts +++ b/src/vs/platform/node/zip.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import * as path from 'path'; import { createWriteStream, WriteStream } from 'fs'; import { Readable } from 'stream'; -import { nfcall, ninvoke, SimpleThrottler, createCancelablePromise } from 'vs/base/common/async'; +import { nfcall, ninvoke, Sequencer, createCancelablePromise } from 'vs/base/common/async'; import { mkdirp, rimraf } from 'vs/base/node/pfs'; import { open as _openZip, Entry, ZipFile } from 'yauzl'; import * as yazl from 'yazl'; @@ -33,10 +33,10 @@ export type ExtractErrorType = 'CorruptZip' | 'Incomplete'; export class ExtractError extends Error { - readonly type: ExtractErrorType; + readonly type?: ExtractErrorType; readonly cause: Error; - constructor(type: ExtractErrorType, cause: Error) { + constructor(type: ExtractErrorType | undefined, cause: Error) { let message = cause.message; switch (type) { @@ -62,7 +62,7 @@ function toExtractError(err: Error): ExtractError { return err; } - let type: ExtractErrorType = void 0; + let type: ExtractErrorType | undefined = void 0; if (/end of central directory record signature not found/.test(err.message)) { type = 'CorruptZip'; @@ -94,7 +94,7 @@ function extractEntry(stream: Readable, fileName: string, mode: number, targetPa try { istream = createWriteStream(targetFileName, { mode }); - istream.once('close', () => c(null)); + istream.once('close', () => c()); istream.once('error', e); stream.once('error', e); stream.pipe(istream); @@ -105,7 +105,7 @@ function extractEntry(stream: Readable, fileName: string, mode: number, targetPa } function extractZip(zipfile: ZipFile, targetPath: string, options: IOptions, logService: ILogService, token: CancellationToken): Promise { - let last = createCancelablePromise(() => Promise.resolve(null)); + let last = createCancelablePromise(() => Promise.resolve()); let extractedEntriesCount = 0; once(token.onCancellationRequested)(() => { @@ -115,7 +115,7 @@ function extractZip(zipfile: ZipFile, targetPath: string, options: IOptions, log }); return new Promise((c, e) => { - const throttler = new SimpleThrottler(); + const throttler = new Sequencer(); const readNextEntry = (token: CancellationToken) => { if (token.isCancellationRequested) { @@ -129,7 +129,7 @@ function extractZip(zipfile: ZipFile, targetPath: string, options: IOptions, log zipfile.once('error', e); zipfile.once('close', () => last.then(() => { if (token.isCancellationRequested || zipfile.entryCount === extractedEntriesCount) { - c(null); + c(); } else { e(new ExtractError('Incomplete', new Error(nls.localize('incompleteExtract', "Incomplete. Found {0} of {1} entries", extractedEntriesCount, zipfile.entryCount)))); } @@ -158,7 +158,7 @@ function extractZip(zipfile: ZipFile, targetPath: string, options: IOptions, log const stream = ninvoke(zipfile, zipfile.openReadStream, 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)); }); }); } @@ -177,7 +177,13 @@ export interface IFile { export function zip(zipPath: string, files: IFile[]): Promise { return new Promise((c, e) => { const zip = new yazl.ZipFile(); - files.forEach(f => f.contents ? zip.addBuffer(typeof f.contents === 'string' ? Buffer.from(f.contents, 'utf8') : f.contents, f.path) : zip.addFile(f.localPath, f.path)); + files.forEach(f => { + if (f.contents) { + zip.addBuffer(typeof f.contents === 'string' ? Buffer.from(f.contents, 'utf8') : f.contents, f.path); + } else if (f.localPath) { + zip.addFile(f.localPath, f.path); + } + }); zip.end(); const zipStream = createWriteStream(zipPath); diff --git a/src/vs/platform/quickinput/common/quickInput.ts b/src/vs/platform/quickinput/common/quickInput.ts index 20e13c85c..e273c0918 100644 --- a/src/vs/platform/quickinput/common/quickInput.ts +++ b/src/vs/platform/quickinput/common/quickInput.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { TPromise } from 'vs/base/common/winjs.base'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { URI } from 'vs/base/common/uri'; @@ -76,7 +75,7 @@ export interface IPickOptions { /** * an optional property for the item to focus initially. */ - activeItem?: TPromise | T; + activeItem?: Promise | T; onKeyMods?: (keyMods: IKeyMods) => void; onDidFocus?: (entry: T) => void; @@ -231,14 +230,14 @@ export interface IQuickInputService { /** * Opens the quick input box for selecting items and returns a promise with the user selected item(s) if any. */ - pick(picks: TPromise[]> | QuickPickInput[], options?: IPickOptions & { canPickMany: true }, token?: CancellationToken): TPromise; - pick(picks: TPromise[]> | QuickPickInput[], options?: IPickOptions & { canPickMany: false }, token?: CancellationToken): TPromise; - pick(picks: TPromise[]> | QuickPickInput[], options?: Omit, 'canPickMany'>, token?: CancellationToken): TPromise; + pick(picks: Thenable[]> | QuickPickInput[], options?: IPickOptions & { canPickMany: true }, token?: CancellationToken): Promise; + pick(picks: Thenable[]> | QuickPickInput[], options?: IPickOptions & { canPickMany: false }, token?: CancellationToken): Promise; + pick(picks: Thenable[]> | QuickPickInput[], options?: Omit, 'canPickMany'>, token?: CancellationToken): Promise; /** * Opens the quick input box for text input and returns a promise with the user typed value if any. */ - input(options?: IInputOptions, token?: CancellationToken): TPromise; + input(options?: IInputOptions, token?: CancellationToken): Promise; backButton: IQuickInputButton; @@ -251,9 +250,9 @@ export interface IQuickInputService { navigate(next: boolean, quickNavigate?: IQuickNavigateConfiguration): void; - accept(): TPromise; + accept(): Promise; - back(): TPromise; + back(): Promise; - cancel(): TPromise; + cancel(): Promise; } diff --git a/src/vs/platform/remote/common/remoteAuthorityResolver.ts b/src/vs/platform/remote/common/remoteAuthorityResolver.ts new file mode 100644 index 000000000..ee8d76667 --- /dev/null +++ b/src/vs/platform/remote/common/remoteAuthorityResolver.ts @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IProgressStep } from 'vs/platform/progress/common/progress'; +import { Event } from 'vs/base/common/event'; + +export const IRemoteAuthorityResolverService = createDecorator('remoteAuthorityResolverService'); + +export interface ResolvedAuthority { + readonly authority: string; + readonly host: string; + readonly port: number; +} + +export type IResolvingProgressEvent = + { type: 'progress', authority: string, data: IProgressStep } + | { type: 'finished', authority: string } + | { type: 'output', authority: string, data: { channel: string, message: string; isErr?: boolean; } }; + +export interface IRemoteAuthorityResolverService { + + _serviceBrand: any; + + onResolvingProgress: Event; + + resolveAuthority(authority: string): Thenable; + + getRemoteAuthorityResolver(authority: string): Thenable; +} + +export interface IRemoteAuthorityResolver { + label: string; + path: string; + authorityPrefix: string; + syncExtensions?: boolean; +} diff --git a/src/typings/electron-proxy-agent.d.ts b/src/vs/platform/remote/common/remoteHosts.ts similarity index 62% rename from src/typings/electron-proxy-agent.d.ts rename to src/vs/platform/remote/common/remoteHosts.ts index 36b5e0922..29e16a549 100644 --- a/src/typings/electron-proxy-agent.d.ts +++ b/src/vs/platform/remote/common/remoteHosts.ts @@ -3,4 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -declare module 'electron-proxy-agent'; +import { URI } from 'vs/base/common/uri'; + +export const REMOTE_HOST_SCHEME = 'vscode-remote'; + +export function getRemoteAuthority(uri: URI) { + return uri.scheme === REMOTE_HOST_SCHEME ? uri.authority : void 0; +} \ No newline at end of file diff --git a/src/vs/platform/remote/node/remoteAgentConnection.ts b/src/vs/platform/remote/node/remoteAgentConnection.ts new file mode 100644 index 000000000..852ae1d20 --- /dev/null +++ b/src/vs/platform/remote/node/remoteAgentConnection.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TPromise } from 'vs/base/common/winjs.base'; +import { Client, Protocol } from 'vs/base/parts/ipc/node/ipc.net'; +import { IExtensionHostDebugParams } from 'vs/platform/environment/common/environment'; + +export interface RemoteAgentConnectionContext { + remoteAuthority: string; + clientId: string; +} + +export function connectRemoteAgentManagement(remoteAuthority: string, host: string, port: number, clientId: string): TPromise> { + throw new Error(`Not implemented`); +} + +export interface IExtensionHostConnectionResult { + protocol: Protocol; + debugPort?: number; +} + +export function connectRemoteAgentExtensionHost(host: string, port: number, debugArguments: IExtensionHostDebugParams): TPromise { + throw new Error(`Not implemented`); +} diff --git a/src/vs/platform/remote/node/remoteAgentFileSystemChannel.ts b/src/vs/platform/remote/node/remoteAgentFileSystemChannel.ts new file mode 100644 index 000000000..c479a3935 --- /dev/null +++ b/src/vs/platform/remote/node/remoteAgentFileSystemChannel.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { generateUuid } from 'vs/base/common/uuid'; +import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { FileChangeType, FileDeleteOptions, FileOverwriteOptions, FileSystemProviderCapabilities, FileType, FileWriteOptions, IFileChange, IFileSystemProvider, IStat, IWatchOptions } from 'vs/platform/files/common/files'; + +export const REMOTE_FILE_SYSTEM_CHANNEL_NAME = 'remotefilesystem'; + +export interface IFileChangeDto { + resource: UriComponents; + type: FileChangeType; +} + +export class RemoteExtensionsFileSystemProvider extends Disposable implements IFileSystemProvider { + + private readonly _session: string; + private readonly _channel: IChannel; + + private readonly _onDidChange = this._register(new Emitter()); + readonly onDidChangeFile: Event = this._onDidChange.event; + + public capabilities: FileSystemProviderCapabilities; + private readonly _onDidChangeCapabilities = this._register(new Emitter()); + readonly onDidChangeCapabilities: Event = this._onDidChangeCapabilities.event; + + constructor(channel: IChannel) { + super(); + this._session = generateUuid(); + this._channel = channel; + + this.setCaseSensitive(true); + + this._channel.listen('filechange', [this._session])((events) => { + this._onDidChange.fire(events.map(RemoteExtensionsFileSystemProvider._createFileChange)); + }); + } + + dispose(): void { + super.dispose(); + } + + setCaseSensitive(isCaseSensitive: boolean) { + let capabilities = ( + FileSystemProviderCapabilities.FileReadWrite + | FileSystemProviderCapabilities.FileFolderCopy + ); + if (isCaseSensitive) { + capabilities |= FileSystemProviderCapabilities.PathCaseSensitive; + } + this.capabilities = capabilities; + this._onDidChangeCapabilities.fire(void 0); + } + + watch(resource: URI, opts: IWatchOptions): IDisposable { + const req = Math.random(); + this._channel.call('watch', [this._session, req, resource, opts]); + return toDisposable(() => { + this._channel.call('unwatch', [this._session, req]); + }); + } + + private static _createFileChange(dto: IFileChangeDto): IFileChange { + return { resource: URI.revive(dto.resource), type: dto.type }; + } + + // --- forwarding calls + + private static _asBuffer(data: Uint8Array): Buffer { + return Buffer.isBuffer(data) ? data : Buffer.from(data.buffer, data.byteOffset, data.byteLength); + } + + stat(resource: URI): Thenable { + return this._channel.call('stat', [resource]); + } + + readFile(resource: URI): Thenable { + return this._channel.call('readFile', [resource]); + } + + writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Thenable { + const contents = RemoteExtensionsFileSystemProvider._asBuffer(content); + return this._channel.call('writeFile', [resource, contents, opts]); + } + + delete(resource: URI, opts: FileDeleteOptions): Thenable { + return this._channel.call('delete', [resource, opts]); + } + + mkdir(resource: URI): Thenable { + return this._channel.call('mkdir', [resource]); + } + + readdir(resource: URI): Thenable<[string, FileType][]> { + return this._channel.call('readdir', [resource]); + } + + rename(resource: URI, target: URI, opts: FileOverwriteOptions): Thenable { + return this._channel.call('rename', [resource, target, opts]); + } + + copy(resource: URI, target: URI, opts: FileOverwriteOptions): Thenable { + return this._channel.call('copy', [resource, target, opts]); + } +} diff --git a/src/vs/platform/remote/node/remoteAuthorityResolverChannel.ts b/src/vs/platform/remote/node/remoteAuthorityResolverChannel.ts new file mode 100644 index 000000000..1e0be3af9 --- /dev/null +++ b/src/vs/platform/remote/node/remoteAuthorityResolverChannel.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { Event, buffer } from 'vs/base/common/event'; +import { ResolvedAuthority, IResolvingProgressEvent, IRemoteAuthorityResolverService, IRemoteAuthorityResolver } from 'vs/platform/remote/common/remoteAuthorityResolver'; + +export class RemoteAuthorityResolverChannel implements IServerChannel { + + onResolvingProgress: Event; + + constructor(private service: IRemoteAuthorityResolverService) { + this.onResolvingProgress = buffer(service.onResolvingProgress, true); + } + + listen(_, event: string): Event { + switch (event) { + case 'onResolvingProgress': return this.onResolvingProgress; + } + + throw new Error('Invalid listen'); + } + + call(_, command: string, args?: any): Thenable { + switch (command) { + case 'resolveAuthority': return this.service.resolveAuthority(args[0]); + case 'getRemoteAuthorityResolver': return this.service.getRemoteAuthorityResolver(args[0]); + } + + throw new Error('Invalid call'); + } +} + +export class RemoteAuthorityResolverChannelClient implements IRemoteAuthorityResolverService { + + _serviceBrand: any; + + private _resolveAuthorityCache: { [authority: string]: Thenable; }; + get onResolvingProgress(): Event { return buffer(this.channel.listen('onResolvingProgress'), true); } + + constructor(private channel: IChannel) { + this._resolveAuthorityCache = Object.create(null); + } + + resolveAuthority(authority: string): Thenable { + if (!this._resolveAuthorityCache[authority]) { + this._resolveAuthorityCache[authority] = this._resolveAuthority(authority); + } + return this._resolveAuthorityCache[authority]; + } + + getRemoteAuthorityResolver(authority: string): Thenable { + return this.channel.call('getRemoteAuthorityResolver', [authority]); + } + + private _resolveAuthority(authority: string): Thenable { + if (authority.indexOf('+') >= 0) { + return this.channel.call('resolveAuthority', [authority]); + } else { + const [host, strPort] = authority.split(':'); + const port = parseInt(strPort, 10); + return Promise.resolve({ authority, host, port }); + } + } + +} diff --git a/src/vs/platform/remote/node/remoteAuthorityResolverService.ts b/src/vs/platform/remote/node/remoteAuthorityResolverService.ts new file mode 100644 index 000000000..f7dc1b90d --- /dev/null +++ b/src/vs/platform/remote/node/remoteAuthorityResolverService.ts @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IRemoteAuthorityResolverService, ResolvedAuthority, IResolvingProgressEvent, IRemoteAuthorityResolver } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ILogService } from 'vs/platform/log/common/log'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Emitter, Event } from 'vs/base/common/event'; +import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; + +export class RemoteAuthorityResolverService extends Disposable implements IRemoteAuthorityResolverService { + + _serviceBrand: any; + + private _onResolvingProgress: Emitter = this._register(new Emitter()); + readonly onResolvingProgress: Event = this._onResolvingProgress.event; + + constructor( + @IEnvironmentService environmentService: IEnvironmentService, + @IConfigurationService configurationService: IConfigurationService, + @ILogService logService: ILogService, + @IExtensionManagementService extensionManagementService: IExtensionManagementService + ) { + super(); + } + + async resolveAuthority(authority: string): Promise { + throw new Error(`Not implemented`); + } + + async getRemoteAuthorityResolver(authority: string): Promise { + throw new Error(`Not implemented`); + } +} diff --git a/src/vs/platform/search/common/replace.ts b/src/vs/platform/search/common/replace.ts index d5100b6fc..564c66da7 100644 --- a/src/vs/platform/search/common/replace.ts +++ b/src/vs/platform/search/common/replace.ts @@ -21,14 +21,18 @@ export class ReplacePattern { let parseParameters: boolean; if (typeof arg2 === 'boolean') { parseParameters = arg2; + this._regExp = arg3; + } else { searchPatternInfo = arg2; - parseParameters = searchPatternInfo.isRegExp; + parseParameters = !!searchPatternInfo.isRegExp; + this._regExp = strings.createRegExp(searchPatternInfo.pattern, !!searchPatternInfo.isRegExp, { matchCase: searchPatternInfo.isCaseSensitive, wholeWord: searchPatternInfo.isWordMatch, multiline: searchPatternInfo.isMultiline, global: false }); } + if (parseParameters) { this.parseReplaceString(replaceString); } - this._regExp = arg3 ? arg3 : strings.createRegExp(searchPatternInfo.pattern, searchPatternInfo.isRegExp, { matchCase: searchPatternInfo.isCaseSensitive, wholeWord: searchPatternInfo.isWordMatch, multiline: searchPatternInfo.isMultiline, global: false }); + if (this._regExp.global) { this._regExp = strings.createRegExp(this._regExp.source, true, { matchCase: !this._regExp.ignoreCase, wholeWord: false, multiline: this._regExp.multiline, global: false }); } @@ -50,7 +54,7 @@ export class ReplacePattern { * Returns the replace string for the first match in the given text. * If text has no matches then returns null. */ - public getReplaceString(text: string): string { + public getReplaceString(text: string): string | null { this._regExp.lastIndex = 0; let match = this._regExp.exec(text); if (match) { @@ -63,6 +67,7 @@ export class ReplacePattern { } return this.pattern; } + return null; } diff --git a/src/vs/platform/search/common/search.ts b/src/vs/platform/search/common/search.ts index 599c2dd2b..08e09a63d 100644 --- a/src/vs/platform/search/common/search.ts +++ b/src/vs/platform/search/common/search.ts @@ -3,17 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { mapArrayOrNot } from 'vs/base/common/arrays'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { Event } from 'vs/base/common/event'; import * as glob from 'vs/base/common/glob'; import { IDisposable } from 'vs/base/common/lifecycle'; import * as objects from 'vs/base/common/objects'; import * as paths from 'vs/base/common/paths'; -import { URI as uri, UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { getNLines } from 'vs/base/common/strings'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { IFilesConfiguration } from 'vs/platform/files/common/files'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { getNLines } from 'vs/base/common/strings'; export const VIEW_ID = 'workbench.view.search'; @@ -25,10 +25,10 @@ export const ISearchService = createDecorator('searchService'); */ export interface ISearchService { _serviceBrand: any; - textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): TPromise; - fileSearch(query: IFileQuery, token?: CancellationToken): TPromise; + textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): Promise; + fileSearch(query: IFileQuery, token?: CancellationToken): Promise; extendQuery(query: ITextQuery | IFileQuery): void; - clearCache(cacheKey: string): TPromise; + clearCache(cacheKey: string): Thenable; registerSearchResultProvider(scheme: string, type: SearchProviderType, provider: ISearchResultProvider): IDisposable; } @@ -57,12 +57,12 @@ export const enum SearchProviderType { } export interface ISearchResultProvider { - textSearch(query: ITextQuery, onProgress?: (p: ISearchProgressItem) => void, token?: CancellationToken): TPromise; - fileSearch(query: IFileQuery, token?: CancellationToken): TPromise; - clearCache(cacheKey: string): TPromise; + textSearch(query: ITextQuery, onProgress?: (p: ISearchProgressItem) => void, token?: CancellationToken): Promise; + fileSearch(query: IFileQuery, token?: CancellationToken): Promise; + clearCache(cacheKey: string): Thenable; } -export interface IFolderQuery { +export interface IFolderQuery { folder: U; excludePattern?: glob.IExpression; includePattern?: glob.IExpression; @@ -109,11 +109,15 @@ export interface ITextQueryProps extends ICommonQueryPr previewOptions?: ITextSearchPreviewOptions; maxFileSize?: number; usePCRE2?: boolean; + afterContext?: number; + beforeContext?: number; + + userDisabledExcludesAndIgnoreFiles?: boolean; } -export type IFileQuery = IFileQueryProps; +export type IFileQuery = IFileQueryProps; export type IRawFileQuery = IFileQueryProps; -export type ITextQuery = ITextQueryProps; +export type ITextQuery = ITextQueryProps; export type IRawTextQuery = ITextQueryProps; export type IRawQuery = IRawTextQuery | IRawFileQuery; @@ -149,9 +153,9 @@ export interface IExtendedExtensionSearchOptions { usePCRE2?: boolean; } -export interface IFileMatch { +export interface IFileMatch { resource?: U; - matches?: ITextSearchResult[]; + results?: ITextSearchResult[]; } export type IRawFileMatch2 = IFileMatch; @@ -170,15 +174,27 @@ export interface ISearchRange { export interface ITextSearchResultPreview { text: string; - match: ISearchRange; + matches: ISearchRange | ISearchRange[]; } -export interface ITextSearchResult { - uri?: uri; - range: ISearchRange; +export interface ITextSearchMatch { + uri?: URI; + ranges: ISearchRange | ISearchRange[]; preview: ITextSearchResultPreview; } +export interface ITextSearchContext { + uri?: URI; + text: string; + lineNumber: number; +} + +export type ITextSearchResult = ITextSearchMatch | ITextSearchContext; + +export function resultIsMatch(result: ITextSearchResult): result is ITextSearchMatch { + return !!(result).preview; +} + export interface IProgress { total?: number; worked?: number; @@ -241,20 +257,20 @@ export interface IFileIndexProviderStats { } export class FileMatch implements IFileMatch { - public matches: ITextSearchResult[] = []; - constructor(public resource: uri) { + public results: ITextSearchResult[] = []; + constructor(public resource: URI) { // empty } } -export class TextSearchResult implements ITextSearchResult { - range: ISearchRange; +export class TextSearchMatch implements ITextSearchMatch { + ranges: ISearchRange | ISearchRange[]; preview: ITextSearchResultPreview; - constructor(text: string, range: ISearchRange, previewOptions?: ITextSearchPreviewOptions) { - this.range = range; + constructor(text: string, range: ISearchRange | ISearchRange[], previewOptions?: ITextSearchPreviewOptions) { + this.ranges = range; - if (previewOptions && previewOptions.matchLines === 1) { + if (previewOptions && previewOptions.matchLines === 1 && !Array.isArray(range)) { // 1 line preview requested text = getNLines(text, previewOptions.matchLines); const leadingChars = Math.floor(previewOptions.charsPerLine / 5); @@ -267,13 +283,15 @@ export class TextSearchResult implements ITextSearchResult { this.preview = { text: previewText, - match: new OneLineRange(0, range.startColumn - previewStart, endColInPreview) + matches: new OneLineRange(0, range.startColumn - previewStart, endColInPreview) }; } else { - // n line or no preview requested + const firstMatchLine = Array.isArray(range) ? range[0].startLineNumber : range.startLineNumber; + + // n line, no preview requested, or multiple matches in the preview this.preview = { text, - match: new SearchRange(0, range.startColumn, range.endLineNumber - range.startLineNumber, range.endColumn) + matches: mapArrayOrNot(range, r => new SearchRange(r.startLineNumber - firstMatchLine, r.startColumn, r.endLineNumber - firstMatchLine, r.endColumn)) }; } } @@ -302,7 +320,7 @@ export class OneLineRange extends SearchRange { export interface ISearchConfigurationProperties { exclude: glob.IExpression; useRipgrep: boolean; - disableRipgrep: boolean; + useLegacySearch: boolean; /** * Use ignore file for file search. */ @@ -344,7 +362,7 @@ export function getExcludes(configuration: ISearchConfiguration): glob.IExpressi return allExcludes; } -export function pathIncludedInQuery(queryProps: ICommonQueryProps, fsPath: string): boolean { +export function pathIncludedInQuery(queryProps: ICommonQueryProps, fsPath: string): boolean { if (queryProps.excludePattern && glob.match(queryProps.excludePattern, fsPath)) { return false; } @@ -367,3 +385,32 @@ export function pathIncludedInQuery(queryProps: ICommonQueryProps, fsPath: return true; } + +export enum SearchErrorCode { + unknownEncoding = 1, + regexParseError, + globParseError, + invalidLiteral, + rgProcessError, + other +} + +export class SearchError extends Error { + constructor(message: string, readonly code?: SearchErrorCode) { + super(message); + } +} + +export function deserializeSearchError(errorMsg: string): SearchError { + try { + const details = JSON.parse(errorMsg); + return new SearchError(details.message, details.code); + } catch (e) { + return new SearchError(errorMsg, SearchErrorCode.other); + } +} + +export function serializeSearchError(searchError: SearchError): Error { + const details = { message: searchError.message, code: searchError.code }; + return new Error(JSON.stringify(details)); +} diff --git a/src/vs/platform/search/test/common/search.test.ts b/src/vs/platform/search/test/common/search.test.ts index 25b89a956..147c0cfc6 100644 --- a/src/vs/platform/search/test/common/search.test.ts +++ b/src/vs/platform/search/test/common/search.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { ITextSearchPreviewOptions, OneLineRange, TextSearchResult, SearchRange } from 'vs/platform/search/common/search'; +import { ITextSearchPreviewOptions, OneLineRange, TextSearchMatch, SearchRange } from 'vs/platform/search/common/search'; suite('TextSearchResult', () => { @@ -12,58 +12,58 @@ suite('TextSearchResult', () => { charsPerLine: 100 }; - function assertPreviewRangeText(text: string, result: TextSearchResult): void { + function assertPreviewRangeText(text: string, result: TextSearchMatch): void { assert.equal( - result.preview.text.substring(result.preview.match.startColumn, result.preview.match.endColumn), + result.preview.text.substring((result.preview.matches).startColumn, (result.preview.matches).endColumn), text); } test('empty without preview options', () => { const range = new OneLineRange(5, 0, 0); - const result = new TextSearchResult('', range); - assert.deepEqual(result.range, range); + const result = new TextSearchMatch('', range); + assert.deepEqual(result.ranges, range); assertPreviewRangeText('', result); }); test('empty with preview options', () => { const range = new OneLineRange(5, 0, 0); - const result = new TextSearchResult('', range, previewOptions1); - assert.deepEqual(result.range, range); + const result = new TextSearchMatch('', range, previewOptions1); + assert.deepEqual(result.ranges, range); assertPreviewRangeText('', result); }); test('short without preview options', () => { const range = new OneLineRange(5, 4, 7); - const result = new TextSearchResult('foo bar', range); - assert.deepEqual(result.range, range); + const result = new TextSearchMatch('foo bar', range); + assert.deepEqual(result.ranges, range); assertPreviewRangeText('bar', result); }); test('short with preview options', () => { const range = new OneLineRange(5, 4, 7); - const result = new TextSearchResult('foo bar', range, previewOptions1); - assert.deepEqual(result.range, range); + const result = new TextSearchMatch('foo bar', range, previewOptions1); + assert.deepEqual(result.ranges, range); assertPreviewRangeText('bar', result); }); test('leading', () => { const range = new OneLineRange(5, 25, 28); - const result = new TextSearchResult('long text very long text foo', range, previewOptions1); - assert.deepEqual(result.range, range); + const result = new TextSearchMatch('long text very long text foo', range, previewOptions1); + assert.deepEqual(result.ranges, range); assertPreviewRangeText('foo', result); }); test('trailing', () => { const range = new OneLineRange(5, 0, 3); - const result = new TextSearchResult('foo long text very long text long text very long text long text very long text long text very long text long text very long text', range, previewOptions1); - assert.deepEqual(result.range, range); + const result = new TextSearchMatch('foo long text very long text long text very long text long text very long text long text very long text long text very long text', range, previewOptions1); + assert.deepEqual(result.ranges, range); assertPreviewRangeText('foo', result); }); test('middle', () => { const range = new OneLineRange(5, 30, 33); - const result = new TextSearchResult('long text very long text long foo text very long text long text very long text long text very long text long text very long text', range, previewOptions1); - assert.deepEqual(result.range, range); + const result = new TextSearchMatch('long text very long text long foo text very long text long text very long text long text very long text long text very long text', range, previewOptions1); + assert.deepEqual(result.ranges, range); assertPreviewRangeText('foo', result); }); @@ -74,8 +74,8 @@ suite('TextSearchResult', () => { }; const range = new OneLineRange(0, 4, 7); - const result = new TextSearchResult('foo bar', range, previewOptions); - assert.deepEqual(result.range, range); + const result = new TextSearchMatch('foo bar', range, previewOptions); + assert.deepEqual(result.ranges, range); assertPreviewRangeText('b', result); }); @@ -86,8 +86,8 @@ suite('TextSearchResult', () => { }; const range = new SearchRange(5, 4, 6, 3); - const result = new TextSearchResult('foo bar\nfoo bar', range, previewOptions); - assert.deepEqual(result.range, range); + const result = new TextSearchMatch('foo bar\nfoo bar', range, previewOptions); + assert.deepEqual(result.ranges, range); assertPreviewRangeText('bar', result); }); diff --git a/src/vs/platform/state/common/state.ts b/src/vs/platform/state/common/state.ts index db914cc44..b6656dca4 100644 --- a/src/vs/platform/state/common/state.ts +++ b/src/vs/platform/state/common/state.ts @@ -10,7 +10,8 @@ export const IStateService = createDecorator('stateService'); export interface IStateService { _serviceBrand: any; - getItem(key: string, defaultValue?: T): T; + getItem(key: string, defaultValue: T): T; + getItem(key: string, defaultValue?: T): T | undefined; setItem(key: string, data: any): void; removeItem(key: string): void; } \ No newline at end of file diff --git a/src/vs/platform/state/node/stateService.ts b/src/vs/platform/state/node/stateService.ts index 57c369a92..bc513bbdb 100644 --- a/src/vs/platform/state/node/stateService.ts +++ b/src/vs/platform/state/node/stateService.ts @@ -13,19 +13,20 @@ import { ILogService } from 'vs/platform/log/common/log'; export class FileStorage { - private database: object | null = null; + private _lazyDatabase: object | null = null; constructor(private dbPath: string, private onError: (error) => void) { } - private ensureLoaded(): void { - if (!this.database) { - this.database = this.loadSync(); + private get database(): object { + if (!this._lazyDatabase) { + this._lazyDatabase = this.loadSync(); } + return this._lazyDatabase; } - getItem(key: string, defaultValue?: T): T { - this.ensureLoaded(); - + getItem(key: string, defaultValue: T): T; + getItem(key: string, defaultValue?: T): T | undefined; + getItem(key: string, defaultValue?: T): T | undefined { const res = this.database[key]; if (isUndefinedOrNull(res)) { return defaultValue; @@ -35,8 +36,6 @@ export class FileStorage { } setItem(key: string, data: any): void { - this.ensureLoaded(); - // Remove an item when it is undefined or null if (isUndefinedOrNull(data)) { return this.removeItem(key); @@ -54,8 +53,6 @@ export class FileStorage { } removeItem(key: string): void { - this.ensureLoaded(); - // Only update if the key is actually present (not undefined) if (!isUndefined(this.database[key])) { this.database[key] = void 0; @@ -94,7 +91,9 @@ export class StateService implements IStateService { this.fileStorage = new FileStorage(path.join(environmentService.userDataPath, 'storage.json'), error => logService.error(error)); } - getItem(key: string, defaultValue?: T): T { + getItem(key: string, defaultValue: T): T; + getItem(key: string, defaultValue: T | undefined): T | undefined; + getItem(key: string, defaultValue?: T): T | undefined { return this.fileStorage.getItem(key, defaultValue); } diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index b45a0009b..49782e4cd 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -4,7 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { Event } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { isUndefinedOrNull } from 'vs/base/common/types'; export const IStorageService = createDecorator('storageService'); @@ -91,32 +93,88 @@ export interface IWorkspaceStorageChangeEvent { scope: StorageScope; } -export const NullStorageService: IStorageService = new class implements IStorageService { +export const InMemoryStorageService: IStorageService = new class extends Disposable implements IStorageService { _serviceBrand = undefined; - onDidChangeStorage = Event.None; - onWillSaveState = Event.None; + private _onDidChangeStorage: Emitter = this._register(new Emitter()); + get onDidChangeStorage(): Event { return this._onDidChangeStorage.event; } + + readonly onWillSaveState = Event.None; + + private globalCache: Map = new Map(); + private workspaceCache: Map = new Map(); + + private getCache(scope: StorageScope): Map { + return scope === StorageScope.GLOBAL ? this.globalCache : this.workspaceCache; + } get(key: string, scope: StorageScope, fallbackValue: string): string; get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined { - return fallbackValue; + const value = this.getCache(scope).get(key); + + if (isUndefinedOrNull(value)) { + return fallbackValue; + } + + return value; } getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined { - return fallbackValue; + const value = this.getCache(scope).get(key); + + if (isUndefinedOrNull(value)) { + return fallbackValue; + } + + return value === 'true'; } getInteger(key: string, scope: StorageScope, fallbackValue: number): number; getInteger(key: string, scope: StorageScope, fallbackValue?: number): number | undefined { - return fallbackValue; + const value = this.getCache(scope).get(key); + + if (isUndefinedOrNull(value)) { + return fallbackValue; + } + + return parseInt(value, 10); } store(key: string, value: any, scope: StorageScope): Promise { + + // We remove the key for undefined/null values + if (isUndefinedOrNull(value)) { + return this.remove(key, scope); + } + + // Otherwise, convert to String and store + const valueStr = String(value); + + // Return early if value already set + const currentValue = this.getCache(scope).get(key); + if (currentValue === valueStr) { + return Promise.resolve(); + } + + // Update in cache + this.getCache(scope).set(key, valueStr); + + // Events + this._onDidChangeStorage.fire({ scope, key }); + return Promise.resolve(); } remove(key: string, scope: StorageScope): Promise { + const wasDeleted = this.getCache(scope).delete(key); + if (!wasDeleted) { + return Promise.resolve(); // Return early if value already deleted + } + + // Events + this._onDidChangeStorage.fire({ scope, key }); + return Promise.resolve(); } }; \ No newline at end of file diff --git a/src/vs/platform/storage/common/storageLegacyMigration.ts b/src/vs/platform/storage/common/storageLegacyMigration.ts index a52346942..5c39dffb4 100644 --- a/src/vs/platform/storage/common/storageLegacyMigration.ts +++ b/src/vs/platform/storage/common/storageLegacyMigration.ts @@ -5,7 +5,6 @@ import { StorageLegacyService, IStorageLegacy } from 'vs/platform/storage/common/storageLegacyService'; import { endsWith, startsWith, rtrim } from 'vs/base/common/strings'; -import { URI } from 'vs/base/common/uri'; /** * We currently store local storage with the following format: @@ -28,154 +27,130 @@ import { URI } from 'vs/base/common/uri'; * => no longer being used (used for empty workspaces previously) */ -const EMPTY_WORKSPACE_PREFIX = `${StorageLegacyService.COMMON_PREFIX}workspace/empty:`; -const MULTI_ROOT_WORKSPACE_PREFIX = `${StorageLegacyService.COMMON_PREFIX}workspace/root:`; +const COMMON_WORKSPACE_PREFIX = `${StorageLegacyService.COMMON_PREFIX}workspace/`; const NO_WORKSPACE_PREFIX = 'storage://workspace/__$noWorkspace__'; export type StorageObject = { [key: string]: string }; export interface IParsedStorage { - global: Map; multiRoot: Map; folder: Map; empty: Map; noWorkspace: StorageObject; } -/** - * Parses the local storage implementation into global, multi root, folder and empty storage. - */ -export function parseStorage(storage: IStorageLegacy): IParsedStorage { - const globalStorage = new Map(); - const noWorkspaceStorage: StorageObject = Object.create(null); - const folderWorkspacesStorage = new Map(); - const emptyWorkspacesStorage = new Map(); - const multiRootWorkspacesStorage = new Map(); +export function parseFolderStorage(storage: IStorageLegacy, folderId: string): StorageObject { const workspaces: { prefix: string; resource: string; }[] = []; + const activeKeys = new Set(); + for (let i = 0; i < storage.length; i++) { const key = storage.key(i); // Workspace Storage (storage://workspace/) - if (startsWith(key, StorageLegacyService.WORKSPACE_PREFIX)) { - - // No Workspace key is for extension development windows - if (key.indexOf('__$noWorkspace__') > 0) { - - // storage://workspace/__$noWorkspace__someKey => someKey - const noWorkspaceStorageKey = key.substr(NO_WORKSPACE_PREFIX.length); + if (!startsWith(key, StorageLegacyService.WORKSPACE_PREFIX)) { + continue; + } - noWorkspaceStorage[noWorkspaceStorageKey] = storage.getItem(key); - } + activeKeys.add(key); - // We are looking for key: storage://workspace//workspaceIdentifier to be able to find all folder - // paths that are known to the storage. is the only way how to parse all folder paths known in storage. - else if (endsWith(key, StorageLegacyService.WORKSPACE_IDENTIFIER)) { + // We are looking for key: storage://workspace//workspaceIdentifier to be able to find all folder + // paths that are known to the storage. is the only way how to parse all folder paths known in storage. + if (endsWith(key, StorageLegacyService.WORKSPACE_IDENTIFIER)) { - // storage://workspace//workspaceIdentifier => / - let workspace = key.substring(StorageLegacyService.WORKSPACE_PREFIX.length, key.length - StorageLegacyService.WORKSPACE_IDENTIFIER.length); + // storage://workspace//workspaceIdentifier => / + let workspace = key.substring(StorageLegacyService.WORKSPACE_PREFIX.length, key.length - StorageLegacyService.WORKSPACE_IDENTIFIER.length); - // macOS/Unix: Users/name/folder/ - // Windows: c%3A/Users/name/folder/ - if (!startsWith(workspace, 'file:')) { - workspace = `file:///${rtrim(workspace, '/')}`; - } + // macOS/Unix: Users/name/folder/ + // Windows: c%3A/Users/name/folder/ + if (!startsWith(workspace, 'file:')) { + workspace = `file:///${rtrim(workspace, '/')}`; + } - // Windows UNC path: file://localhost/c%3A/Users/name/folder/ - else { - workspace = rtrim(workspace, '/'); - } + // Windows UNC path: file://localhost/c%3A/Users/name/folder/ + else { + workspace = rtrim(workspace, '/'); + } - // storage://workspace//workspaceIdentifier => storage://workspace// - const prefix = key.substr(0, key.length - StorageLegacyService.WORKSPACE_IDENTIFIER.length); + // storage://workspace//workspaceIdentifier => storage://workspace// + const prefix = key.substr(0, key.length - StorageLegacyService.WORKSPACE_IDENTIFIER.length); + if (startsWith(workspace, folderId)) { workspaces.push({ prefix, resource: workspace }); } + } + } - // Empty workspace key: storage://workspace/empty:/ - else if (startsWith(key, EMPTY_WORKSPACE_PREFIX)) { + // With all the folder paths known we can now extract storage for each path. We have to go through all workspaces + // from the longest path first to reliably extract the storage. The reason is that one folder path can be a parent + // of another folder path and as such a simple indexOf check is not enough. + const workspacesByLength = workspaces.sort((w1, w2) => w1.prefix.length >= w2.prefix.length ? -1 : 1); - // storage://workspace/empty:/ => - const emptyWorkspaceId = key.substring(EMPTY_WORKSPACE_PREFIX.length, key.indexOf('/', EMPTY_WORKSPACE_PREFIX.length)); - const emptyWorkspaceResource = URI.from({ path: emptyWorkspaceId, scheme: 'empty' }).toString(); + const folderWorkspaceStorage: StorageObject = Object.create(null); - let emptyWorkspaceStorage = emptyWorkspacesStorage.get(emptyWorkspaceResource); - if (!emptyWorkspaceStorage) { - emptyWorkspaceStorage = Object.create(null); - emptyWorkspacesStorage.set(emptyWorkspaceResource, emptyWorkspaceStorage); - } + workspacesByLength.forEach(workspace => { + activeKeys.forEach(key => { + if (!startsWith(key, workspace.prefix)) { + return; // not part of workspace prefix or already handled + } - // storage://workspace/empty:/someKey => someKey - const storageKey = key.substr(EMPTY_WORKSPACE_PREFIX.length + emptyWorkspaceId.length + 1 /* trailing / */); + activeKeys.delete(key); - emptyWorkspaceStorage[storageKey] = storage.getItem(key); + if (workspace.resource === folderId) { + // storage://workspace//someKey => someKey + const storageKey = key.substr(workspace.prefix.length); + folderWorkspaceStorage[storageKey] = storage.getItem(key); } + }); + }); - // Multi root workspace key: storage://workspace/root:/ - else if (startsWith(key, MULTI_ROOT_WORKSPACE_PREFIX)) { - - // storage://workspace/root:/ => - const multiRootWorkspaceId = key.substring(MULTI_ROOT_WORKSPACE_PREFIX.length, key.indexOf('/', MULTI_ROOT_WORKSPACE_PREFIX.length)); - const multiRootWorkspaceResource = URI.from({ path: multiRootWorkspaceId, scheme: 'root' }).toString(); + return folderWorkspaceStorage; +} - let multiRootWorkspaceStorage = multiRootWorkspacesStorage.get(multiRootWorkspaceResource); - if (!multiRootWorkspaceStorage) { - multiRootWorkspaceStorage = Object.create(null); - multiRootWorkspacesStorage.set(multiRootWorkspaceResource, multiRootWorkspaceStorage); - } +export function parseNoWorkspaceStorage(storage: IStorageLegacy) { + const noWorkspacePrefix = `${StorageLegacyService.WORKSPACE_PREFIX}__$noWorkspace__`; - // storage://workspace/root:/someKey => someKey - const storageKey = key.substr(MULTI_ROOT_WORKSPACE_PREFIX.length + multiRootWorkspaceId.length + 1 /* trailing / */); + const noWorkspaceStorage: StorageObject = Object.create(null); + for (let i = 0; i < storage.length; i++) { + const key = storage.key(i); - multiRootWorkspaceStorage[storageKey] = storage.getItem(key); - } + // No Workspace key is for extension development windows + if (startsWith(key, noWorkspacePrefix) && !endsWith(key, StorageLegacyService.WORKSPACE_IDENTIFIER)) { + // storage://workspace/__$noWorkspace__someKey => someKey + noWorkspaceStorage[key.substr(NO_WORKSPACE_PREFIX.length)] = storage.getItem(key); } + } - // Global Storage (storage://global) - else if (startsWith(key, StorageLegacyService.GLOBAL_PREFIX)) { + return noWorkspaceStorage; +} - // storage://global/someKey => someKey - const globalStorageKey = key.substr(StorageLegacyService.GLOBAL_PREFIX.length); - if (startsWith(globalStorageKey, StorageLegacyService.COMMON_PREFIX)) { - continue; // filter out faulty keys that have the form storage://something/storage:// - } +export function parseEmptyStorage(storage: IStorageLegacy, targetWorkspaceId: string): StorageObject { + const emptyStoragePrefix = `${COMMON_WORKSPACE_PREFIX}${targetWorkspaceId}/`; + + const emptyWorkspaceStorage: StorageObject = Object.create(null); + for (let i = 0; i < storage.length; i++) { + const key = storage.key(i); - globalStorage.set(globalStorageKey, storage.getItem(key)); + if (startsWith(key, emptyStoragePrefix) && !endsWith(key, StorageLegacyService.WORKSPACE_IDENTIFIER)) { + // storage://workspace/empty:/someKey => someKey + emptyWorkspaceStorage[key.substr(emptyStoragePrefix.length)] = storage.getItem(key); } } - // With all the folder paths known we can now extract storage for each path. We have to go through all workspaces - // from the longest path first to reliably extract the storage. The reason is that one folder path can be a parent - // of another folder path and as such a simple indexOf check is not enough. - const workspacesByLength = workspaces.sort((w1, w2) => w1.prefix.length >= w2.prefix.length ? -1 : 1); - const handledKeys = new Map(); - workspacesByLength.forEach(workspace => { - for (let i = 0; i < storage.length; i++) { - const key = storage.key(i); - - if (handledKeys.has(key) || !startsWith(key, workspace.prefix)) { - continue; // not part of workspace prefix or already handled - } - - handledKeys.set(key, true); + return emptyWorkspaceStorage; +} - let folderWorkspaceStorage = folderWorkspacesStorage.get(workspace.resource); - if (!folderWorkspaceStorage) { - folderWorkspaceStorage = Object.create(null); - folderWorkspacesStorage.set(workspace.resource, folderWorkspaceStorage); - } +export function parseMultiRootStorage(storage: IStorageLegacy, targetWorkspaceId: string): StorageObject { + const multiRootStoragePrefix = `${COMMON_WORKSPACE_PREFIX}${targetWorkspaceId}/`; - // storage://workspace//someKey => someKey - const storageKey = key.substr(workspace.prefix.length); + const multiRootWorkspaceStorage: StorageObject = Object.create(null); + for (let i = 0; i < storage.length; i++) { + const key = storage.key(i); - folderWorkspaceStorage[storageKey] = storage.getItem(key); + if (startsWith(key, multiRootStoragePrefix) && !endsWith(key, StorageLegacyService.WORKSPACE_IDENTIFIER)) { + // storage://workspace/root:/someKey => someKey + multiRootWorkspaceStorage[key.substr(multiRootStoragePrefix.length)] = storage.getItem(key); } - }); + } - return { - global: globalStorage, - multiRoot: multiRootWorkspacesStorage, - folder: folderWorkspacesStorage, - empty: emptyWorkspacesStorage, - noWorkspace: noWorkspaceStorage - }; -} \ No newline at end of file + return multiRootWorkspaceStorage; +} diff --git a/src/vs/platform/storage/node/storageService.ts b/src/vs/platform/storage/node/storageService.ts index e8224e48b..78f5f1d53 100644 --- a/src/vs/platform/storage/node/storageService.ts +++ b/src/vs/platform/storage/node/storageService.ts @@ -5,10 +5,9 @@ import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; -import { ILogService } from 'vs/platform/log/common/log'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { IWorkspaceStorageChangeEvent, IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { Storage, IStorageLoggingOptions } from 'vs/base/node/storage'; +import { Storage, IStorageLoggingOptions, NullStorage, IStorage } from 'vs/base/node/storage'; import { IStorageLegacyService, StorageLegacyScope } from 'vs/platform/storage/common/storageLegacyService'; import { startsWith } from 'vs/base/common/strings'; import { Action } from 'vs/base/common/actions'; @@ -17,6 +16,7 @@ import { localize } from 'vs/nls'; import { mark, getDuration } from 'vs/base/common/performance'; import { join, basename } from 'path'; import { copy } from 'vs/base/node/pfs'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export class StorageService extends Disposable implements IStorageService { _serviceBrand: any; @@ -29,7 +29,10 @@ export class StorageService extends Disposable implements IStorageService { private _onWillSaveState: Emitter = this._register(new Emitter()); get onWillSaveState(): Event { return this._onWillSaveState.event; } - private bufferedStorageErrors: (string | Error)[] = []; + private _hasErrors = false; + get hasErrors(): boolean { return this._hasErrors; } + + private bufferedStorageErrors?: (string | Error)[] = []; private _onStorageError: Emitter = this._register(new Emitter()); get onStorageError(): Event { if (Array.isArray(this.bufferedStorageErrors)) { @@ -47,28 +50,29 @@ export class StorageService extends Disposable implements IStorageService { return this._onStorageError.event; } - private globalStorage: Storage; + private globalStorage: IStorage; private globalStorageWorkspacePath: string; private workspaceStoragePath: string; - private workspaceStorage: Storage; + private workspaceStorage: IStorage; private workspaceStorageListener: IDisposable; private loggingOptions: IStorageLoggingOptions; constructor( workspaceStoragePath: string, - @ILogService logService: ILogService, - @IEnvironmentService environmentService: IEnvironmentService + disableGlobalStorage: boolean, + @ILogService logService: ILogService ) { super(); this.loggingOptions = { - info: environmentService.verbose || environmentService.logStorage, - infoLogger: msg => logService.info(msg), - errorLogger: error => { + logTrace: (logService.getLevel() === LogLevel.Trace) ? msg => logService.trace(msg) : void 0, + logError: error => { logService.error(error); + this._hasErrors = true; + if (Array.isArray(this.bufferedStorageErrors)) { this.bufferedStorageErrors.push(error); } else { @@ -78,7 +82,7 @@ export class StorageService extends Disposable implements IStorageService { }; this.globalStorageWorkspacePath = workspaceStoragePath === StorageService.IN_MEMORY_PATH ? StorageService.IN_MEMORY_PATH : StorageService.IN_MEMORY_PATH; - this.globalStorage = new Storage({ path: this.globalStorageWorkspacePath, logging: this.loggingOptions }); + this.globalStorage = disableGlobalStorage ? new NullStorage() : new Storage({ path: this.globalStorageWorkspacePath, logging: this.loggingOptions }); this._register(this.globalStorage.onDidChangeStorage(key => this.handleDidChangeStorage(key, StorageScope.GLOBAL))); this.createWorkspaceStorage(workspaceStoragePath); @@ -101,13 +105,15 @@ export class StorageService extends Disposable implements IStorageService { } init(): Promise { - mark('willInitGlobalStorage'); mark('willInitWorkspaceStorage'); + return this.workspaceStorage.init().then(() => { + mark('didInitWorkspaceStorage'); - return Promise.all([ - this.globalStorage.init().then(() => mark('didInitGlobalStorage')), - this.workspaceStorage.init().then(() => mark('didInitWorkspaceStorage')) - ]).then(() => void 0); + mark('willInitGlobalStorage'); + return this.globalStorage.init().then(() => { + mark('didInitGlobalStorage'); + }); + }); } get(key: string, scope: StorageScope, fallbackValue: string): string; @@ -148,7 +154,7 @@ export class StorageService extends Disposable implements IStorageService { ]).then(() => void 0); } - private getStorage(scope: StorageScope): Storage { + private getStorage(scope: StorageScope): IStorage { return scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage; } @@ -189,8 +195,8 @@ export class StorageService extends Disposable implements IStorageService { workspaceItemsParsed.set(key, safeParse(value)); }); - console.group(`Storage: Global (check: ${result[2]}, load: ${getDuration('willInitGlobalStorage', 'didInitGlobalStorage')}, path: ${this.globalStorageWorkspacePath})`); - let globalValues = []; + console.group(`Storage: Global (integrity: ${result[2]}, load: ${getDuration('willInitGlobalStorage', 'didInitGlobalStorage')}, path: ${this.globalStorageWorkspacePath})`); + let globalValues: { key: string, value: string }[] = []; globalItems.forEach((value, key) => { globalValues.push({ key, value }); }); @@ -199,8 +205,8 @@ export class StorageService extends Disposable implements IStorageService { console.log(globalItemsParsed); - console.group(`Storage: Workspace (check: ${result[3]}, load: ${getDuration('willInitWorkspaceStorage', 'didInitWorkspaceStorage')}, path: ${this.workspaceStoragePath})`); - let workspaceValues = []; + console.group(`Storage: Workspace (integrity: ${result[3]}, load: ${getDuration('willInitWorkspaceStorage', 'didInitWorkspaceStorage')}, path: ${this.workspaceStoragePath})`); + let workspaceValues: { key: string, value: string }[] = []; workspaceItems.forEach((value, key) => { workspaceValues.push({ key, value }); }); @@ -211,7 +217,7 @@ export class StorageService extends Disposable implements IStorageService { }); } - migrate(toWorkspaceStorageFolder: string): Promise { + migrate(toWorkspaceStorageFolder: string): Thenable { if (this.workspaceStoragePath === StorageService.IN_MEMORY_PATH) { return Promise.resolve(); // no migration needed if running in memory } @@ -264,14 +270,18 @@ export class DelegatingStorageService extends Disposable implements IStorageServ get onWillSaveState(): Event { return this._onWillSaveState.event; } private closed: boolean; + private useLegacyWorkspaceStorage: boolean; constructor( private storageService: IStorageService, private storageLegacyService: IStorageLegacyService, - private logService: ILogService + private logService: ILogService, + configurationService: IConfigurationService ) { super(); + this.useLegacyWorkspaceStorage = configurationService.inspect('workbench.enableLegacyStorage').value === true; + this.registerListeners(); } @@ -282,7 +292,7 @@ export class DelegatingStorageService extends Disposable implements IStorageServ const globalKeyMarker = 'storage://global/'; window.addEventListener('storage', e => { - if (startsWith(e.key, globalKeyMarker)) { + if (e.key && startsWith(e.key, globalKeyMarker)) { const key = e.key.substr(globalKeyMarker.length); this._onDidChangeStorage.fire({ key, scope: StorageScope.GLOBAL }); @@ -294,24 +304,27 @@ export class DelegatingStorageService extends Disposable implements IStorageServ return this.storageService as StorageService; } - get(key: string, scope: StorageScope, fallbackValue?: string): string { - if (scope === StorageScope.WORKSPACE) { + get(key: string, scope: StorageScope, fallbackValue: string): string; + get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined { + if (scope === StorageScope.WORKSPACE && !this.useLegacyWorkspaceStorage) { return this.storageService.get(key, scope, fallbackValue); } return this.storageLegacyService.get(key, this.convertScope(scope), fallbackValue); } - getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean { - if (scope === StorageScope.WORKSPACE) { + getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; + getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined { + if (scope === StorageScope.WORKSPACE && !this.useLegacyWorkspaceStorage) { return this.storageService.getBoolean(key, scope, fallbackValue); } return this.storageLegacyService.getBoolean(key, this.convertScope(scope), fallbackValue); } - getInteger(key: string, scope: StorageScope, fallbackValue?: number): number { - if (scope === StorageScope.WORKSPACE) { + getInteger(key: string, scope: StorageScope, fallbackValue: number): number; + getInteger(key: string, scope: StorageScope, fallbackValue?: number): number | undefined { + if (scope === StorageScope.WORKSPACE && !this.useLegacyWorkspaceStorage) { return this.storageService.getInteger(key, scope, fallbackValue); } diff --git a/src/vs/platform/storage/test/browser/storageLegacyMigration.test.ts b/src/vs/platform/storage/test/browser/storageLegacyMigration.test.ts index 9090fffa4..2aee7230e 100644 --- a/src/vs/platform/storage/test/browser/storageLegacyMigration.test.ts +++ b/src/vs/platform/storage/test/browser/storageLegacyMigration.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { StorageLegacyScope, StorageLegacyService } from 'vs/platform/storage/common/storageLegacyService'; -import { parseStorage } from 'vs/platform/storage/common/storageLegacyMigration'; +import { parseEmptyStorage, parseMultiRootStorage, parseFolderStorage } from 'vs/platform/storage/common/storageLegacyMigration'; import { URI } from 'vs/base/common/uri'; import { startsWith } from 'vs/base/common/strings'; @@ -20,18 +20,6 @@ suite('Storage Migration', () => { storage.clear(); }); - test('Parse Storage (Global)', () => { - const service = createService(); - - const parsed = parseStorage(storage); - - assert.equal(parsed.global.size, 4); - assert.equal(parsed.global.get('key1'), service.get('key1')); - assert.equal(parsed.global.get('key2.something'), service.get('key2.something')); - assert.equal(parsed.global.get('key3/special'), service.get('key3/special')); - assert.equal(parsed.global.get('key4 space'), service.get('key4 space')); - }); - test('Parse Storage (mixed)', () => { // Fill the storage with multiple workspaces of all kinds (empty, root, folders) @@ -74,23 +62,21 @@ suite('Storage Migration', () => { const services = workspaceIds.map(id => createService(id)); - const parsed = parseStorage(storage); - services.forEach((service, index) => { let expectedKeyCount = 4; let storageToTest; const workspaceId = workspaceIds[index]; if (startsWith(workspaceId, 'file:')) { - storageToTest = parsed.folder.get(workspaceId); + storageToTest = parseFolderStorage(storage, workspaceId); expectedKeyCount++; // workspaceIdentifier gets added! } else if (startsWith(workspaceId, 'empty:')) { - storageToTest = parsed.empty.get(workspaceId); + storageToTest = parseEmptyStorage(storage, workspaceId); } else if (startsWith(workspaceId, 'root:')) { - storageToTest = parsed.multiRoot.get(workspaceId); + storageToTest = parseMultiRootStorage(storage, workspaceId); } - assert.equal(Object.keys(storageToTest).length, expectedKeyCount); + assert.equal(Object.keys(storageToTest).length, expectedKeyCount, 's'); assert.equal(storageToTest['key1'], service.get('key1', StorageLegacyScope.WORKSPACE)); assert.equal(storageToTest['key2.something'], service.get('key2.something', StorageLegacyScope.WORKSPACE)); assert.equal(storageToTest['key3/special'], service.get('key3/special', StorageLegacyScope.WORKSPACE)); @@ -115,16 +101,15 @@ suite('Storage Migration', () => { s2.store('s2key3/special', true, StorageLegacyScope.WORKSPACE); s2.store('s2key4 space', 4, StorageLegacyScope.WORKSPACE); - const parsed = parseStorage(storage); - const s1Storage = parsed.folder.get(ws1); + const s1Storage = parseFolderStorage(storage, ws1); assert.equal(Object.keys(s1Storage).length, 5); assert.equal(s1Storage['s1key1'], s1.get('s1key1', StorageLegacyScope.WORKSPACE)); assert.equal(s1Storage['s1key2.something'], s1.get('s1key2.something', StorageLegacyScope.WORKSPACE)); assert.equal(s1Storage['s1key3/special'], s1.get('s1key3/special', StorageLegacyScope.WORKSPACE)); assert.equal(s1Storage['s1key4 space'], s1.get('s1key4 space', StorageLegacyScope.WORKSPACE)); - const s2Storage = parsed.folder.get(ws2); + const s2Storage = parseFolderStorage(storage, ws2); assert.equal(Object.keys(s2Storage).length, 5); assert.equal(s2Storage['s2key1'], s2.get('s2key1', StorageLegacyScope.WORKSPACE)); assert.equal(s2Storage['s2key2.something'], s2.get('s2key2.something', StorageLegacyScope.WORKSPACE)); diff --git a/src/vs/platform/storage/test/node/storageService.test.ts b/src/vs/platform/storage/test/node/storageService.test.ts index 9310947f4..b15d56fd8 100644 --- a/src/vs/platform/storage/test/node/storageService.test.ts +++ b/src/vs/platform/storage/test/node/storageService.test.ts @@ -5,7 +5,7 @@ import { strictEqual, ok, equal } from 'assert'; import { StorageScope } from 'vs/platform/storage/common/storage'; -import { TestStorageService, TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices'; +import { TestStorageService } from 'vs/workbench/test/workbenchTestServices'; import { StorageService } from 'vs/platform/storage/node/storageService'; import { generateUuid } from 'vs/base/common/uuid'; import { join } from 'path'; @@ -84,7 +84,7 @@ suite('StorageService', () => { const storageDir = uniqueStorageDir(); await mkdirp(storageDir); - const storage = new StorageService(join(storageDir, 'storage.db'), new NullLogService(), TestEnvironmentService); + const storage = new StorageService(join(storageDir, 'storage.db'), false, new NullLogService()); await storage.init(); storage.store('bar', 'foo', StorageScope.WORKSPACE); diff --git a/src/vs/platform/telemetry/common/telemetry.ts b/src/vs/platform/telemetry/common/telemetry.ts index 0c25b3e58..159ad58da 100644 --- a/src/vs/platform/telemetry/common/telemetry.ts +++ b/src/vs/platform/telemetry/common/telemetry.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const ITelemetryService = createDecorator('telemetryService'); @@ -28,9 +27,9 @@ export interface ITelemetryService { * Sends a telemetry event that has been privacy approved. * Do not call this unless you have been given approval. */ - publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): TPromise; + publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Thenable; - getTelemetryInfo(): TPromise; + getTelemetryInfo(): Thenable; isOptedIn: boolean; } diff --git a/src/vs/platform/telemetry/common/telemetryService.ts b/src/vs/platform/telemetry/common/telemetryService.ts index 3d8af6dab..3b9110a8c 100644 --- a/src/vs/platform/telemetry/common/telemetryService.ts +++ b/src/vs/platform/telemetry/common/telemetryService.ts @@ -10,14 +10,13 @@ import { ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils' import { optional } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { cloneAndChange, mixin } from 'vs/base/common/objects'; import { Registry } from 'vs/platform/registry/common/platform'; export interface ITelemetryServiceConfig { appender: ITelemetryAppender; - commonProperties?: TPromise<{ [name: string]: any }>; + commonProperties?: Thenable<{ [name: string]: any }>; piiPaths?: string[]; } @@ -29,7 +28,7 @@ export class TelemetryService implements ITelemetryService { _serviceBrand: any; private _appender: ITelemetryAppender; - private _commonProperties: TPromise<{ [name: string]: any; }>; + private _commonProperties: Thenable<{ [name: string]: any; }>; private _piiPaths: string[]; private _userOptIn: boolean; @@ -41,7 +40,7 @@ export class TelemetryService implements ITelemetryService { @optional(IConfigurationService) private _configurationService: IConfigurationService ) { this._appender = config.appender; - this._commonProperties = config.commonProperties || TPromise.as({}); + this._commonProperties = config.commonProperties || Promise.resolve({}); this._piiPaths = config.piiPaths || []; this._userOptIn = true; @@ -73,7 +72,7 @@ export class TelemetryService implements ITelemetryService { return this._userOptIn; } - getTelemetryInfo(): TPromise { + getTelemetryInfo(): Thenable { return this._commonProperties.then(values => { // well known properties let sessionId = values['sessionID']; @@ -88,10 +87,10 @@ export class TelemetryService implements ITelemetryService { this._disposables = dispose(this._disposables); } - publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): TPromise { + publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Thenable { // don't send events when the user is optout if (!this._userOptIn) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } return this._commonProperties.then(values => { @@ -132,6 +131,8 @@ export class TelemetryService implements ITelemetryService { const nodeModulesRegex = /^[\\\/]?(node_modules|node_modules\.asar)[\\\/]/; const fileRegex = /(file:\/\/)?([a-zA-Z]:(\\\\|\\|\/)|(\\\\|\\|\/))?([\w-\._]+(\\\\|\\|\/))+[\w-\._]*/g; + let lastIndex = 0; + updatedStack = ''; while (true) { const result = fileRegex.exec(stack); @@ -140,9 +141,13 @@ export class TelemetryService implements ITelemetryService { } // Anoynimize user file paths that do not need to be retained or cleaned up. if (!nodeModulesRegex.test(result[0]) && cleanUpIndexes.every(([x, y]) => result.index < x || result.index >= y)) { - updatedStack = updatedStack.slice(0, result.index) + result[0].replace(/./g, 'a') + updatedStack.slice(fileRegex.lastIndex); + updatedStack += stack.substring(lastIndex, result.index) + ''; + lastIndex = fileRegex.lastIndex; } } + if (lastIndex < stack.length) { + updatedStack += stack.substr(lastIndex); + } } // sanitize with configured cleanup patterns @@ -169,4 +174,4 @@ Registry.as(Extensions.Configuration).registerConfigurat 'tags': ['usesOnlineServices'] } } -}); \ No newline at end of file +}); diff --git a/src/vs/platform/telemetry/common/telemetryUtils.ts b/src/vs/platform/telemetry/common/telemetryUtils.ts index e9f64e328..22b6ed962 100644 --- a/src/vs/platform/telemetry/common/telemetryUtils.ts +++ b/src/vs/platform/telemetry/common/telemetryUtils.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable } from 'vs/base/common/lifecycle'; import { guessMimeTypes } from 'vs/base/common/mime'; import * as paths from 'vs/base/common/paths'; @@ -16,11 +15,11 @@ import { ILogService } from 'vs/platform/log/common/log'; export const NullTelemetryService = new class implements ITelemetryService { _serviceBrand: undefined; publicLog(eventName: string, data?: ITelemetryData) { - return TPromise.wrap(void 0); + return Promise.resolve(void 0); } isOptedIn: true; - getTelemetryInfo(): TPromise { - return TPromise.wrap({ + getTelemetryInfo(): Promise { + return Promise.resolve({ instanceId: 'someValue.instanceId', sessionId: 'someValue.sessionId', machineId: 'someValue.machineId' @@ -30,17 +29,17 @@ export const NullTelemetryService = new class implements ITelemetryService { export interface ITelemetryAppender { log(eventName: string, data: any): void; - dispose(): TPromise; + dispose(): Thenable; } export function combinedAppender(...appenders: ITelemetryAppender[]): ITelemetryAppender { return { log: (e, d) => appenders.forEach(a => a.log(e, d)), - dispose: () => TPromise.join(appenders.map(a => a.dispose())) + dispose: () => Promise.all(appenders.map(a => a.dispose())) }; } -export const NullAppender: ITelemetryAppender = { log: () => null, dispose: () => TPromise.as(null) }; +export const NullAppender: ITelemetryAppender = { log: () => null, dispose: () => Promise.resolve(null) }; export class LogAppender implements ITelemetryAppender { @@ -48,8 +47,8 @@ export class LogAppender implements ITelemetryAppender { private commonPropertiesRegex = /^sessionID$|^version$|^timestamp$|^commitHash$|^common\./; constructor(@ILogService private readonly _logService: ILogService) { } - dispose(): TPromise { - return TPromise.as(undefined); + dispose(): Promise { + return Promise.resolve(undefined); } log(eventName: string, data: any): void { @@ -66,19 +65,21 @@ export class LogAppender implements ITelemetryAppender { /* __GDPR__FRAGMENT__ "URIDescriptor" : { "mimeType" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "scheme": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "ext": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "path": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ export interface URIDescriptor { mimeType?: string; + scheme?: string; ext?: string; path?: string; } export function telemetryURIDescriptor(uri: URI, hashPath: (path: string) => string): URIDescriptor { const fsPath = uri && uri.fsPath; - return fsPath ? { mimeType: guessMimeTypes(fsPath).join(', '), ext: paths.extname(fsPath), path: hashPath(fsPath) } : {}; + return fsPath ? { mimeType: guessMimeTypes(fsPath).join(', '), scheme: uri.scheme, ext: paths.extname(fsPath), path: hashPath(fsPath) } : {}; } /** @@ -134,6 +135,7 @@ const configurationValueWhitelist = [ 'editor.overviewRulerLanes', 'editor.overviewRulerBorder', 'editor.cursorBlinking', + 'editor.cursorSmoothCaretAnimation', 'editor.cursorStyle', 'editor.mouseWheelZoom', 'editor.fontLigatures', diff --git a/src/vs/platform/telemetry/node/appInsightsAppender.ts b/src/vs/platform/telemetry/node/appInsightsAppender.ts index 857443a4f..96440af93 100644 --- a/src/vs/platform/telemetry/node/appInsightsAppender.ts +++ b/src/vs/platform/telemetry/node/appInsightsAppender.ts @@ -6,7 +6,6 @@ import * as appInsights from 'applicationinsights'; import { isObject } from 'vs/base/common/types'; import { safeStringify, mixin } from 'vs/base/common/objects'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils'; import { ILogService } from 'vs/platform/log/common/log'; @@ -144,9 +143,9 @@ export class AppInsightsAppender implements ITelemetryAppender { }); } - dispose(): TPromise { + dispose(): Promise { if (this._aiClient) { - return new TPromise(resolve => { + return new Promise(resolve => { this._aiClient.flush({ callback: () => { // all data flushed @@ -158,4 +157,4 @@ export class AppInsightsAppender implements ITelemetryAppender { } return undefined; } -} \ No newline at end of file +} diff --git a/src/vs/platform/telemetry/node/commonProperties.ts b/src/vs/platform/telemetry/node/commonProperties.ts index d02cd24f2..573306636 100644 --- a/src/vs/platform/telemetry/node/commonProperties.ts +++ b/src/vs/platform/telemetry/node/commonProperties.ts @@ -5,11 +5,10 @@ import * as Platform from 'vs/base/common/platform'; import * as os from 'os'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as uuid from 'vs/base/common/uuid'; import { readFile } from 'vs/base/node/pfs'; -export function resolveCommonProperties(commit: string, version: string, machineId: string, installSourcePath: string): TPromise<{ [name: string]: string; }> { +export function resolveCommonProperties(commit: string, version: string, machineId: string, installSourcePath: string): Promise<{ [name: string]: string; }> { const result: { [name: string]: string; } = Object.create(null); // __GDPR__COMMON__ "common.machineId" : { "endPoint": "MacAddressHash", "classification": "EndUserPseudonymizedInformation", "purpose": "FeatureInsight" } result['common.machineId'] = machineId; @@ -58,4 +57,4 @@ export function resolveCommonProperties(commit: string, version: string, machine }, error => { return result; }); -} \ No newline at end of file +} diff --git a/src/vs/platform/telemetry/node/telemetryIpc.ts b/src/vs/platform/telemetry/node/telemetryIpc.ts index 9acd46151..a99e372fa 100644 --- a/src/vs/platform/telemetry/node/telemetryIpc.ts +++ b/src/vs/platform/telemetry/node/telemetryIpc.ts @@ -3,8 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; import { ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils'; import { Event } from 'vs/base/common/event'; @@ -13,37 +12,32 @@ export interface ITelemetryLog { data?: any; } -export interface ITelemetryAppenderChannel extends IChannel { - call(command: 'log', data: ITelemetryLog): Thenable; - call(command: string, arg: any): Thenable; -} - -export class TelemetryAppenderChannel implements ITelemetryAppenderChannel { +export class TelemetryAppenderChannel implements IServerChannel { constructor(private appender: ITelemetryAppender) { } - listen(event: string, arg?: any): Event { - throw new Error('No events'); + listen(_, event: string): Event { + throw new Error(`Event not found: ${event}`); } - call(command: string, { eventName, data }: ITelemetryLog): Thenable { + call(_, command: string, { eventName, data }: ITelemetryLog): Thenable { this.appender.log(eventName, data); - return TPromise.as(null); + return Promise.resolve(null); } } export class TelemetryAppenderClient implements ITelemetryAppender { - constructor(private channel: ITelemetryAppenderChannel) { } + constructor(private channel: IChannel) { } log(eventName: string, data?: any): any { this.channel.call('log', { eventName, data }) - .then(null, err => `Failed to log telemetry: ${console.warn(err)}`); + .then(undefined, err => `Failed to log telemetry: ${console.warn(err)}`); - return TPromise.as(null); + return Promise.resolve(null); } dispose(): any { // TODO } -} \ No newline at end of file +} diff --git a/src/vs/platform/telemetry/node/telemetryNodeUtils.ts b/src/vs/platform/telemetry/node/telemetryNodeUtils.ts index 808c678c5..6c42c1e75 100644 --- a/src/vs/platform/telemetry/node/telemetryNodeUtils.ts +++ b/src/vs/platform/telemetry/node/telemetryNodeUtils.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; + import { URI } from 'vs/base/common/uri'; import product from 'vs/platform/node/product'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -export function addGAParameters(telemetryService: ITelemetryService, environmentService: IEnvironmentService, uri: URI, origin: string, experiment = '1'): TPromise { +export function addGAParameters(telemetryService: ITelemetryService, environmentService: IEnvironmentService, uri: URI, origin: string, experiment = '1'): Thenable { if (environmentService.isBuilt && !environmentService.isExtensionDevelopment && !environmentService.args['disable-telemetry'] && !!product.enableTelemetry) { if (uri.scheme === 'https' && uri.authority === 'code.visualstudio.com') { return telemetryService.getTelemetryInfo() @@ -18,5 +18,5 @@ export function addGAParameters(telemetryService: ITelemetryService, environment }); } } - return TPromise.as(uri); + return Promise.resolve(uri); } diff --git a/src/vs/platform/telemetry/node/workbenchCommonProperties.ts b/src/vs/platform/telemetry/node/workbenchCommonProperties.ts index a3a467510..d6d44f3b5 100644 --- a/src/vs/platform/telemetry/node/workbenchCommonProperties.ts +++ b/src/vs/platform/telemetry/node/workbenchCommonProperties.ts @@ -3,14 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import * as uuid from 'vs/base/common/uuid'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; export const lastSessionDateStorageKey = 'telemetry.lastSessionDate'; -export function resolveWorkbenchCommonProperties(storageService: IStorageService, commit: string, version: string, machineId: string, installSourcePath: string): TPromise<{ [name: string]: string }> { +export function resolveWorkbenchCommonProperties(storageService: IStorageService, commit: string, version: string, machineId: string, installSourcePath: string): Promise<{ [name: string]: string }> { return resolveCommonProperties(commit, version, machineId, installSourcePath).then(result => { // __GDPR__COMMON__ "common.version.shell" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } result['common.version.shell'] = process.versions && (process).versions['electron']; @@ -23,7 +22,7 @@ export function resolveWorkbenchCommonProperties(storageService: IStorageService // __GDPR__COMMON__ "common.firstSessionDate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } result['common.firstSessionDate'] = getOrCreateFirstSessionDate(storageService); // __GDPR__COMMON__ "common.lastSessionDate" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - result['common.lastSessionDate'] = lastSessionDate; + result['common.lastSessionDate'] = lastSessionDate || ''; // __GDPR__COMMON__ "common.isNewSession" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } result['common.isNewSession'] = !lastSessionDate ? '1' : '0'; // __GDPR__COMMON__ "common.instanceId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } @@ -49,4 +48,4 @@ function getOrCreateFirstSessionDate(storageService: IStorageService): string { storageService.store(key, firstSessionDate, StorageScope.GLOBAL); return firstSessionDate; -} \ No newline at end of file +} 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 88ed55b60..69d75420e 100644 --- a/src/vs/platform/telemetry/test/electron-browser/commonProperties.test.ts +++ b/src/vs/platform/telemetry/test/electron-browser/commonProperties.test.ts @@ -7,7 +7,7 @@ import * as path from 'path'; import * as os from 'os'; import * as fs from 'fs'; import { resolveWorkbenchCommonProperties } from 'vs/platform/telemetry/node/workbenchCommonProperties'; -import { getRandomTestPath, TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices'; +import { getRandomTestPath } from 'vs/workbench/test/workbenchTestServices'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { StorageService } from 'vs/platform/storage/node/storageService'; import { NullLogService } from 'vs/platform/log/common/log'; @@ -24,7 +24,7 @@ suite('Telemetry - common properties', function () { let nestStorage2Service: IStorageService; setup(() => { - nestStorage2Service = new StorageService(':memory:', new NullLogService(), TestEnvironmentService); + nestStorage2Service = new StorageService(':memory:', false, new NullLogService()); }); teardown(done => { diff --git a/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts b/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts index 8ae67b193..712287504 100644 --- a/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts +++ b/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { Emitter } from 'vs/base/common/event'; -import { TPromise } from 'vs/base/common/winjs.base'; import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry'; import { NullAppender, ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils'; import * as Errors from 'vs/base/common/errors'; import * as sinon from 'sinon'; import { getConfigurationValue } from 'vs/platform/configuration/common/configuration'; +import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; class TestTelemetryAppender implements ITelemetryAppender { @@ -30,9 +30,9 @@ class TestTelemetryAppender implements ITelemetryAppender { return this.events.length; } - public dispose(): TPromise { + public dispose(): Promise { this.isDisposed = true; - return TPromise.as(null); + return Promise.resolve(null); } } @@ -48,7 +48,7 @@ class ErrorTestingSettings { public noSuchFileMessage: string; public stack: string[]; public randomUserFile: string = 'a/path/that/doe_snt/con-tain/code/names.js'; - public anonymizedRandomUserFile: string = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; + public anonymizedRandomUserFile: string = ''; public nodeModulePathToRetain: string = 'node_modules/path/that/shouldbe/retained/names.js:14:15854'; public nodeModuleAsarPathToRetain: string = 'node_modules.asar/path/that/shouldbe/retained/names.js:14:12354'; @@ -139,7 +139,7 @@ suite('TelemetryService', () => { let testAppender = new TestTelemetryAppender(); let service = new TelemetryService({ appender: testAppender, - commonProperties: TPromise.as({ foo: 'JA!', get bar() { return Math.random(); } }) + commonProperties: Promise.resolve({ foo: 'JA!', get bar() { return Math.random(); } }) }, undefined); return service.publicLog('testEvent').then(_ => { @@ -157,7 +157,7 @@ suite('TelemetryService', () => { let testAppender = new TestTelemetryAppender(); let service = new TelemetryService({ appender: testAppender, - commonProperties: TPromise.as({ foo: 'JA!', get bar() { return Math.random(); } }) + commonProperties: Promise.resolve({ foo: 'JA!', get bar() { return Math.random(); } }) }, undefined); return service.publicLog('testEvent', { hightower: 'xl', price: 8000 }).then(_ => { @@ -176,7 +176,7 @@ suite('TelemetryService', () => { test('TelemetryInfo comes from properties', function () { let service = new TelemetryService({ appender: NullAppender, - commonProperties: TPromise.as({ + commonProperties: Promise.resolve({ sessionID: 'one', ['common.instanceId']: 'two', ['common.machineId']: 'three', @@ -204,14 +204,29 @@ suite('TelemetryService', () => { }); })); - test('Error events', sinon.test(function (this: any) { + class JoinableTelemetryService extends TelemetryService { + + private readonly promises: Thenable[] = []; + + join(): Promise { + return Promise.all(this.promises); + } + + publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Thenable { + let p = super.publicLog(eventName, data, anonymizeFilePaths); + this.promises.push(p); + return p; + } + } + + test('Error events', sinon.test(async function (this: any) { let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler(); Errors.setUnexpectedErrorHandler(() => { }); try { let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); @@ -223,6 +238,8 @@ suite('TelemetryService', () => { Errors.onUnexpectedError(e); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); + assert.equal(testAppender.getEventsCount(), 1); assert.equal(testAppender.events[0].eventName, 'UnhandledError'); assert.equal(testAppender.events[0].data.msg, 'This is a test.'); @@ -263,17 +280,18 @@ suite('TelemetryService', () => { // } // })); - test('Handle global errors', sinon.test(function (this: any) { + test('Handle global errors', sinon.test(async function (this: any) { let errorStub = sinon.stub(); window.onerror = errorStub; let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let testError = new Error('test'); (window.onerror)('Error Message', 'file.js', 2, 42, testError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.equal(errorStub.alwaysCalledWithExactly('Error Message', 'file.js', 2, 42, testError), true); assert.equal(errorStub.callCount, 1); @@ -290,12 +308,12 @@ suite('TelemetryService', () => { service.dispose(); })); - test('Error Telemetry removes PII from filename with spaces', sinon.test(function (this: any) { + test('Error Telemetry removes PII from filename with spaces', sinon.test(async function (this: any) { let errorStub = sinon.stub(); window.onerror = errorStub; let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let personInfoWithSpaces = settings.personalInfo.slice(0, 2) + ' ' + settings.personalInfo.slice(2); @@ -303,6 +321,7 @@ suite('TelemetryService', () => { dangerousFilenameError.stack = settings.stack; (window.onerror)('dangerousFilename', settings.dangerousPathWithImportantInfo.replace(settings.personalInfo, personInfoWithSpaces) + '/test.js', 2, 42, dangerousFilenameError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.equal(errorStub.callCount, 1); assert.equal(testAppender.events[0].data.file.indexOf(settings.dangerousPathWithImportantInfo.replace(settings.personalInfo, personInfoWithSpaces)), -1); @@ -313,47 +332,51 @@ suite('TelemetryService', () => { })); test('Uncaught Error Telemetry removes PII from filename', sinon.test(function (this: any) { + let clock = this.clock; let errorStub = sinon.stub(); window.onerror = errorStub; let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let dangerousFilenameError: any = new Error('dangerousFilename'); dangerousFilenameError.stack = settings.stack; (window.onerror)('dangerousFilename', settings.dangerousPathWithImportantInfo + '/test.js', 2, 42, dangerousFilenameError); - this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); - - assert.equal(errorStub.callCount, 1); - assert.equal(testAppender.events[0].data.file.indexOf(settings.dangerousPathWithImportantInfo), -1); - - dangerousFilenameError = new Error('dangerousFilename'); - dangerousFilenameError.stack = settings.stack; - (window.onerror)('dangerousFilename', settings.dangerousPathWithImportantInfo + '/test.js', 2, 42, dangerousFilenameError); - this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + return service.join().then(() => { + assert.equal(errorStub.callCount, 1); + assert.equal(testAppender.events[0].data.file.indexOf(settings.dangerousPathWithImportantInfo), -1); - assert.equal(errorStub.callCount, 2); - assert.equal(testAppender.events[0].data.file.indexOf(settings.dangerousPathWithImportantInfo), -1); - assert.equal(testAppender.events[0].data.file, settings.importantInfo + '/test.js'); + dangerousFilenameError = new Error('dangerousFilename'); + dangerousFilenameError.stack = settings.stack; + (window.onerror)('dangerousFilename', settings.dangerousPathWithImportantInfo + '/test.js', 2, 42, dangerousFilenameError); + clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + return service.join(); + }).then(() => { + assert.equal(errorStub.callCount, 2); + assert.equal(testAppender.events[0].data.file.indexOf(settings.dangerousPathWithImportantInfo), -1); + assert.equal(testAppender.events[0].data.file, settings.importantInfo + '/test.js'); - errorTelemetry.dispose(); - service.dispose(); + errorTelemetry.dispose(); + service.dispose(); + }); })); - test('Unexpected Error Telemetry removes PII', sinon.test(function (this: any) { + test('Unexpected Error Telemetry removes PII', sinon.test(async function (this: any) { let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler(); Errors.setUnexpectedErrorHandler(() => { }); try { let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let dangerousPathWithoutImportantInfoError: any = new Error(settings.dangerousPathWithoutImportantInfo); dangerousPathWithoutImportantInfoError.stack = settings.stack; Errors.onUnexpectedError(dangerousPathWithoutImportantInfoError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1); assert.equal(testAppender.events[0].data.msg.indexOf(settings.filePrefix), -1); @@ -371,18 +394,19 @@ suite('TelemetryService', () => { } })); - test('Uncaught Error Telemetry removes PII', sinon.test(function (this: any) { + test('Uncaught Error Telemetry removes PII', sinon.test(async function (this: any) { let errorStub = sinon.stub(); window.onerror = errorStub; let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let dangerousPathWithoutImportantInfoError: any = new Error('dangerousPathWithoutImportantInfo'); dangerousPathWithoutImportantInfoError.stack = settings.stack; (window.onerror)(settings.dangerousPathWithoutImportantInfo, 'test.js', 2, 42, dangerousPathWithoutImportantInfoError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.equal(errorStub.callCount, 1); // Test that no file information remains, esp. personal info @@ -397,7 +421,7 @@ suite('TelemetryService', () => { service.dispose(); })); - test('Unexpected Error Telemetry removes PII but preserves Code file path', sinon.test(function (this: any) { + test('Unexpected Error Telemetry removes PII but preserves Code file path', sinon.test(async function (this: any) { let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler(); Errors.setUnexpectedErrorHandler(() => { }); @@ -405,7 +429,7 @@ suite('TelemetryService', () => { try { let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let dangerousPathWithImportantInfoError: any = new Error(settings.dangerousPathWithImportantInfo); @@ -414,6 +438,7 @@ suite('TelemetryService', () => { // Test that important information remains but personal info does not Errors.onUnexpectedError(dangerousPathWithImportantInfoError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.importantInfo), -1); assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1); @@ -432,18 +457,19 @@ suite('TelemetryService', () => { } })); - test('Uncaught Error Telemetry removes PII but preserves Code file path', sinon.test(function (this: any) { + test('Uncaught Error Telemetry removes PII but preserves Code file path', sinon.test(async function (this: any) { let errorStub = sinon.stub(); window.onerror = errorStub; let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let dangerousPathWithImportantInfoError: any = new Error('dangerousPathWithImportantInfo'); dangerousPathWithImportantInfoError.stack = settings.stack; (window.onerror)(settings.dangerousPathWithImportantInfo, 'test.js', 2, 42, dangerousPathWithImportantInfoError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.equal(errorStub.callCount, 1); // Test that important information remains but personal info does not @@ -460,7 +486,7 @@ suite('TelemetryService', () => { service.dispose(); })); - test('Unexpected Error Telemetry removes PII but preserves Code file path with node modules', sinon.test(function (this: any) { + test('Unexpected Error Telemetry removes PII but preserves Code file path with node modules', sinon.test(async function (this: any) { let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler(); Errors.setUnexpectedErrorHandler(() => { }); @@ -468,7 +494,7 @@ suite('TelemetryService', () => { try { let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let dangerousPathWithImportantInfoError: any = new Error(settings.dangerousPathWithImportantInfo); @@ -477,6 +503,7 @@ suite('TelemetryService', () => { Errors.onUnexpectedError(dangerousPathWithImportantInfoError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.notEqual(testAppender.events[0].data.callstack.indexOf('(' + settings.nodeModuleAsarPathToRetain), -1); assert.notEqual(testAppender.events[0].data.callstack.indexOf('(' + settings.nodeModulePathToRetain), -1); @@ -491,18 +518,19 @@ suite('TelemetryService', () => { } })); - test('Uncaught Error Telemetry removes PII but preserves Code file path', sinon.test(function (this: any) { + test('Uncaught Error Telemetry removes PII but preserves Code file path', sinon.test(async function (this: any) { let errorStub = sinon.stub(); window.onerror = errorStub; let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let dangerousPathWithImportantInfoError: any = new Error('dangerousPathWithImportantInfo'); dangerousPathWithImportantInfoError.stack = settings.stack; (window.onerror)(settings.dangerousPathWithImportantInfo, 'test.js', 2, 42, dangerousPathWithImportantInfoError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.equal(errorStub.callCount, 1); @@ -516,7 +544,7 @@ suite('TelemetryService', () => { })); - test('Unexpected Error Telemetry removes PII but preserves Code file path when PIIPath is configured', sinon.test(function (this: any) { + test('Unexpected Error Telemetry removes PII but preserves Code file path when PIIPath is configured', sinon.test(async function (this: any) { let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler(); Errors.setUnexpectedErrorHandler(() => { }); @@ -524,7 +552,7 @@ suite('TelemetryService', () => { try { let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender, piiPaths: [settings.personalInfo + '/resources/app/'] }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender, piiPaths: [settings.personalInfo + '/resources/app/'] }, undefined); const errorTelemetry = new ErrorTelemetry(service); let dangerousPathWithImportantInfoError: any = new Error(settings.dangerousPathWithImportantInfo); @@ -533,6 +561,7 @@ suite('TelemetryService', () => { // Test that important information remains but personal info does not Errors.onUnexpectedError(dangerousPathWithImportantInfoError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.importantInfo), -1); assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1); @@ -551,18 +580,19 @@ suite('TelemetryService', () => { } })); - test('Uncaught Error Telemetry removes PII but preserves Code file path when PIIPath is configured', sinon.test(function (this: any) { + test('Uncaught Error Telemetry removes PII but preserves Code file path when PIIPath is configured', sinon.test(async function (this: any) { let errorStub = sinon.stub(); window.onerror = errorStub; let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender, piiPaths: [settings.personalInfo + '/resources/app/'] }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender, piiPaths: [settings.personalInfo + '/resources/app/'] }, undefined); const errorTelemetry = new ErrorTelemetry(service); let dangerousPathWithImportantInfoError: any = new Error('dangerousPathWithImportantInfo'); dangerousPathWithImportantInfoError.stack = settings.stack; (window.onerror)(settings.dangerousPathWithImportantInfo, 'test.js', 2, 42, dangerousPathWithImportantInfoError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.equal(errorStub.callCount, 1); // Test that important information remains but personal info does not @@ -579,7 +609,7 @@ suite('TelemetryService', () => { service.dispose(); })); - test('Unexpected Error Telemetry removes PII but preserves Missing Model error message', sinon.test(function (this: any) { + test('Unexpected Error Telemetry removes PII but preserves Missing Model error message', sinon.test(async function (this: any) { let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler(); Errors.setUnexpectedErrorHandler(() => { }); @@ -587,7 +617,7 @@ suite('TelemetryService', () => { try { let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let missingModelError: any = new Error(settings.missingModelMessage); @@ -597,6 +627,7 @@ suite('TelemetryService', () => { // error message does (Received model events for missing model) Errors.onUnexpectedError(missingModelError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.missingModelPrefix), -1); assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1); @@ -614,18 +645,19 @@ suite('TelemetryService', () => { } })); - test('Uncaught Error Telemetry removes PII but preserves Missing Model error message', sinon.test(function (this: any) { + test('Uncaught Error Telemetry removes PII but preserves Missing Model error message', sinon.test(async function (this: any) { let errorStub = sinon.stub(); window.onerror = errorStub; let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let missingModelError: any = new Error('missingModelMessage'); missingModelError.stack = settings.stack; (window.onerror)(settings.missingModelMessage, 'test.js', 2, 42, missingModelError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.equal(errorStub.callCount, 1); // Test that no file information remains, but this particular @@ -643,7 +675,7 @@ suite('TelemetryService', () => { service.dispose(); })); - test('Unexpected Error Telemetry removes PII but preserves No Such File error message', sinon.test(function (this: any) { + test('Unexpected Error Telemetry removes PII but preserves No Such File error message', sinon.test(async function (this: any) { let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler(); Errors.setUnexpectedErrorHandler(() => { }); @@ -651,7 +683,7 @@ suite('TelemetryService', () => { try { let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let noSuchFileError: any = new Error(settings.noSuchFileMessage); @@ -661,6 +693,7 @@ suite('TelemetryService', () => { // error message does (ENOENT: no such file or directory) Errors.onUnexpectedError(noSuchFileError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.notEqual(testAppender.events[0].data.msg.indexOf(settings.noSuchFilePrefix), -1); assert.equal(testAppender.events[0].data.msg.indexOf(settings.personalInfo), -1); @@ -678,7 +711,7 @@ suite('TelemetryService', () => { } })); - test('Uncaught Error Telemetry removes PII but preserves No Such File error message', sinon.test(function (this: any) { + test('Uncaught Error Telemetry removes PII but preserves No Such File error message', sinon.test(async function (this: any) { let origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler(); Errors.setUnexpectedErrorHandler(() => { }); @@ -687,13 +720,14 @@ suite('TelemetryService', () => { window.onerror = errorStub; let settings = new ErrorTestingSettings(); let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ appender: testAppender }, undefined); + let service = new JoinableTelemetryService({ appender: testAppender }, undefined); const errorTelemetry = new ErrorTelemetry(service); let noSuchFileError: any = new Error('noSuchFileMessage'); noSuchFileError.stack = settings.stack; (window.onerror)(settings.noSuchFileMessage, 'test.js', 2, 42, noSuchFileError); this.clock.tick(ErrorTelemetry.ERROR_FLUSH_TIMEOUT); + await service.join(); assert.equal(errorStub.callCount, 1); // Test that no file information remains, but this particular @@ -770,4 +804,4 @@ suite('TelemetryService', () => { service.dispose(); }); -}); \ No newline at end of file +}); diff --git a/src/vs/platform/theme/common/themeService.ts b/src/vs/platform/theme/common/themeService.ts index c77d01813..21ea86501 100644 --- a/src/vs/platform/theme/common/themeService.ts +++ b/src/vs/platform/theme/common/themeService.ts @@ -47,7 +47,7 @@ export interface ITheme { readonly type: ThemeType; /** - * Resolves the color of the given color identifer. If the theme does not + * Resolves the color of the given color identifier. If the theme does not * specify the color, the default color is returned unless useDefault is set to false. * @param color the id of the color * @param useDefault specifies if the default color should be used. If not set, the default is used. diff --git a/src/vs/platform/update/common/update.ts b/src/vs/platform/update/common/update.ts index 676598c4d..b734fb12f 100644 --- a/src/vs/platform/update/common/update.ts +++ b/src/vs/platform/update/common/update.ts @@ -48,7 +48,8 @@ export const enum StateType { export const enum UpdateType { Setup, - Archive + Archive, + Snap } export type Uninitialized = { type: StateType.Uninitialized }; diff --git a/src/vs/platform/update/electron-main/updateService.linux.ts b/src/vs/platform/update/electron-main/updateService.linux.ts index 03d40b4fe..05963efcf 100644 --- a/src/vs/platform/update/electron-main/updateService.linux.ts +++ b/src/vs/platform/update/electron-main/updateService.linux.ts @@ -16,6 +16,9 @@ import { asJson } from 'vs/base/node/request'; import { TPromise } from 'vs/base/common/winjs.base'; import { shell } from 'electron'; import { CancellationToken } from 'vs/base/common/cancellation'; +import * as path from 'path'; +import { spawn } from 'child_process'; +import { realpath } from 'fs'; export class LinuxUpdateService extends AbstractUpdateService { @@ -43,33 +46,58 @@ export class LinuxUpdateService extends AbstractUpdateService { this.setState(State.CheckingForUpdates(context)); - this.requestService.request({ url: this.url }, CancellationToken.None) - .then(asJson) - .then(update => { - if (!update || !update.url || !update.version || !update.productVersion) { + if (process.env.SNAP && process.env.SNAP_REVISION) { + this.checkForSnapUpdate(); + } else { + this.requestService.request({ url: this.url }, CancellationToken.None) + .then(asJson) + .then(update => { + if (!update || !update.url || !update.version || !update.productVersion) { + /* __GDPR__ + "update:notAvailable" : { + "explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ + this.telemetryService.publicLog('update:notAvailable', { explicit: !!context }); + + this.setState(State.Idle(UpdateType.Archive)); + } else { + this.setState(State.AvailableForDownload(update)); + } + }) + .then(null, err => { + this.logService.error(err); + /* __GDPR__ - "update:notAvailable" : { - "explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } + "update:notAvailable" : { + "explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } */ this.telemetryService.publicLog('update:notAvailable', { explicit: !!context }); + this.setState(State.Idle(UpdateType.Archive, err.message || err)); + }); + } + } - this.setState(State.Idle(UpdateType.Archive)); - } else { - this.setState(State.AvailableForDownload(update)); - } - }) - .then(null, err => { - this.logService.error(err); - - /* __GDPR__ - "update:notAvailable" : { - "explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ - this.telemetryService.publicLog('update:notAvailable', { explicit: !!context }); - this.setState(State.Idle(UpdateType.Archive, err.message || err)); - }); + private checkForSnapUpdate(): void { + // If the application was installed as a snap, updates happen in the + // background automatically, we just need to check to see if an update + // has already happened. + realpath(`/snap/${product.applicationName}/current`, (err, resolvedCurrentSnapPath) => { + if (err) { + this.logService.error('update#checkForSnapUpdate(): Could not get realpath of application.'); + return; + } + + const currentRevision = path.basename(resolvedCurrentSnapPath); + + if (process.env.SNAP_REVISION !== currentRevision) { + // TODO@joao: snap + this.setState(State.Ready({ version: '', productVersion: '' })); + } else { + this.setState(State.Idle(UpdateType.Archive)); + } + }); } protected doDownloadUpdate(state: AvailableForDownload): TPromise { @@ -84,4 +112,21 @@ export class LinuxUpdateService extends AbstractUpdateService { this.setState(State.Idle(UpdateType.Archive)); return TPromise.as(void 0); } + + protected doQuitAndInstall(): void { + this.logService.trace('update#quitAndInstall(): running raw#quitAndInstall()'); + + const snap = process.env.SNAP; + + // TODO@joao what to do? + if (!snap) { + return; + } + + // Allow 3 seconds for VS Code to close + spawn('bash', ['-c', path.join(snap, `usr/share/${product.applicationName}/snapUpdate.sh`)], { + detached: true, + stdio: ['ignore', 'ignore', 'ignore'] + }); + } } diff --git a/src/vs/platform/update/electron-main/updateService.snap.ts b/src/vs/platform/update/electron-main/updateService.snap.ts new file mode 100644 index 000000000..ca5094a9a --- /dev/null +++ b/src/vs/platform/update/electron-main/updateService.snap.ts @@ -0,0 +1,224 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event, Emitter, fromNodeEventEmitter, filterEvent, debounceEvent } from 'vs/base/common/event'; +import { Throttler, timeout } from 'vs/base/common/async'; +import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; +import product from 'vs/platform/node/product'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IUpdateService, State, StateType, AvailableForDownload, UpdateType } from 'vs/platform/update/common/update'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ILogService } from 'vs/platform/log/common/log'; +import * as path from 'path'; +import { realpath, watch } from 'fs'; +import { spawn } from 'child_process'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; + +abstract class AbstractUpdateService2 implements IUpdateService { + + _serviceBrand: any; + + private _state: State = State.Uninitialized; + private throttler: Throttler = new Throttler(); + + private _onStateChange = new Emitter(); + get onStateChange(): Event { return this._onStateChange.event; } + + get state(): State { + return this._state; + } + + protected setState(state: State): void { + this.logService.info('update#setState', state.type); + this._state = state; + this._onStateChange.fire(state); + } + + constructor( + @ILifecycleService private lifecycleService: ILifecycleService, + @IEnvironmentService environmentService: IEnvironmentService, + @ILogService protected logService: ILogService, + ) { + if (environmentService.disableUpdates) { + this.logService.info('update#ctor - updates are disabled'); + return; + } + + this.setState(State.Idle(this.getUpdateType())); + + // Start checking for updates after 30 seconds + this.scheduleCheckForUpdates(30 * 1000).then(undefined, err => this.logService.error(err)); + } + + private scheduleCheckForUpdates(delay = 60 * 60 * 1000): Thenable { + return timeout(delay) + .then(() => this.checkForUpdates(null)) + .then(() => { + // Check again after 1 hour + return this.scheduleCheckForUpdates(60 * 60 * 1000); + }); + } + + checkForUpdates(context: any): TPromise { + this.logService.trace('update#checkForUpdates, state = ', this.state.type); + + if (this.state.type !== StateType.Idle) { + return TPromise.as(void 0); + } + + return this.throttler.queue(() => TPromise.as(this.doCheckForUpdates(context))); + } + + downloadUpdate(): TPromise { + this.logService.trace('update#downloadUpdate, state = ', this.state.type); + + if (this.state.type !== StateType.AvailableForDownload) { + return TPromise.as(void 0); + } + + return this.doDownloadUpdate(this.state); + } + + protected doDownloadUpdate(state: AvailableForDownload): TPromise { + return TPromise.as(void 0); + } + + applyUpdate(): TPromise { + this.logService.trace('update#applyUpdate, state = ', this.state.type); + + if (this.state.type !== StateType.Downloaded) { + return TPromise.as(void 0); + } + + return this.doApplyUpdate(); + } + + protected doApplyUpdate(): TPromise { + return TPromise.as(void 0); + } + + quitAndInstall(): TPromise { + this.logService.trace('update#quitAndInstall, state = ', this.state.type); + + if (this.state.type !== StateType.Ready) { + return TPromise.as(void 0); + } + + this.logService.trace('update#quitAndInstall(): before lifecycle quit()'); + + this.lifecycleService.quit(true /* from update */).then(vetod => { + this.logService.trace(`update#quitAndInstall(): after lifecycle quit() with veto: ${vetod}`); + if (vetod) { + return; + } + + this.logService.trace('update#quitAndInstall(): running raw#quitAndInstall()'); + this.doQuitAndInstall(); + }); + + return TPromise.as(void 0); + } + + + protected getUpdateType(): UpdateType { + return UpdateType.Snap; + } + + protected doQuitAndInstall(): void { + // noop + } + + abstract isLatestVersion(): TPromise; + protected abstract doCheckForUpdates(context: any): void; +} + +export class SnapUpdateService extends AbstractUpdateService2 { + + _serviceBrand: any; + + constructor( + @ILifecycleService lifecycleService: ILifecycleService, + @IEnvironmentService environmentService: IEnvironmentService, + @ILogService logService: ILogService, + @ITelemetryService private telemetryService: ITelemetryService + ) { + super(lifecycleService, environmentService, logService); + + if (typeof process.env.SNAP === 'undefined') { + throw new Error(`'SNAP' environment variable not set`); + } + const watcher = watch(path.dirname(process.env.SNAP)); + const onChange = fromNodeEventEmitter(watcher, 'change', (_, fileName: string) => fileName); + const onCurrentChange = filterEvent(onChange, n => n === 'current'); + const onDebouncedCurrentChange = debounceEvent(onCurrentChange, (_, e) => e, 2000); + const listener = onDebouncedCurrentChange(this.checkForUpdates, this); + + lifecycleService.onShutdown(() => { + listener.dispose(); + watcher.close(); + }); + } + + protected doCheckForUpdates(context: any): void { + this.setState(State.CheckingForUpdates(context)); + + this.isUpdateAvailable().then(result => { + if (result) { + this.setState(State.Ready({ version: 'something', productVersion: 'someting' })); + } else { + /* __GDPR__ + "update:notAvailable" : { + "explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ + this.telemetryService.publicLog('update:notAvailable', { explicit: !!context }); + + this.setState(State.Idle(UpdateType.Snap)); + } + }, err => { + this.logService.error(err); + + /* __GDPR__ + "update:notAvailable" : { + "explicit" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ + this.telemetryService.publicLog('update:notAvailable', { explicit: !!context }); + this.setState(State.Idle(UpdateType.Snap, err.message || err)); + }); + } + + protected doQuitAndInstall(): void { + this.logService.trace('update#quitAndInstall(): running raw#quitAndInstall()'); + + if (typeof process.env.SNAP === 'undefined') { + return; + } + + // Allow 3 seconds for VS Code to close + spawn('bash', ['-c', path.join(process.env.SNAP, `usr/share/${product.applicationName}/snapUpdate.sh`)], { + detached: true, + stdio: ['ignore', 'ignore', 'ignore'] + }); + } + + private isUpdateAvailable(): TPromise { + return new TPromise((c, e) => { + realpath(`/snap/${product.applicationName}/current`, (err, resolvedCurrentSnapPath) => { + if (err) { return e(err); } + + const currentRevision = path.basename(resolvedCurrentSnapPath); + c(process.env.SNAP_REVISION !== currentRevision); + }); + }); + } + + isLatestVersion(): TPromise { + return this.isUpdateAvailable().then(null, err => { + this.logService.error('update#checkForSnapUpdate(): Could not get realpath of application.'); + return undefined; + }); + } +} diff --git a/src/vs/platform/update/electron-main/updateService.win32.ts b/src/vs/platform/update/electron-main/updateService.win32.ts index 9bb827b2b..c63bd1802 100644 --- a/src/vs/platform/update/electron-main/updateService.win32.ts +++ b/src/vs/platform/update/electron-main/updateService.win32.ts @@ -11,7 +11,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; import { IRequestService } from 'vs/platform/request/node/request'; import product from 'vs/platform/node/product'; -import { TPromise, Promise } from 'vs/base/common/winjs.base'; import { State, IUpdate, StateType, AvailableForDownload, UpdateType } from 'vs/platform/update/common/update'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -23,19 +22,12 @@ import { tmpdir } from 'os'; import { spawn } from 'child_process'; import { shell } from 'electron'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { timeout } from 'vs/base/common/async'; -function pollUntil(fn: () => boolean, timeout = 1000): TPromise { - return new TPromise(c => { - const poll = () => { - if (fn()) { - c(null); - } else { - setTimeout(poll, timeout); - } - }; - - poll(); - }); +async function pollUntil(fn: () => boolean, millis = 1000): Promise { + while (!fn()) { + await timeout(millis); + } } interface IAvailableUpdate { @@ -61,7 +53,7 @@ export class Win32UpdateService extends AbstractUpdateService { private availableUpdate: IAvailableUpdate | undefined; @memoize - get cachePath(): TPromise { + get cachePath(): Thenable { const result = path.join(tmpdir(), `vscode-update-${product.target}-${process.arch}`); return pfs.mkdirp(result, null).then(() => result); } @@ -128,12 +120,12 @@ export class Win32UpdateService extends AbstractUpdateService { this.telemetryService.publicLog('update:notAvailable', { explicit: !!context }); this.setState(State.Idle(updateType)); - return TPromise.as(null); + return Promise.resolve(null); } if (updateType === UpdateType.Archive) { this.setState(State.AvailableForDownload(update)); - return TPromise.as(null); + return Promise.resolve(null); } this.setState(State.Downloading(update)); @@ -142,7 +134,7 @@ export class Win32UpdateService extends AbstractUpdateService { return this.getUpdatePackagePath(update.version).then(updatePackagePath => { return pfs.exists(updatePackagePath).then(exists => { if (exists) { - return TPromise.as(updatePackagePath); + return Promise.resolve(updatePackagePath); } const url = update.url; @@ -184,35 +176,40 @@ export class Win32UpdateService extends AbstractUpdateService { }); } - protected doDownloadUpdate(state: AvailableForDownload): TPromise { + protected async doDownloadUpdate(state: AvailableForDownload): Promise { shell.openExternal(state.update.url); this.setState(State.Idle(getUpdateType())); - return TPromise.as(null); } - private getUpdatePackagePath(version: string): TPromise { - return this.cachePath.then(cachePath => path.join(cachePath, `CodeSetup-${product.quality}-${version}.exe`)); + private async getUpdatePackagePath(version: string): Promise { + const cachePath = await this.cachePath; + return path.join(cachePath, `CodeSetup-${product.quality}-${version}.exe`); } - private cleanup(exceptVersion: string | null = null): Promise { + private async cleanup(exceptVersion: string | null = null): Promise { const filter = exceptVersion ? one => !(new RegExp(`${product.quality}-${exceptVersion}\\.exe$`).test(one)) : () => true; - return this.cachePath - .then(cachePath => pfs.readdir(cachePath) - .then(all => Promise.join(all - .filter(filter) - .map(one => pfs.unlink(path.join(cachePath, one)).then(null, () => null)) - )) - ); + const cachePath = await this.cachePath; + const versions = await pfs.readdir(cachePath); + + const promises = versions.filter(filter).map(async one => { + try { + await pfs.unlink(path.join(cachePath, one)); + } catch (err) { + // ignore + } + }); + + await Promise.all(promises); } - protected doApplyUpdate(): TPromise { + protected doApplyUpdate(): Thenable { if (this.state.type !== StateType.Downloaded && this.state.type !== StateType.Downloading) { - return TPromise.as(null); + return Promise.resolve(null); } if (!this.availableUpdate) { - return TPromise.as(null); + return Promise.resolve(null); } const update = this.state.update; diff --git a/src/vs/platform/update/node/updateIpc.ts b/src/vs/platform/update/node/updateIpc.ts index 5581cf09f..7c01e2a79 100644 --- a/src/vs/platform/update/node/updateIpc.ts +++ b/src/vs/platform/update/node/updateIpc.ts @@ -4,36 +4,23 @@ *--------------------------------------------------------------------------------------------*/ import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; import { Event, Emitter } from 'vs/base/common/event'; import { IUpdateService, State } from 'vs/platform/update/common/update'; -export interface IUpdateChannel extends IChannel { - listen(event: 'onStateChange'): Event; - listen(command: string, arg?: any): Event; - - call(command: 'checkForUpdates', arg: any): TPromise; - call(command: 'downloadUpdate'): TPromise; - call(command: 'applyUpdate'): TPromise; - call(command: 'quitAndInstall'): TPromise; - call(command: '_getInitialState'): TPromise; - call(command: 'isLatestVersion'): TPromise; - call(command: string, arg?: any): TPromise; -} - -export class UpdateChannel implements IUpdateChannel { +export class UpdateChannel implements IServerChannel { constructor(private service: IUpdateService) { } - listen(event: string, arg?: any): Event { + listen(_, event: string): Event { switch (event) { case 'onStateChange': return this.service.onStateChange; } - throw new Error('No event found'); + throw new Error(`Event not found: ${event}`); } - call(command: string, arg?: any): TPromise { + call(_, command: string, arg?: any): TPromise { switch (command) { case 'checkForUpdates': return this.service.checkForUpdates(arg); case 'downloadUpdate': return this.service.downloadUpdate(); @@ -42,7 +29,8 @@ export class UpdateChannel implements IUpdateChannel { case '_getInitialState': return TPromise.as(this.service.state); case 'isLatestVersion': return this.service.isLatestVersion(); } - return undefined; + + throw new Error(`Call not found: ${command}`); } } @@ -56,17 +44,17 @@ export class UpdateChannelClient implements IUpdateService { private _state: State = State.Uninitialized; get state(): State { return this._state; } - constructor(private channel: IUpdateChannel) { + constructor(private channel: IChannel) { // always set this._state as the state changes this.onStateChange(state => this._state = state); - channel.call('_getInitialState').then(state => { + channel.call('_getInitialState').then(state => { // fire initial state this._onStateChange.fire(state); // fire subsequent states as they come in from remote - this.channel.listen('onStateChange')(state => this._onStateChange.fire(state)); + this.channel.listen('onStateChange')(state => this._onStateChange.fire(state)); }); } diff --git a/src/vs/platform/url/common/url.ts b/src/vs/platform/url/common/url.ts index 0ef98b611..f58130897 100644 --- a/src/vs/platform/url/common/url.ts +++ b/src/vs/platform/url/common/url.ts @@ -6,18 +6,16 @@ import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; -export const ID = 'urlService'; -export const IURLService = createDecorator(ID); +export const IURLService = createDecorator('urlService'); export interface IURLHandler { - handleURL(uri: URI): TPromise; + handleURL(uri: URI): Thenable; } export interface IURLService { _serviceBrand: any; - open(url: URI): TPromise; + open(url: URI): Thenable; registerHandler(handler: IURLHandler): IDisposable; } diff --git a/src/vs/platform/url/common/urlService.ts b/src/vs/platform/url/common/urlService.ts index 1f3b94ce2..d70094077 100644 --- a/src/vs/platform/url/common/urlService.ts +++ b/src/vs/platform/url/common/urlService.ts @@ -6,7 +6,6 @@ import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; import { URI } from 'vs/base/common/uri'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; import { first } from 'vs/base/common/async'; declare module Array { @@ -19,7 +18,7 @@ export class URLService implements IURLService { private handlers = new Set(); - open(uri: URI): TPromise { + open(uri: URI): Thenable { const handlers = Array.from(this.handlers); return first(handlers.map(h => () => h.handleURL(uri)), undefined, false); } @@ -36,11 +35,11 @@ export class RelayURLService extends URLService implements IURLHandler { super(); } - open(uri: URI): TPromise { + open(uri: URI): Thenable { return this.urlService.open(uri); } - handleURL(uri: URI): TPromise { + handleURL(uri: URI): Thenable { return super.open(uri); } -} \ No newline at end of file +} diff --git a/src/vs/platform/url/electron-main/electronUrlListener.ts b/src/vs/platform/url/electron-main/electronUrlListener.ts index 9a662f649..233f7c2ab 100644 --- a/src/vs/platform/url/electron-main/electronUrlListener.ts +++ b/src/vs/platform/url/electron-main/electronUrlListener.ts @@ -11,6 +11,7 @@ import { URI } from 'vs/base/common/uri'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; import { ReadyState } from 'vs/platform/windows/common/windows'; +import { isWindows } from 'vs/base/common/platform'; function uriFromRawUrl(url: string): URI | null { try { @@ -42,7 +43,9 @@ export class ElectronURLListener { } }); - app.setAsDefaultProtocolClient(product.urlProtocol, process.execPath, ['--open-url', '--']); + if (isWindows) { + app.setAsDefaultProtocolClient(product.urlProtocol, process.execPath, ['--open-url', '--']); + } const onOpenElectronUrl = mapEvent( fromNodeEventEmitter(app, 'open-url', (event: Electron.Event, url: string) => ({ event, url })), diff --git a/src/vs/platform/url/node/urlIpc.ts b/src/vs/platform/url/node/urlIpc.ts index 2721d7a0a..40e14de4b 100644 --- a/src/vs/platform/url/node/urlIpc.ts +++ b/src/vs/platform/url/node/urlIpc.ts @@ -3,31 +3,26 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; import { URI } from 'vs/base/common/uri'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; -export interface IURLServiceChannel extends IChannel { - call(command: 'open', url: string): Thenable; - call(command: string, arg?: any): Thenable; -} - -export class URLServiceChannel implements IURLServiceChannel { +export class URLServiceChannel implements IServerChannel { constructor(private service: IURLService) { } - listen(event: string, arg?: any): Event { - throw new Error('No events'); + listen(_, event: string): Event { + throw new Error(`Event not found: ${event}`); } - call(command: string, arg?: any): Thenable { + call(_, command: string, arg?: any): Thenable { switch (command) { case 'open': return this.service.open(URI.revive(arg)); } - return undefined; + + throw new Error(`Call not found: ${command}`); } } @@ -37,8 +32,8 @@ export class URLServiceChannelClient implements IURLService { constructor(private channel: IChannel) { } - open(url: URI): TPromise { - return TPromise.wrap(this.channel.call('open', url.toJSON())); + open(url: URI): Thenable { + return this.channel.call('open', url.toJSON()); } registerHandler(handler: IURLHandler): IDisposable { @@ -46,24 +41,20 @@ export class URLServiceChannelClient implements IURLService { } } -export interface IURLHandlerChannel extends IChannel { - call(command: 'handleURL', arg: any): Thenable; - call(command: string, arg?: any): Thenable; -} - -export class URLHandlerChannel implements IURLHandlerChannel { +export class URLHandlerChannel implements IServerChannel { constructor(private handler: IURLHandler) { } - listen(event: string, arg?: any): Event { - throw new Error('No events'); + listen(_, event: string): Event { + throw new Error(`Event not found: ${event}`); } - call(command: string, arg?: any): Thenable { + call(_, command: string, arg?: any): Thenable { switch (command) { case 'handleURL': return this.handler.handleURL(URI.revive(arg)); } - return undefined; + + throw new Error(`Call not found: ${command}`); } } @@ -71,7 +62,7 @@ export class URLHandlerChannelClient implements IURLHandler { constructor(private channel: IChannel) { } - handleURL(uri: URI): TPromise { - return TPromise.wrap(this.channel.call('handleURL', uri.toJSON())); + handleURL(uri: URI): Thenable { + return this.channel.call('handleURL', uri.toJSON()); } -} \ No newline at end of file +} diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 87e6692e3..0317dca6b 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -7,8 +7,8 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event, latch, anyEvent } from 'vs/base/common/event'; import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; -import { IProcessEnvironment } from 'vs/base/common/platform'; -import { ParsedArgs } from 'vs/platform/environment/common/environment'; +import { IProcessEnvironment, isMacintosh, isWindows } from 'vs/base/common/platform'; +import { ParsedArgs, IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IRecentlyOpened } from 'vs/platform/history/common/history'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; @@ -16,6 +16,7 @@ import { ExportData } from 'vs/base/common/performance'; import { LogLevel } from 'vs/platform/log/common/log'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export const IWindowsService = createDecorator('windowsService'); @@ -83,6 +84,7 @@ export interface SaveDialogOptions { } export interface INewWindowOptions { + remoteAuthority?: string; } export interface IDevToolsOptions { @@ -236,11 +238,46 @@ export interface IWindowSettings { menuBarVisibility: MenuBarVisibility; newWindowDimensions: 'default' | 'inherit' | 'maximized' | 'fullscreen'; nativeTabs: boolean; + nativeFullScreen: boolean; enableMenuBarMnemonics: boolean; closeWhenEmpty: boolean; + smoothScrollingWorkaround: boolean; clickThroughInactive: boolean; } +export function getTitleBarStyle(configurationService: IConfigurationService, environment: IEnvironmentService): 'native' | 'custom' { + const configuration = configurationService.getValue('window'); + + const isDev = !environment.isBuilt || environment.isExtensionDevelopment; + if (isMacintosh && isDev) { + return 'native'; // not enabled when developing due to https://github.com/electron/electron/issues/3647 + } + + if (configuration) { + const useNativeTabs = isMacintosh && configuration.nativeTabs === true; + if (useNativeTabs) { + return 'native'; // native tabs on sierra do not work with custom title style + } + + const useSimpleFullScreen = isMacintosh && configuration.nativeFullScreen === false; + if (useSimpleFullScreen) { + return 'native'; // simple fullscreen does not work well with custom title style (https://github.com/Microsoft/vscode/issues/63291) + } + + const smoothScrollingWorkaround = isWindows && configuration.smoothScrollingWorkaround === true; + if (smoothScrollingWorkaround) { + return 'native'; // smooth scrolling workaround does not work with custom title style + } + + const style = configuration.titleBarStyle; + if (style === 'native') { + return 'native'; + } + } + + return 'custom'; // default to custom on all OS +} + export const enum OpenContext { // opening when running from the command line @@ -343,6 +380,8 @@ export interface IWindowConfiguration extends ParsedArgs { workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; + remoteAuthority?: string; + zoomLevel?: number; fullscreen?: boolean; maximized?: boolean; diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 839722840..0f68cac72 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -38,6 +38,8 @@ export interface ICodeWindow { openedWorkspace: IWorkspaceIdentifier; backupPath: string; + remoteAuthority: string; + isExtensionDevelopmentHost: boolean; isExtensionTestHost: boolean; @@ -60,6 +62,7 @@ export interface ICodeWindow { sendWhenReady(channel: string, ...args: any[]): void; toggleFullScreen(): void; + isFullScreen(): boolean; hasHiddenTitleBarStyle(): boolean; setRepresentedFilename(name: string): void; getRepresentedFilename(): string; diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index 74d1bc605..d215306ae 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; +import * as os from 'os'; import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { assign } from 'vs/base/common/objects'; @@ -136,7 +137,7 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable if (codeWindow) { const contents = codeWindow.win.webContents; - if (isMacintosh && codeWindow.hasHiddenTitleBarStyle() && !codeWindow.win.isFullScreen() && !contents.isDevToolsOpened()) { + if (isMacintosh && codeWindow.hasHiddenTitleBarStyle() && !codeWindow.isFullScreen() && !contents.isDevToolsOpened()) { contents.openDevTools({ mode: 'undocked' }); // due to https://github.com/electron/electron/issues/3647 } else { contents.toggleDevTools(); @@ -509,7 +510,7 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable } const detail = nls.localize('aboutDetail', - "Version: {0}\nCommit: {1}\nDate: {2}\nElectron: {3}\nChrome: {4}\nNode.js: {5}\nV8: {6}\nArchitecture: {7}", + "Version: {0}\nCommit: {1}\nDate: {2}\nElectron: {3}\nChrome: {4}\nNode.js: {5}\nV8: {6}\nOS: {7}", version, product.commit || 'Unknown', product.date || 'Unknown', @@ -517,7 +518,7 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable process.versions['chrome'], process.versions['node'], process.versions['v8'], - process.arch + `${os.type()} ${os.arch()} ${os.release()}` ); const ok = nls.localize('okButton', "OK"); diff --git a/src/vs/platform/windows/node/windowsIpc.ts b/src/vs/platform/windows/node/windowsIpc.ts index c2441ba4c..033a99380 100644 --- a/src/vs/platform/windows/node/windowsIpc.ts +++ b/src/vs/platform/windows/node/windowsIpc.ts @@ -5,78 +5,15 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { Event, buffer } from 'vs/base/common/event'; -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; import { IWindowsService, INativeOpenDialogOptions, IEnterWorkspaceResult, CrashReporterStartOptions, IMessageBoxResult, MessageBoxOptions, SaveDialogOptions, OpenDialogOptions, IDevToolsOptions, INewWindowOptions } from 'vs/platform/windows/common/windows'; import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IRecentlyOpened } from 'vs/platform/history/common/history'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; -import { URI, UriComponents } from 'vs/base/common/uri'; +import { URI } from 'vs/base/common/uri'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; -export interface IWindowsChannel extends IChannel { - listen(event: 'onWindowOpen'): Event; - listen(event: 'onWindowFocus'): Event; - listen(event: 'onWindowBlur'): Event; - listen(event: 'onWindowMaximize'): Event; - listen(event: 'onWindowUnmaximize'): Event; - listen(event: 'onRecentlyOpenedChange'): Event; - listen(event: string, arg?: any): Event; - - call(command: 'pickFileFolderAndOpen', arg: INativeOpenDialogOptions): Thenable; - call(command: 'pickFileAndOpen', arg: INativeOpenDialogOptions): Thenable; - call(command: 'pickFolderAndOpen', arg: INativeOpenDialogOptions): Thenable; - call(command: 'pickWorkspaceAndOpen', arg: INativeOpenDialogOptions): Thenable; - call(command: 'showMessageBox', arg: [number, MessageBoxOptions]): Thenable; - call(command: 'showSaveDialog', arg: [number, SaveDialogOptions]): Thenable; - call(command: 'showOpenDialog', arg: [number, OpenDialogOptions]): Thenable; - call(command: 'reloadWindow', arg: [number, ParsedArgs]): Thenable; - call(command: 'openDevTools', arg: [number, IDevToolsOptions]): Thenable; - call(command: 'toggleDevTools', arg: number): Thenable; - call(command: 'closeWorkspace', arg: number): Thenable; - call(command: 'enterWorkspace', arg: [number, string]): Thenable; - call(command: 'createAndEnterWorkspace', arg: [number, IWorkspaceFolderCreationData[], string]): Thenable; - call(command: 'saveAndEnterWorkspace', arg: [number, string]): Thenable; - call(command: 'toggleFullScreen', arg: number): Thenable; - call(command: 'setRepresentedFilename', arg: [number, string]): Thenable; - call(command: 'addRecentlyOpened', arg: UriComponents[]): Thenable; - call(command: 'removeFromRecentlyOpened', arg: (IWorkspaceIdentifier | UriComponents | string)[]): Thenable; - call(command: 'clearRecentlyOpened'): Thenable; - call(command: 'getRecentlyOpened', arg: number): Thenable; - call(command: 'newWindowTab'): Thenable; - call(command: 'showPreviousWindowTab'): Thenable; - call(command: 'showNextWindowTab'): Thenable; - call(command: 'moveWindowTabToNewWindow'): Thenable; - call(command: 'mergeAllWindowTabs'): Thenable; - call(command: 'toggleWindowTabsBar'): Thenable; - call(command: 'updateTouchBar', arg: [number, ISerializableCommandAction[][]]): Thenable; - call(command: 'focusWindow', arg: number): Thenable; - call(command: 'closeWindow', arg: number): Thenable; - call(command: 'isFocused', arg: number): Thenable; - call(command: 'isMaximized', arg: number): Thenable; - call(command: 'maximizeWindow', arg: number): Thenable; - call(command: 'unmaximizeWindow', arg: number): Thenable; - call(command: 'minimizeWindow', arg: number): Thenable; - call(command: 'onWindowTitleDoubleClick', arg: number): Thenable; - call(command: 'setDocumentEdited', arg: [number, boolean]): Thenable; - call(command: 'quit'): Thenable; - call(command: 'openWindow', arg: [number, URI[], { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs }]): Thenable; - call(command: 'openNewWindow', arg: INewWindowOptions): Thenable; - call(command: 'showWindow', arg: number): Thenable; - call(command: 'getWindows'): Thenable<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]>; - call(command: 'getWindowCount'): Thenable; - call(command: 'relaunch', arg: [{ addArgs?: string[], removeArgs?: string[] }]): Thenable; - call(command: 'whenSharedProcessReady'): Thenable; - call(command: 'toggleSharedProcess'): Thenable; - call(command: 'log', arg: [string, string[]]): Thenable; - call(command: 'showItemInFolder', arg: string): Thenable; - call(command: 'getActiveWindowId'): Thenable; - call(command: 'openExternal', arg: string): Thenable; - call(command: 'startCrashReporter', arg: CrashReporterStartOptions): Thenable; - call(command: 'openAboutDialog'): Thenable; - call(command: 'resolveProxy', arg: [number, string]): Thenable; -} - -export class WindowsChannel implements IWindowsChannel { +export class WindowsChannel implements IServerChannel { private onWindowOpen: Event; private onWindowFocus: Event; @@ -94,7 +31,7 @@ export class WindowsChannel implements IWindowsChannel { this.onRecentlyOpenedChange = buffer(service.onRecentlyOpenedChange, true); } - listen(event: string, arg?: any): Event { + listen(_, event: string): Event { switch (event) { case 'onWindowOpen': return this.onWindowOpen; case 'onWindowFocus': return this.onWindowFocus; @@ -104,10 +41,10 @@ export class WindowsChannel implements IWindowsChannel { case 'onRecentlyOpenedChange': return this.onRecentlyOpenedChange; } - throw new Error('No event found'); + throw new Error(`Event not found: ${event}`); } - call(command: string, arg?: any): Thenable { + call(_, command: string, arg?: any): Thenable { switch (command) { case 'pickFileFolderAndOpen': return this.service.pickFileFolderAndOpen(arg); case 'pickFileAndOpen': return this.service.pickFileAndOpen(arg); @@ -123,7 +60,7 @@ export class WindowsChannel implements IWindowsChannel { case 'enterWorkspace': return this.service.enterWorkspace(arg[0], arg[1]); case 'createAndEnterWorkspace': { const rawFolders: IWorkspaceFolderCreationData[] = arg[1]; - let folders: IWorkspaceFolderCreationData[]; + let folders: IWorkspaceFolderCreationData[] | undefined = undefined; if (Array.isArray(rawFolders)) { folders = rawFolders.map(rawFolder => { return { @@ -181,7 +118,8 @@ export class WindowsChannel implements IWindowsChannel { case 'openAboutDialog': return this.service.openAboutDialog(); case 'resolveProxy': return this.service.resolveProxy(arg[0], arg[1]); } - return undefined; + + throw new Error(`Call not found: ${command}`); } } @@ -189,7 +127,7 @@ export class WindowsChannelClient implements IWindowsService { _serviceBrand: any; - constructor(private channel: IWindowsChannel) { } + constructor(private channel: IChannel) { } get onWindowOpen(): Event { return this.channel.listen('onWindowOpen'); } get onWindowFocus(): Event { return this.channel.listen('onWindowFocus'); } @@ -275,7 +213,7 @@ export class WindowsChannelClient implements IWindowsService { } getRecentlyOpened(windowId: number): TPromise { - return TPromise.wrap(this.channel.call('getRecentlyOpened', windowId)) + return TPromise.wrap(this.channel.call('getRecentlyOpened', windowId)) .then(recentlyOpened => { recentlyOpened.workspaces = recentlyOpened.workspaces.map(workspace => isWorkspaceIdentifier(workspace) ? workspace : URI.revive(workspace)); recentlyOpened.files = recentlyOpened.files.map(URI.revive); @@ -372,7 +310,7 @@ export class WindowsChannelClient implements IWindowsService { } getWindows(): TPromise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]> { - return TPromise.wrap(this.channel.call('getWindows').then(result => { result.forEach(win => win.folderUri = win.folderUri ? URI.revive(win.folderUri) : win.folderUri); return result; })); + return TPromise.wrap(this.channel.call<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]>('getWindows').then(result => { result.forEach(win => win.folderUri = win.folderUri ? URI.revive(win.folderUri) : win.folderUri); return result; })); } getWindowCount(): TPromise { diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index b6238f5ee..04cc86b69 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -85,9 +85,9 @@ export interface IWorkspacesMainService extends IWorkspacesService { createWorkspaceSync(folders?: IWorkspaceFolderCreationData[]): IWorkspaceIdentifier; - resolveWorkspace(path: string): TPromise; + resolveWorkspace(path: string): TPromise; - resolveWorkspaceSync(path: string): IResolvedWorkspace; + resolveWorkspaceSync(path: string): IResolvedWorkspace | null; isUntitledWorkspace(workspace: IWorkspaceIdentifier): boolean; diff --git a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts index e652ff8cd..2d204490b 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts @@ -50,7 +50,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain this.workspacesHome = environmentService.workspacesHome; } - resolveWorkspace(path: string): TPromise { + resolveWorkspace(path: string): TPromise { if (!this.isWorkspacePath(path)) { return TPromise.as(null); // does not look like a valid workspace config file } @@ -58,7 +58,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain return readFile(path, 'utf8').then(contents => this.doResolveWorkspace(path, contents)); } - resolveWorkspaceSync(path: string): IResolvedWorkspace { + resolveWorkspaceSync(path: string): IResolvedWorkspace | null { if (!this.isWorkspacePath(path)) { return null; // does not look like a valid workspace config file } @@ -77,7 +77,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain return this.isInsideWorkspacesHome(path) || extname(path) === `.${WORKSPACE_EXTENSION}`; } - private doResolveWorkspace(path: string, contents: string): IResolvedWorkspace { + private doResolveWorkspace(path: string, contents: string): IResolvedWorkspace | null { try { const workspace = this.doParseStoredWorkspace(path, contents); diff --git a/src/vs/platform/workspaces/node/workspacesIpc.ts b/src/vs/platform/workspaces/node/workspacesIpc.ts index bdfc6d7b0..333407eff 100644 --- a/src/vs/platform/workspaces/node/workspacesIpc.ts +++ b/src/vs/platform/workspaces/node/workspacesIpc.ts @@ -4,29 +4,24 @@ *--------------------------------------------------------------------------------------------*/ import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; import { IWorkspacesService, IWorkspaceIdentifier, IWorkspaceFolderCreationData, IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces'; import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; -export interface IWorkspacesChannel extends IChannel { - call(command: 'createWorkspace', arg: [IWorkspaceFolderCreationData[]]): Thenable; - call(command: string, arg?: any): Thenable; -} - -export class WorkspacesChannel implements IWorkspacesChannel { +export class WorkspacesChannel implements IServerChannel { constructor(private service: IWorkspacesMainService) { } - listen(event: string, arg?: any): Event { - throw new Error('No events'); + listen(_, event: string): Event { + throw new Error(`Event not found: ${event}`); } - call(command: string, arg?: any): Thenable { + call(_, command: string, arg?: any): Thenable { switch (command) { case 'createWorkspace': { const rawFolders: IWorkspaceFolderCreationData[] = arg; - let folders: IWorkspaceFolderCreationData[]; + let folders: IWorkspaceFolderCreationData[] | undefined = undefined; if (Array.isArray(rawFolders)) { folders = rawFolders.map(rawFolder => { return { @@ -40,7 +35,7 @@ export class WorkspacesChannel implements IWorkspacesChannel { } } - return void 0; + throw new Error(`Call not found: ${command}`); } } @@ -48,7 +43,7 @@ export class WorkspacesChannelClient implements IWorkspacesService { _serviceBrand: any; - constructor(private channel: IWorkspacesChannel) { } + constructor(private channel: IChannel) { } createWorkspace(folders?: IWorkspaceFolderCreationData[]): TPromise { return TPromise.wrap(this.channel.call('createWorkspace', folders)); diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 70b7b2239..f7eb1412f 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -928,12 +928,12 @@ declare module 'vscode' { overviewRulerColor?: string | ThemeColor; /** - * Defines the rendering options of the attachment that is inserted before the decorated text + * Defines the rendering options of the attachment that is inserted before the decorated text. */ before?: ThemableDecorationAttachmentRenderOptions; /** - * Defines the rendering options of the attachment that is inserted after the decorated text + * Defines the rendering options of the attachment that is inserted after the decorated text. */ after?: ThemableDecorationAttachmentRenderOptions; } @@ -1046,12 +1046,12 @@ declare module 'vscode' { export interface ThemableDecorationInstanceRenderOptions { /** - * Defines the rendering options of the attachment that is inserted before the decorated text + * Defines the rendering options of the attachment that is inserted before the decorated text. */ before?: ThemableDecorationAttachmentRenderOptions; /** - * Defines the rendering options of the attachment that is inserted after the decorated text + * Defines the rendering options of the attachment that is inserted after the decorated text. */ after?: ThemableDecorationAttachmentRenderOptions; } @@ -2208,34 +2208,7 @@ declare module 'vscode' { * Provides additional metadata over normal [location](#Location) definitions, including the range of * the defining symbol */ - export interface DefinitionLink { - /** - * Span of the symbol being defined in the source file. - * - * Used as the underlined span for mouse definition hover. Defaults to the word range at - * the definition position. - */ - originSelectionRange?: Range; - - /** - * The resource identifier of the definition. - */ - targetUri: Uri; - - /** - * The full range of the definition. - * - * For a class definition for example, this would be the entire body of the class definition. - */ - targetRange: Range; - - /** - * The span of the symbol definition. - * - * For a class definition, this would be the class name itself in the class definition. - */ - targetSelectionRange?: Range; - } + export type DefinitionLink = LocationLink; /** * The definition of a symbol represented as one or many [locations](#Location). @@ -2299,6 +2272,30 @@ declare module 'vscode' { provideTypeDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; } + /** + * The declaration of a symbol representation as one or many [locations](#Location) + * or [location links][#LocationLink]. + */ + export type Declaration = Location | Location[] | LocationLink[]; + + /** + * The declaration provider interface defines the contract between extensions and + * the go to declaration feature. + */ + export interface DeclarationProvider { + + /** + * Provide the declaration of the symbol at the given position and document. + * + * @param document The document in which the command was invoked. + * @param position The position at which the command was invoked. + * @param token A cancellation token. + * @return A declaration or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideDeclaration(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + } + /** * The MarkdownString represents human readable text that supports formatting via the * markdown syntax. Standard markdown is supported, also tables, but no embedded html. @@ -2614,6 +2611,16 @@ declare module 'vscode' { provideDocumentSymbols(document: TextDocument, token: CancellationToken): ProviderResult; } + /** + * Metadata about a document symbol provider. + */ + export interface DocumentSymbolProviderMetadata { + /** + * A human readable string that is shown when multiple outlines trees show for one document. + */ + label?: string; + } + /** * The workspace symbol provider interface defines the contract between extensions and * the [symbol search](https://code.visualstudio.com/docs/editor/editingevolved#_open-symbol-by-name)-feature. @@ -3252,6 +3259,13 @@ declare module 'vscode' { */ commitCharacters?: string[]; + /** + * Keep whitespace of the [insertText](#CompletionItem.insertText) as is. By default, the editor adjusts leading + * whitespace of new lines so that they match the indentation of the line for which the item is accepeted - setting + * this to `true` will prevent that. + */ + keepWhitespace?: boolean; + /** * @deprecated Use `CompletionItem.insertText` and `CompletionItem.range` instead. * @@ -3993,6 +4007,35 @@ declare module 'vscode' { constructor(uri: Uri, rangeOrPosition: Range | Position); } + /** + * Represents the connection of two locations. Provides additional metadata over normal [locations](#Location), + * including an origin range. + */ + export interface LocationLink { + /** + * Span of the origin of this link. + * + * Used as the underlined span for mouse definition hover. Defaults to the word range at + * the definition position. + */ + originSelectionRange?: Range; + + /** + * The target resource identifier of this link. + */ + targetUri: Uri; + + /** + * The full target range of this link. + */ + targetRange: Range; + + /** + * The span of this link. + */ + targetSelectionRange?: Range; + } + /** * The event that is fired when diagnostics change. */ @@ -4670,6 +4713,11 @@ declare module 'vscode' { * Controls whether to show the "Terminal will be reused by tasks, press any key to close it" message. */ showReuseMessage?: boolean; + + /** + * Controls whether the terminal is cleared before executing the task. + */ + clear?: boolean; } /** @@ -5715,6 +5763,24 @@ declare module 'vscode' { deserializeWebviewPanel(webviewPanel: WebviewPanel, state: any): Thenable; } + /** + * The clipboard provides read and write access to the system's clipboard. + */ + export interface Clipboard { + + /** + * Read the current clipboard contents as text. + * @returns A thenable that resolves to a string. + */ + readText(): Thenable; + + /** + * Writes text into the clipboard. + * @returns A thenable that resolves when writing happened. + */ + writeText(value: string): Thenable; + } + /** * Namespace describing the environment the editor runs in. */ @@ -5741,6 +5807,11 @@ declare module 'vscode' { */ export let language: string; + /** + * The system clipboard. + */ + export const clipboard: Clipboard; + /** * A unique identifier for the computer. * @@ -5840,6 +5911,7 @@ declare module 'vscode' { * the command handler function doesn't return anything. */ export function executeCommand(command: string, ...rest: any[]): Thenable; + export function executeCommand(command: 'vscode.previewHtml', error: { '⚠️ The vscode.previewHtml command is deprecated and will be removed. Please switch to using the Webview Api': never }, ...rest: any[]): Thenable; /** * Retrieve the list of all available commands. Commands starting an underscore are @@ -6379,10 +6451,10 @@ declare module 'vscode' { /** * Create a [TreeView](#TreeView) for the view contributed using the extension point `views`. * @param viewId Id of the view contributed using the extension point `views`. - * @param options Options object to provide [TreeDataProvider](#TreeDataProvider) for the view. + * @param options Options for creating the [TreeView](#TreeView) * @returns a [TreeView](#TreeView). */ - export function createTreeView(viewId: string, options: { treeDataProvider: TreeDataProvider }): TreeView; + export function createTreeView(viewId: string, options: TreeViewOptions): TreeView; /** * Registers a [uri handler](#UriHandler) capable of handling system-wide [uris](#Uri). @@ -6421,6 +6493,22 @@ declare module 'vscode' { export function registerWebviewPanelSerializer(viewType: string, serializer: WebviewPanelSerializer): Disposable; } + /** + * Options for creating a [TreeView](#TreeView) + */ + export interface TreeViewOptions { + + /** + * A data provider that provides tree data. + */ + treeDataProvider: TreeDataProvider; + + /** + * Whether to show collapse all action or not. + */ + showCollapseAll?: boolean; + } + /** * The event that is fired when an element in the [TreeView](#TreeView) is expanded or collapsed */ @@ -6496,13 +6584,15 @@ declare module 'vscode' { * Reveals the given element in the tree view. * If the tree view is not visible then the tree view is shown and element is revealed. * - * By default revealed element is selected and not focused. + * By default revealed element is selected. * In order to not to select, set the option `select` to `false`. * In order to focus, set the option `focus` to `true`. + * In order to expand the revealed element, set the option `expand` to `true`. To expand recursively set `expand` to the number of levels to expand. + * **NOTE:** You can expand only to 3 levels maximum. * * **NOTE:** [TreeDataProvider](#TreeDataProvider) is required to implement [getParent](#TreeDataProvider.getParent) method to access this API. */ - reveal(element: T, options?: { select?: boolean, focus?: boolean }): Thenable; + reveal(element: T, options?: { select?: boolean, focus?: boolean, expand?: boolean | number }): Thenable; } /** @@ -7668,6 +7758,19 @@ declare module 'vscode' { */ export function registerTypeDefinitionProvider(selector: DocumentSelector, provider: TypeDefinitionProvider): Disposable; + /** + * Register a declaration provider. + * + * Multiple providers can be registered for a language. In that case providers are asked in + * parallel and the results are merged. A failing provider (rejected promise or exception) will + * not cause a failure of the whole operation. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A declaration provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerDeclarationProvider(selector: DocumentSelector, provider: DeclarationProvider): Disposable; + /** * Register a hover provider. * @@ -7703,9 +7806,10 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider A document symbol provider. + * @param metaData metadata about the provider * @return A [disposable](#Disposable) that unregisters this provider when being disposed. */ - export function registerDocumentSymbolProvider(selector: DocumentSelector, provider: DocumentSymbolProvider): Disposable; + export function registerDocumentSymbolProvider(selector: DocumentSelector, provider: DocumentSymbolProvider, metaData?: DocumentSymbolProviderMetadata): Disposable; /** * Register a workspace symbol provider. @@ -8131,6 +8235,19 @@ declare module 'vscode' { */ readonly name: string; + /** + * The workspace folder of this session or `undefined` for a folderless setup. + */ + readonly workspaceFolder: WorkspaceFolder | undefined; + + /** + * The "resolved" [debug configuration](#DebugConfiguration) of this session. + * "Resolved" means that + * - all variables have been substituted and + * - platform specific attribute sections have been "flattened" for the matching platform and removed for non-matching platforms. + */ + readonly configuration: DebugConfiguration; + /** * Send a custom request to the debug adapter. */ @@ -8167,7 +8284,7 @@ declare module 'vscode' { * Provides initial [debug configuration](#DebugConfiguration). If more than one debug configuration provider is * registered for the same type, debug configurations are concatenated in arbitrary order. * - * @param folder The workspace folder for which the configurations are used or undefined for a folderless setup. + * @param folder The workspace folder for which the configurations are used or `undefined` for a folderless setup. * @param token A cancellation token. * @return An array of [debug configurations](#DebugConfiguration). */ @@ -8180,7 +8297,7 @@ declare module 'vscode' { * Returning the value 'undefined' prevents the debug session from starting. * Returning the value 'null' prevents the debug session from starting and opens the underlying debug configuration instead. * - * @param folder The workspace folder from which the configuration originates from or undefined for a folderless setup. + * @param folder The workspace folder from which the configuration originates from or `undefined` for a folderless setup. * @param debugConfiguration The [debug configuration](#DebugConfiguration) to resolve. * @param token A cancellation token. * @return The resolved debug configuration or undefined or null. @@ -8232,6 +8349,10 @@ declare module 'vscode' { * The base class of all breakpoint types. */ export class Breakpoint { + /** + * The unique ID of the breakpoint. + */ + readonly id: string; /** * Is breakpoint enabled. */ @@ -8296,6 +8417,7 @@ declare module 'vscode' { /** * The currently active [debug console](#DebugConsole). + * If no debug session is active, output sent to the debug console is not shown. */ export let activeDebugConsole: DebugConsole; diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 8639ce750..0a65c4c71 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -3,7 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// This is the place for API experiments and proposals. +/** + * This is the place for API experiments and proposals. + * These API are NOT stable and subject to change. They are only available in the Insiders + * distribution and CANNOT be used in published extensions. + * + * To test these API in local environment: + * - Use Insiders release of VS Code. + * - Add `"enableProposedApi": true` to your package.json. + * - Copy this file to your project. + */ declare module 'vscode' { @@ -11,46 +20,6 @@ declare module 'vscode' { export function sampleFunction(): Thenable; } - //#region Joh - https://github.com/Microsoft/vscode/issues/57093 - - /** - * An insert text rule defines how the [`insertText`](#CompletionItem.insertText) of a - * completion item should be modified. - */ - export enum CompletionItemInsertTextRule { - - /** - * Keep whitespace as is. By default, the editor adjusts leading - * whitespace of new lines so that they match the indentation of - * the line for which the item is accepeted. - */ - KeepWhitespace = 0b01 - } - - export interface CompletionItem { - - /** - * Rules about how/if the `insertText` should be modified by the - * editor. Can be a bit mask of many rules. - */ - insertTextRules?: CompletionItemInsertTextRule; - } - - //#endregion - - //#region Joh - clipboard https://github.com/Microsoft/vscode/issues/217 - - export interface Clipboard { - readText(): Thenable; - writeText(value: string): Thenable; - } - - export namespace env { - export const clipboard: Clipboard; - } - - //#endregion - //#region Joh - read/write in chunks export interface FileSystemProvider { @@ -181,6 +150,16 @@ declare module 'vscode' { * See the vscode setting `"files.encoding"` */ encoding?: string; + + /** + * Number of lines of context to include before each match. + */ + beforeContext?: number; + + /** + * Number of lines of context to include after each match. + */ + afterContext?: number; } /** @@ -214,7 +193,7 @@ declare module 'vscode' { /** * The maximum number of results to be returned. */ - maxResults: number; + maxResults?: number; } /** @@ -225,39 +204,62 @@ declare module 'vscode' { /** * A preview of the text result. */ - export interface TextSearchResultPreview { + export interface TextSearchMatchPreview { /** - * The matching line of text, or a portion of the matching line that contains the match. - * For now, this can only be a single line. + * The matching lines of text, or a portion of the matching line that contains the match. */ text: string; /** * The Range within `text` corresponding to the text of the match. + * The number of matches must match the TextSearchMatch's range property. */ - match: Range; + matches: Range | Range[]; } /** * A match from a text search */ - export interface TextSearchResult { + export interface TextSearchMatch { /** * The uri for the matching document. */ uri: Uri; /** - * The range of the match within the document. + * The range of the match within the document, or multiple ranges for multiple matches. */ - range: Range; + ranges: Range | Range[]; + + /** + * A preview of the text match. + */ + preview: TextSearchMatchPreview; + } + + /** + * A line of context surrounding a TextSearchMatch. + */ + export interface TextSearchContext { + /** + * The uri for the matching document. + */ + uri: Uri; + + /** + * One line of text. + * previewOptions.charsPerLine applies to this + */ + text: string; /** - * A preview of the text result. + * The line number of this line of context. */ - preview: TextSearchResultPreview; + lineNumber: number; } + export type TextSearchResult = TextSearchMatch | TextSearchContext; + /** * A FileIndexProvider provides a list of files in the given folder. VS Code will filter that list for searching with quickopen or from other extensions. * @@ -365,6 +367,16 @@ declare module 'vscode' { * Options to specify the size of the result text preview. */ previewOptions?: TextSearchPreviewOptions; + + /** + * Number of lines of context to include before each match. + */ + beforeContext?: number; + + /** + * Number of lines of context to include after each match. + */ + afterContext?: number; } export namespace workspace { @@ -493,40 +505,54 @@ declare module 'vscode' { //#region André: debug /** - * Represents a debug adapter executable and optional arguments passed to it. + * Represents a debug adapter executable and optional arguments and runtime options passed to it. */ export class DebugAdapterExecutable { - readonly type: 'executable'; + /** + * Creates a description for a debug adapter based on an executable program. + * + * @param command The command or executable path that implements the debug adapter. + * @param args Optional arguments to be passed to the command or executable. + * @param options Optional options to be used when starting the command or executable. + */ + constructor(command: string, args?: string[], options?: DebugAdapterExecutableOptions); /** - * The command path of the debug adapter executable. - * A command must be either an absolute path or the name of an executable looked up via the PATH environment variable. - * The special value 'node' will be mapped to VS Code's built-in node runtime. + * The command or path of the debug adapter executable. + * A command must be either an absolute path of an executable or the name of an command to be looked up via the PATH environment variable. + * The special value 'node' will be mapped to VS Code's built-in Node.js runtime. */ readonly command: string; /** - * Optional arguments passed to the debug adapter executable. + * The arguments passed to the debug adapter executable. Defaults to an empty array. */ readonly args: string[]; /** - * The additional environment of the executed program or shell. If omitted - * the parent process' environment is used. If provided it is merged with - * the parent process' environment. + * Optional options to be used when the debug adapter is started. + * Defaults to undefined. */ - readonly env?: { [key: string]: string }; + readonly options?: DebugAdapterExecutableOptions; + } + + /** + * Options for a debug adapter executable. + */ + export interface DebugAdapterExecutableOptions { /** - * The working directory for the debug adapter. + * The additional environment of the executed program or shell. If omitted + * the parent process' environment is used. If provided it is merged with + * the parent process' environment. */ - readonly cwd?: string; + env?: { [key: string]: string }; /** - * Create a description for a debug adapter based on an executable program. + * The current working directory for the executed debug adapter. */ - constructor(command: string, args?: string[], env?: { [key: string]: string }, cwd?: string); + cwd?: string; } /** @@ -534,8 +560,6 @@ declare module 'vscode' { */ export class DebugAdapterServer { - readonly type: 'server'; - /** * The port. */ @@ -557,8 +581,6 @@ declare module 'vscode' { */ export class DebugAdapterImplementation { - readonly type: 'implementation'; - readonly implementation: any; /** @@ -570,10 +592,53 @@ declare module 'vscode' { export type DebugAdapterDescriptor = DebugAdapterExecutable | DebugAdapterServer | DebugAdapterImplementation; + export interface DebugAdapterProvider { + /** + * 'provideDebugAdapter' is called at the start of a debug session to provide details about the debug adapter to use. + * These details must be returned as objects of type [DebugAdapterDescriptor](#DebugAdapterDescriptor). + * Currently two types of debug adapters are supported: + * - a debug adapter executable is specified as a command path and arguments (see [DebugAdapterExecutable](#DebugAdapterExecutable)), + * - a debug adapter server reachable via a communication port (see [DebugAdapterServer](#DebugAdapterServer)). + * If the method is not implemented the default behavior is this: + * provideDebugAdapter(session: DebugSession, executable: DebugAdapterExecutable) { + * if (typeof session.configuration.debugServer === 'number') { + * return new DebugAdapterServer(session.configuration.debugServer); + * } + * return executable; + * } + * @param session The [debug session](#DebugSession) for which the debug adapter will be used. + * @param executable The debug adapter's executable information as specified in the package.json (or undefined if no such information exists). + * @return a [debug adapter descriptor](#DebugAdapterDescriptor) or undefined. + */ + provideDebugAdapter(session: DebugSession, executable: DebugAdapterExecutable | undefined): ProviderResult; + } + + export namespace debug { + /** + * Register a [debug adapter provider](#DebugAdapterProvider) for a specific debug type. + * An extension is only allowed to register a DebugAdapterProvider for the debug type(s) defined by the extension. Otherwise an error is thrown. + * Registering more than one DebugAdapterProvider for a debug type results in an error. + * + * @param type The debug type for which the provider is registered. + * @param provider The [debug adapter provider](#DebugAdapterProvider) to register. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerDebugAdapterProvider(debugType: string, provider: DebugAdapterProvider): Disposable; + + /** + * Register a factory callback for the given debug type that is is called at the start of a debug session in order + * to return a "tracker" object that provides read-access to the communication between VS Code and a debug adapter. + * + * @param debugType A specific debug type or '*' for matching all debug types. + * @param callback A factory callback that is called at the start of a debug session to return a tracker object or `undefined`. + */ + export function registerDebugAdapterTracker(debugType: string, callback: (session: DebugSession) => DebugAdapterTracker | undefined): Disposable; + } + /** * A Debug Adapter Tracker is a means to track the communication between VS Code and a Debug Adapter. */ - export interface IDebugAdapterTracker { + export interface DebugAdapterTracker { // VS Code -> Debug Adapter startDebugAdapter?(): void; toDebugAdapter?(message: any): void; @@ -587,43 +652,19 @@ declare module 'vscode' { export interface DebugConfigurationProvider { /** - * The optional method 'provideDebugAdapter' is called at the start of a debug session to provide details about the debug adapter to use. - * These details must be returned as objects of type DebugAdapterDescriptor. - * Currently two types of debug adapters are supported: - * - a debug adapter executable specified as a command path and arguments (see DebugAdapterExecutable), - * - a debug adapter server reachable via a communication port (see DebugAdapterServer). - * If the method is not implemented the default behavior is this: - * provideDebugAdapter(session: DebugSession, folder: WorkspaceFolder | undefined, executable: DebugAdapterExecutable, config: DebugConfiguration, token?: CancellationToken) { - * if (typeof config.debugServer === 'number') { - * return new DebugAdapterServer(config.debugServer); - * } - * return executable; - * } - * An extension is only allowed to register a DebugConfigurationProvider with a provideDebugAdapter method if the extension defines the debug type. Otherwise an error is thrown. - * Registering more than one DebugConfigurationProvider with a provideDebugAdapter method for a type results in an error. - * @param session The [debug session](#DebugSession) for which the debug adapter will be used. - * @param folder The workspace folder from which the configuration originates from or undefined for a folderless setup. - * @param executable The debug adapter's executable information as specified in the package.json (or undefined if no such information exists). - * @param config The resolved debug configuration. - * @param token A cancellation token. - * @return a [debug adapter's descriptor](#DebugAdapterDescriptor) or undefined. + * Deprecated, use DebugAdapterProvider.provideDebugAdapter instead. + * @deprecated Use DebugAdapterProvider.provideDebugAdapter instead */ - provideDebugAdapter?(session: DebugSession, folder: WorkspaceFolder | undefined, executable: DebugAdapterExecutable | undefined, config: DebugConfiguration, token?: CancellationToken): ProviderResult; + debugAdapterExecutable?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult; /** + * Preliminary API, do not use in production. + * * The optional method 'provideDebugAdapterTracker' is called at the start of a debug session to provide a tracker that gives access to the communication between VS Code and a Debug Adapter. * @param session The [debug session](#DebugSession) for which the tracker will be used. - * @param folder The workspace folder from which the configuration originates from or undefined for a folderless setup. - * @param config The resolved debug configuration. * @param token A cancellation token. */ - provideDebugAdapterTracker?(session: DebugSession, folder: WorkspaceFolder | undefined, config: DebugConfiguration, token?: CancellationToken): ProviderResult; - - /** - * Deprecated, use DebugConfigurationProvider.provideDebugAdapter instead. - * @deprecated Use DebugConfigurationProvider.provideDebugAdapter instead - */ - debugAdapterExecutable?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult; + provideDebugAdapterTracker?(session: DebugSession, workspaceFolder: WorkspaceFolder | undefined, config: DebugConfiguration, token?: CancellationToken): ProviderResult; } //#endregion @@ -724,6 +765,21 @@ declare module 'vscode' { //#endregion + //#region Joao: SCM Input Box + + /** + * Represents the input box in the Source Control viewlet. + */ + export interface SourceControlInputBox { + + /** + * Controls whether the input box is visible (default is `true`). + */ + visible: boolean; + } + + //#endregion + //#region Comments /** * Comments provider related APIs are still in early stages, they may be changed significantly during our API experiments. @@ -1079,12 +1135,9 @@ declare module 'vscode' { TriggerCharacter = 2, /** - * Signature help was retriggered. - * - * Retriggers occur when the signature help is already active and can be caused by typing a trigger character - * or by a cursor move. + * Signature help was triggered by the cursor moving or by the document content changing. */ - Retrigger = 3, + ContentChange = 3, } /** @@ -1100,9 +1153,18 @@ declare module 'vscode' { /** * Character that caused signature help to be requested. * - * This is `undefined` for manual triggers or retriggers for a cursor move. + * This is `undefined` when signature help is not triggered by typing, such as when invoking signature help + * or when moving the cursor. */ readonly triggerCharacter?: string; + + /** + * Whether or not signature help was previously showing when triggered. + * + * Retriggers occur when the signature help is already active and can be caused by typing a trigger character + * or by a cursor move. + */ + readonly isRetrigger: boolean; } export interface SignatureHelpProvider { @@ -1132,7 +1194,17 @@ declare module 'vscode' { } //#endregion - //#region Tree Item Label Highlights + //#region Tree View + + export interface TreeView extends Disposable { + + /** + * An optional human-readable message that will be rendered in the view. + */ + message?: string | MarkdownString; + + } + /** * Label describing the [Tree item](#TreeItem) */ @@ -1144,12 +1216,21 @@ declare module 'vscode' { label: string; /** - * Ranges in the label to highlight. + * Ranges in the label to highlight. A range is defined as a tuple of two number where the + * first is the inclusive start index and the second the exclusive end index */ highlights?: [number, number][]; } + export interface TreeItem { + /** + * A human readable string which is rendered less prominent. + * When `true`, it is derived from [resourceUri](#TreeItem.resourceUri) and when `falsy`, it is not shown. + */ + description?: string | boolean; + } + export class TreeItem2 extends TreeItem { /** * Label describing this item. When `falsy`, it is derived from [resourceUri](#TreeItem.resourceUri). @@ -1165,14 +1246,27 @@ declare module 'vscode' { //#endregion //#region Task + export enum RerunBehavior { + reevaluate = 1, + useEvaluated = 2, + } + + + export interface RunOptions { + /** + * Controls the behavior of a task when it is rerun. + */ + rerunBehavior?: RerunBehavior; + } + /** - * Controls how the task is presented in the UI. + * A task to execute */ - export interface TaskPresentationOptions { + export class Task2 extends Task { /** - * Controls whether the terminal is cleared before executing the task. + * Run options for the task */ - clear?: boolean; + runOptions: RunOptions; } //#endregion diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 296f5b46f..3f1efc2e3 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -8,7 +8,7 @@ import { forEach } from 'vs/base/common/collections'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { ExtensionMessageCollector, ExtensionsRegistry, IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { ViewContainer, ViewsRegistry, ICustomViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views'; -import { CustomTreeViewPanel, CustomTreeViewer } from 'vs/workbench/browser/parts/views/customView'; +import { CustomTreeViewPanel, CustomTreeView } from 'vs/workbench/browser/parts/views/customView'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { coalesce, } from 'vs/base/common/arrays'; import { viewsContainersExtensionPoint } from 'vs/workbench/api/browser/viewsContainersExtensionPoint'; @@ -131,7 +131,7 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution { when: ContextKeyExpr.deserialize(item.when), canToggleVisibility: true, collapsed: this.showCollapsed(container), - treeViewer: this.instantiationService.createInstance(CustomTreeViewer, item.id, container), + treeView: this.instantiationService.createInstance(CustomTreeView, item.id, container), order: extension.description.id === container.extensionId ? index + 1 : void 0 }; diff --git a/src/vs/workbench/api/electron-browser/mainThreadClipboard.ts b/src/vs/workbench/api/electron-browser/mainThreadClipboard.ts index 14c3f9bb6..649725622 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadClipboard.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadClipboard.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import { clipboard } from 'electron'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { MainContext, MainThreadClipboardShape } from '../node/extHost.protocol'; diff --git a/src/vs/workbench/api/electron-browser/mainThreadComments.ts b/src/vs/workbench/api/electron-browser/mainThreadComments.ts index 75111969d..347b9b6d0 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadComments.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadComments.ts @@ -5,7 +5,6 @@ import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ICodeEditor, isCodeEditor, isDiffEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import * as modes from 'vs/editor/common/modes'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { keys } from 'vs/base/common/map'; @@ -16,114 +15,78 @@ import { ICommentService } from 'vs/workbench/parts/comments/electron-browser/co import { COMMENTS_PANEL_ID } from 'vs/workbench/parts/comments/electron-browser/commentsPanel'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { URI } from 'vs/base/common/uri'; -import { ReviewController } from 'vs/workbench/parts/comments/electron-browser/commentsEditorContribution'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { generateUuid } from 'vs/base/common/uuid'; +export class ExtensionCommentProviderHandler implements modes.DocumentCommentProvider { + private _proxy: ExtHostCommentsShape; + private _handle: number; + + constructor(proxy: ExtHostCommentsShape, handle: number) { + this._proxy = proxy; + this._handle = handle; + } + + async provideDocumentComments(uri, token) { + return this._proxy.$provideDocumentComments(this._handle, uri); + } + + async createNewCommentThread(uri, range, text, token) { + return this._proxy.$createNewCommentThread(this._handle, uri, range, text); + } + + async replyToCommentThread(uri, range, thread, text, token) { + return this._proxy.$replyToCommentThread(this._handle, uri, range, thread, text); + } + + async editComment(uri, comment, text, token) { + return this._proxy.$editComment(this._handle, uri, comment, text); + } + + async deleteComment(uri, comment, token) { + return this._proxy.$deleteComment(this._handle, uri, comment); + } + + onDidChangeCommentThreads = null; +} @extHostNamedCustomer(MainContext.MainThreadComments) export class MainThreadComments extends Disposable implements MainThreadCommentsShape { private _disposables: IDisposable[]; private _proxy: ExtHostCommentsShape; private _documentProviders = new Map(); private _workspaceProviders = new Map(); + private _handlers = new Map(); private _firstSessionStart: boolean; - private _visibleModels: { [key /** editor widget id */: string]: string /** model id */ }; constructor( extHostContext: IExtHostContext, @IEditorService private _editorService: IEditorService, @ICommentService private _commentService: ICommentService, @IPanelService private _panelService: IPanelService, - @ICodeEditorService private _codeEditorService: ICodeEditorService + @ITelemetryService private _telemetryService: ITelemetryService ) { super(); this._disposables = []; this._firstSessionStart = true; - this._visibleModels = {}; this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostComments); - this._disposables.push(this._editorService.onDidVisibleEditorsChange(e => { - const editors = this.getFocusedEditors(); - const visibleEditors = this.getVisibleEditors(); - - const _visibleEditors = {}; - visibleEditors.forEach(editor => { - if (!editor.hasModel()) { - return; // we need a model - } - const id = editor.getId(); - const model = editor.getModel(); - if (model === null) { - return; - } - - if (editors.filter(ed => ed.getId() === id).length > 0) { - // it's an active editor, we are going to update this editor's comments anyways - } else { - if (this._visibleModels[id]) { - // it's the same active editor, but we may want to check if the model is still the same - let modelId = model.getModeId(); - if (modelId !== this._visibleModels[id]) { - editors.push(editor); - } - } else { - // update - editors.push(editor); - } - } - - _visibleEditors[id] = model.getModeId(); - }); - - this._visibleModels = _visibleEditors; - - if (!editors || !editors.length) { - return; - } - - editors.forEach(editor => { - const controller = ReviewController.get(editor); - if (!controller) { - return; - } - - if (!editor.getModel()) { - return; - } - - const outerEditorURI = editor.getModel().uri; - this.provideDocumentComments(outerEditorURI).then(commentInfos => { - this._commentService.setDocumentComments(outerEditorURI, commentInfos.filter(info => info !== null)); - }); - }); - })); } $registerDocumentCommentProvider(handle: number): void { this._documentProviders.set(handle, undefined); + const handler = new ExtensionCommentProviderHandler(this._proxy, handle); - this._commentService.registerDataProvider( - handle, - { - provideDocumentComments: async (uri, token) => { - return this._proxy.$provideDocumentComments(handle, uri); - }, - onDidChangeCommentThreads: null, - createNewCommentThread: async (uri, range, text, token) => { - return this._proxy.$createNewCommentThread(handle, uri, range, text); - }, - replyToCommentThread: async (uri, range, thread, text, token) => { - return this._proxy.$replyToCommentThread(handle, uri, range, thread, text); - }, - editComment: async (uri, comment, text, token) => { - return this._proxy.$editComment(handle, uri, comment, text); - }, - deleteComment: async (uri, comment, token) => { - return this._proxy.$deleteComment(handle, uri, comment); - } - } - ); + const providerId = generateUuid(); + this._handlers.set(handle, providerId); + + this._commentService.registerDataProvider(providerId, handler); } - $registerWorkspaceCommentProvider(handle: number): void { + $registerWorkspaceCommentProvider(handle: number, extensionId: string): void { this._workspaceProviders.set(handle, undefined); + + const providerId = generateUuid(); + this._handlers.set(handle, providerId); + this._panelService.setPanelEnablement(COMMENTS_PANEL_ID, true); if (this._firstSessionStart) { this._panelService.openPanel(COMMENTS_PANEL_ID); @@ -131,14 +94,25 @@ export class MainThreadComments extends Disposable implements MainThreadComments } this._proxy.$provideWorkspaceComments(handle).then(commentThreads => { if (commentThreads) { - this._commentService.setWorkspaceComments(handle, commentThreads); + this._commentService.setWorkspaceComments(providerId, commentThreads); } }); + + /* __GDPR__ + "comments:registerWorkspaceCommentProvider" : { + "extensionId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this._telemetryService.publicLog('comments:registerWorkspaceCommentProvider', { + extensionId: extensionId + }); } $unregisterDocumentCommentProvider(handle: number): void { this._documentProviders.delete(handle); - this._commentService.unregisterDataProvider(handle); + const handlerId = this._handlers.get(handle); + this._commentService.unregisterDataProvider(handlerId); + this._handlers.delete(handle); } $unregisterWorkspaceCommentProvider(handle: number): void { @@ -146,12 +120,15 @@ export class MainThreadComments extends Disposable implements MainThreadComments if (this._workspaceProviders.size === 0) { this._panelService.setPanelEnablement(COMMENTS_PANEL_ID, false); } - this._commentService.removeWorkspaceComments(handle); + const handlerId = this._handlers.get(handle); + this._commentService.removeWorkspaceComments(handlerId); + this._handlers.delete(handle); } $onDidCommentThreadsChange(handle: number, event: modes.CommentThreadChangedEvent) { // notify comment service - this._commentService.updateComments(event); + const providerId = this._handlers.get(handle); + this._commentService.updateComments(providerId, event); } getVisibleEditors(): ICodeEditor[] { @@ -171,27 +148,6 @@ export class MainThreadComments extends Disposable implements MainThreadComments return ret; } - getFocusedEditors(): ICodeEditor[] { - let activeControl = this._editorService.activeControl; - if (activeControl) { - if (isCodeEditor(activeControl.getControl())) { - return [this._editorService.activeControl.getControl() as ICodeEditor]; - } - - if (isDiffEditor(activeControl.getControl())) { - let diffEditor = activeControl.getControl() as IDiffEditor; - return [diffEditor.getOriginalEditor(), diffEditor.getModifiedEditor()]; - } - } - - let editor = this._codeEditorService.getFocusedCodeEditor(); - - if (editor) { - return [editor]; - } - return []; - } - async provideWorkspaceComments(): Promise { const result: modes.CommentThread[] = []; for (const handle of keys(this._workspaceProviders)) { @@ -214,6 +170,5 @@ export class MainThreadComments extends Disposable implements MainThreadComments this._workspaceProviders.clear(); this._documentProviders.forEach(value => dispose(value)); this._documentProviders.clear(); - this._visibleModels = {}; } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts index 4dde5071f..06f992b91 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDebugService.ts @@ -5,8 +5,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { URI as uri } from 'vs/base/common/uri'; -import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, ITerminalSettings, IDebugAdapter, IDebugAdapterProvider, IDebugSession } from 'vs/workbench/parts/debug/common/debug'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, ITerminalSettings, IDebugAdapter, IDebugAdapterProvider, IDebugSession, IDebugAdapterFactory } from 'vs/workbench/parts/debug/common/debug'; import { ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID, MainContext, IExtHostContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, ISourceBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto @@ -16,17 +15,18 @@ import severity from 'vs/base/common/severity'; import { AbstractDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { convertToVSCPaths, convertToDAPaths, stringToUri, uriToString } from 'vs/workbench/parts/debug/common/debugUtils'; -import { deepClone } from 'vs/base/common/objects'; - @extHostNamedCustomer(MainContext.MainThreadDebugService) -export class MainThreadDebugService implements MainThreadDebugServiceShape, IDebugAdapterProvider { +export class MainThreadDebugService implements MainThreadDebugServiceShape, IDebugAdapterFactory { private _proxy: ExtHostDebugServiceShape; private _toDispose: IDisposable[]; private _breakpointEventsActive: boolean; private _debugAdapters: Map; private _debugAdaptersHandleCounter = 1; + private _debugConfigurationProviders: Map; + private _debugAdapterProviders: Map; + private _sessions: Set; constructor( extHostContext: IExtHostContext, @@ -43,12 +43,16 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb })); this._toDispose.push(debugService.onDidEndSession(session => { this._proxy.$acceptDebugSessionTerminated(this.getSessionDto(session)); + this._sessions.delete(session.getId()); })); this._toDispose.push(debugService.getViewModel().onDidFocusSession(session => { this._proxy.$acceptDebugSessionActiveChanged(this.getSessionDto(session)); })); - this._debugAdapters = new Map(); + this._debugAdapters = new Map(); + this._debugConfigurationProviders = new Map(); + this._debugAdapterProviders = new Map(); + this._sessions = new Set(); } public dispose(): void { @@ -57,29 +61,28 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb // interface IDebugAdapterProvider - createDebugAdapter(session: IDebugSession, folder: IWorkspaceFolder, config: IConfig): IDebugAdapter { + createDebugAdapter(session: IDebugSession): IDebugAdapter { const handle = this._debugAdaptersHandleCounter++; - const da = new ExtensionHostDebugAdapter(handle, this._proxy, this.getSessionDto(session), folder, config); + const da = new ExtensionHostDebugAdapter(this, handle, this._proxy, session); this._debugAdapters.set(handle, da); return da; } - substituteVariables(folder: IWorkspaceFolder, config: IConfig): TPromise { - return TPromise.wrap(this._proxy.$substituteVariables(folder ? folder.uri : undefined, config)); + substituteVariables(folder: IWorkspaceFolder, config: IConfig): Promise { + return Promise.resolve(this._proxy.$substituteVariables(folder ? folder.uri : undefined, config)); } - runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise { - return TPromise.wrap(this._proxy.$runInTerminal(args, config)); + runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise { + return Promise.resolve(this._proxy.$runInTerminal(args, config)); } - // RPC methods (MainThreadDebugServiceShape) public $registerDebugTypes(debugTypes: string[]) { - this._toDispose.push(this.debugService.getConfigurationManager().registerDebugAdapterProvider(debugTypes, this)); + this._toDispose.push(this.debugService.getConfigurationManager().registerDebugAdapterFactory(debugTypes, this)); } - public $startBreakpointEvents(): Thenable { + public $startBreakpointEvents(): void { if (!this._breakpointEventsActive) { this._breakpointEventsActive = true; @@ -114,8 +117,6 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb }); } } - - return TPromise.wrap(undefined); } public $registerBreakpoints(DTOs: (ISourceMultiBreakpointDto | IFunctionBreakpointDto)[]): Thenable { @@ -156,27 +157,53 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb }; if (hasProvide) { provider.provideDebugConfigurations = (folder) => { - return TPromise.wrap(this._proxy.$provideDebugConfigurations(handle, folder)); + return Promise.resolve(this._proxy.$provideDebugConfigurations(handle, folder)); }; } if (hasResolve) { provider.resolveDebugConfiguration = (folder, config) => { - return TPromise.wrap(this._proxy.$resolveDebugConfiguration(handle, folder, config)); + return Promise.resolve(this._proxy.$resolveDebugConfiguration(handle, folder, config)); }; } if (hasProvideDebugAdapter) { - provider.provideDebugAdapter = (session, folder, config) => { - return TPromise.wrap(this._proxy.$provideDebugAdapter(handle, this.getSessionDto(session), folder, config)); + provider.debugAdapterExecutable = (folder) => { + return Promise.resolve(this._proxy.$legacyDebugAdapterExecutable(handle, folder)); }; } - this.debugService.getConfigurationManager().registerDebugConfigurationProvider(handle, provider); + this._debugConfigurationProviders.set(handle, provider); + this._toDispose.push(this.debugService.getConfigurationManager().registerDebugConfigurationProvider(provider)); - return TPromise.wrap(undefined); + return Promise.resolve(undefined); + } + + public $unregisterDebugConfigurationProvider(handle: number): void { + const provider = this._debugConfigurationProviders.get(handle); + if (provider) { + this._debugConfigurationProviders.delete(handle); + this.debugService.getConfigurationManager().unregisterDebugConfigurationProvider(provider); + } } - public $unregisterDebugConfigurationProvider(handle: number): Thenable { - this.debugService.getConfigurationManager().unregisterDebugConfigurationProvider(handle); - return TPromise.wrap(undefined); + public $registerDebugAdapterProvider(debugType: string, handle: number): Thenable { + + const provider = { + type: debugType, + provideDebugAdapter: session => { + return Promise.resolve(this._proxy.$provideDebugAdapter(handle, this.getSessionDto(session))); + } + }; + this._debugAdapterProviders.set(handle, provider); + this._toDispose.push(this.debugService.getConfigurationManager().registerDebugAdapterProvider(provider)); + + return Promise.resolve(undefined); + } + + public $unregisterDebugAdapterProvider(handle: number): void { + const provider = this._debugAdapterProviders.get(handle); + if (provider) { + this._debugAdapterProviders.delete(handle); + this.debugService.getConfigurationManager().unregisterDebugAdapterProvider(provider); + } } public $startDebugging(_folderUri: uri | undefined, nameOrConfiguration: string | IConfig): Thenable { @@ -185,7 +212,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb return this.debugService.startDebugging(launch, nameOrConfiguration).then(success => { return success; }, err => { - return TPromise.wrapError(new Error(err && err.message ? err.message : 'cannot start debugging')); + return Promise.reject(new Error(err && err.message ? err.message : 'cannot start debugging')); }); } @@ -196,27 +223,24 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb if (response && response.success) { return response.body; } else { - return TPromise.wrapError(new Error(response ? response.message : 'custom request failed')); + return Promise.reject(new Error(response ? response.message : 'custom request failed')); } }); } - return TPromise.wrapError(new Error('debug session not found')); + return Promise.reject(new Error('debug session not found')); } - public $appendDebugConsole(value: string): Thenable { + public $appendDebugConsole(value: string): void { // Use warning as severity to get the orange color for messages coming from the debug extension const session = this.debugService.getViewModel().focusedSession; if (session) { session.appendToRepl(value, severity.Warning); } - return TPromise.wrap(undefined); } public $acceptDAMessage(handle: number, message: DebugProtocol.ProtocolMessage) { - convertToVSCPaths(message, source => uriToString(source)); - - this._debugAdapters.get(handle).acceptMessage(message); + this._debugAdapters.get(handle).acceptMessage(convertToVSCPaths(message, source => uriToString(source))); } public $acceptDAError(handle: number, name: string, message: string, stack: string) { @@ -229,13 +253,21 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb // dto helpers - private getSessionDto(session: IDebugSession): IDebugSessionDto { + getSessionDto(session: IDebugSession): IDebugSessionDto { if (session) { - return { - id: session.getId(), - type: session.configuration.type, - name: session.configuration.name - }; + const sessionID = session.getId(); + if (this._sessions.has(sessionID)) { + return sessionID; + } else { + this._sessions.add(sessionID); + return { + id: sessionID, + type: session.configuration.type, + name: session.configuration.name, + folderUri: session.root ? session.root.uri : undefined, + configuration: session.configuration + }; + } } return undefined; } @@ -276,7 +308,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb */ class ExtensionHostDebugAdapter extends AbstractDebugAdapter { - constructor(private _handle: number, private _proxy: ExtHostDebugServiceShape, private _sessionDto: IDebugSessionDto, private folder: IWorkspaceFolder, private config: IConfig) { + constructor(private _ds: MainThreadDebugService, private _handle: number, private _proxy: ExtHostDebugServiceShape, private _session: IDebugSession) { super(); } @@ -288,21 +320,16 @@ class ExtensionHostDebugAdapter extends AbstractDebugAdapter { this._onExit.fire(code); } - public startSession(): TPromise { - return TPromise.wrap(this._proxy.$startDASession(this._handle, this._sessionDto, this.folder ? this.folder.uri : undefined, this.config)); + public startSession(): Promise { + return Promise.resolve(this._proxy.$startDASession(this._handle, this._ds.getSessionDto(this._session))); } public sendMessage(message: DebugProtocol.ProtocolMessage): void { - // since we modify Source.paths in the message in place, we need to make a copy of it (see #61129) - const msg = deepClone(message); - - convertToDAPaths(msg, source => stringToUri(source)); - - this._proxy.$sendDAMessage(this._handle, msg); + this._proxy.$sendDAMessage(this._handle, convertToDAPaths(message, source => stringToUri(source))); } - public stopSession(): TPromise { - return TPromise.wrap(this._proxy.$stopDASession(this._handle)); + public stopSession(): Promise { + return Promise.resolve(this._proxy.$stopDASession(this._handle)); } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadDocumentContentProviders.ts b/src/vs/workbench/api/electron-browser/mainThreadDocumentContentProviders.ts index c42d28942..82419e929 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadDocumentContentProviders.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadDocumentContentProviders.ts @@ -6,7 +6,6 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; @@ -16,11 +15,13 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { ExtHostContext, ExtHostDocumentContentProvidersShape, IExtHostContext, MainContext, MainThreadDocumentContentProvidersShape } from '../node/extHost.protocol'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; @extHostNamedCustomer(MainContext.MainThreadDocumentContentProviders) export class MainThreadDocumentContentProviders implements MainThreadDocumentContentProvidersShape { - private _resourceContentProvider: { [handle: number]: IDisposable } = Object.create(null); + private readonly _resourceContentProvider = new Map(); + private readonly _pendingUpdate = new Map(); private readonly _proxy: ExtHostDocumentContentProvidersShape; constructor( @@ -28,38 +29,37 @@ export class MainThreadDocumentContentProviders implements MainThreadDocumentCon @ITextModelService private readonly _textModelResolverService: ITextModelService, @IModeService private readonly _modeService: IModeService, @IModelService private readonly _modelService: IModelService, - @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, - @ICodeEditorService codeEditorService: ICodeEditorService + @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocumentContentProviders); } - public dispose(): void { - for (let handle in this._resourceContentProvider) { - this._resourceContentProvider[handle].dispose(); - } + dispose(): void { + this._resourceContentProvider.forEach(p => p.dispose()); + this._pendingUpdate.forEach(source => source.dispose()); } $registerTextContentProvider(handle: number, scheme: string): void { - this._resourceContentProvider[handle] = this._textModelResolverService.registerTextModelContentProvider(scheme, { + const registration = this._textModelResolverService.registerTextModelContentProvider(scheme, { provideTextContent: (uri: URI): Thenable => { return this._proxy.$provideTextDocumentContent(handle, uri).then(value => { if (typeof value === 'string') { const firstLineText = value.substr(0, 1 + value.search(/\r?\n/)); - const mode = this._modeService.getOrCreateModeByFilepathOrFirstLine(uri.fsPath, firstLineText); - return this._modelService.createModel(value, mode, uri); + const languageSelection = this._modeService.createByFilepathOrFirstLine(uri.fsPath, firstLineText); + return this._modelService.createModel(value, languageSelection, uri); } return undefined; }); } }); + this._resourceContentProvider.set(handle, registration); } $unregisterTextContentProvider(handle: number): void { - const registration = this._resourceContentProvider[handle]; + const registration = this._resourceContentProvider.get(handle); if (registration) { registration.dispose(); - delete this._resourceContentProvider[handle]; + this._resourceContentProvider.delete(handle); } } @@ -69,11 +69,27 @@ export class MainThreadDocumentContentProviders implements MainThreadDocumentCon return; } + // cancel and dispose an existing update + if (this._pendingUpdate.has(model.id)) { + this._pendingUpdate.get(model.id).cancel(); + } + + // create and keep update token + const myToken = new CancellationTokenSource(); + this._pendingUpdate.set(model.id, myToken); + this._editorWorkerService.computeMoreMinimalEdits(model.uri, [{ text: value, range: model.getFullModelRange() }]).then(edits => { + // remove token + this._pendingUpdate.delete(model.id); + + if (myToken.token.isCancellationRequested) { + // ignore this + return; + } if (edits.length > 0) { // use the evil-edit as these models show in readonly-editor only model.applyEdits(edits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text))); } - }, onUnexpectedError); + }).catch(onUnexpectedError); } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts index 8dabd0a4b..ab621c9f7 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts @@ -51,10 +51,12 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape { class RemoteFileSystemProvider implements IFileSystemProvider { private readonly _onDidChange = new Emitter(); - private readonly _registrations: IDisposable[]; + private readonly _registration: IDisposable; readonly onDidChangeFile: Event = this._onDidChange.event; + readonly capabilities: FileSystemProviderCapabilities; + readonly onDidChangeCapabilities: Event = Event.None; constructor( fileService: IFileService, @@ -64,11 +66,11 @@ class RemoteFileSystemProvider implements IFileSystemProvider { private readonly _proxy: ExtHostFileSystemShape ) { this.capabilities = capabilities; - this._registrations = [fileService.registerProvider(scheme, this)]; + this._registration = fileService.registerProvider(scheme, this); } dispose(): void { - dispose(this._registrations); + this._registration.dispose(); this._onDidChange.dispose(); } diff --git a/src/vs/workbench/api/electron-browser/mainThreadHeapService.ts b/src/vs/workbench/api/electron-browser/mainThreadHeapService.ts index 0666cca98..e3efb95b7 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadHeapService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadHeapService.ts @@ -137,4 +137,4 @@ export class MainThreadHeapService { } -registerSingleton(IHeapService, HeapService); +registerSingleton(IHeapService, HeapService, true); diff --git a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts index 0a36ba745..2d31b7541 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts @@ -148,7 +148,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha // --- declaration - $registerDeclaractionSupport(handle: number, selector: ISerializedDocumentFilter[]): void { + $registerDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void { this._registrations[handle] = modes.DefinitionProviderRegistry.register(typeConverters.LanguageSelector.from(selector), { provideDefinition: (model, position, token): Thenable => { return this._proxy.$provideDefinition(handle, model.uri, position, token).then(MainThreadLanguageFeatures._reviveDefinitionLinkDto); @@ -156,6 +156,14 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha }); } + $registerDeclarationSupport(handle: number, selector: ISerializedDocumentFilter[]): void { + this._registrations[handle] = modes.DeclarationProviderRegistry.register(typeConverters.LanguageSelector.from(selector), { + provideDeclaration: (model, position, token) => { + return this._proxy.$provideDeclaration(handle, model.uri, position, token).then(MainThreadLanguageFeatures._reviveDefinitionLinkDto); + } + }); + } + $registerImplementationSupport(handle: number, selector: ISerializedDocumentFilter[]): void { this._registrations[handle] = modes.ImplementationProviderRegistry.register(typeConverters.LanguageSelector.from(selector), { provideImplementation: (model, position, token): Thenable => { diff --git a/src/vs/workbench/api/electron-browser/mainThreadLanguages.ts b/src/vs/workbench/api/electron-browser/mainThreadLanguages.ts index 77d088522..6ea6e3b35 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadLanguages.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadLanguages.ts @@ -33,12 +33,11 @@ export class MainThreadLanguages implements MainThreadLanguagesShape { if (!model) { return Promise.reject(new Error('Invalid uri')); } - return this._modeService.getOrCreateMode(languageId).then(mode => { - if (mode.getId() !== languageId) { - return Promise.reject(new Error(`Unknown language id: ${languageId}`)); - } - this._modelService.setMode(model, mode); - return undefined; - }); + const languageIdentifier = this._modeService.getLanguageIdentifier(languageId); + if (!languageIdentifier || languageIdentifier.language !== languageId) { + return Promise.reject(new Error(`Unknown language id: ${languageId}`)); + } + this._modelService.setMode(model, this._modeService.create(languageId)); + return Promise.resolve(undefined); } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadOutputService.ts b/src/vs/workbench/api/electron-browser/mainThreadOutputService.ts index 711a957ef..256f62fb1 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadOutputService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadOutputService.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { Registry } from 'vs/platform/registry/common/platform'; import { IOutputService, IOutputChannel, OUTPUT_PANEL_ID, Extensions, IOutputChannelRegistry } from 'vs/workbench/parts/output/common/output'; import { IPartService } from 'vs/workbench/services/part/common/partService'; @@ -11,7 +10,7 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { MainThreadOutputServiceShape, MainContext, IExtHostContext, ExtHostOutputServiceShape, ExtHostContext } from '../node/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { UriComponents, URI } from 'vs/base/common/uri'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { anyEvent } from 'vs/base/common/event'; @extHostNamedCustomer(MainContext.MainThreadOutputService) @@ -46,15 +45,11 @@ export class MainThreadOutputService extends Disposable implements MainThreadOut setVisibleChannel(); } - public dispose(): void { - super.dispose(); - // Leave all the existing channels intact (e.g. might help with troubleshooting) - } - public $register(label: string, log: boolean, file?: UriComponents): Thenable { const id = 'extension-output-#' + (MainThreadOutputService._idPool++); Registry.as(Extensions.OutputChannels).registerChannel({ id, label, file: file ? URI.revive(file) : null, log }); - return TPromise.as(id); + this._register(toDisposable(() => this.$dispose(id))); + return Promise.resolve(id); } public $append(channelId: string, value: string): Thenable { @@ -92,7 +87,7 @@ export class MainThreadOutputService extends Disposable implements MainThreadOut public $close(channelId: string): Thenable { const panel = this._panelService.getActivePanel(); if (panel && panel.getId() === OUTPUT_PANEL_ID && channelId === this._outputService.getActiveChannel().id) { - return this._partService.setPanelHidden(true); + this._partService.setPanelHidden(true); } return undefined; diff --git a/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts b/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts index 871cd1ce3..0c15505bd 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadQuickOpen.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { IPickOptions, IInputOptions, IQuickInputService, IQuickInput } from 'vs/platform/quickinput/common/quickInput'; import { InputBoxOptions } from 'vscode'; import { ExtHostContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, TransferQuickPickItems, MainContext, IExtHostContext, TransferQuickInput, TransferQuickInputButton } from 'vs/workbench/api/node/extHost.protocol'; @@ -21,10 +20,10 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { private _proxy: ExtHostQuickOpenShape; private _quickInputService: IQuickInputService; - private _doSetItems: (items: TransferQuickPickItems[]) => any; - private _doSetError: (error: Error) => any; - private _contents: TPromise; - private _token: number = 0; + private _items: Record = {}; constructor( extHostContext: IExtHostContext, @@ -37,21 +36,9 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { public dispose(): void { } - $show(options: IPickOptions, token: CancellationToken): Thenable { - const myToken = ++this._token; - - this._contents = new TPromise((c, e) => { - this._doSetItems = (items) => { - if (myToken === this._token) { - c(items); - } - }; - - this._doSetError = (error) => { - if (myToken === this._token) { - e(error); - } - }; + $show(instance: number, options: IPickOptions, token: CancellationToken): Thenable { + const contents = new Promise((resolve, reject) => { + this._items[instance] = { resolve, reject }; }); options = { @@ -64,14 +51,14 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { }; if (options.canPickMany) { - return this._quickInputService.pick(this._contents, options as { canPickMany: true }, token).then(items => { + return this._quickInputService.pick(contents, options as { canPickMany: true }, token).then(items => { if (items) { return items.map(item => item.handle); } return undefined; }); } else { - return this._quickInputService.pick(this._contents, options, token).then(item => { + return this._quickInputService.pick(contents, options, token).then(item => { if (item) { return item.handle; } @@ -80,16 +67,18 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { } } - $setItems(items: TransferQuickPickItems[]): Thenable { - if (this._doSetItems) { - this._doSetItems(items); + $setItems(instance: number, items: TransferQuickPickItems[]): Thenable { + if (this._items[instance]) { + this._items[instance].resolve(items); + delete this._items[instance]; } return undefined; } - $setError(error: Error): Thenable { - if (this._doSetError) { - this._doSetError(error); + $setError(instance: number, error: Error): Thenable { + if (this._items[instance]) { + this._items[instance].reject(error); + delete this._items[instance]; } return undefined; } @@ -210,7 +199,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { input[param] = params[param]; } } - return TPromise.as(undefined); + return Promise.resolve(undefined); } $dispose(sessionId: number): Thenable { @@ -219,6 +208,6 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape { session.input.dispose(); this.sessions.delete(sessionId); } - return TPromise.as(undefined); + return Promise.resolve(undefined); } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadSCM.ts b/src/vs/workbench/api/electron-browser/mainThreadSCM.ts index b1e2f9341..3602f7f3c 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSCM.ts @@ -395,6 +395,16 @@ export class MainThreadSCM implements MainThreadSCMShape { repository.input.placeholder = placeholder; } + $setInputBoxVisibility(sourceControlHandle: number, visible: boolean): void { + const repository = this._repositories[sourceControlHandle]; + + if (!repository) { + return; + } + + repository.input.visible = visible; + } + $setValidationProviderIsEnabled(sourceControlHandle: number, enabled: boolean): void { const repository = this._repositories[sourceControlHandle]; diff --git a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts index 2e66dbf18..d4413adaf 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts @@ -24,7 +24,7 @@ import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerServ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IProgressService2, ProgressLocation } from 'vs/platform/progress/common/progress'; import { localize } from 'vs/nls'; -import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; import { ILogService } from 'vs/platform/log/common/log'; import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService'; import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; @@ -251,7 +251,7 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant { }); }).then(edits => { - if (!isFalsyOrEmpty(edits) && versionNow === model.getVersionId()) { + if (isNonEmptyArray(edits) && versionNow === model.getVersionId()) { const editor = findEditor(model, this._editorService); if (editor) { this._editsWithEditor(editor, edits); diff --git a/src/vs/workbench/api/electron-browser/mainThreadSearch.ts b/src/vs/workbench/api/electron-browser/mainThreadSearch.ts index a29485a24..e65bb2586 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSearch.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSearch.ts @@ -7,7 +7,6 @@ import { isFalsyOrEmpty } from 'vs/base/common/arrays'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { values } from 'vs/base/common/map'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IFileMatch, IRawFileMatch2, ISearchComplete, ISearchCompleteStats, ISearchProgressItem, ISearchResultProvider, ISearchService, QueryType, SearchProviderType, ITextQuery, IFileQuery } from 'vs/platform/search/common/search'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; @@ -78,7 +77,7 @@ class SearchOperation { addMatch(match: IFileMatch): void { if (this.matches.has(match.resource.toString())) { // Merge with previous IFileMatches - this.matches.get(match.resource.toString()).matches.push(...match.matches); + this.matches.get(match.resource.toString()).results.push(...match.results); } else { this.matches.set(match.resource.toString(), match); } @@ -108,17 +107,17 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable { dispose(this._registrations); } - fileSearch(query: IFileQuery, token: CancellationToken = CancellationToken.None): TPromise { + fileSearch(query: IFileQuery, token: CancellationToken = CancellationToken.None): Promise { return this.doSearch(query, null, token); } - textSearch(query: ITextQuery, onProgress?: (p: ISearchProgressItem) => void, token: CancellationToken = CancellationToken.None): TPromise { + textSearch(query: ITextQuery, onProgress?: (p: ISearchProgressItem) => void, token: CancellationToken = CancellationToken.None): Promise { return this.doSearch(query, onProgress, token); } - doSearch(query: ITextQuery | IFileQuery, onProgress?: (p: ISearchProgressItem) => void, token: CancellationToken = CancellationToken.None): TPromise { + doSearch(query: ITextQuery | IFileQuery, onProgress?: (p: ISearchProgressItem) => void, token: CancellationToken = CancellationToken.None): Promise { if (isFalsyOrEmpty(query.folderQueries)) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } const search = new SearchOperation(onProgress); @@ -128,17 +127,17 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable { ? this._proxy.$provideFileSearchResults(this._handle, search.id, query, token) : this._proxy.$provideTextSearchResults(this._handle, search.id, query, token); - return TPromise.wrap(searchP).then((result: ISearchCompleteStats) => { + return Promise.resolve(searchP).then((result: ISearchCompleteStats) => { this._searches.delete(search.id); return { results: values(search.matches), stats: result.stats, limitHit: result.limitHit }; }, err => { this._searches.delete(search.id); - return TPromise.wrapError(err); + return Promise.reject(err); }); } - clearCache(cacheKey: string): TPromise { - return TPromise.wrap(this._proxy.$clearCache(cacheKey)); + clearCache(cacheKey: string): Promise { + return Promise.resolve(this._proxy.$clearCache(cacheKey)); } handleFindMatch(session: number, dataOrUri: (UriComponents | IRawFileMatch2)[]): void { @@ -149,10 +148,10 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable { const searchOp = this._searches.get(session); dataOrUri.forEach(result => { - if ((result).matches) { + if ((result).results) { searchOp.addMatch({ resource: URI.revive((result).resource), - matches: (result).matches + results: (result).results }); } else { searchOp.addMatch({ diff --git a/src/vs/workbench/api/electron-browser/mainThreadTask.ts b/src/vs/workbench/api/electron-browser/mainThreadTask.ts index ca910c482..61d5a6336 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTask.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTask.ts @@ -8,27 +8,30 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import * as Objects from 'vs/base/common/objects'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as Types from 'vs/base/common/types'; import * as Platform from 'vs/base/common/platform'; import { IStringDictionary } from 'vs/base/common/collections'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ContributedTask, ExtensionTaskSourceTransfer, KeyedTaskIdentifier, TaskExecution, Task, TaskEvent, TaskEventKind, - PresentationOptions, CommandOptions, CommandConfiguration, RuntimeType, CustomTask, TaskScope, TaskSource, TaskSourceKind, ExtensionTaskSource, RevealKind, PanelKind + PresentationOptions, CommandOptions, CommandConfiguration, RuntimeType, CustomTask, TaskScope, TaskSource, TaskSourceKind, ExtensionTaskSource, RevealKind, PanelKind, RunOptions } from 'vs/workbench/parts/tasks/common/tasks'; -import { TaskDefinition } from 'vs/workbench/parts/tasks/node/tasks'; -import { ITaskService, TaskFilter } from 'vs/workbench/parts/tasks/common/taskService'; +import { ResolveSet, ResolvedVariables } from 'vs/workbench/parts/tasks/common/taskSystem'; +import { ITaskService, TaskFilter, ITaskProvider } from 'vs/workbench/parts/tasks/common/taskService'; + +import { TaskDefinition } from 'vs/workbench/parts/tasks/node/tasks'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { ExtHostContext, MainThreadTaskShape, ExtHostTaskShape, MainContext, IExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; import { TaskDefinitionDTO, TaskExecutionDTO, ProcessExecutionOptionsDTO, TaskPresentationOptionsDTO, - ProcessExecutionDTO, ShellExecutionDTO, ShellExecutionOptionsDTO, TaskDTO, TaskSourceDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO + ProcessExecutionDTO, ShellExecutionDTO, ShellExecutionOptionsDTO, TaskDTO, TaskSourceDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO, + RunOptionsDTO } from 'vs/workbench/api/shared/tasks'; namespace TaskExecutionDTO { @@ -97,6 +100,15 @@ namespace TaskPresentationOptionsDTO { } } +namespace RunOptionsDTO { + export function from(value: RunOptions): RunOptionsDTO { + if (value === void 0 || value === null) { + return undefined; + } + return Objects.assign(Object.create(null), value); + } +} + namespace ProcessExecutionOptionsDTO { export function from(value: CommandOptions): ProcessExecutionOptionsDTO { if (value === void 0 || value === null) { @@ -287,7 +299,8 @@ namespace TaskDTO { presentationOptions: task.command ? TaskPresentationOptionsDTO.from(task.command.presentation) : undefined, isBackground: task.isBackground, problemMatchers: [], - hasDefinedMatchers: ContributedTask.is(task) ? task.hasDefinedMatchers : false + hasDefinedMatchers: ContributedTask.is(task) ? task.hasDefinedMatchers : false, + runOptions: RunOptionsDTO.from(task.runOptions), }; if (task.group) { result.group = task.group; @@ -342,7 +355,8 @@ namespace TaskDTO { command: command, isBackground: !!task.isBackground, problemMatchers: task.problemMatchers.slice(), - hasDefinedMatchers: task.hasDefinedMatchers + hasDefinedMatchers: task.hasDefinedMatchers, + runOptions: task.runOptions, }; return result; } @@ -362,7 +376,7 @@ export class MainThreadTask implements MainThreadTaskShape { private _extHostContext: IExtHostContext; private _proxy: ExtHostTaskShape; - private _activeHandles: { [handle: number]: boolean; }; + private _providers: Map; constructor( extHostContext: IExtHostContext, @@ -370,7 +384,7 @@ export class MainThreadTask implements MainThreadTaskShape { @IWorkspaceContextService private readonly _workspaceContextServer: IWorkspaceContextService ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTask); - this._activeHandles = Object.create(null); + this._providers = new Map(); this._taskService.onDidStateChange((event: TaskEvent) => { let task = event.__task; if (event.kind === TaskEventKind.Start) { @@ -386,16 +400,16 @@ export class MainThreadTask implements MainThreadTaskShape { } public dispose(): void { - Object.keys(this._activeHandles).forEach((handle) => { - this._taskService.unregisterTaskProvider(parseInt(handle, 10)); + this._providers.forEach((value) => { + value.disposable.dispose(); }); - this._activeHandles = Object.create(null); + this._providers.clear(); } public $registerTaskProvider(handle: number): Thenable { - this._taskService.registerTaskProvider(handle, { + let provider: ITaskProvider = { provideTasks: (validTypes: IStringDictionary) => { - return TPromise.wrap(this._proxy.$provideTasks(handle, validTypes)).then((value) => { + return Promise.resolve(this._proxy.$provideTasks(handle, validTypes)).then((value) => { let tasks: Task[] = []; for (let task of value.tasks) { let taskTransfer = task._source as any as ExtensionTaskSourceTransfer; @@ -417,15 +431,15 @@ export class MainThreadTask implements MainThreadTaskShape { return value; }); } - }); - this._activeHandles[handle] = true; - return TPromise.wrap(undefined); + }; + let disposable = this._taskService.registerTaskProvider(provider); + this._providers.set(handle, { disposable, provider }); + return Promise.resolve(undefined); } public $unregisterTaskProvider(handle: number): Thenable { - this._taskService.unregisterTaskProvider(handle); - delete this._activeHandles[handle]; - return TPromise.wrap(undefined); + this._providers.delete(handle); + return Promise.resolve(undefined); } public $fetchTasks(filter?: TaskFilterDTO): Thenable { @@ -442,7 +456,7 @@ export class MainThreadTask implements MainThreadTaskShape { } public $executeTask(value: TaskHandleDTO | TaskDTO): Thenable { - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { if (TaskHandleDTO.is(value)) { let workspaceFolder = this._workspaceContextServer.getWorkspaceFolder(URI.revive(value.workspaceFolder)); this._taskService.getTask(workspaceFolder, value.id, true).then((task: Task) => { @@ -468,7 +482,7 @@ export class MainThreadTask implements MainThreadTaskShape { } public $terminateTask(id: string): Thenable { - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { this._taskService.getActiveTasks().then((tasks) => { for (let task of tasks) { if (id === task._id) { @@ -506,12 +520,18 @@ export class MainThreadTask implements MainThreadTaskShape { return URI.parse(`${info.scheme}://${info.authority}${path}`); }, context: this._extHostContext, - resolveVariables: (workspaceFolder: IWorkspaceFolder, variables: Set): TPromise> => { + resolveVariables: (workspaceFolder: IWorkspaceFolder, toResolve: ResolveSet): Promise => { let vars: string[] = []; - variables.forEach(item => vars.push(item)); - return TPromise.wrap(this._proxy.$resolveVariables(workspaceFolder.uri, vars)).then(values => { - let result = new Map(); - Object.keys(values).forEach(key => result.set(key, values[key])); + toResolve.variables.forEach(item => vars.push(item)); + return Promise.resolve(this._proxy.$resolveVariables(workspaceFolder.uri, { process: toResolve.process, variables: vars })).then(values => { + let result = { + process: undefined as string, + variables: new Map() + }; + Object.keys(values.variables).forEach(key => result.variables.set(key, values[key])); + if (Types.isString(values.process)) { + result.process = values.process; + } return result; }); } diff --git a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts index 35192f668..0369631af 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts @@ -5,7 +5,6 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalProcessExtHostProxy, ITerminalProcessExtHostRequest, ITerminalDimensions, EXT_HOST_CREATION_DELAY } from 'vs/workbench/parts/terminal/common/terminal'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, ShellLaunchConfigDto } from 'vs/workbench/api/node/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; @@ -64,12 +63,12 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape ignoreConfigurationCwd: true, env }; - return TPromise.as(this.terminalService.createTerminal(shellLaunchConfig).id); + return Promise.resolve(this.terminalService.createTerminal(shellLaunchConfig).id); } public $createTerminalRenderer(name: string): Thenable { const instance = this.terminalService.createTerminalRenderer(name); - return TPromise.as(instance.id); + return Promise.resolve(instance.id); } public $show(terminalId: number, preserveFocus: boolean): void { diff --git a/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts b/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts index 50a345b39..d798d579b 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts @@ -3,13 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { Disposable } from 'vs/base/common/lifecycle'; import { ExtHostContext, MainThreadTreeViewsShape, ExtHostTreeViewsShape, MainContext, IExtHostContext } from '../node/extHost.protocol'; -import { ITreeViewDataProvider, ITreeItem, IViewsService, ITreeViewer, ViewsRegistry, ICustomViewDescriptor } from 'vs/workbench/common/views'; +import { ITreeViewDataProvider, ITreeItem, IViewsService, ITreeView, ViewsRegistry, ICustomViewDescriptor, IRevealOptions } from 'vs/workbench/common/views'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { distinct } from 'vs/base/common/arrays'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { isUndefinedOrNull, isNumber } from 'vs/base/common/types'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; @extHostNamedCustomer(MainContext.MainThreadTreeViews) export class MainThreadTreeViews extends Disposable implements MainThreadTreeViewsShape { @@ -26,12 +27,13 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTreeViews); } - $registerTreeViewDataProvider(treeViewId: string): void { + $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean }): void { const dataProvider = new TreeViewDataProvider(treeViewId, this._proxy, this.notificationService); this._dataProviders.set(treeViewId, dataProvider); - const viewer = this.getTreeViewer(treeViewId); + const viewer = this.getTreeView(treeViewId); if (viewer) { viewer.dataProvider = dataProvider; + viewer.showCollapseAllAction = !!options.showCollapseAll; this.registerListeners(treeViewId, viewer); this._proxy.$setVisible(treeViewId, viewer.visible); } else { @@ -39,41 +41,84 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie } } - $reveal(treeViewId: string, item: ITreeItem, parentChain: ITreeItem[], options: { select: boolean, focus: boolean }): Thenable { + $reveal(treeViewId: string, item: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Thenable { return this.viewsService.openView(treeViewId, options.focus) .then(() => { - const viewer = this.getTreeViewer(treeViewId); - return viewer ? viewer.reveal(item, parentChain, options) : null; + const viewer = this.getTreeView(treeViewId); + return this.reveal(viewer, this._dataProviders.get(treeViewId), item, parentChain, options); }); } - $refresh(treeViewId: string, itemsToRefreshByHandle: { [treeItemHandle: string]: ITreeItem }): TPromise { - const viewer = this.getTreeViewer(treeViewId); + $refresh(treeViewId: string, itemsToRefreshByHandle: { [treeItemHandle: string]: ITreeItem }): Thenable { + const viewer = this.getTreeView(treeViewId); const dataProvider = this._dataProviders.get(treeViewId); if (viewer && dataProvider) { const itemsToRefresh = dataProvider.getItemsToRefresh(itemsToRefreshByHandle); return viewer.refresh(itemsToRefresh.length ? itemsToRefresh : void 0); } - return TPromise.as(null); + return null; } - private registerListeners(treeViewId: string, treeViewer: ITreeViewer): void { - this._register(treeViewer.onDidExpandItem(item => this._proxy.$setExpanded(treeViewId, item.handle, true))); - this._register(treeViewer.onDidCollapseItem(item => this._proxy.$setExpanded(treeViewId, item.handle, false))); - this._register(treeViewer.onDidChangeSelection(items => this._proxy.$setSelection(treeViewId, items.map(({ handle }) => handle)))); - this._register(treeViewer.onDidChangeVisibility(isVisible => this._proxy.$setVisible(treeViewId, isVisible))); + $setMessage(treeViewId: string, message: string | IMarkdownString): void { + const viewer = this.getTreeView(treeViewId); + if (viewer) { + viewer.message = message; + } } - private getTreeViewer(treeViewId: string): ITreeViewer { + private async reveal(treeView: ITreeView, dataProvider: TreeViewDataProvider, item: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Promise { + options = options ? options : { select: false, focus: false }; + const select = isUndefinedOrNull(options.select) ? false : options.select; + const focus = isUndefinedOrNull(options.focus) ? false : options.focus; + let expand = Math.min(isNumber(options.expand) ? options.expand : options.expand === true ? 1 : 0, 3); + + if (dataProvider.isEmpty()) { + // Refresh if empty + await treeView.refresh(); + } + for (const parent of parentChain) { + await treeView.expand(parent); + } + item = dataProvider.getItem(item.handle); + if (item) { + await treeView.reveal(item); + if (select) { + treeView.setSelection([item]); + } + if (focus) { + treeView.setFocus(item); + } + let itemsToExpand = [item]; + for (; itemsToExpand.length > 0 && expand > 0; expand--) { + await treeView.expand(itemsToExpand); + itemsToExpand = itemsToExpand.reduce((result, item) => { + item = dataProvider.getItem(item.handle); + if (item && item.children && item.children.length) { + result.push(...item.children); + } + return result; + }, []); + } + } + } + + private registerListeners(treeViewId: string, treeView: ITreeView): void { + this._register(treeView.onDidExpandItem(item => this._proxy.$setExpanded(treeViewId, item.handle, true))); + this._register(treeView.onDidCollapseItem(item => this._proxy.$setExpanded(treeViewId, item.handle, false))); + this._register(treeView.onDidChangeSelection(items => this._proxy.$setSelection(treeViewId, items.map(({ handle }) => handle)))); + this._register(treeView.onDidChangeVisibility(isVisible => this._proxy.$setVisible(treeViewId, isVisible))); + } + + private getTreeView(treeViewId: string): ITreeView { const viewDescriptor: ICustomViewDescriptor = ViewsRegistry.getView(treeViewId); - return viewDescriptor ? viewDescriptor.treeViewer : null; + return viewDescriptor ? viewDescriptor.treeView : null; } dispose(): void { this._dataProviders.forEach((dataProvider, treeViewId) => { - const treeViewer = this.getTreeViewer(treeViewId); - if (treeViewer) { - treeViewer.dataProvider = null; + const treeView = this.getTreeView(treeViewId); + if (treeView) { + treeView.dataProvider = null; } }); this._dataProviders.clear(); @@ -93,24 +138,21 @@ class TreeViewDataProvider implements ITreeViewDataProvider { ) { } - getChildren(treeItem?: ITreeItem): TPromise { - if (treeItem && treeItem.children) { - return TPromise.as(treeItem.children); - } - return TPromise.wrap(this._proxy.$getChildren(this.treeViewId, treeItem ? treeItem.handle : void 0) - .then(children => { - return this.postGetChildren(children); - }, err => { - this.notificationService.error(err); - return []; - })); + getChildren(treeItem?: ITreeItem): Promise { + return Promise.resolve(this._proxy.$getChildren(this.treeViewId, treeItem ? treeItem.handle : void 0) + .then( + children => this.postGetChildren(children), + err => { + this.notificationService.error(err); + return []; + })); } getItemsToRefresh(itemsToRefreshByHandle: { [treeItemHandle: string]: ITreeItem }): ITreeItem[] { const itemsToRefresh: ITreeItem[] = []; if (itemsToRefreshByHandle) { for (const treeItemHandle of Object.keys(itemsToRefreshByHandle)) { - const currentTreeItem = this.itemsMap.get(treeItemHandle); + const currentTreeItem = this.getItem(treeItemHandle); if (currentTreeItem) { // Refresh only if the item exists const treeItem = itemsToRefreshByHandle[treeItemHandle]; // Update the current item with refreshed item @@ -132,6 +174,14 @@ class TreeViewDataProvider implements ITreeViewDataProvider { return itemsToRefresh; } + getItem(treeItemHandle: string): ITreeItem { + return this.itemsMap.get(treeItemHandle); + } + + isEmpty(): boolean { + return this.itemsMap.size === 0; + } + private postGetChildren(elements: ITreeItem[]): ITreeItem[] { const result: ITreeItem[] = []; if (elements) { diff --git a/src/vs/workbench/api/electron-browser/mainThreadUrls.ts b/src/vs/workbench/api/electron-browser/mainThreadUrls.ts index f2ee377a1..ab3585598 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadUrls.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadUrls.ts @@ -5,7 +5,6 @@ import { ExtHostContext, IExtHostContext, MainContext, MainThreadUrlsShape, ExtHostUrlsShape } from 'vs/workbench/api/node/extHost.protocol'; import { extHostNamedCustomer } from './extHostCustomers'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; import { URI } from 'vs/base/common/uri'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -19,12 +18,12 @@ class ExtensionUrlHandler implements IURLHandler { readonly extensionId: string ) { } - handleURL(uri: URI): TPromise { + handleURL(uri: URI): Promise { if (uri.authority !== this.extensionId) { - return TPromise.as(false); + return Promise.resolve(false); } - return TPromise.wrap(this.proxy.$handleExternalUri(this.handle, uri)).then(() => true); + return Promise.resolve(this.proxy.$handleExternalUri(this.handle, uri)).then(() => true); } } @@ -49,14 +48,14 @@ export class MainThreadUrls implements MainThreadUrlsShape { this.handlers.set(handle, { extensionId, disposable }); this.inactiveExtensionUrlHandler.registerExtensionHandler(extensionId, handler); - return TPromise.as(null); + return Promise.resolve(null); } $unregisterUriHandler(handle: number): Thenable { const tuple = this.handlers.get(handle); if (!tuple) { - return TPromise.as(null); + return Promise.resolve(null); } const { extensionId, disposable } = tuple; @@ -65,7 +64,7 @@ export class MainThreadUrls implements MainThreadUrlsShape { this.handlers.delete(handle); disposable.dispose(); - return TPromise.as(null); + return Promise.resolve(null); } dispose(): void { diff --git a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts index 03c26a2eb..74a4a6532 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts @@ -5,7 +5,6 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import * as map from 'vs/base/common/map'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { localize } from 'vs/nls'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IOpenerService } from 'vs/platform/opener/common/opener'; @@ -19,6 +18,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import * as vscode from 'vscode'; import { extHostNamedCustomer } from './extHostCustomers'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @extHostNamedCustomer(MainContext.MainThreadWebviews) export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviver { @@ -45,7 +45,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv @IWebviewEditorService private readonly _webviewService: IWebviewEditorService, @IOpenerService private readonly _openerService: IOpenerService, @IExtensionService private readonly _extensionService: IExtensionService, - + @ITelemetryService private readonly _telemetryService: ITelemetryService ) { this._proxy = context.getProxy(ExtHostContext.ExtHostWebviews); _editorService.onDidActiveEditorChange(this.onActiveEditorChanged, this, this._toDispose); @@ -68,6 +68,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv title: string, showOptions: { viewColumn: EditorViewColumn | null, preserveFocus: boolean }, options: WebviewInputOptions, + extensionId: string, extensionLocation: UriComponents ): void { const mainThreadShowOptions: ICreateWebViewShowOptions = Object.create(null); @@ -84,6 +85,13 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv this._webviews.set(handle, webview); this._activeWebview = handle; + + /* __GDPR__ + "webviews:createWebviewPanel" : { + "extensionId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this._telemetryService.publicLog('webviews:createWebviewPanel', { extensionId: extensionId }); } public $disposeWebview(handle: WebviewPanelHandle): void { @@ -133,7 +141,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv editor.sendMessage(message); } - return TPromise.as(editors.length > 0); + return Promise.resolve(editors.length > 0); } public $registerSerializer(viewType: string): void { @@ -144,9 +152,9 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv this._revivers.delete(viewType); } - public reviveWebview(webview: WebviewEditorInput): TPromise { + public reviveWebview(webview: WebviewEditorInput): Promise { const viewType = webview.state.viewType; - return this._extensionService.activateByEvent(`onWebviewPanel:${viewType}`).then(() => { + return Promise.resolve(this._extensionService.activateByEvent(`onWebviewPanel:${viewType}`).then(() => { const handle = 'revival-' + MainThreadWebviews.revivalPool++; this._webviews.set(handle, webview); webview._events = this.createWebviewEventDelegate(handle); @@ -164,7 +172,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv .then(undefined, () => { webview.html = MainThreadWebviews.getDeserializationFailedContents(viewType); }); - }); + })); } public canRevive(webview: WebviewEditorInput): boolean { @@ -175,14 +183,13 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv return this._revivers.has(webview.state.viewType) || !!webview.reviver; } - private _onWillShutdown(): TPromise { + private _onWillShutdown(): boolean { this._webviews.forEach((view) => { if (this.canRevive(view)) { view.state.state = view.webviewState; } }); - - return TPromise.as(false); // Don't veto shutdown + return false; // Don't veto shutdown } private createWebviewEventDelegate(handle: WebviewPanelHandle) { @@ -317,4 +324,4 @@ function reviveWebviewIcon( light: URI.revive(value.light), dark: URI.revive(value.dark) }; -} \ No newline at end of file +} diff --git a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts index 9427e5fc2..a57c1ae65 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts @@ -183,7 +183,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { query._reason = 'startTextSearch'; const onProgress = (p: ISearchProgressItem) => { - if (p.matches) { + if (p.results) { this._proxy.$handleTextSearchResult(p, requestId); } }; diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index c5b4176b5..d173cde3e 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -59,7 +59,7 @@ import { ExtHostUrls } from 'vs/workbench/api/node/extHostUrls'; import { ExtHostWebviews } from 'vs/workbench/api/node/extHostWebview'; import { ExtHostWindow } from 'vs/workbench/api/node/extHostWindow'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; -import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionDescription, throwProposedApiError, checkProposedApiEnabled, nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier'; import * as vscode from 'vscode'; @@ -67,16 +67,6 @@ export interface IExtensionApiFactory { (extension: IExtensionDescription): typeof vscode; } -export function checkProposedApiEnabled(extension: IExtensionDescription): void { - if (!extension.enableProposedApi) { - throwProposedApiError(extension); - } -} - -function throwProposedApiError(extension: IExtensionDescription): never { - throw new Error(`[${extension.id}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${extension.id}`); -} - function proposedApiFunction(extension: IExtensionDescription, fn: T): T { if (extension.enableProposedApi) { return fn; @@ -144,7 +134,8 @@ export function createApiFactory( const extHostLanguages = new ExtHostLanguages(rpcProtocol, extHostDocuments); // Register an output channel for exthost log - extHostOutputService.createOutputChannelFromLogFile(localize('extensionsLog', "Extension Host"), extHostLogService.logFile); + const name = localize('extensionsLog', "Extension Host"); + extHostOutputService.createOutputChannelFromLogFile(name, extHostLogService.logFile); // Register API-ish commands ExtHostApiCommands.register(extHostCommands); @@ -156,7 +147,7 @@ export function createApiFactory( // extension should specify then the `file`-scheme, e.g `{ scheme: 'fooLang', language: 'fooLang' }` // We only inform once, it is not a warning because we just want to raise awareness and because // we cannot say if the extension is doing it right or wrong... - let checkSelector = (function () { + const checkSelector = (function () { let done = (!extension.isUnderDevelopment); function informOnce(selector: vscode.DocumentSelector) { if (!done) { @@ -181,6 +172,24 @@ export function createApiFactory( }; })(); + // Warn when trying to use the vscode.previewHtml command as it does not work properly in all scenarios and + // has security concerns. + const checkCommand = (() => { + let done = !extension.isUnderDevelopment; + const informOnce = () => { + if (!done) { + done = true; + console.warn(`Extension '${extension.id}' uses the 'vscode.previewHtml' command which is deprecated and will be removed. Please update your extension to use the Webview API: https://go.microsoft.com/fwlink/?linkid=2039309`); + } + }; + return (commandId: string) => { + if (commandId === 'vscode.previewHtml') { + informOnce(); + } + return commandId; + }; + })(); + // namespace: commands const commands: typeof vscode.commands = { registerCommand(id: string, command: (...args: any[]) => T | Thenable, thisArgs?: any): vscode.Disposable { @@ -220,7 +229,7 @@ export function createApiFactory( }); }), executeCommand(id: string, ...args: any[]): Thenable { - return extHostCommands.executeCommand(id, ...args); + return extHostCommands.executeCommand(checkCommand(id), ...args); }, getCommands(filterInternal: boolean = false): Thenable { return extHostCommands.getCommands(filterInternal); @@ -243,7 +252,6 @@ export function createApiFactory( return extHostLogService.onDidChangeLogLevel; }, get clipboard(): vscode.Clipboard { - checkProposedApiEnabled(extension); return extHostClipboard; } }); @@ -283,64 +291,67 @@ export function createApiFactory( return score(typeConverters.LanguageSelector.from(selector), document.uri, document.languageId, true); }, registerCodeActionsProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable { - return extHostLanguageFeatures.registerCodeActionProvider(checkSelector(selector), provider, extension, metadata); + return extHostLanguageFeatures.registerCodeActionProvider(extension, checkSelector(selector), provider, metadata); }, registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable { - return extHostLanguageFeatures.registerCodeLensProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerCodeLensProvider(extension, checkSelector(selector), provider); }, registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable { - return extHostLanguageFeatures.registerDefinitionProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerDefinitionProvider(extension, checkSelector(selector), provider); + }, + registerDeclarationProvider(selector: vscode.DocumentSelector, provider: vscode.DeclarationProvider): vscode.Disposable { + return extHostLanguageFeatures.registerDeclarationProvider(extension, checkSelector(selector), provider); }, registerImplementationProvider(selector: vscode.DocumentSelector, provider: vscode.ImplementationProvider): vscode.Disposable { - return extHostLanguageFeatures.registerImplementationProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerImplementationProvider(extension, checkSelector(selector), provider); }, registerTypeDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.TypeDefinitionProvider): vscode.Disposable { - return extHostLanguageFeatures.registerTypeDefinitionProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerTypeDefinitionProvider(extension, checkSelector(selector), provider); }, registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider): vscode.Disposable { - return extHostLanguageFeatures.registerHoverProvider(checkSelector(selector), provider, extension.id); + return extHostLanguageFeatures.registerHoverProvider(extension, checkSelector(selector), provider, extension.id); }, registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable { - return extHostLanguageFeatures.registerDocumentHighlightProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerDocumentHighlightProvider(extension, checkSelector(selector), provider); }, registerReferenceProvider(selector: vscode.DocumentSelector, provider: vscode.ReferenceProvider): vscode.Disposable { - return extHostLanguageFeatures.registerReferenceProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerReferenceProvider(extension, checkSelector(selector), provider); }, registerRenameProvider(selector: vscode.DocumentSelector, provider: vscode.RenameProvider): vscode.Disposable { - return extHostLanguageFeatures.registerRenameProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerRenameProvider(extension, checkSelector(selector), provider); }, - registerDocumentSymbolProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider): vscode.Disposable { - return extHostLanguageFeatures.registerDocumentSymbolProvider(checkSelector(selector), provider, extension); + registerDocumentSymbolProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider, metadata?: vscode.DocumentSymbolProviderMetadata): vscode.Disposable { + return extHostLanguageFeatures.registerDocumentSymbolProvider(extension, checkSelector(selector), provider, metadata); }, registerWorkspaceSymbolProvider(provider: vscode.WorkspaceSymbolProvider): vscode.Disposable { - return extHostLanguageFeatures.registerWorkspaceSymbolProvider(provider); + return extHostLanguageFeatures.registerWorkspaceSymbolProvider(extension, provider); }, registerDocumentFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentFormattingEditProvider): vscode.Disposable { - return extHostLanguageFeatures.registerDocumentFormattingEditProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerDocumentFormattingEditProvider(extension, checkSelector(selector), provider); }, registerDocumentRangeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentRangeFormattingEditProvider): vscode.Disposable { - return extHostLanguageFeatures.registerDocumentRangeFormattingEditProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerDocumentRangeFormattingEditProvider(extension, checkSelector(selector), provider); }, registerOnTypeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.OnTypeFormattingEditProvider, firstTriggerCharacter: string, ...moreTriggerCharacters: string[]): vscode.Disposable { - return extHostLanguageFeatures.registerOnTypeFormattingEditProvider(checkSelector(selector), provider, [firstTriggerCharacter].concat(moreTriggerCharacters)); + return extHostLanguageFeatures.registerOnTypeFormattingEditProvider(extension, checkSelector(selector), provider, [firstTriggerCharacter].concat(moreTriggerCharacters)); }, registerSignatureHelpProvider(selector: vscode.DocumentSelector, provider: vscode.SignatureHelpProvider, firstItem?: string | vscode.SignatureHelpProviderMetadata, ...remaining: string[]): vscode.Disposable { if (typeof firstItem === 'object') { - return extHostLanguageFeatures.registerSignatureHelpProvider(checkSelector(selector), provider, firstItem); + return extHostLanguageFeatures.registerSignatureHelpProvider(extension, checkSelector(selector), provider, firstItem); } - return extHostLanguageFeatures.registerSignatureHelpProvider(checkSelector(selector), provider, typeof firstItem === 'undefined' ? [] : [firstItem, ...remaining]); + return extHostLanguageFeatures.registerSignatureHelpProvider(extension, checkSelector(selector), provider, typeof firstItem === 'undefined' ? [] : [firstItem, ...remaining]); }, registerCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, ...triggerCharacters: string[]): vscode.Disposable { - return extHostLanguageFeatures.registerCompletionItemProvider(checkSelector(selector), provider, triggerCharacters); + return extHostLanguageFeatures.registerCompletionItemProvider(extension, checkSelector(selector), provider, triggerCharacters); }, registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable { - return extHostLanguageFeatures.registerDocumentLinkProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerDocumentLinkProvider(extension, checkSelector(selector), provider); }, registerColorProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable { - return extHostLanguageFeatures.registerColorProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerColorProvider(extension, checkSelector(selector), provider); }, registerFoldingRangeProvider(selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider): vscode.Disposable { - return extHostLanguageFeatures.registerFoldingRangeProvider(checkSelector(selector), provider); + return extHostLanguageFeatures.registerFoldingRangeProvider(extension, checkSelector(selector), provider); }, setLanguageConfiguration: (language: string, configuration: vscode.LanguageConfiguration): vscode.Disposable => { return extHostLanguageFeatures.setLanguageConfiguration(language, configuration); @@ -449,7 +460,7 @@ export function createApiFactory( return extHostOutputService.createOutputChannel(name); }, createWebviewPanel(viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, options: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel { - return extHostWebviews.createWebview(extension.extensionLocation, viewType, title, showOptions, options); + return extHostWebviews.createWebview(extension, viewType, title, showOptions, options); }, createTerminal(nameOrOptions: vscode.TerminalOptions | string, shellPath?: string, shellArgs?: string[]): vscode.Terminal { if (typeof nameOrOptions === 'object') { @@ -461,10 +472,10 @@ export function createApiFactory( return extHostTerminalService.createTerminalRenderer(name); }), registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider): vscode.Disposable { - return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider); + return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider, extension); }, createTreeView(viewId: string, options: { treeDataProvider: vscode.TreeDataProvider }): vscode.TreeView { - return extHostTreeViews.createTreeView(viewId, options); + return extHostTreeViews.createTreeView(viewId, options, extension); }, registerWebviewPanelSerializer: (viewType: string, serializer: vscode.WebviewPanelSerializer) => { return extHostWebviews.registerWebviewPanelSerializer(viewType, serializer); @@ -618,7 +629,7 @@ export function createApiFactory( return exthostCommentProviders.registerDocumentCommentProvider(provider); }), registerWorkspaceCommentProvider: proposedApiFunction(extension, (provider: vscode.WorkspaceCommentProvider) => { - return exthostCommentProviders.registerWorkspaceCommentProvider(provider); + return exthostCommentProviders.registerWorkspaceCommentProvider(extension.id, provider); }), onDidRenameFile: proposedApiFunction(extension, (listener, thisArg?, disposables?) => { return extHostFileSystemEvent.onDidRenameFile(listener, thisArg, disposables); @@ -665,7 +676,13 @@ export function createApiFactory( return extHostDebugService.onDidChangeBreakpoints(listener, thisArgs, disposables); }, registerDebugConfigurationProvider(debugType: string, provider: vscode.DebugConfigurationProvider) { - return extHostDebugService.registerDebugConfigurationProvider(extension, debugType, provider); + return extHostDebugService.registerDebugConfigurationProvider(debugType, provider); + }, + registerDebugAdapterProvider(debugType: string, provider: vscode.DebugAdapterProvider) { + return extHostDebugService.registerDebugAdapterProvider(extension, debugType, provider); + }, + registerDebugAdapterTracker(debugType: string, callback: (session: vscode.DebugSession) => vscode.DebugAdapterTracker | undefined) { + return extHostDebugService.registerDebugAdapterTracker(debugType, callback); }, startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration) { return extHostDebugService.startDebugging(folder, nameOrConfig); @@ -730,7 +747,6 @@ export function createApiFactory( CommentThreadCollapsibleState: extHostTypes.CommentThreadCollapsibleState, CompletionItem: extHostTypes.CompletionItem, CompletionItemKind: extHostTypes.CompletionItemKind, - CompletionItemInsertTextRule: extension.enableProposedApi ? extHostTypes.CompletionItemInsertTextRule : null, CompletionList: extHostTypes.CompletionList, CompletionTriggerKind: extHostTypes.CompletionTriggerKind, ConfigurationTarget: extHostTypes.ConfigurationTarget, @@ -768,6 +784,7 @@ export function createApiFactory( QuickInputButtons: extHostTypes.QuickInputButtons, Range: extHostTypes.Range, RelativePattern: extHostTypes.RelativePattern, + RerunBehavior: extHostTypes.RerunBehavior, Selection: extHostTypes.Selection, ShellExecution: extHostTypes.ShellExecution, ShellQuoting: extHostTypes.ShellQuoting, @@ -781,6 +798,7 @@ export function createApiFactory( SymbolInformation: extHostTypes.SymbolInformation, SymbolKind: extHostTypes.SymbolKind, Task: extHostTypes.Task, + Task2: extHostTypes.Task, TaskGroup: extHostTypes.TaskGroup, TaskPanelKind: extHostTypes.TaskPanelKind, TaskRevealKind: extHostTypes.TaskRevealKind, @@ -882,19 +900,3 @@ function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchT return defaultApiImpl; }; } - -const nullExtensionDescription: IExtensionDescription = { - id: 'nullExtensionDescription', - name: 'Null Extension Description', - publisher: 'vscode', - activationEvents: undefined, - contributes: undefined, - enableProposedApi: false, - engines: undefined, - extensionDependencies: undefined, - extensionLocation: undefined, - isBuiltin: false, - isUnderDevelopment: false, - main: undefined, - version: undefined -}; diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index bb31f02ce..0461e14bb 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -32,7 +32,7 @@ import { ThemeColor } from 'vs/platform/theme/common/themeService'; import { EndOfLine, IFileOperationOptions, TextEditorLineNumbersStyle } from 'vs/workbench/api/node/extHostTypes'; import { EditorViewColumn } from 'vs/workbench/api/shared/editor'; import { TaskDTO, TaskExecutionDTO, TaskFilterDTO, TaskHandleDTO, TaskProcessEndedDTO, TaskProcessStartedDTO, TaskSystemInfoDTO } from 'vs/workbench/api/shared/tasks'; -import { ITreeItem } from 'vs/workbench/common/views'; +import { ITreeItem, IRevealOptions } from 'vs/workbench/common/views'; import { IAdapterDescriptor, IConfig, ITerminalSettings } from 'vs/workbench/parts/debug/common/debug'; import { ITextQueryBuilderOptions } from 'vs/workbench/parts/search/common/queryBuilder'; import { TaskSet } from 'vs/workbench/parts/tasks/common/tasks'; @@ -42,6 +42,7 @@ import { IRPCProtocol, ProxyIdentifier, createExtHostContextProxyIdentifier as c import { IProgressOptions, IProgressStep } from 'vs/platform/progress/common/progress'; import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; import * as vscode from 'vscode'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; export interface IEnvironment { isExtensionDevelopmentDebug: boolean; @@ -68,6 +69,7 @@ export interface IInitData { telemetryInfo: ITelemetryInfo; logLevel: LogLevel; logsLocation: URI; + remoteAuthority?: string | null; } export interface IConfigurationInitData extends IConfigurationData { @@ -80,6 +82,7 @@ export interface IWorkspaceConfigurationChangeEventData { } export interface IExtHostContext extends IRPCProtocol { + remoteAuthority: string; } export interface IMainContext extends IRPCProtocol { @@ -102,7 +105,7 @@ export interface MainThreadCommandsShape extends IDisposable { export interface MainThreadCommentsShape extends IDisposable { $registerDocumentCommentProvider(handle: number): void; $unregisterDocumentCommentProvider(handle: number): void; - $registerWorkspaceCommentProvider(handle: number): void; + $registerWorkspaceCommentProvider(handle: number, extensionId: string): void; $unregisterWorkspaceCommentProvider(handle: number): void; $onDidCommentThreadsChange(handle: number, event: modes.CommentThreadChangedEvent): void; } @@ -210,9 +213,10 @@ export interface MainThreadTextEditorsShape extends IDisposable { } export interface MainThreadTreeViewsShape extends IDisposable { - $registerTreeViewDataProvider(treeViewId: string): void; + $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean }): void; $refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): Thenable; - $reveal(treeViewId: string, treeItem: ITreeItem, parentChain: ITreeItem[], options: { select: boolean, focus: boolean }): Thenable; + $reveal(treeViewId: string, treeItem: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Thenable; + $setMessage(treeViewId: string, message: string | IMarkdownString): void; } export interface MainThreadErrorsShape extends IDisposable { @@ -274,10 +278,11 @@ export interface ISerializedSignatureHelpProviderMetadata { export interface MainThreadLanguageFeaturesShape extends IDisposable { $unregister(handle: number): void; - $registerOutlineSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: string): void; + $registerOutlineSupport(handle: number, selector: ISerializedDocumentFilter[], label: string): void; $registerCodeLensSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number): void; $emitCodeLensEvent(eventHandle: number, event?: any): void; - $registerDeclaractionSupport(handle: number, selector: ISerializedDocumentFilter[]): void; + $registerDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void; + $registerDeclarationSupport(handle: number, selector: ISerializedDocumentFilter[]): void; $registerImplementationSupport(handle: number, selector: ISerializedDocumentFilter[]): void; $registerTypeDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void; $registerHoverProvider(handle: number, selector: ISerializedDocumentFilter[]): void; @@ -416,9 +421,9 @@ export interface TransferInputBox extends BaseTransferQuickInput { } export interface MainThreadQuickOpenShape extends IDisposable { - $show(options: IPickOptions, token: CancellationToken): Thenable; - $setItems(items: TransferQuickPickItems[]): Thenable; - $setError(error: Error): Thenable; + $show(instance: number, options: IPickOptions, token: CancellationToken): Thenable; + $setItems(instance: number, items: TransferQuickPickItems[]): Thenable; + $setError(instance: number, error: Error): Thenable; $input(options: vscode.InputBoxOptions, validateInput: boolean, token: CancellationToken): Thenable; $createOrUpdate(params: TransferQuickInput): Thenable; $dispose(id: number): Thenable; @@ -446,7 +451,7 @@ export interface WebviewPanelShowOptions { } export interface MainThreadWebviewsShape extends IDisposable { - $createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionLocation: UriComponents): void; + $createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionId: string, extensionLocation: UriComponents): void; $disposeWebview(handle: WebviewPanelHandle): void; $reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void; $setTitle(handle: WebviewPanelHandle, value: string): void; @@ -579,6 +584,7 @@ export interface MainThreadSCMShape extends IDisposable { $setInputBoxValue(sourceControlHandle: number, value: string): void; $setInputBoxPlaceholder(sourceControlHandle: number, placeholder: string): void; + $setInputBoxVisibility(sourceControlHandle: number, visible: boolean): void; $setValidationProviderIsEnabled(sourceControlHandle: number, enabled: boolean): void; } @@ -590,11 +596,13 @@ export interface MainThreadDebugServiceShape extends IDisposable { $acceptDAError(handle: number, name: string, message: string, stack: string): void; $acceptDAExit(handle: number, code: number, signal: string): void; $registerDebugConfigurationProvider(type: string, hasProvideMethod: boolean, hasResolveMethod: boolean, hasProvideDaMethod: boolean, hasProvideTrackerMethod: boolean, handle: number): Thenable; - $unregisterDebugConfigurationProvider(handle: number): Thenable; + $registerDebugAdapterProvider(type: string, handle: number): Thenable; + $unregisterDebugConfigurationProvider(handle: number): void; + $unregisterDebugAdapterProvider(handle: number): void; $startDebugging(folder: UriComponents | undefined, nameOrConfig: string | vscode.DebugConfiguration): Thenable; $customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): Thenable; - $appendDebugConsole(value: string): Thenable; - $startBreakpointEvents(): Thenable; + $appendDebugConsole(value: string): void; + $startBreakpointEvents(): void; $registerBreakpoints(breakpoints: (ISourceMultiBreakpointDto | IFunctionBreakpointDto)[]): Thenable; $unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[]): Thenable; } @@ -840,6 +848,7 @@ export interface ExtHostLanguageFeaturesShape { $provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Thenable; $resolveCodeLens(handle: number, resource: UriComponents, symbol: modes.ICodeLensSymbol, token: CancellationToken): Thenable; $provideDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable; + $provideDeclaration(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable; $provideImplementation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable; $provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable; $provideHover(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable; @@ -913,7 +922,7 @@ export interface ExtHostTaskShape { $onDidStartTaskProcess(value: TaskProcessStartedDTO): void; $onDidEndTaskProcess(value: TaskProcessEndedDTO): void; $OnDidEndTask(execution: TaskExecutionDTO): void; - $resolveVariables(workspaceFolder: UriComponents, variables: string[]): Thenable; + $resolveVariables(workspaceFolder: UriComponents, toResolve: { process?: { name: string; cwd?: string }, variables: string[] }): Thenable<{ process?: string; variables: { [key: string]: string } }>; } export interface IBreakpointDto { @@ -957,21 +966,26 @@ export interface ISourceMultiBreakpointDto { }[]; } -export interface IDebugSessionDto { +export interface IDebugSessionFullDto { id: DebugSessionUUID; type: string; name: string; + folderUri: UriComponents | undefined; + configuration: IConfig; } +export type IDebugSessionDto = IDebugSessionFullDto | DebugSessionUUID; + export interface ExtHostDebugServiceShape { $substituteVariables(folder: UriComponents | undefined, config: IConfig): Thenable; - $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Thenable; - $startDASession(handle: number, session: IDebugSessionDto, folder: UriComponents | undefined, debugConfiguration: IConfig): Thenable; + $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Thenable; + $startDASession(handle: number, session: IDebugSessionDto): Thenable; $stopDASession(handle: number): Thenable; $sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): void; $resolveDebugConfiguration(handle: number, folder: UriComponents | undefined, debugConfiguration: IConfig): Thenable; $provideDebugConfigurations(handle: number, folder: UriComponents | undefined): Thenable; - $provideDebugAdapter(handle: number, session: IDebugSessionDto, folderUri: UriComponents | undefined, debugConfiguration: IConfig): Thenable; + $legacyDebugAdapterExecutable(handle: number, folderUri: UriComponents | undefined): Thenable; // TODO@AW legacy + $provideDebugAdapter(handle: number, session: IDebugSessionDto): Thenable; $acceptDebugSessionStarted(session: IDebugSessionDto): void; $acceptDebugSessionTerminated(session: IDebugSessionDto): void; $acceptDebugSessionActiveChanged(session: IDebugSessionDto): void; diff --git a/src/vs/workbench/api/node/extHostApiCommands.ts b/src/vs/workbench/api/node/extHostApiCommands.ts index 9a397bc72..7eb6e84e1 100644 --- a/src/vs/workbench/api/node/extHostApiCommands.ts +++ b/src/vs/workbench/api/node/extHostApiCommands.ts @@ -47,6 +47,14 @@ export class ExtHostApiCommands { ], returns: 'A promise that resolves to an array of Location-instances.' }); + this._register('vscode.executeDeclarationProvider', this._executeDeclaraionProvider, { + description: 'Execute all declaration provider.', + args: [ + { name: 'uri', description: 'Uri of a text document', constraint: URI }, + { name: 'position', description: 'Position of a symbol', constraint: types.Position } + ], + returns: 'A promise that resolves to an array of Location-instances.' + }); this._register('vscode.executeTypeDefinitionProvider', this._executeTypeDefinitionProvider, { description: 'Execute all type definition providers.', args: [ @@ -292,6 +300,15 @@ export class ExtHostApiCommands { .then(tryMapWith(typeConverters.location.to)); } + private _executeDeclaraionProvider(resource: URI, position: types.Position): Thenable { + const args = { + resource, + position: position && typeConverters.Position.from(position) + }; + return this._commands.executeCommand('_executeDeclarationProvider', args) + .then(tryMapWith(typeConverters.location.to)); + } + private _executeTypeDefinitionProvider(resource: URI, position: types.Position): Thenable { const args = { resource, diff --git a/src/vs/workbench/api/node/extHostClipboard.ts b/src/vs/workbench/api/node/extHostClipboard.ts index 342845ba0..90c739c54 100644 --- a/src/vs/workbench/api/node/extHostClipboard.ts +++ b/src/vs/workbench/api/node/extHostClipboard.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import { IMainContext, MainContext, MainThreadClipboardShape } from 'vs/workbench/api/node/extHost.protocol'; import * as vscode from 'vscode'; diff --git a/src/vs/workbench/api/node/extHostCommands.ts b/src/vs/workbench/api/node/extHostCommands.ts index 00f933667..5de0838f9 100644 --- a/src/vs/workbench/api/node/extHostCommands.ts +++ b/src/vs/workbench/api/node/extHostCommands.ts @@ -10,7 +10,7 @@ import * as extHostTypeConverter from 'vs/workbench/api/node/extHostTypeConverte import { cloneAndChange } from 'vs/base/common/objects'; import { MainContext, MainThreadCommandsShape, ExtHostCommandsShape, ObjectIdentifier, IMainContext } from './extHost.protocol'; import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService'; -import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; import * as modes from 'vs/editor/common/modes'; import * as vscode from 'vscode'; import { ILogService } from 'vs/platform/log/common/log'; @@ -189,7 +189,7 @@ export class CommandsConverter { title: command.title }; - if (command.command && !isFalsyOrEmpty(command.arguments)) { + if (command.command && isNonEmptyArray(command.arguments)) { // we have a contributed command with arguments. that // means we don't want to send the arguments around diff --git a/src/vs/workbench/api/node/extHostComments.ts b/src/vs/workbench/api/node/extHostComments.ts index 3970fd566..cbd48c1a5 100644 --- a/src/vs/workbench/api/node/extHostComments.ts +++ b/src/vs/workbench/api/node/extHostComments.ts @@ -31,11 +31,12 @@ export class ExtHostComments implements ExtHostCommentsShape { } registerWorkspaceCommentProvider( + extensionId: string, provider: vscode.WorkspaceCommentProvider ): vscode.Disposable { const handle = ExtHostComments.handlePool++; this._workspaceProviders.set(handle, provider); - this._proxy.$registerWorkspaceCommentProvider(handle); + this._proxy.$registerWorkspaceCommentProvider(handle, extensionId); this.registerListeners(handle, provider); return { @@ -145,7 +146,6 @@ export class ExtHostComments implements ExtHostCommentsShape { provider.onDidChangeCommentThreads(event => { this._proxy.$onDidCommentThreadsChange(handle, { - owner: handle, changed: event.changed.map(thread => convertToCommentThread(provider, thread, this._commandsConverter)), added: event.added.map(thread => convertToCommentThread(provider, thread, this._commandsConverter)), removed: event.removed.map(thread => convertToCommentThread(provider, thread, this._commandsConverter)) @@ -156,7 +156,6 @@ export class ExtHostComments implements ExtHostCommentsShape { function convertCommentInfo(owner: number, provider: vscode.DocumentCommentProvider, vscodeCommentInfo: vscode.CommentInfo, commandsConverter: CommandsConverter): modes.CommentInfo { return { - owner: owner, threads: vscodeCommentInfo.threads.map(x => convertToCommentThread(provider, x, commandsConverter)), commentingRanges: vscodeCommentInfo.commentingRanges ? vscodeCommentInfo.commentingRanges.map(range => extHostTypeConverter.Range.from(range)) : [] }; diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index 5176cbe16..840dd9a06 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -9,19 +9,17 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; import { asThenable } from 'vs/base/common/async'; import * as nls from 'vs/nls'; -import { deepClone } from 'vs/base/common/objects'; import { MainContext, MainThreadDebugServiceShape, ExtHostDebugServiceShape, DebugSessionUUID, IMainContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto } from 'vs/workbench/api/node/extHost.protocol'; import * as vscode from 'vscode'; -import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint, DebugAdapterServer, DebugAdapterExecutable } from 'vs/workbench/api/node/extHostTypes'; -import { generateUuid } from 'vs/base/common/uuid'; +import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint, DebugAdapterServer, DebugAdapterExecutable, DebugAdapterImplementation } from 'vs/workbench/api/node/extHostTypes'; import { ExecutableDebugAdapter, SocketDebugAdapter, AbstractDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors'; -import { ITerminalSettings, IDebuggerContribution, IConfig, IDebugAdapter } from 'vs/workbench/parts/debug/common/debug'; +import { ITerminalSettings, IDebuggerContribution, IConfig, IDebugAdapter, IDebugAdapterServer, IDebugAdapterExecutable, IDebugAdapterImplementation, IAdapterDescriptor } from 'vs/workbench/parts/debug/common/debug'; import { getTerminalLauncher, hasChildprocesses, prepareCommand } from 'vs/workbench/parts/debug/node/terminals'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/node/variableResolver'; @@ -37,10 +35,11 @@ import { IExtensionDescription } from 'vs/workbench/services/extensions/common/e export class ExtHostDebugService implements ExtHostDebugServiceShape { - private _providerHandleCounter: number; - private _providerByHandle: Map; - private _providerByType: Map; - private _providers: TypeProviderPair[]; + private _configProviderHandleCounter: number; + private _configProviders: TypeProviderPair[]; + + private _adapterProviderHandleCounter: number; + private _adapterProviders: TypeDaProviderPair[]; private _debugServiceProxy: MainThreadDebugServiceShape; private _debugSessions: Map = new Map(); @@ -70,7 +69,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { private _aexCommands: Map; private _debugAdapters: Map; - private _debugAdaptersTrackers: Map; + private _debugAdaptersTrackers: Map; private _variableResolver: IConfigurationResolverService; @@ -86,10 +85,11 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { private _terminalService: ExtHostTerminalService, private _commandService: ExtHostCommands ) { - this._providerHandleCounter = 0; - this._providerByHandle = new Map(); - this._providerByType = new Map(); - this._providers = []; + this._configProviderHandleCounter = 0; + this._configProviders = []; + + this._adapterProviderHandleCounter = 0; + this._adapterProviders = []; this._aexCommands = new Map(); this._debugAdapters = new Map(); @@ -156,23 +156,15 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { this.startBreakpoints(); - // assign uuids for brand new breakpoints - const breakpoints: vscode.Breakpoint[] = []; - for (const bp of breakpoints0) { - let id = bp['_id']; - if (id) { // has already id - if (this._breakpoints.has(id)) { - // already there - } else { - breakpoints.push(bp); - } - } else { - id = generateUuid(); - bp['_id'] = id; + // filter only new breakpoints + const breakpoints = breakpoints0.filter(bp => { + const id = bp.id; + if (!this._breakpoints.has(id)) { this._breakpoints.set(id, bp); - breakpoints.push(bp); + return true; } - } + return false; + }); // send notification for added breakpoints this.fireBreakpointChanges(breakpoints, [], []); @@ -193,7 +185,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { dtos.push(dto); } dto.lines.push({ - id: bp['_id'], + id: bp.id, enabled: bp.enabled, condition: bp.condition, hitCondition: bp.hitCondition, @@ -204,7 +196,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { } else if (bp instanceof FunctionBreakpoint) { dtos.push({ type: 'function', - id: bp['_id'], + id: bp.id, enabled: bp.enabled, hitCondition: bp.hitCondition, logMessage: bp.logMessage, @@ -223,20 +215,14 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { this.startBreakpoints(); // remove from array - const breakpoints: vscode.Breakpoint[] = []; - for (const b of breakpoints0) { - let id = b['_id']; - if (id && this._breakpoints.delete(id)) { - breakpoints.push(b); - } - } + const breakpoints = breakpoints0.filter(b => this._breakpoints.delete(b.id)); // send notification this.fireBreakpointChanges([], breakpoints, []); // unregister with VS Code - const ids = breakpoints.filter(bp => bp instanceof SourceBreakpoint).map(bp => bp['_id']); - const fids = breakpoints.filter(bp => bp instanceof FunctionBreakpoint).map(bp => bp['_id']); + const ids = breakpoints.filter(bp => bp instanceof SourceBreakpoint).map(bp => bp.id); + const fids = breakpoints.filter(bp => bp instanceof FunctionBreakpoint).map(bp => bp.id); return this._debugServiceProxy.$unregisterBreakpoints(ids, fids); } @@ -244,49 +230,69 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { return this._debugServiceProxy.$startDebugging(folder ? folder.uri : undefined, nameOrConfig); } - public registerDebugConfigurationProvider(extension: IExtensionDescription, type: string, provider: vscode.DebugConfigurationProvider): vscode.Disposable { + public registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider): vscode.Disposable { if (!provider) { return new Disposable(() => { }); } - // if a provider has a provideDebugAdapter method, we check the constraints specified in the API doc - if (provider.provideDebugAdapter) { - - // a provider with this method can only be registered in the extension that contributes the debugger - if (!this.definesDebugType(extension, type)) { - throw new Error(`method 'provideDebugAdapter' must only be called from the extension that defines the '${type}' debugger.`); - } - - // make sure that only one provider for this type is registered - if (this._providerByType.has(type)) { - throw new Error(`a provider with method 'provideDebugAdapter' can only be registered once per a type.`); - } else { - this._providerByType.set(type, provider); - } - } - - let handle = this._providerHandleCounter++; - this._providerByHandle.set(handle, provider); - this._providers.push({ type, provider }); + let handle = this._configProviderHandleCounter++; + this._configProviders.push({ type, handle, provider }); this._debugServiceProxy.$registerDebugConfigurationProvider(type, !!provider.provideDebugConfigurations, !!provider.resolveDebugConfiguration, - !!provider.debugAdapterExecutable || !!provider.provideDebugAdapter, - !!provider.provideDebugAdapterTracker, handle); + !!provider.debugAdapterExecutable, // TODO@AW: deprecated + !!provider.provideDebugAdapterTracker, // TODO@AW: deprecated + handle); return new Disposable(() => { - this._providerByHandle.delete(handle); - this._providerByType.delete(type); - this._providers = this._providers.filter(p => p.provider !== provider); // remove + this._configProviders = this._configProviders.filter(p => p.provider !== provider); // remove this._debugServiceProxy.$unregisterDebugConfigurationProvider(handle); }); } + public registerDebugAdapterProvider(extension: IExtensionDescription, type: string, provider: vscode.DebugAdapterProvider): vscode.Disposable { + + if (!provider) { + return new Disposable(() => { }); + } + + // a DebugAdapterProvider can only be registered in the extension that contributes the debugger + if (!this.definesDebugType(extension, type)) { + throw new Error(`method 'provideDebugAdapter' must only be called from the extension that defines the '${type}' debugger.`); + } + + // make sure that only one provider for this type is registered + if (this.getAdapterProviderByType(type)) { + throw new Error(`a provider with method 'provideDebugAdapter' can only be registered once per a type.`); + } + + let handle = this._adapterProviderHandleCounter++; + this._adapterProviders.push({ type, handle, provider }); + + this._debugServiceProxy.$registerDebugAdapterProvider(type, handle); + + return new Disposable(() => { + this._adapterProviders = this._adapterProviders.filter(p => p.provider !== provider); // remove + this._debugServiceProxy.$unregisterDebugAdapterProvider(handle); + }); + } + + public registerDebugAdapterTracker(debugType: string, callback: (session: vscode.DebugSession) => vscode.DebugAdapterTracker | undefined) { + + const provider: vscode.DebugConfigurationProvider = { + provideDebugAdapterTracker(session: vscode.DebugSession) { + return callback(session); + } + }; + + return this.registerDebugConfigurationProvider(debugType, provider); + } + // RPC methods (ExtHostDebugServiceShape) - public $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Thenable { + public $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Thenable { if (args.kind === 'integrated') { @@ -317,12 +323,12 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { this._integratedTerminalInstance.show(); - return new Promise((resolve) => { - setTimeout(_ => { - const command = prepareCommand(args, config); - this._integratedTerminalInstance.sendText(command, true); - resolve(void 0); - }, 500); + return this._integratedTerminalInstance.processId.then(shellProcessId => { + + const command = prepareCommand(args, config); + this._integratedTerminalInstance.sendText(command, true); + + return shellProcessId; }); }); @@ -355,11 +361,13 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { return Promise.resolve(this._variableResolver.resolveAny(ws, config)); } - public $startDASession(handle: number, sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): Thenable { + public $startDASession(debugAdapterHandle: number, sessionDto: IDebugSessionDto): Thenable { const mythis = this; - return this.getAdapterDescriptor(this._providerByType.get(config.type), sessionDto, folderUri, config).then(adapter => { + const session = this.getSession(sessionDto); + return this.getAdapterDescriptor(this.getAdapterProviderByType(session.type), session).then(x => { + const adapter = this.convertToDto(x); let da: AbstractDebugAdapter | undefined = undefined; switch (adapter.type) { @@ -369,7 +377,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { break; case 'executable': - da = new ExecutableDebugAdapter(adapter, config.type); + da = new ExecutableDebugAdapter(adapter, session.type); break; case 'implementation': @@ -381,39 +389,36 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { } if (da) { - this._debugAdapters.set(handle, da); + this._debugAdapters.set(debugAdapterHandle, da); - return this.getDebugAdapterTrackers(sessionDto, folderUri, config).then(tracker => { + return this.getDebugAdapterTrackers(session).then(tracker => { if (tracker) { - this._debugAdaptersTrackers.set(handle, tracker); + this._debugAdaptersTrackers.set(debugAdapterHandle, tracker); } da.onMessage(message => { - // since we modify Source.paths in the message in place, we need to make a copy of it (see #61129) - const msg = deepClone(message); - if (tracker) { - tracker.fromDebugAdapter(msg); + tracker.fromDebugAdapter(message); } // DA -> VS Code - convertToVSCPaths(msg, source => stringToUri(source)); + message = convertToVSCPaths(message, source => stringToUri(source)); - mythis._debugServiceProxy.$acceptDAMessage(handle, msg); + mythis._debugServiceProxy.$acceptDAMessage(debugAdapterHandle, message); }); da.onError(err => { if (tracker) { tracker.debugAdapterError(err); } - this._debugServiceProxy.$acceptDAError(handle, err.name, err.message, err.stack); + this._debugServiceProxy.$acceptDAError(debugAdapterHandle, err.name, err.message, err.stack); }); da.onExit(code => { if (tracker) { tracker.debugAdapterExit(code, null); } - this._debugServiceProxy.$acceptDAExit(handle, code, null); + this._debugServiceProxy.$acceptDAExit(debugAdapterHandle, code, null); }); if (tracker) { @@ -428,32 +433,33 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { }); } - public $sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): Promise { + public $sendDAMessage(debugAdapterHandle: number, message: DebugProtocol.ProtocolMessage): Promise { + // VS Code -> DA - convertToDAPaths(message, source => uriToString(source)); + message = convertToDAPaths(message, source => uriToString(source)); - const tracker = this._debugAdaptersTrackers.get(handle); + const tracker = this._debugAdaptersTrackers.get(debugAdapterHandle); // TODO@AW: same handle? if (tracker) { tracker.toDebugAdapter(message); } - const da = this._debugAdapters.get(handle); + const da = this._debugAdapters.get(debugAdapterHandle); if (da) { da.sendMessage(message); } return void 0; } - public $stopDASession(handle: number): Thenable { + public $stopDASession(debugAdapterHandle: number): Thenable { - const tracker = this._debugAdaptersTrackers.get(handle); - this._debugAdaptersTrackers.delete(handle); + const tracker = this._debugAdaptersTrackers.get(debugAdapterHandle); + this._debugAdaptersTrackers.delete(debugAdapterHandle); if (tracker) { tracker.stopDebugAdapter(); } - const da = this._debugAdapters.get(handle); - this._debugAdapters.delete(handle); + const da = this._debugAdapters.get(debugAdapterHandle); + this._debugAdapters.delete(debugAdapterHandle); if (da) { return da.stopSession(); } else { @@ -478,7 +484,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { const uri = URI.revive(bpd.uri); bp = new SourceBreakpoint(new Location(uri, new Position(bpd.line, bpd.character)), bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage); } - bp['_id'] = bpd.id; + (bp as any)._id = bpd.id; this._breakpoints.set(bpd.id, bp); a.push(bp); } @@ -522,8 +528,8 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { this.fireBreakpointChanges(a, r, c); } - public $provideDebugConfigurations(handle: number, folderUri: UriComponents | undefined): Thenable { - let provider = this._providerByHandle.get(handle); + public $provideDebugConfigurations(configProviderHandle: number, folderUri: UriComponents | undefined): Thenable { + let provider = this.getConfigProviderByHandle(configProviderHandle); if (!provider) { return Promise.reject(new Error('no handler found')); } @@ -533,8 +539,8 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { return asThenable(() => provider.provideDebugConfigurations(this.getFolder(folderUri), CancellationToken.None)); } - public $resolveDebugConfiguration(handle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration): Thenable { - let provider = this._providerByHandle.get(handle); + public $resolveDebugConfiguration(configProviderHandle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration): Thenable { + let provider = this.getConfigProviderByHandle(configProviderHandle); if (!provider) { return Promise.reject(new Error('no handler found')); } @@ -544,15 +550,24 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { return asThenable(() => provider.resolveDebugConfiguration(this.getFolder(folderUri), debugConfiguration, CancellationToken.None)); } - public $provideDebugAdapter(handle: number, sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): Thenable { - let provider = this._providerByHandle.get(handle); + // TODO@AW legacy + public $legacyDebugAdapterExecutable(configProviderHandle: number, folderUri: UriComponents | undefined): Thenable { + let provider = this.getConfigProviderByHandle(configProviderHandle); if (!provider) { return Promise.reject(new Error('no handler found')); } - if (!provider.debugAdapterExecutable && !provider.provideDebugAdapter) { - return Promise.reject(new Error('handler has no methods provideDebugAdapter or debugAdapterExecutable')); + if (!provider.debugAdapterExecutable) { + return Promise.reject(new Error('handler has no method debugAdapterExecutable')); } - return this.getAdapterDescriptor(provider, this.getSession(sessionDto), folderUri, config); + return asThenable(() => provider.debugAdapterExecutable(this.getFolder(folderUri), CancellationToken.None)).then(x => this.convertToDto(x)); + } + + public $provideDebugAdapter(adapterProviderHandle: number, sessionDto: IDebugSessionDto): Thenable { + let adapterProvider = this.getAdapterProviderByHandle(adapterProviderHandle); + if (!adapterProvider) { + return Promise.reject(new Error('no handler found')); + } + return this.getAdapterDescriptor(adapterProvider, this.getSession(sessionDto)).then(x => this.convertToDto(x)); } public $acceptDebugSessionStarted(sessionDto: IDebugSessionDto): void { @@ -562,8 +577,11 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { public $acceptDebugSessionTerminated(sessionDto: IDebugSessionDto): void { - this._onDidTerminateDebugSession.fire(this.getSession(sessionDto)); - this._debugSessions.delete(sessionDto.id); + const session = this.getSession(sessionDto); + if (session) { + this._onDidTerminateDebugSession.fire(session); + this._debugSessions.delete(session.id); + } } public $acceptDebugSessionActiveChanged(sessionDto: IDebugSessionDto): void { @@ -584,6 +602,54 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { // private & dto helpers + private convertToDto(x: vscode.DebugAdapterDescriptor): IAdapterDescriptor { + if (x instanceof DebugAdapterExecutable) { + return { + type: 'executable', + command: x.command, + args: x.args, + options: x.options + }; + } else if (x instanceof DebugAdapterServer) { + return { + type: 'server', + port: x.port, + host: x.host + }; + } else if (x instanceof DebugAdapterImplementation) { + return { + type: 'implementation', + implementation: x.implementation + }; + } else { + throw new Error('unexpected type'); + } + } + + private getAdapterProviderByType(type: string): vscode.DebugAdapterProvider { + const results = this._adapterProviders.filter(p => p.type === type); + if (results.length > 0) { + return results[0].provider; + } + return undefined; + } + + private getAdapterProviderByHandle(handle: number): vscode.DebugAdapterProvider { + const results = this._adapterProviders.filter(p => p.handle === handle); + if (results.length > 0) { + return results[0].provider; + } + return undefined; + } + + private getConfigProviderByHandle(handle: number): vscode.DebugConfigurationProvider { + const results = this._configProviders.filter(p => p.handle === handle); + if (results.length > 0) { + return results[0].provider; + } + return undefined; + } + private definesDebugType(ed: IExtensionDescription, type: string) { if (ed.contributes) { const debuggers = ed.contributes['debuggers']; @@ -601,54 +667,75 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { return false; } - private getDebugAdapterTrackers(sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): Promise { - - const session = this.getSession(sessionDto); - const folder = this.getFolder(folderUri); + private getDebugAdapterTrackers(session: ExtHostDebugSession): Promise { + const config = session.configuration; const type = config.type; - const promises = this._providers + const promises = this._configProviders .filter(pair => pair.provider.provideDebugAdapterTracker && (pair.type === type || pair.type === '*')) - .map(pair => pair.provider.provideDebugAdapterTracker(session, folder, config, CancellationToken.None)); + .map(pair => asThenable(() => pair.provider.provideDebugAdapterTracker(session, session.workspaceFolder, session.configuration, CancellationToken.None)).then(p => p).catch(err => null)); - return Promise.all(promises).then(trackers => { - if (trackers.length > 0) { - return new MultiTracker(trackers); - } + return Promise.race([ + Promise.all(promises).then(trackers => { + trackers = trackers.filter(t => t); // filter null + if (trackers.length > 0) { + return new MultiTracker(trackers); + } + return undefined; + }), + new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + clearTimeout(timeout); + reject(new Error('timeout')); + }, 1000); + }) + ]).catch(err => { + // ignore errors return undefined; }); } - private getAdapterDescriptor(debugConfigProvider, sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): Thenable { + private getAdapterDescriptor(adapterProvider: vscode.DebugAdapterProvider, session: ExtHostDebugSession): Thenable { // a "debugServer" attribute in the launch config takes precedence - if (typeof config.debugServer === 'number') { - return Promise.resolve(new DebugAdapterServer(config.debugServer)); + const serverPort = session.configuration.debugServer; + if (typeof serverPort === 'number') { + return Promise.resolve(new DebugAdapterServer(serverPort)); } - if (debugConfigProvider) { - // try the proposed "provideDebugAdapter" API - if (debugConfigProvider.provideDebugAdapter) { - const adapterExecutable = ExecutableDebugAdapter.platformAdapterExecutable(this._extensionService.getAllExtensionDescriptions(), config.type); - return asThenable(() => debugConfigProvider.provideDebugAdapter(this.getSession(sessionDto), this.getFolder(folderUri), adapterExecutable, config, CancellationToken.None)); - } - // try the deprecated "debugAdapterExecutable" API - if (debugConfigProvider.debugAdapterExecutable) { - return asThenable(() => debugConfigProvider.debugAdapterExecutable(this.getFolder(folderUri), CancellationToken.None)); + // TODO@AW legacy + const pairs = this._configProviders.filter(p => p.type === session.type); + if (pairs.length > 0) { + if (pairs[0].provider.debugAdapterExecutable) { + return asThenable(() => pairs[0].provider.debugAdapterExecutable(session.workspaceFolder, CancellationToken.None)); } } + if (adapterProvider) { + return asThenable(() => adapterProvider.provideDebugAdapter(session, this.daExecutableFromPackage(session))); + } + // try deprecated command based extension API "adapterExecutableCommand" to determine the executable - const aex = this._aexCommands.get(config.type); + // TODO@AW legacy + const aex = this._aexCommands.get(session.type); if (aex) { - const rootFolder = folderUri ? URI.revive(folderUri).toString() : undefined; + const folder = session.workspaceFolder; + const rootFolder = folder ? folder.uri.toString() : undefined; return this._commandService.executeCommand(aex, rootFolder).then((ae: { command: string, args: string[] }) => { return new DebugAdapterExecutable(ae.command, ae.args || []); }); } // fallback: use executable information from package.json - return Promise.resolve(ExecutableDebugAdapter.platformAdapterExecutable(this._extensionService.getAllExtensionDescriptions(), config.type)); + return Promise.resolve(this.daExecutableFromPackage(session)); + } + + private daExecutableFromPackage(session: ExtHostDebugSession): DebugAdapterExecutable | undefined { + const dae = ExecutableDebugAdapter.platformAdapterExecutable(this._extensionService.getAllExtensionDescriptions(), session.type); + if (dae) { + return new DebugAdapterExecutable(dae.command, dae.args, dae.options); + } + return undefined; } private startBreakpoints() { @@ -670,12 +757,13 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { private getSession(dto: IDebugSessionDto): ExtHostDebugSession { if (dto) { - let debugSession = this._debugSessions.get(dto.id); - if (!debugSession) { - debugSession = new ExtHostDebugSession(this._debugServiceProxy, dto.id, dto.type, dto.name); - this._debugSessions.set(dto.id, debugSession); + if (typeof dto === 'string') { + return this._debugSessions.get(dto); + } else { + const debugSession = new ExtHostDebugSession(this._debugServiceProxy, dto.id, dto.type, dto.name, this.getFolder(dto.folderUri), dto.configuration); + this._debugSessions.set(debugSession.id, debugSession); + return debugSession; } - return debugSession; } return undefined; } @@ -691,17 +779,13 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { export class ExtHostDebugSession implements vscode.DebugSession { - private _debugServiceProxy: MainThreadDebugServiceShape; - - private _id: DebugSessionUUID; - private _type: string; - private _name: string; - - constructor(proxy: MainThreadDebugServiceShape, id: DebugSessionUUID, type: string, name: string) { - this._debugServiceProxy = proxy; - this._id = id; - this._type = type; - this._name = name; + constructor( + private _debugServiceProxy: MainThreadDebugServiceShape, + private _id: DebugSessionUUID, + private _type: string, + private _name: string, + private _workspaceFolder: vscode.WorkspaceFolder | undefined, + private _configuration: vscode.DebugConfiguration) { } public get id(): string { @@ -716,6 +800,14 @@ export class ExtHostDebugSession implements vscode.DebugSession { return this._name; } + public get workspaceFolder(): vscode.WorkspaceFolder | undefined { + return this._workspaceFolder; + } + + public get configuration(): vscode.DebugConfiguration { + return this._configuration; + } + public customRequest(command: string, args: any): Thenable { return this._debugServiceProxy.$customDebugAdapterRequest(this._id, command, args); } @@ -757,7 +849,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ return configurationService.getConfiguration(undefined, folderUri).get(section); }, getExecPath: (): string | undefined => { - return undefined; // does not exist in EH + return process.env['VSCODE_EXEC_PATH']; }, getFilePath: (): string | undefined => { const activeEditor = editorService.activeEditor(); @@ -789,18 +881,19 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ interface TypeProviderPair { type: string; + handle: number; provider: vscode.DebugConfigurationProvider; } -interface IDapTransport { - start(cb: (msg: DebugProtocol.ProtocolMessage) => void, errorcb: (event: DebugProtocol.Event) => void); - send(message: DebugProtocol.ProtocolMessage); - stop(): void; +interface TypeDaProviderPair { + type: string; + handle: number; + provider: vscode.DebugAdapterProvider; } -class MultiTracker implements vscode.IDebugAdapterTracker { +class MultiTracker implements vscode.DebugAdapterTracker { - constructor(private trackers: vscode.IDebugAdapterTracker[]) { + constructor(private trackers: vscode.DebugAdapterTracker[]) { } startDebugAdapter(): void { @@ -828,57 +921,56 @@ class MultiTracker implements vscode.IDebugAdapterTracker { } } -class DirectTransport implements IDapTransport { - - private _sendUp: (msg: DebugProtocol.ProtocolMessage) => void; - - constructor(private da: DirectDebugAdapter) { - } - - start(cb: (msg: DebugProtocol.ProtocolMessage) => void, errorcb: (event: DebugProtocol.Event) => void) { - this._sendUp = cb; - } - - sendUp(message: DebugProtocol.ProtocolMessage) { - this._sendUp(message); - } - - // DA -> VSCode - send(message: DebugProtocol.ProtocolMessage) { - this.da.acceptMessage(message); - } - - stop(): void { - throw new Error('Method not implemented.'); - } +interface IDapTransport { + start(cb: (msg: DebugProtocol.ProtocolMessage) => void, errorcb: (event: DebugProtocol.Event) => void); + send(message: DebugProtocol.ProtocolMessage); + stop(): void; } -class DirectDebugAdapter extends AbstractDebugAdapter { +class DirectDebugAdapter extends AbstractDebugAdapter implements IDapTransport { readonly onError: Event; readonly onExit: Event; - private transport: DirectTransport; + private _sendUp: (msg: DebugProtocol.ProtocolMessage) => void; constructor(implementation: any) { super(); if (implementation.__setTransport) { - this.transport = new DirectTransport(this); - implementation.__setTransport(this.transport); + implementation.__setTransport(this); } } + // IDapTransport + start(cb: (msg: DebugProtocol.ProtocolMessage) => void, errorcb: (event: DebugProtocol.Event) => void) { + this._sendUp = cb; + } + + // AbstractDebugAdapter startSession(): Promise { return Promise.resolve(void 0); } + // AbstractDebugAdapter // VSCode -> DA sendMessage(message: DebugProtocol.ProtocolMessage): void { - this.transport.sendUp(message); + this._sendUp(message); } + // AbstractDebugAdapter stopSession(): Promise { - this.transport.stop(); + this.stop(); return Promise.resolve(void 0); } + + // IDapTransport + // DA -> VSCode + send(message: DebugProtocol.ProtocolMessage) { + this.acceptMessage(message); + } + + // IDapTransport + stop(): void { + throw new Error('Method not implemented.'); + } } diff --git a/src/vs/workbench/api/node/extHostExtensionActivator.ts b/src/vs/workbench/api/node/extHostExtensionActivator.ts index aa1108d2e..48475d388 100644 --- a/src/vs/workbench/api/node/extHostExtensionActivator.ts +++ b/src/vs/workbench/api/node/extHostExtensionActivator.ts @@ -42,6 +42,14 @@ export interface IExtensionAPI { // _extensionAPIBrand: any; } +/* __GDPR__FRAGMENT__ + "ExtensionActivationTimes" : { + "startup": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "codeLoadingTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "activateCallTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "activateResolvedTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } + } +*/ export class ExtensionActivationTimes { public static readonly NONE = new ExtensionActivationTimes(false, -1, -1, -1); diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index e46bbe9c8..b3783a674 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -20,6 +20,7 @@ import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; +import { connectProxyResolver } from 'vs/workbench/node/proxyResolver'; class ExtensionMemento implements IExtensionMemento { @@ -163,6 +164,9 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { const apiFactory = createApiFactory(initData, extHostContext, extHostWorkspace, extHostConfiguration, this, this._extHostLogService, this._storage); initializeExtensionApi(this, apiFactory).then(() => { + // Do this when extension service exists, but extensions are not being activated yet. + return connectProxyResolver(extHostWorkspace, extHostConfiguration, this, this._extHostLogService, this._mainThreadTelemetry); + }).then(() => { this._activator = new ExtensionsActivator(this._registry, { showMessage: (severity: Severity, message: string): void => { @@ -309,13 +313,33 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { const activationTimes = activatedExtension.activationTimes; let activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null); this._proxy.$onExtensionActivated(extensionDescription.id, activationTimes.startup, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, activationEvent); + this._logExtensionActivationTimes(extensionDescription, reason, 'success', activationTimes); return activatedExtension; }, (err) => { this._proxy.$onExtensionActivationFailed(extensionDescription.id); + this._logExtensionActivationTimes(extensionDescription, reason, 'failure'); throw err; }); } + private _logExtensionActivationTimes(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason, outcome: string, activationTimes?: ExtensionActivationTimes) { + let event = getTelemetryActivationEvent(extensionDescription, reason); + /* __GDPR__ + "extensionActivationTimes" : { + "${include}": [ + "${TelemetryActivationEvent}", + "${ExtensionActivationTimes}" + ], + "outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this._mainThreadTelemetry.$publicLog('extensionActivationTimes', { + ...event, + ...(activationTimes || {}), + outcome, + }); + } + private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise { let event = getTelemetryActivationEvent(extensionDescription, reason); /* __GDPR__ @@ -430,6 +454,7 @@ function getTelemetryActivationEvent(extensionDescription: IExtensionDescription "TelemetryActivationEvent" : { "id": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, "name": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, + "extensionVersion": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, "publisherDisplayName": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "activationEvents": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "isBuiltin": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, @@ -439,6 +464,7 @@ function getTelemetryActivationEvent(extensionDescription: IExtensionDescription let event = { id: extensionDescription.id, name: extensionDescription.name, + extensionVersion: extensionDescription.version, publisherDisplayName: extensionDescription.publisher, activationEvents: extensionDescription.activationEvents ? extensionDescription.activationEvents.join(',') : null, isBuiltin: extensionDescription.isBuiltin, diff --git a/src/vs/workbench/api/node/extHostFileSystem.ts b/src/vs/workbench/api/node/extHostFileSystem.ts index 60ad98ac2..6a605ba91 100644 --- a/src/vs/workbench/api/node/extHostFileSystem.ts +++ b/src/vs/workbench/api/node/extHostFileSystem.ts @@ -7,49 +7,98 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { MainContext, IMainContext, ExtHostFileSystemShape, MainThreadFileSystemShape, IFileChangeDto } from './extHost.protocol'; import * as vscode from 'vscode'; import * as files from 'vs/platform/files/common/files'; -import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { values } from 'vs/base/common/map'; -import { Range, FileChangeType } from 'vs/workbench/api/node/extHostTypes'; +import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle'; +import { FileChangeType, DocumentLink } from 'vs/workbench/api/node/extHostTypes'; +import * as typeConverter from 'vs/workbench/api/node/extHostTypeConverters'; import { ExtHostLanguageFeatures } from 'vs/workbench/api/node/extHostLanguageFeatures'; import { Schemas } from 'vs/base/common/network'; import { LabelRules } from 'vs/platform/label/common/label'; +import { State, StateMachine, LinkComputer } from 'vs/editor/common/modes/linkComputer'; +import { commonPrefixLength } from 'vs/base/common/strings'; +import { CharCode } from 'vs/base/common/charCode'; -class FsLinkProvider implements vscode.DocumentLinkProvider { +class FsLinkProvider { - private _schemes = new Set(); - private _regex: RegExp; + private _schemes: string[] = []; + private _stateMachine: StateMachine; add(scheme: string): void { - this._regex = undefined; - this._schemes.add(scheme); + this._stateMachine = undefined; + this._schemes.push(scheme); } delete(scheme: string): void { - if (this._schemes.delete(scheme)) { - this._regex = undefined; + let idx = this._schemes.indexOf(scheme); + if (idx >= 0) { + this._schemes.splice(idx, 1); + this._stateMachine = undefined; } } - provideDocumentLinks(document: vscode.TextDocument): vscode.ProviderResult { - if (this._schemes.size === 0) { - return undefined; - } - if (!this._regex) { - this._regex = new RegExp(`(${(values(this._schemes).join('|'))}):[^\\s]+`, 'gi'); - } - let result: vscode.DocumentLink[] = []; - let max = Math.min(document.lineCount, 2500); - for (let line = 0; line < max; line++) { - this._regex.lastIndex = 0; - let textLine = document.lineAt(line); - let m: RegExpMatchArray; - while (m = this._regex.exec(textLine.text)) { - const target = URI.parse(m[0]); - if (target.path[0] !== '/') { - continue; + private _initStateMachine(): void { + if (!this._stateMachine) { + + // sort and compute common prefix with previous scheme + // then build state transitions based on the data + const schemes = this._schemes.sort(); + const edges = []; + let prevScheme: string; + let prevState: State; + let nextState = State.LastKnownState; + for (const scheme of schemes) { + + // skip the common prefix of the prev scheme + // and continue with its last state + let pos = !prevScheme ? 0 : commonPrefixLength(prevScheme, scheme); + if (pos === 0) { + prevState = State.Start; + } else { + prevState = nextState; + } + + for (; pos < scheme.length; pos++) { + // keep creating new (next) states until the + // end (and the BeforeColon-state) is reached + if (pos + 1 === scheme.length) { + nextState = State.BeforeColon; + } else { + nextState += 1; + } + edges.push([prevState, scheme.toUpperCase().charCodeAt(pos), nextState]); + edges.push([prevState, scheme.toLowerCase().charCodeAt(pos), nextState]); + prevState = nextState; } - const range = new Range(line, this._regex.lastIndex - m[0].length, line, this._regex.lastIndex); - result.push({ target, range }); + + prevScheme = scheme; + } + + // all link must match this pattern `:/` + edges.push([State.BeforeColon, CharCode.Colon, State.AfterColon]); + edges.push([State.AfterColon, CharCode.Slash, State.End]); + + this._stateMachine = new StateMachine(edges); + } + } + + provideDocumentLinks(document: vscode.TextDocument): vscode.ProviderResult { + this._initStateMachine(); + + const result: vscode.DocumentLink[] = []; + const links = LinkComputer.computeLinks({ + getLineContent(lineNumber: number): string { + return document.lineAt(lineNumber - 1).text; + }, + getLineCount(): number { + return document.lineCount; + } + }, this._stateMachine); + + for (const link of links) { + try { + let uri = URI.parse(link.url, true); + result.push(new DocumentLink(typeConverter.Range.to(link.range), uri)); + } catch (err) { + // ignore } } return result; @@ -64,9 +113,10 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { private readonly _usedSchemes = new Set(); private readonly _watches = new Map(); + private _linkProviderRegistration: IDisposable; private _handlePool: number = 0; - constructor(mainContext: IMainContext, extHostLanguageFeatures: ExtHostLanguageFeatures) { + constructor(mainContext: IMainContext, private _extHostLanguageFeatures: ExtHostLanguageFeatures) { this._proxy = mainContext.getProxy(MainContext.MainThreadFileSystem); this._usedSchemes.add(Schemas.file); this._usedSchemes.add(Schemas.untitled); @@ -77,8 +127,16 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { this._usedSchemes.add(Schemas.https); this._usedSchemes.add(Schemas.mailto); this._usedSchemes.add(Schemas.data); + } - extHostLanguageFeatures.registerDocumentLinkProvider('*', this._linkProvider); + dispose(): void { + dispose(this._linkProviderRegistration); + } + + private _registerLinkProviderIfNotYetRegistered(): void { + if (!this._linkProviderRegistration) { + this._linkProviderRegistration = this._extHostLanguageFeatures.registerDocumentLinkProvider(undefined, '*', this._linkProvider); + } } registerFileSystemProvider(scheme: string, provider: vscode.FileSystemProvider, options: { isCaseSensitive?: boolean, isReadonly?: boolean } = {}) { @@ -87,6 +145,9 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { throw new Error(`a provider for the scheme '${scheme}' is already registered`); } + // + this._registerLinkProviderIfNotYetRegistered(); + const handle = this._handlePool++; this._linkProvider.add(scheme); this._usedSchemes.add(scheme); diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index bb9eae102..b735f50fb 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -19,7 +19,7 @@ import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesSh import { regExpLeadsToEndlessLoop } from 'vs/base/common/strings'; import { IPosition } from 'vs/editor/common/core/position'; import { IRange, Range as EditorRange } from 'vs/editor/common/core/range'; -import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { isFalsyOrEmpty, isNonEmptyArray } from 'vs/base/common/arrays'; import { isObject } from 'vs/base/common/types'; import { ISelection, Selection } from 'vs/editor/common/core/selection'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; @@ -166,6 +166,20 @@ class DefinitionAdapter { } } +class DeclarationAdapter { + + constructor( + private readonly _documents: ExtHostDocuments, + private readonly _provider: vscode.DeclarationProvider + ) { } + + provideDeclaration(resource: URI, position: IPosition, token: CancellationToken): Thenable { + let doc = this._documents.getDocumentData(resource).document; + let pos = typeConvert.Position.to(position); + return asThenable(() => this._provider.provideDeclaration(doc, pos, token)).then(convertToDefinitionLinks); + } +} + class ImplementationAdapter { constructor( @@ -419,7 +433,7 @@ class NavigateTypeAdapter { provideWorkspaceSymbols(search: string, token: CancellationToken): Thenable { const result: WorkspaceSymbolsDto = IdObject.mixin({ symbols: [] }); return asThenable(() => this._provider.provideWorkspaceSymbols(search, token)).then(value => { - if (!isFalsyOrEmpty(value)) { + if (isNonEmptyArray(value)) { for (const item of value) { if (!item) { // drop @@ -664,14 +678,14 @@ class SuggestAdapter { label: item.label, kind: typeConvert.CompletionItemKind.from(item.kind), detail: item.detail, - documentation: item.documentation, + documentation: typeConvert.MarkdownString.fromStrict(item.documentation), filterText: item.filterText, sortText: item.sortText, preselect: item.preselect, // range: undefined, insertText: undefined, - insertTextRules: typeConvert.CompletionItemInsertTextRule.from(item.insertTextRules), + insertTextRules: item.keepWhitespace ? modes.CompletionItemInsertTextRule.KeepWhitespace : 0, additionalTextEdits: item.additionalTextEdits && item.additionalTextEdits.map(typeConvert.TextEdit.from), command: this._commands.toInternal(item.command), commitCharacters: item.commitCharacters, @@ -840,7 +854,14 @@ type Adapter = OutlineAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapt | DocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter | DocumentFormattingAdapter | RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter | SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter - | ColorProviderAdapter | FoldingProviderAdapter; + | ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter; + +class AdapterData { + constructor( + readonly adapter: Adapter, + readonly extension: IExtensionDescription | undefined + ) { } +} export interface ISchemeTransformer { transformOutgoing(scheme: string): string; @@ -856,7 +877,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { private _commands: ExtHostCommands; private _heapService: ExtHostHeapService; private _diagnostics: ExtHostDiagnostics; - private _adapter = new Map(); + private _adapter = new Map(); private readonly _logService: ILogService; constructor( @@ -925,24 +946,37 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { } private _withAdapter(handle: number, ctor: { new(...args: any[]): A }, callback: (adapter: A) => Thenable): Thenable { - let adapter = this._adapter.get(handle); - if (!(adapter instanceof ctor)) { - return Promise.reject(new Error('no adapter found')); + let data = this._adapter.get(handle); + if (data.adapter instanceof ctor) { + let t1: number; + if (data.extension) { + t1 = Date.now(); + this._logService.trace(`[${data.extension.id}] INVOKE provider '${(ctor as any).name}'`); + } + let p = callback(data.adapter); + if (data.extension) { + Promise.resolve(p).then( + () => this._logService.trace(`[${data.extension.id}] provider DONE after ${Date.now() - t1}ms`), + err => this._logService.trace(`[${data.extension.id}] provider FAILED`, err) + ); + } + return p; } - return callback(adapter); + return Promise.reject(new Error('no adapter found')); } - private _addNewAdapter(adapter: Adapter): number { + private _addNewAdapter(adapter: Adapter, extension: IExtensionDescription): number { const handle = this._nextHandle(); - this._adapter.set(handle, adapter); + this._adapter.set(handle, new AdapterData(adapter, extension)); return handle; } // --- outline - registerDocumentSymbolProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider, extension?: IExtensionDescription): vscode.Disposable { - const handle = this._addNewAdapter(new OutlineAdapter(this._documents, provider)); - this._proxy.$registerOutlineSupport(handle, this._transformDocumentSelector(selector), extension ? extension.displayName || extension.name : undefined); + registerDocumentSymbolProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider, metadata?: vscode.DocumentSymbolProviderMetadata): vscode.Disposable { + const handle = this._addNewAdapter(new OutlineAdapter(this._documents, provider), extension); + const displayName = (metadata && metadata.label) || (extension && (extension.displayName || extension.name)) || undefined; + this._proxy.$registerOutlineSupport(handle, this._transformDocumentSelector(selector), displayName); return this._createDisposable(handle); } @@ -952,11 +986,11 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- code lens - registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable { + registerCodeLensProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable { const handle = this._nextHandle(); const eventHandle = typeof provider.onDidChangeCodeLenses === 'function' ? this._nextHandle() : undefined; - this._adapter.set(handle, new CodeLensAdapter(this._documents, this._commands.converter, this._heapService, provider)); + this._adapter.set(handle, new AdapterData(new CodeLensAdapter(this._documents, this._commands.converter, this._heapService, provider), extension)); this._proxy.$registerCodeLensSupport(handle, this._transformDocumentSelector(selector), eventHandle); let result = this._createDisposable(handle); @@ -978,9 +1012,9 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- declaration - registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable { - const handle = this._addNewAdapter(new DefinitionAdapter(this._documents, provider)); - this._proxy.$registerDeclaractionSupport(handle, this._transformDocumentSelector(selector)); + registerDefinitionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable { + const handle = this._addNewAdapter(new DefinitionAdapter(this._documents, provider), extension); + this._proxy.$registerDefinitionSupport(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } @@ -988,8 +1022,18 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._withAdapter(handle, DefinitionAdapter, adapter => adapter.provideDefinition(URI.revive(resource), position, token)); } - registerImplementationProvider(selector: vscode.DocumentSelector, provider: vscode.ImplementationProvider): vscode.Disposable { - const handle = this._addNewAdapter(new ImplementationAdapter(this._documents, provider)); + registerDeclarationProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DeclarationProvider): vscode.Disposable { + const handle = this._addNewAdapter(new DeclarationAdapter(this._documents, provider), extension); + this._proxy.$registerDeclarationSupport(handle, this._transformDocumentSelector(selector)); + return this._createDisposable(handle); + } + + $provideDeclaration(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable { + return this._withAdapter(handle, DeclarationAdapter, adapter => adapter.provideDeclaration(URI.revive(resource), position, token)); + } + + registerImplementationProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.ImplementationProvider): vscode.Disposable { + const handle = this._addNewAdapter(new ImplementationAdapter(this._documents, provider), extension); this._proxy.$registerImplementationSupport(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } @@ -998,8 +1042,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._withAdapter(handle, ImplementationAdapter, adapter => adapter.provideImplementation(URI.revive(resource), position, token)); } - registerTypeDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.TypeDefinitionProvider): vscode.Disposable { - const handle = this._addNewAdapter(new TypeDefinitionAdapter(this._documents, provider)); + registerTypeDefinitionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.TypeDefinitionProvider): vscode.Disposable { + const handle = this._addNewAdapter(new TypeDefinitionAdapter(this._documents, provider), extension); this._proxy.$registerTypeDefinitionSupport(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } @@ -1010,8 +1054,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- extra info - registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider, extensionId?: string): vscode.Disposable { - const handle = this._addNewAdapter(new HoverAdapter(this._documents, provider)); + registerHoverProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.HoverProvider, extensionId?: string): vscode.Disposable { + const handle = this._addNewAdapter(new HoverAdapter(this._documents, provider), extension); this._proxy.$registerHoverProvider(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } @@ -1022,8 +1066,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- occurrences - registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable { - const handle = this._addNewAdapter(new DocumentHighlightAdapter(this._documents, provider)); + registerDocumentHighlightProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable { + const handle = this._addNewAdapter(new DocumentHighlightAdapter(this._documents, provider), extension); this._proxy.$registerDocumentHighlightProvider(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } @@ -1034,8 +1078,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- references - registerReferenceProvider(selector: vscode.DocumentSelector, provider: vscode.ReferenceProvider): vscode.Disposable { - const handle = this._addNewAdapter(new ReferenceAdapter(this._documents, provider)); + registerReferenceProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.ReferenceProvider): vscode.Disposable { + const handle = this._addNewAdapter(new ReferenceAdapter(this._documents, provider), extension); this._proxy.$registerReferenceSupport(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } @@ -1046,8 +1090,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- quick fix - registerCodeActionProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, extension?: IExtensionDescription, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable { - const handle = this._addNewAdapter(new CodeActionAdapter(this._documents, this._commands.converter, this._diagnostics, provider, this._logService, extension ? extension.id : '')); + registerCodeActionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable { + const handle = this._addNewAdapter(new CodeActionAdapter(this._documents, this._commands.converter, this._diagnostics, provider, this._logService, extension.id), extension); this._proxy.$registerQuickFixSupport(handle, this._transformDocumentSelector(selector), metadata && metadata.providedCodeActionKinds ? metadata.providedCodeActionKinds.map(kind => kind.value) : undefined); return this._createDisposable(handle); } @@ -1059,8 +1103,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- formatting - registerDocumentFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentFormattingEditProvider): vscode.Disposable { - const handle = this._addNewAdapter(new DocumentFormattingAdapter(this._documents, provider)); + registerDocumentFormattingEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentFormattingEditProvider): vscode.Disposable { + const handle = this._addNewAdapter(new DocumentFormattingAdapter(this._documents, provider), extension); this._proxy.$registerDocumentFormattingSupport(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } @@ -1069,8 +1113,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._withAdapter(handle, DocumentFormattingAdapter, adapter => adapter.provideDocumentFormattingEdits(URI.revive(resource), options, token)); } - registerDocumentRangeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentRangeFormattingEditProvider): vscode.Disposable { - const handle = this._addNewAdapter(new RangeFormattingAdapter(this._documents, provider)); + registerDocumentRangeFormattingEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentRangeFormattingEditProvider): vscode.Disposable { + const handle = this._addNewAdapter(new RangeFormattingAdapter(this._documents, provider), extension); this._proxy.$registerRangeFormattingSupport(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } @@ -1079,8 +1123,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._withAdapter(handle, RangeFormattingAdapter, adapter => adapter.provideDocumentRangeFormattingEdits(URI.revive(resource), range, options, token)); } - registerOnTypeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.OnTypeFormattingEditProvider, triggerCharacters: string[]): vscode.Disposable { - const handle = this._addNewAdapter(new OnTypeFormattingAdapter(this._documents, provider)); + registerOnTypeFormattingEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.OnTypeFormattingEditProvider, triggerCharacters: string[]): vscode.Disposable { + const handle = this._addNewAdapter(new OnTypeFormattingAdapter(this._documents, provider), extension); this._proxy.$registerOnTypeFormattingSupport(handle, this._transformDocumentSelector(selector), triggerCharacters); return this._createDisposable(handle); } @@ -1091,8 +1135,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- navigate types - registerWorkspaceSymbolProvider(provider: vscode.WorkspaceSymbolProvider): vscode.Disposable { - const handle = this._addNewAdapter(new NavigateTypeAdapter(provider)); + registerWorkspaceSymbolProvider(extension: IExtensionDescription, provider: vscode.WorkspaceSymbolProvider): vscode.Disposable { + const handle = this._addNewAdapter(new NavigateTypeAdapter(provider), extension); this._proxy.$registerNavigateTypeSupport(handle); return this._createDisposable(handle); } @@ -1111,8 +1155,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- rename - registerRenameProvider(selector: vscode.DocumentSelector, provider: vscode.RenameProvider): vscode.Disposable { - const handle = this._addNewAdapter(new RenameAdapter(this._documents, provider)); + registerRenameProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.RenameProvider): vscode.Disposable { + const handle = this._addNewAdapter(new RenameAdapter(this._documents, provider), extension); this._proxy.$registerRenameSupport(handle, this._transformDocumentSelector(selector), RenameAdapter.supportsResolving(provider)); return this._createDisposable(handle); } @@ -1127,8 +1171,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- suggestion - registerCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, triggerCharacters: string[]): vscode.Disposable { - const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider)); + registerCompletionItemProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, triggerCharacters: string[]): vscode.Disposable { + const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider), extension); this._proxy.$registerSuggestSupport(handle, this._transformDocumentSelector(selector), triggerCharacters, SuggestAdapter.supportsResolving(provider)); return this._createDisposable(handle); } @@ -1147,12 +1191,12 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- parameter hints - registerSignatureHelpProvider(selector: vscode.DocumentSelector, provider: vscode.SignatureHelpProvider, metadataOrTriggerChars?: string[] | vscode.SignatureHelpProviderMetadata): vscode.Disposable { + registerSignatureHelpProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.SignatureHelpProvider, metadataOrTriggerChars?: string[] | vscode.SignatureHelpProviderMetadata): vscode.Disposable { const metadata: ISerializedSignatureHelpProviderMetadata = Array.isArray(metadataOrTriggerChars) ? { triggerCharacters: metadataOrTriggerChars, retriggerCharacters: [] } : metadataOrTriggerChars; - const handle = this._addNewAdapter(new SignatureHelpAdapter(this._documents, provider)); + const handle = this._addNewAdapter(new SignatureHelpAdapter(this._documents, provider), extension); this._proxy.$registerSignatureHelpProvider(handle, this._transformDocumentSelector(selector), metadata); return this._createDisposable(handle); } @@ -1163,8 +1207,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- links - registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable { - const handle = this._addNewAdapter(new LinkProviderAdapter(this._documents, this._heapService, provider)); + registerDocumentLinkProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable { + const handle = this._addNewAdapter(new LinkProviderAdapter(this._documents, this._heapService, provider), extension); this._proxy.$registerDocumentLinkProvider(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } @@ -1177,8 +1221,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.resolveLink(link, token)); } - registerColorProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable { - const handle = this._addNewAdapter(new ColorProviderAdapter(this._documents, provider)); + registerColorProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable { + const handle = this._addNewAdapter(new ColorProviderAdapter(this._documents, provider), extension); this._proxy.$registerDocumentColorProvider(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } @@ -1191,8 +1235,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColorPresentations(URI.revive(resource), colorInfo, token)); } - registerFoldingRangeProvider(selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider): vscode.Disposable { - const handle = this._addNewAdapter(new FoldingProviderAdapter(this._documents, provider)); + registerFoldingRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider): vscode.Disposable { + const handle = this._addNewAdapter(new FoldingProviderAdapter(this._documents, provider), extension); this._proxy.$registerFoldingRangeProvider(handle, this._transformDocumentSelector(selector)); return this._createDisposable(handle); } diff --git a/src/vs/workbench/api/node/extHostQuickOpen.ts b/src/vs/workbench/api/node/extHostQuickOpen.ts index d9c926450..7b1b7e8dd 100644 --- a/src/vs/workbench/api/node/extHostQuickOpen.ts +++ b/src/vs/workbench/api/node/extHostQuickOpen.ts @@ -28,6 +28,8 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { private _sessions = new Map(); + private _instances = 0; + constructor(mainContext: IMainContext, workspace: ExtHostWorkspace, commands: ExtHostCommands) { this._proxy = mainContext.getProxy(MainContext.MainThreadQuickOpen); this._workspace = workspace; @@ -44,7 +46,9 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { const itemsPromise = >Promise.resolve(itemsOrItemsPromise); - const quickPickWidget = this._proxy.$show({ + const instance = ++this._instances; + + const quickPickWidget = this._proxy.$show(instance, { placeHolder: options && options.placeHolder, matchOnDescription: options && options.matchOnDescription, matchOnDetail: options && options.matchOnDetail, @@ -99,7 +103,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { } // show items - this._proxy.$setItems(pickItems); + this._proxy.$setItems(instance, pickItems); return quickPickWidget.then(handle => { if (typeof handle === 'number') { @@ -115,7 +119,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { return undefined; } - this._proxy.$setError(err); + this._proxy.$setError(instance, err); return Promise.reject(err); }); @@ -140,8 +144,6 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { return undefined; } - this._proxy.$setError(err); - return Promise.reject(err); }); } diff --git a/src/vs/workbench/api/node/extHostSCM.ts b/src/vs/workbench/api/node/extHostSCM.ts index e0bf0ca9a..169dbf965 100644 --- a/src/vs/workbench/api/node/extHostSCM.ts +++ b/src/vs/workbench/api/node/extHostSCM.ts @@ -198,6 +198,18 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox { this._proxy.$setValidationProviderIsEnabled(this._sourceControlHandle, !!fn); } + private _visible: boolean = true; + + get visible(): boolean { + return this._visible; + } + + set visible(visible: boolean | undefined) { + visible = !!visible; + this._visible = visible; + this._proxy.$setInputBoxVisibility(this._sourceControlHandle, visible); + } + constructor(private _extension: IExtensionDescription, private _proxy: MainThreadSCMShape, private _sourceControlHandle: number) { // noop } diff --git a/src/vs/workbench/api/node/extHostSearch.fileIndex.ts b/src/vs/workbench/api/node/extHostSearch.fileIndex.ts index ec5fe1975..c59b1ab89 100644 --- a/src/vs/workbench/api/node/extHostSearch.fileIndex.ts +++ b/src/vs/workbench/api/node/extHostSearch.fileIndex.ts @@ -13,7 +13,6 @@ import * as resources from 'vs/base/common/resources'; import { StopWatch } from 'vs/base/common/stopwatch'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { compareItemsByScore, IItemAccessor, prepareQuery, ScorerCache } from 'vs/base/parts/quickopen/common/quickOpenScorer'; import { ICachedSearchStats, IFileIndexProviderStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, ISearchCompleteStats } from 'vs/platform/search/common/search'; import { IDirectoryEntry, IDirectoryTree, IInternalFileMatch } from 'vs/workbench/services/search/node/fileSearchManager'; @@ -65,11 +64,11 @@ export class FileIndexSearchEngine { this.activeCancellationTokens = new Set(); } - public search(_onResult: (match: IInternalFileMatch) => void): TPromise<{ isLimitHit: boolean, stats: IFileIndexProviderStats }> { + public search(_onResult: (match: IInternalFileMatch) => void): Promise<{ isLimitHit: boolean, stats: IFileIndexProviderStats }> { // Searches a single folder const folderQuery = this.config.folderQueries[0]; - return new TPromise<{ isLimitHit: boolean, stats: IFileIndexProviderStats }>((resolve, reject) => { + return new Promise<{ isLimitHit: boolean, stats: IFileIndexProviderStats }>((resolve, reject) => { const onResult = (match: IInternalFileMatch) => { this.resultCount++; _onResult(match); @@ -116,9 +115,9 @@ export class FileIndexSearchEngine { }); } - private searchInFolder(fq: IFolderQuery, onResult: (match: IInternalFileMatch) => void): TPromise { + private searchInFolder(fq: IFolderQuery, onResult: (match: IInternalFileMatch) => void): Promise { let cancellation = new CancellationTokenSource(); - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { const options = this.getSearchOptionsForFolder(fq); const tree = this.initDirectoryTree(); @@ -146,7 +145,7 @@ export class FileIndexSearchEngine { let providerSW: StopWatch; let providerTime: number; let fileWalkTime: number; - new TPromise(resolve => process.nextTick(resolve)) + new Promise(resolve => process.nextTick(resolve)) .then(() => { this.activeCancellationTokens.add(cancellation); providerSW = StopWatch.create(); @@ -304,7 +303,7 @@ export class FileIndexSearchManager { private readonly folderCacheKeys = new Map>(); - public fileSearch(config: IFileQuery, provider: vscode.FileIndexProvider, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): TPromise { + public fileSearch(config: IFileQuery, provider: vscode.FileIndexProvider, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): Promise { if (config.sortByScore) { let sortedSearch = this.trySortedSearchFromCache(config, token); if (!sortedSearch) { @@ -359,7 +358,7 @@ export class FileIndexSearchManager { }; } - private doSortedSearch(engine: FileIndexSearchEngine, config: IFileQuery, token: CancellationToken): TPromise { + private doSortedSearch(engine: FileIndexSearchEngine, config: IFileQuery, token: CancellationToken): Promise { let allResultsPromise = createCancelablePromise>(token => { return this.doSearch(engine, token); }); @@ -413,7 +412,7 @@ export class FileIndexSearchManager { return this.caches[cacheKey] = new Cache(); } - private trySortedSearchFromCache(config: IFileQuery, token: CancellationToken): TPromise { + private trySortedSearchFromCache(config: IFileQuery, token: CancellationToken): Promise { const folderCacheKey = this.getFolderCacheKey(config); const cache = folderCacheKey && this.caches[folderCacheKey]; if (!cache) { @@ -447,7 +446,7 @@ export class FileIndexSearchManager { return undefined; } - private sortResults(config: IFileQuery, results: IInternalFileMatch[], scorerCache: ScorerCache, token: CancellationToken): TPromise { + private sortResults(config: IFileQuery, results: IInternalFileMatch[], scorerCache: ScorerCache, token: CancellationToken): Promise { // we use the same compare function that is used later when showing the results using fuzzy scoring // this is very important because we are also limiting the number of results by config.maxResults // and as such we want the top items to be included in this result set if the number of items @@ -469,7 +468,7 @@ export class FileIndexSearchManager { } } - private getResultsFromCache(cache: Cache, searchValue: string, token: CancellationToken): TPromise> { + private getResultsFromCache(cache: Cache, searchValue: string, token: CancellationToken): Promise> { const cacheLookupSW = StopWatch.create(); if (path.isAbsolute(searchValue)) { @@ -503,7 +502,7 @@ export class FileIndexSearchManager { const cacheLookupTime = cacheLookupSW.elapsed(); const cacheFilterSW = StopWatch.create(); - return new TPromise>((c, e) => { + return new Promise>((c, e) => { token.onCancellationRequested(() => e(canceled())); cacheRow.promise.then(complete => { @@ -539,7 +538,7 @@ export class FileIndexSearchManager { }); } - private doSearch(engine: FileIndexSearchEngine, token: CancellationToken): TPromise> { + private doSearch(engine: FileIndexSearchEngine, token: CancellationToken): Promise> { token.onCancellationRequested(() => engine.cancel()); const results: IInternalFileMatch[] = []; const onResult = match => results.push(match); @@ -553,7 +552,7 @@ export class FileIndexSearchManager { }); } - public clearCache(cacheKey: string): TPromise { + public clearCache(cacheKey: string): Promise { if (!this.folderCacheKeys.has(cacheKey)) { return Promise.resolve(undefined); } diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index ba2442e42..bbb65956b 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -3,11 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as path from 'path'; + import { URI, UriComponents } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import * as Objects from 'vs/base/common/objects'; import { asThenable } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; +import { win32 } from 'vs/base/node/processes'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import * as tasks from 'vs/workbench/parts/tasks/common/tasks'; @@ -400,7 +403,8 @@ namespace Tasks { command: command, isBackground: !!task.isBackground, problemMatchers: task.problemMatchers.slice(), - hasDefinedMatchers: (task as types.Task).hasDefinedMatchers + hasDefinedMatchers: (task as types.Task).hasDefinedMatchers, + runOptions: (task).runOptions ? (task).runOptions : { rerunBehavior: tasks.RerunBehavior.reevaluate }, }; return result; } @@ -605,7 +609,7 @@ namespace TaskDTO { if (typeof value.scope === 'number') { scope = value.scope; } else { - scope = value.scope.uri.toJSON(); + scope = value.scope.uri; } } if (!definition || !scope) { @@ -626,7 +630,8 @@ namespace TaskDTO { group: group, presentationOptions: TaskPresentationOptionsDTO.from(value.presentationOptions), problemMatchers: value.problemMatchers, - hasDefinedMatchers: (value as types.Task).hasDefinedMatchers + hasDefinedMatchers: (value as types.Task).hasDefinedMatchers, + runOptions: (value).runOptions ? (value).runOptions : { rerunBehavior: tasks.RerunBehavior.reevaluate }, }; return result; } @@ -883,9 +888,12 @@ export class ExtHostTask implements ExtHostTaskShape { }); } - public $resolveVariables(uriComponents: UriComponents, variables: string[]): any { + public $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Thenable<{ process?: string, variables: { [key: string]: string; } }> { let uri: URI = URI.revive(uriComponents); - let result: { [key: string]: string; } = Object.create(null); + let result = { + process: undefined as string, + variables: Object.create(null) + }; let workspaceFolder = this._workspaceService.resolveWorkspaceFolder(uri); let resolver = new ExtHostVariableResolverService(this._workspaceService, this._editorService, this._configurationService); let ws: IWorkspaceFolder = { @@ -896,10 +904,24 @@ export class ExtHostTask implements ExtHostTaskShape { throw new Error('Not implemented'); } }; - for (let variable of variables) { - result[variable] = resolver.resolve(ws, variable); + for (let variable of toResolve.variables) { + result.variables[variable] = resolver.resolve(ws, variable); + } + if (toResolve.process !== void 0) { + let paths: string[] | undefined = undefined; + if (toResolve.process.path !== void 0) { + paths = toResolve.process.path.split(path.delimiter); + for (let i = 0; i < paths.length; i++) { + paths[i] = resolver.resolve(ws, paths[i]); + } + } + result.process = win32.findExecutable( + resolver.resolve(ws, toResolve.process.name), + toResolve.process.cwd !== void 0 ? resolver.resolve(ws, toResolve.process.cwd) : undefined, + paths + ); } - return result; + return Promise.resolve(result); } private nextHandle(): number { diff --git a/src/vs/workbench/api/node/extHostTextEditor.ts b/src/vs/workbench/api/node/extHostTextEditor.ts index 26d6f9584..b9716f635 100644 --- a/src/vs/workbench/api/node/extHostTextEditor.ts +++ b/src/vs/workbench/api/node/extHostTextEditor.ts @@ -25,7 +25,7 @@ export class TextEditorDecorationType implements vscode.TextEditorDecorationType constructor(proxy: MainThreadTextEditorsShape, options: vscode.DecorationRenderOptions) { this.key = TextEditorDecorationType._Keys.nextId(); this._proxy = proxy; - this._proxy.$registerTextEditorDecorationType(this.key, /* URI vs Uri */ options); + this._proxy.$registerTextEditorDecorationType(this.key, TypeConverters.DecorationRenderOptions.from(options)); } public dispose(): void { diff --git a/src/vs/workbench/api/node/extHostTreeViews.ts b/src/vs/workbench/api/node/extHostTreeViews.ts index e6d4ee22a..9b3af4240 100644 --- a/src/vs/workbench/api/node/extHostTreeViews.ts +++ b/src/vs/workbench/api/node/extHostTreeViews.ts @@ -10,17 +10,19 @@ import { URI } from 'vs/base/common/uri'; import { debounceEvent, Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.protocol'; -import { ITreeItem, TreeViewItemHandleArg, ITreeItemLabel } from 'vs/workbench/common/views'; +import { ITreeItem, TreeViewItemHandleArg, ITreeItemLabel, IRevealOptions } from 'vs/workbench/common/views'; import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands'; import { asThenable } from 'vs/base/common/async'; -import { TreeItemCollapsibleState, ThemeIcon } from 'vs/workbench/api/node/extHostTypes'; +import { TreeItemCollapsibleState, ThemeIcon, MarkdownString } from 'vs/workbench/api/node/extHostTypes'; import { isUndefinedOrNull, isString } from 'vs/base/common/types'; import { equals } from 'vs/base/common/arrays'; import { ILogService } from 'vs/platform/log/common/log'; +import { IExtensionDescription, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; +import * as typeConvert from 'vs/workbench/api/node/extHostTypeConverters'; type TreeItemHandle = string; -function toTreeItemLabel(label: any): ITreeItemLabel { +function toTreeItemLabel(label: any, extension: IExtensionDescription): ITreeItemLabel { if (isString(label)) { return { label }; } @@ -28,6 +30,7 @@ function toTreeItemLabel(label: any): ITreeItemLabel { if (label && typeof label === 'object' && typeof label.label === 'string') { + checkProposedApiEnabled(extension); let highlights: [number, number][] = void 0; if (Array.isArray(label.highlights)) { highlights = (<[number, number][]>label.highlights).filter((highlight => highlight.length === 2 && typeof highlight[0] === 'number' && typeof highlight[1] === 'number')); @@ -59,16 +62,20 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { }); } - registerTreeDataProvider(id: string, treeDataProvider: vscode.TreeDataProvider): vscode.Disposable { - const treeView = this.createTreeView(id, { treeDataProvider }); + registerTreeDataProvider(id: string, treeDataProvider: vscode.TreeDataProvider, extension: IExtensionDescription): vscode.Disposable { + const treeView = this.createTreeView(id, { treeDataProvider }, extension); return { dispose: () => treeView.dispose() }; } - createTreeView(viewId: string, options: { treeDataProvider: vscode.TreeDataProvider }): vscode.TreeView { + createTreeView(viewId: string, options: vscode.TreeViewOptions, extension: IExtensionDescription): vscode.TreeView { if (!options || !options.treeDataProvider) { throw new Error('Options with treeDataProvider is mandatory'); } - const treeView = this.createExtHostTreeViewer(viewId, options.treeDataProvider); + if (options.showCollapseAll) { + checkProposedApiEnabled(extension); + } + + const treeView = this.createExtHostTreeView(viewId, options, extension); return { get onDidCollapseElement() { return treeView.onDidCollapseElement; }, get onDidExpandElement() { return treeView.onDidExpandElement; }, @@ -76,7 +83,9 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { get onDidChangeSelection() { return treeView.onDidChangeSelection; }, get visible() { return treeView.visible; }, get onDidChangeVisibility() { return treeView.onDidChangeVisibility; }, - reveal: (element: T, options?: { select?: boolean, focus?: boolean }): Thenable => { + get message() { return treeView.message; }, + set message(message: string | MarkdownString) { treeView.message = message; }, + reveal: (element: T, options?: IRevealOptions): Thenable => { return treeView.reveal(element, options); }, dispose: () => { @@ -118,8 +127,8 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { treeView.setVisible(isVisible); } - private createExtHostTreeViewer(id: string, dataProvider: vscode.TreeDataProvider): ExtHostTreeView { - const treeView = new ExtHostTreeView(id, dataProvider, this._proxy, this.commands.converter, this.logService); + private createExtHostTreeView(id: string, options: vscode.TreeViewOptions, extension: IExtensionDescription): ExtHostTreeView { + const treeView = new ExtHostTreeView(id, options, this._proxy, this.commands.converter, this.logService, extension); this.treeViews.set(id, treeView); return treeView; } @@ -141,6 +150,8 @@ class ExtHostTreeView extends Disposable { private static LABEL_HANDLE_PREFIX = '0'; private static ID_HANDLE_PREFIX = '1'; + private readonly dataProvider: vscode.TreeDataProvider; + private roots: TreeNode[] | null = null; private elements: Map = new Map(); private nodes: Map = new Map(); @@ -165,9 +176,10 @@ class ExtHostTreeView extends Disposable { private refreshPromise: Promise = Promise.resolve(null); - constructor(private viewId: string, private dataProvider: vscode.TreeDataProvider, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter, private logService: ILogService) { + constructor(private viewId: string, options: vscode.TreeViewOptions, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter, private logService: ILogService, private extension: IExtensionDescription) { super(); - this.proxy.$registerTreeViewDataProvider(viewId); + this.dataProvider = options.treeDataProvider; + this.proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll }); if (this.dataProvider.onDidChangeTreeData) { let refreshingPromise, promiseCallback; this._register(debounceEvent(this.dataProvider.onDidChangeTreeData, (last, current) => { @@ -201,10 +213,11 @@ class ExtHostTreeView extends Disposable { return this.elements.get(treeItemHandle); } - reveal(element: T, options?: { select?: boolean, focus?: boolean }): Promise { + reveal(element: T, options?: IRevealOptions): Promise { options = options ? options : { select: true, focus: false }; const select = isUndefinedOrNull(options.select) ? true : options.select; const focus = isUndefinedOrNull(options.focus) ? false : options.focus; + const expand = isUndefinedOrNull(options.expand) ? false : options.expand; if (typeof this.dataProvider.getParent !== 'function') { return Promise.reject(new Error(`Required registered TreeDataProvider to implement 'getParent' method to access 'reveal' method`)); @@ -212,7 +225,17 @@ class ExtHostTreeView extends Disposable { return this.refreshPromise .then(() => this.resolveUnknownParentChain(element)) .then(parentChain => this.resolveTreeNode(element, parentChain[parentChain.length - 1]) - .then(treeNode => this.proxy.$reveal(this.viewId, treeNode.item, parentChain.map(p => p.item), { select, focus })), error => this.logService.error(error)); + .then(treeNode => this.proxy.$reveal(this.viewId, treeNode.item, parentChain.map(p => p.item), { select, focus, expand })), error => this.logService.error(error)); + } + + private _message: string | MarkdownString; + get message(): string | MarkdownString { + return this._message; + } + + set message(message: string | MarkdownString) { + this._message = message; + this.proxy.$setMessage(this.viewId, typeConvert.MarkdownString.fromStrict(this._message)); } setExpanded(treeItemHandle: TreeItemHandle, expanded: boolean): void { @@ -407,7 +430,8 @@ class ExtHostTreeView extends Disposable { const item = { handle, parentHandle: parent ? parent.item.handle : void 0, - label: toTreeItemLabel(extensionTreeItem.label), + label: toTreeItemLabel(extensionTreeItem.label, this.extension), + description: extensionTreeItem.description, resourceUri: extensionTreeItem.resourceUri, tooltip: typeof extensionTreeItem.tooltip === 'string' ? extensionTreeItem.tooltip : void 0, command: extensionTreeItem.command ? this.commands.toInternal(extensionTreeItem.command) : void 0, @@ -426,7 +450,7 @@ class ExtHostTreeView extends Disposable { return `${ExtHostTreeView.ID_HANDLE_PREFIX}/${id}`; } - const treeItemLabel = toTreeItemLabel(label); + const treeItemLabel = toTreeItemLabel(label, this.extension); const prefix: string = parent ? parent.item.handle : ExtHostTreeView.LABEL_HANDLE_PREFIX; let elementId = treeItemLabel ? treeItemLabel.label : resourceUri ? basename(resourceUri.path) : ''; elementId = elementId.indexOf('/') !== -1 ? elementId.replace('/', '//') : elementId; diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index d629ab6db..e50f4755d 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -8,8 +8,8 @@ import * as types from './extHostTypes'; import * as search from 'vs/workbench/parts/search/common/search'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { EditorViewColumn } from 'vs/workbench/api/shared/editor'; -import { IDecorationOptions } from 'vs/editor/common/editorCommon'; -import { EndOfLineSequence } from 'vs/editor/common/model'; +import { IDecorationOptions, IThemeDecorationRenderOptions, IDecorationRenderOptions, IContentDecorationRenderOptions } from 'vs/editor/common/editorCommon'; +import { EndOfLineSequence, TrackedRangeStickiness } from 'vs/editor/common/model'; import * as vscode from 'vscode'; import { URI } from 'vs/base/common/uri'; import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress'; @@ -24,6 +24,7 @@ import { MarkerSeverity, IRelatedInformation, IMarkerData, MarkerTag } from 'vs/ import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors'; import { isString, isNumber } from 'vs/base/common/types'; +import * as marked from 'vs/base/common/marked/marked'; export interface PositionLike { line: number; @@ -209,17 +210,34 @@ export namespace MarkdownString { } export function from(markup: vscode.MarkdownString | vscode.MarkedString): htmlContent.IMarkdownString { + let res: htmlContent.IMarkdownString; if (isCodeblock(markup)) { const { language, value } = markup; - return { value: '```' + language + '\n' + value + '\n```\n' }; + res = { value: '```' + language + '\n' + value + '\n```\n' }; } else if (htmlContent.isMarkdownString(markup)) { - return markup; + res = markup; } else if (typeof markup === 'string') { - return { value: markup }; + res = { value: markup }; } else { - return { value: '' }; + res = { value: '' }; } + + // extract uris into a separate object + res.uris = Object.create(null); + let renderer = new marked.Renderer(); + renderer.image = renderer.link = (href: string): string => { + try { + res.uris[href] = URI.parse(href, true); + } catch (e) { + // ignore + } + return ''; + }; + marked(res.value, { renderer }); + + return res; } + export function to(value: htmlContent.IMarkdownString): vscode.MarkdownString { const ret = new htmlContent.MarkdownString(value.value); ret.isTrusted = value.isTrusted; @@ -252,6 +270,126 @@ export function fromRangeOrRangeWithMessage(ranges: vscode.Range[] | vscode.Deco } } +function pathOrURIToURI(value: string | URI): URI { + if (typeof value === 'undefined') { + return value; + } + if (typeof value === 'string') { + return URI.file(value); + } else { + return value; + } +} + +export namespace ThemableDecorationAttachmentRenderOptions { + export function from(options: vscode.ThemableDecorationAttachmentRenderOptions): IContentDecorationRenderOptions { + if (typeof options === 'undefined') { + return options; + } + return { + contentText: options.contentText, + contentIconPath: pathOrURIToURI(options.contentIconPath), + border: options.border, + borderColor: options.borderColor, + fontStyle: options.fontStyle, + fontWeight: options.fontWeight, + textDecoration: options.textDecoration, + color: options.color, + backgroundColor: options.backgroundColor, + margin: options.margin, + width: options.width, + height: options.height, + }; + } +} + +export namespace ThemableDecorationRenderOptions { + export function from(options: vscode.ThemableDecorationRenderOptions): IThemeDecorationRenderOptions { + if (typeof options === 'undefined') { + return options; + } + return { + backgroundColor: options.backgroundColor, + outline: options.outline, + outlineColor: options.outlineColor, + outlineStyle: options.outlineStyle, + outlineWidth: options.outlineWidth, + border: options.border, + borderColor: options.borderColor, + borderRadius: options.borderRadius, + borderSpacing: options.borderSpacing, + borderStyle: options.borderStyle, + borderWidth: options.borderWidth, + fontStyle: options.fontStyle, + fontWeight: options.fontWeight, + textDecoration: options.textDecoration, + cursor: options.cursor, + color: options.color, + opacity: options.opacity, + letterSpacing: options.letterSpacing, + gutterIconPath: pathOrURIToURI(options.gutterIconPath), + gutterIconSize: options.gutterIconSize, + overviewRulerColor: options.overviewRulerColor, + before: ThemableDecorationAttachmentRenderOptions.from(options.before), + after: ThemableDecorationAttachmentRenderOptions.from(options.after), + }; + } +} + +export namespace DecorationRangeBehavior { + export function from(value: types.DecorationRangeBehavior): TrackedRangeStickiness { + if (typeof value === 'undefined') { + return value; + } + switch (value) { + case types.DecorationRangeBehavior.OpenOpen: + return TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges; + case types.DecorationRangeBehavior.ClosedClosed: + return TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges; + case types.DecorationRangeBehavior.OpenClosed: + return TrackedRangeStickiness.GrowsOnlyWhenTypingBefore; + case types.DecorationRangeBehavior.ClosedOpen: + return TrackedRangeStickiness.GrowsOnlyWhenTypingAfter; + } + } +} + +export namespace DecorationRenderOptions { + export function from(options: vscode.DecorationRenderOptions): IDecorationRenderOptions { + return { + isWholeLine: options.isWholeLine, + rangeBehavior: DecorationRangeBehavior.from(options.rangeBehavior), + overviewRulerLane: options.overviewRulerLane, + light: ThemableDecorationRenderOptions.from(options.light), + dark: ThemableDecorationRenderOptions.from(options.dark), + + backgroundColor: options.backgroundColor, + outline: options.outline, + outlineColor: options.outlineColor, + outlineStyle: options.outlineStyle, + outlineWidth: options.outlineWidth, + border: options.border, + borderColor: options.borderColor, + borderRadius: options.borderRadius, + borderSpacing: options.borderSpacing, + borderStyle: options.borderStyle, + borderWidth: options.borderWidth, + fontStyle: options.fontStyle, + fontWeight: options.fontWeight, + textDecoration: options.textDecoration, + cursor: options.cursor, + color: options.color, + opacity: options.opacity, + letterSpacing: options.letterSpacing, + gutterIconPath: pathOrURIToURI(options.gutterIconPath), + gutterIconSize: options.gutterIconSize, + overviewRulerColor: options.overviewRulerColor, + before: ThemableDecorationAttachmentRenderOptions.from(options.before), + after: ThemableDecorationAttachmentRenderOptions.from(options.after), + }; + } +} + export namespace TextEdit { export function from(edit: vscode.TextEdit): modes.TextEdit { @@ -340,7 +478,7 @@ export namespace SymbolKind { _fromMapping[types.SymbolKind.TypeParameter] = modes.SymbolKind.TypeParameter; export function from(kind: vscode.SymbolKind): modes.SymbolKind { - return _fromMapping[kind] || modes.SymbolKind.Property; + return typeof _fromMapping[kind] === 'number' ? _fromMapping[kind] : modes.SymbolKind.Property; } export function to(kind: modes.SymbolKind): vscode.SymbolKind { @@ -543,25 +681,6 @@ export namespace CompletionItemKind { } } -export namespace CompletionItemInsertTextRule { - - export function from(rule: types.CompletionItemInsertTextRule): modes.CompletionItemInsertTextRule { - let result = 0; - if ((rule & types.CompletionItemInsertTextRule.KeepWhitespace)) { - result += modes.CompletionItemInsertTextRule.KeepWhitespace; - } - return result; - } - - export function to(rule: modes.CompletionItemInsertTextRule): types.CompletionItemInsertTextRule { - let result = 0; - if ((rule & modes.CompletionItemInsertTextRule.KeepWhitespace)) { - result += types.CompletionItemInsertTextRule.KeepWhitespace; - } - return result; - } -} - export namespace CompletionItem { export function to(suggestion: modes.CompletionItem): types.CompletionItem { @@ -575,7 +694,7 @@ export namespace CompletionItem { result.preselect = suggestion.preselect; result.commitCharacters = suggestion.commitCharacters; result.range = Range.to(suggestion.range); - result.insertTextRules = CompletionItemInsertTextRule.to(suggestion.insertTextRules); + result.keepWhitespace = Boolean(suggestion.insertTextRules & modes.CompletionItemInsertTextRule.KeepWhitespace); // 'inserText'-logic if (suggestion.insertTextRules & modes.CompletionItemInsertTextRule.InsertAsSnippet) { result.insertText = new types.SnippetString(suggestion.insertText); diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 3aee6feeb..787778bb1 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -14,6 +14,7 @@ import { relative } from 'path'; import { startsWith } from 'vs/base/common/strings'; import { values } from 'vs/base/common/map'; import { coalesce, equals } from 'vs/base/common/arrays'; +import { generateUuid } from 'vs/base/common/uuid'; export class Disposable { @@ -48,9 +49,13 @@ export class Disposable { export class Position { static Min(...positions: Position[]): Position { - let result = positions.pop(); - for (let p of positions) { - if (p.isBefore(result)) { + if (positions.length === 0) { + throw new TypeError(); + } + let result = positions[0]; + for (let i = 1; i < positions.length; i++) { + let p = positions[i]; + if (p.isBefore(result!)) { result = p; } } @@ -58,9 +63,13 @@ export class Position { } static Max(...positions: Position[]): Position { - let result = positions.pop(); - for (let p of positions) { - if (p.isAfter(result)) { + if (positions.length === 0) { + throw new TypeError(); + } + let result = positions[0]; + for (let i = 1; i < positions.length; i++) { + let p = positions[i]; + if (p.isAfter(result!)) { result = p; } } @@ -438,7 +447,7 @@ export class TextEdit { } static setEndOfLine(eol: EndOfLine): TextEdit { - let ret = new TextEdit(undefined, undefined); + let ret = new TextEdit(new Range(new Position(0, 0), new Position(0, 0)), ''); ret.newEol = eol; return ret; } @@ -504,8 +513,8 @@ export interface IFileOperationOptions { export interface IFileOperation { _type: 1; - from: URI; - to: URI; + from?: URI; + to?: URI; options?: IFileOperationOptions; } @@ -558,7 +567,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { for (let i = 0; i < this._edits.length; i++) { const element = this._edits[i]; if (element._type === 2 && element.uri.toString() === uri.toString()) { - this._edits[i] = undefined; + this._edits[i] = undefined!; // will be coalesced down below } } this._edits = coalesce(this._edits); @@ -579,9 +588,6 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { res.push(candidate.edit); } } - if (res.length === 0) { - return undefined; - } return res; } @@ -600,8 +606,8 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { return values(textEdits); } - _allEntries(): ([URI, TextEdit[]] | [URI, URI, IFileOperationOptions])[] { - let res: ([URI, TextEdit[]] | [URI, URI, IFileOperationOptions])[] = []; + _allEntries(): ([URI, TextEdit[]] | [URI?, URI?, IFileOperationOptions?])[] { + let res: ([URI, TextEdit[]] | [URI?, URI?, IFileOperationOptions?])[] = []; for (let edit of this._edits) { if (edit._type === 1) { res.push([edit.from, edit.to, edit.options]); @@ -811,7 +817,7 @@ export class Diagnostic { }; } - static isEqual(a: Diagnostic, b: Diagnostic): boolean { + static isEqual(a: Diagnostic | undefined, b: Diagnostic | undefined): boolean { if (a === b) { return true; } @@ -832,7 +838,7 @@ export class Diagnostic { export class Hover { public contents: vscode.MarkdownString[] | vscode.MarkedString[]; - public range: Range; + public range: Range | undefined; constructor( contents: vscode.MarkdownString | vscode.MarkedString | vscode.MarkdownString[] | vscode.MarkedString[], @@ -916,7 +922,7 @@ export class SymbolInformation { name: string; location: Location; kind: SymbolKind; - containerName: string; + containerName: string | undefined; constructor(name: string, kind: SymbolKind, containerName: string, location: Location); constructor(name: string, kind: SymbolKind, range: Range, uri?: URI, containerName?: string); @@ -932,7 +938,7 @@ export class SymbolInformation { if (locationOrUri instanceof Location) { this.location = locationOrUri; } else if (rangeOrContainer instanceof Range) { - this.location = new Location(locationOrUri, rangeOrContainer); + this.location = new Location(locationOrUri!, rangeOrContainer); } SymbolInformation.validate(this); @@ -1035,7 +1041,7 @@ export class CodeLens { range: Range; - command: vscode.Command; + command: vscode.Command | undefined; constructor(range: Range, command?: vscode.Command) { this.range = range; @@ -1115,7 +1121,7 @@ export class SignatureHelp { export enum SignatureHelpTriggerReason { Invoke = 1, TriggerCharacter = 2, - Retrigger = 3, + ContentChange = 3, } export enum CompletionTriggerKind { @@ -1157,21 +1163,17 @@ export enum CompletionItemKind { TypeParameter = 24 } -export enum CompletionItemInsertTextRule { - KeepWhitespace = 0b1 -} - export class CompletionItem implements vscode.CompletionItem { label: string; - kind: CompletionItemKind; + kind: CompletionItemKind | undefined; detail: string; documentation: string | MarkdownString; sortText: string; filterText: string; preselect: boolean; insertText: string | SnippetString; - insertTextRules: CompletionItemInsertTextRule; + keepWhitespace?: boolean; range: Range; commitCharacters: string[]; textEdit: TextEdit; @@ -1186,7 +1188,7 @@ export class CompletionItem implements vscode.CompletionItem { toJSON(): any { return { label: this.label, - kind: CompletionItemKind[this.kind], + kind: this.kind && CompletionItemKind[this.kind], detail: this.detail, documentation: this.documentation, sortText: this.sortText, @@ -1589,7 +1591,12 @@ export enum TaskScope { Workspace = 2 } -export class Task implements vscode.Task { +export enum RerunBehavior { + reevaluate = 1, + useEvaluated = 2, +} + +export class Task implements vscode.Task2 { private __id: string; @@ -1603,6 +1610,7 @@ export class Task implements vscode.Task { private _source: string; private _group: TaskGroup; private _presentationOptions: vscode.TaskPresentationOptions; + private _runOptions: vscode.RunOptions; constructor(definition: vscode.TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution, problemMatchers?: string | string[]); constructor(definition: vscode.TaskDefinition, scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder, name: string, source: string, execution?: ProcessExecution | ShellExecution, problemMatchers?: string | string[]); @@ -1780,6 +1788,18 @@ export class Task implements vscode.Task { this.clear(); this._presentationOptions = value; } + + get runOptions(): vscode.RunOptions { + return this._runOptions; + } + + set runOptions(value: vscode.RunOptions) { + if (value === null) { + value = undefined; + } + this.clear(); + this._runOptions = value; + } } @@ -1877,6 +1897,8 @@ export class RelativePattern implements IRelativePattern { export class Breakpoint { + private _id: string | undefined; + readonly enabled: boolean; readonly condition?: string; readonly hitCondition?: string; @@ -1894,6 +1916,13 @@ export class Breakpoint { this.logMessage = logMessage; } } + + get id(): string { + if (!this._id) { + this._id = generateUuid(); + } + return this._id; + } } export class SourceBreakpoint extends Breakpoint { @@ -1921,24 +1950,20 @@ export class FunctionBreakpoint extends Breakpoint { } export class DebugAdapterExecutable implements vscode.DebugAdapterExecutable { - readonly type = 'executable'; readonly command: string; readonly args: string[]; - readonly env?: { [key: string]: string }; - readonly cwd?: string; + readonly options?: vscode.DebugAdapterExecutableOptions; - constructor(command: string, args?: string[], env?: { [key: string]: string }, cwd?: string) { + constructor(command: string, args: string[], options?: vscode.DebugAdapterExecutableOptions) { this.command = command; - this.args = args; - this.env = env; - this.cwd = cwd; + this.args = args || []; + this.options = options; } } export class DebugAdapterServer implements vscode.DebugAdapterServer { - readonly type = 'server'; readonly port: number; - readonly host: string; + readonly host?: string; constructor(port: number, host?: string) { this.port = port; @@ -1947,7 +1972,6 @@ export class DebugAdapterServer implements vscode.DebugAdapterServer { } export class DebugAdapterImplementation implements vscode.DebugAdapterImplementation { - readonly type = 'implementation'; readonly implementation: any; constructor(transport: any) { diff --git a/src/vs/workbench/api/node/extHostWebview.ts b/src/vs/workbench/api/node/extHostWebview.ts index 4b22d0c28..93c50645b 100644 --- a/src/vs/workbench/api/node/extHostWebview.ts +++ b/src/vs/workbench/api/node/extHostWebview.ts @@ -10,6 +10,7 @@ import { EditorViewColumn } from 'vs/workbench/api/shared/editor'; import * as vscode from 'vscode'; import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState } from './extHost.protocol'; import { Disposable } from './extHostTypes'; +import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; type IconPath = URI | { light: URI, dark: URI }; @@ -238,7 +239,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { } public createWebview( - extensionLocation: URI, + extension: IExtensionDescription, viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, @@ -251,7 +252,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { }; const handle = ExtHostWebviews.newHandle(); - this._proxy.$createWebviewPanel(handle, viewType, title, webviewShowOptions, options, extensionLocation); + this._proxy.$createWebviewPanel(handle, viewType, title, webviewShowOptions, options, extension.id, extension.extensionLocation); const webview = new ExtHostWebview(handle, this._proxy, options); const panel = new ExtHostWebviewPanel(handle, this._proxy, viewType, title, viewColumn, options, webview); diff --git a/src/vs/workbench/api/node/extHostWorkspace.ts b/src/vs/workbench/api/node/extHostWorkspace.ts index 3d9ed9010..b982e5149 100644 --- a/src/vs/workbench/api/node/extHostWorkspace.ts +++ b/src/vs/workbench/api/node/extHostWorkspace.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { join, relative } from 'path'; -import { delta as arrayDelta } from 'vs/base/common/arrays'; +import { delta as arrayDelta, mapArrayOrNot } from 'vs/base/common/arrays'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; import { TernarySearchTree } from 'vs/base/common/map'; import { Counter } from 'vs/base/common/numbers'; @@ -16,14 +17,13 @@ import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { ILogService } from 'vs/platform/log/common/log'; import { Severity } from 'vs/platform/notification/common/notification'; -import { IRawFileMatch2 } from 'vs/platform/search/common/search'; +import { IRawFileMatch2, resultIsMatch } from 'vs/platform/search/common/search'; import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { Range, RelativePattern } from 'vs/workbench/api/node/extHostTypes'; +import { ITextQueryBuilderOptions } from 'vs/workbench/parts/search/common/queryBuilder'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import * as vscode from 'vscode'; import { ExtHostWorkspaceShape, IMainContext, IWorkspaceData, MainContext, MainThreadMessageServiceShape, MainThreadWorkspaceShape } from './extHost.protocol'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { ITextQueryBuilderOptions } from 'vs/workbench/parts/search/common/queryBuilder'; function isFolderEqual(folderA: URI, folderB: URI): boolean { return isEqual(folderA, folderB, !isLinux); @@ -408,6 +408,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { fileEncoding: options.encoding, maxResults: options.maxResults, previewOptions, + afterContext: options.afterContext, + beforeContext: options.beforeContext, includePattern: options.include && globPatternToString(options.include), excludePattern: options.exclude && globPatternToString(options.exclude) @@ -420,15 +422,28 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { return; } - p.matches.forEach(match => { - callback({ - uri: URI.revive(p.resource), - preview: { - text: match.preview.text, - match: new Range(match.preview.match.startLineNumber, match.preview.match.startColumn, match.preview.match.endLineNumber, match.preview.match.endColumn) - }, - range: new Range(match.range.startLineNumber, match.range.startColumn, match.range.endLineNumber, match.range.endColumn) - }); + const uri = URI.revive(p.resource); + p.results.forEach(result => { + if (resultIsMatch(result)) { + callback({ + uri, + preview: { + text: result.preview.text, + matches: mapArrayOrNot( + result.preview.matches, + m => new Range(m.startLineNumber, m.startColumn, m.endLineNumber, m.endColumn)) + }, + ranges: mapArrayOrNot( + result.ranges, + r => new Range(r.startLineNumber, r.startColumn, r.endLineNumber, r.endColumn)) + }); + } else { + callback({ + uri, + text: result.text, + lineNumber: result.lineNumber + }); + } }); }; diff --git a/src/vs/workbench/api/shared/tasks.ts b/src/vs/workbench/api/shared/tasks.ts index 4710c201e..79ae249f6 100644 --- a/src/vs/workbench/api/shared/tasks.ts +++ b/src/vs/workbench/api/shared/tasks.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { UriComponents } from 'vs/base/common/uri'; +import { RerunBehavior } from 'vs/workbench/parts/tasks/common/tasks'; export interface TaskDefinitionDTO { type: string; @@ -19,6 +20,10 @@ export interface TaskPresentationOptionsDTO { clear?: boolean; } +export interface RunOptionsDTO { + rerunBehavior?: RerunBehavior; +} + export interface ExecutionOptionsDTO { cwd?: string; env?: { [key: string]: string }; @@ -82,6 +87,7 @@ export interface TaskDTO { presentationOptions: TaskPresentationOptionsDTO; problemMatchers: string[]; hasDefinedMatchers: boolean; + runOptions: RunOptionsDTO; } export interface TaskExecutionDTO { diff --git a/src/vs/workbench/browser/actions.ts b/src/vs/workbench/browser/actions.ts index 3a936e8da..a7af610e5 100644 --- a/src/vs/workbench/browser/actions.ts +++ b/src/vs/workbench/browser/actions.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { Registry } from 'vs/platform/registry/common/platform'; import { Action, IAction } from 'vs/base/common/actions'; import { BaseActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -92,7 +91,7 @@ export class ContributableActionProvider implements IActionProvider { return false; } - getActions(tree: ITree, element: any): TPromise { + getActions(tree: ITree, element: any): IAction[] { const actions: IAction[] = []; const context = this.toContext(tree, element); @@ -105,7 +104,7 @@ export class ContributableActionProvider implements IActionProvider { } } - return Promise.resolve(prepareActions(actions)); + return prepareActions(actions); } hasSecondaryActions(tree: ITree, element: any): boolean { @@ -122,7 +121,7 @@ export class ContributableActionProvider implements IActionProvider { return false; } - getSecondaryActions(tree: ITree, element: any): TPromise { + getSecondaryActions(tree: ITree, element: any): IAction[] { const actions: IAction[] = []; const context = this.toContext(tree, element); @@ -135,7 +134,7 @@ export class ContributableActionProvider implements IActionProvider { } } - return Promise.resolve(prepareActions(actions)); + return prepareActions(actions); } getActionItem(tree: ITree, element: any, action: Action): BaseActionItem { diff --git a/src/vs/workbench/browser/actions/toggleCenteredLayout.ts b/src/vs/workbench/browser/actions/toggleCenteredLayout.ts index 1d3382097..4f2ac2e1b 100644 --- a/src/vs/workbench/browser/actions/toggleCenteredLayout.ts +++ b/src/vs/workbench/browser/actions/toggleCenteredLayout.ts @@ -42,12 +42,3 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { }, order: 3 }); - -MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '2_layouts', - command: { - id: 'workbench.action.editorLayoutCentered', - title: nls.localize({ key: 'miCenteredEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Centered") - }, - order: 2 -}); diff --git a/src/vs/workbench/browser/actions/toggleSidebarVisibility.ts b/src/vs/workbench/browser/actions/toggleSidebarVisibility.ts index c5d450fae..921dc26b5 100644 --- a/src/vs/workbench/browser/actions/toggleSidebarVisibility.ts +++ b/src/vs/workbench/browser/actions/toggleSidebarVisibility.ts @@ -28,8 +28,9 @@ export class ToggleSidebarVisibilityAction extends Action { run(): Thenable { const hideSidebar = this.partService.isVisible(Parts.SIDEBAR_PART); + this.partService.setSideBarHidden(hideSidebar); - return this.partService.setSideBarHidden(hideSidebar); + return Promise.resolve(null); } } diff --git a/src/vs/workbench/browser/composite.ts b/src/vs/workbench/browser/composite.ts index 18f21d183..aa9936a1f 100644 --- a/src/vs/workbench/browser/composite.ts +++ b/src/vs/workbench/browser/composite.ts @@ -118,14 +118,11 @@ export abstract class Composite extends Component implements IComposite { * is called more than once during workbench lifecycle depending on the user interaction. * The composite will be on-DOM if visible is set to true and off-DOM otherwise. * - * The returned promise is complete when the composite is visible. As such it is valid - * to do a long running operation from this call. Typically this operation should be - * fast though because setVisible might be called many times during a session. + * Typically this operation should be fast though because setVisible might be called many times during a session. + * If there is a long running opertaion it is fine to have it running in the background asyncly and return before. */ - setVisible(visible: boolean): Promise { + setVisible(visible: boolean): void { this.visible = visible; - - return Promise.resolve(null); } /** diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 9c322e9ce..737ddd849 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -25,7 +25,6 @@ import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart'; import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; import { StatusbarPart } from 'vs/workbench/browser/parts/statusbar/statusbarPart'; import { getZoomFactor } from 'vs/base/browser/browser'; -import * as perf from 'vs/base/common/performance'; const TITLE_BAR_HEIGHT = isMacintosh ? 22 : 30; const STATUS_BAR_HEIGHT = 22; @@ -261,7 +260,6 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr let sidebarPosition = this.partService.getSideBarPosition(); let isSidebarVisible = this.partService.isVisible(Parts.SIDEBAR_PART); let newSashWidth = (sidebarPosition === Position.LEFT) ? startSidebarWidth + e.currentX - startX : startSidebarWidth - e.currentX + startX; - let promise: Thenable = Promise.resolve(null); // Sidebar visible if (isSidebarVisible) { @@ -269,7 +267,7 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr // Automatically hide side bar when a certain threshold is met if (newSashWidth + HIDE_SIDEBAR_WIDTH_THRESHOLD < this.partLayoutInfo.sidebar.minWidth) { let dragCompensation = this.partLayoutInfo.sidebar.minWidth - HIDE_SIDEBAR_WIDTH_THRESHOLD; - promise = this.partService.setSideBarHidden(true); + this.partService.setSideBarHidden(true); startX = (sidebarPosition === Position.LEFT) ? Math.max(this.activitybarWidth, e.currentX - dragCompensation) : Math.min(e.currentX + dragCompensation, this.workbenchSize.width - this.activitybarWidth); this.sidebarWidth = startSidebarWidth; // when restoring sidebar, restore to the sidebar width we started from } @@ -287,12 +285,12 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr (sidebarPosition === Position.RIGHT && startX - e.currentX >= this.partLayoutInfo.sidebar.minWidth)) { startSidebarWidth = this.partLayoutInfo.sidebar.minWidth - (sidebarPosition === Position.LEFT ? e.currentX - startX : startX - e.currentX); this.sidebarWidth = this.partLayoutInfo.sidebar.minWidth; - promise = this.partService.setSideBarHidden(false); + this.partService.setSideBarHidden(false); } } if (doLayout) { - promise.then(() => this.layout({ source: Parts.SIDEBAR_PART })); + this.layout({ source: Parts.SIDEBAR_PART }); } })); @@ -300,7 +298,6 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr let doLayout = false; let isPanelVisible = this.partService.isVisible(Parts.PANEL_PART); let newSashHeight = startPanelHeight - (e.currentY - startY); - let promise: Thenable = Promise.resolve(null); // Panel visible if (isPanelVisible) { @@ -308,7 +305,7 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr // Automatically hide panel when a certain threshold is met if (newSashHeight + HIDE_PANEL_HEIGHT_THRESHOLD < this.partLayoutInfo.panel.minHeight) { let dragCompensation = this.partLayoutInfo.panel.minHeight - HIDE_PANEL_HEIGHT_THRESHOLD; - promise = this.partService.setPanelHidden(true); + this.partService.setPanelHidden(true); startY = Math.min(this.sidebarHeight - this.statusbarHeight - this.titlebarHeight, e.currentY + dragCompensation); this.panelHeight = startPanelHeight; // when restoring panel, restore to the panel height we started from } @@ -325,12 +322,12 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr if (startY - e.currentY >= this.partLayoutInfo.panel.minHeight) { startPanelHeight = 0; this.panelHeight = this.partLayoutInfo.panel.minHeight; - promise = this.partService.setPanelHidden(false); + this.partService.setPanelHidden(false); } } if (doLayout) { - promise.then(() => this.layout({ source: Parts.PANEL_PART })); + this.layout({ source: Parts.PANEL_PART }); } })); @@ -338,7 +335,6 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr let doLayout = false; let isPanelVisible = this.partService.isVisible(Parts.PANEL_PART); let newSashWidth = startPanelWidth - (e.currentX - startXTwo); - let promise: Thenable = Promise.resolve(null); // Panel visible if (isPanelVisible) { @@ -346,7 +342,7 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr // Automatically hide panel when a certain threshold is met if (newSashWidth + HIDE_PANEL_WIDTH_THRESHOLD < this.partLayoutInfo.panel.minWidth) { let dragCompensation = this.partLayoutInfo.panel.minWidth - HIDE_PANEL_WIDTH_THRESHOLD; - promise = this.partService.setPanelHidden(true); + this.partService.setPanelHidden(true); startXTwo = Math.min(this.workbenchSize.width - this.activitybarWidth, e.currentX + dragCompensation); this.panelWidth = startPanelWidth; // when restoring panel, restore to the panel height we started from } @@ -363,12 +359,12 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr if (startXTwo - e.currentX >= this.partLayoutInfo.panel.minWidth) { startPanelWidth = 0; this.panelWidth = this.partLayoutInfo.panel.minWidth; - promise = this.partService.setPanelHidden(false); + this.partService.setPanelHidden(false); } } if (doLayout) { - promise.then(() => this.layout({ source: Parts.PANEL_PART })); + this.layout({ source: Parts.PANEL_PART }); } })); @@ -397,7 +393,8 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr this.sidebarWidth = Math.max(optimalWidth, DEFAULT_SIDEBAR_PART_WIDTH); this.storageService.store(WorkbenchLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL); - this.partService.setSideBarHidden(false).then(() => this.layout()); + this.partService.setSideBarHidden(false); + this.layout(); })); this._register(this.sashXTwo.onDidReset(() => { @@ -553,7 +550,6 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr // Bug on Chrome: Sometimes Chrome wants to scroll the workbench container on layout changes. The fix is to reset scrolling in this case. // uses set time to ensure this happens in th next frame (RAF will be at the end of this JS time slice and we don't want that) setTimeout(() => { - perf.mark('willCheckAndFixWorkbenchLayout'); const workbenchContainer = this.workbenchContainer; if (workbenchContainer.scrollTop > 0) { workbenchContainer.scrollTop = 0; @@ -561,7 +557,6 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr if (workbenchContainer.scrollLeft > 0) { workbenchContainer.scrollLeft = 0; } - perf.mark('didCheckAndFixWorkbenchLayout'); }); // Title Part diff --git a/src/vs/workbench/browser/panel.ts b/src/vs/workbench/browser/panel.ts index b44595941..aa6b2dad5 100644 --- a/src/vs/workbench/browser/panel.ts +++ b/src/vs/workbench/browser/panel.ts @@ -77,10 +77,12 @@ export abstract class TogglePanelAction extends Action { run(): Thenable { if (this.isPanelFocused()) { - return this.partService.setPanelHidden(true); + this.partService.setPanelHidden(true); + } else { + this.panelService.openPanel(this.panelId, true); } - return this.panelService.openPanel(this.panelId, true); + return Promise.resolve(null); } private isPanelActive(): boolean { diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index 8df902409..353017619 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -62,7 +62,8 @@ export class ViewletActivityAction extends ActivityAction { // Hide sidebar if selected viewlet already visible if (sideBarVisible && activeViewlet && activeViewlet.getId() === this.activity.id) { this.logAction('hide'); - return this.partService.setSideBarHidden(true); + this.partService.setSideBarHidden(true); + return Promise.resolve(null); } this.logAction('show'); @@ -96,7 +97,8 @@ export class ToggleViewletAction extends Action { // Hide sidebar if selected viewlet already visible if (sideBarVisible && activeViewlet && activeViewlet.getId() === this._viewlet.id) { - return this.partService.setSideBarHidden(true); + this.partService.setSideBarHidden(true); + return Promise.resolve(null); } return this.viewletService.openViewlet(this._viewlet.id, true); @@ -158,7 +160,7 @@ export class GlobalActivityActionItem extends ActivityActionItem { this.contextMenuService.showContextMenu({ getAnchor: () => location, - getActions: () => Promise.resolve(actions), + getActions: () => actions, onHide: () => dispose(actions) }); } @@ -194,7 +196,7 @@ export class PlaceHolderToggleCompositePinnedAction extends ToggleCompositePinne } } -class SwitchSidebarViewAction extends Action { +class SwitchSideBarViewAction extends Action { constructor( id: string, @@ -223,10 +225,10 @@ class SwitchSidebarViewAction extends Action { } } -export class PreviousSidebarViewAction extends SwitchSidebarViewAction { +export class PreviousSideBarViewAction extends SwitchSideBarViewAction { - static readonly ID = 'workbench.action.previousSidebarView'; - static LABEL = nls.localize('previousSidebarView', 'Previous Sidebar View'); + static readonly ID = 'workbench.action.previousSideBarView'; + static LABEL = nls.localize('previousSideBarView', 'Previous Side Bar View'); constructor( id: string, @@ -242,10 +244,10 @@ export class PreviousSidebarViewAction extends SwitchSidebarViewAction { } } -export class NextSidebarViewAction extends SwitchSidebarViewAction { +export class NextSideBarViewAction extends SwitchSideBarViewAction { - static readonly ID = 'workbench.action.nextSidebarView'; - static LABEL = nls.localize('nextSidebarView', 'Next Sidebar View'); + static readonly ID = 'workbench.action.nextSideBarView'; + static LABEL = nls.localize('nextSideBarView', 'Next Side Bar View'); constructor( id: string, @@ -262,8 +264,8 @@ export class NextSidebarViewAction extends SwitchSidebarViewAction { } const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(PreviousSidebarViewAction, PreviousSidebarViewAction.ID, PreviousSidebarViewAction.LABEL), 'View: Open Previous Sidebar View', nls.localize('view', "View")); -registry.registerWorkbenchAction(new SyncActionDescriptor(NextSidebarViewAction, NextSidebarViewAction.ID, NextSidebarViewAction.LABEL), 'View: Open Next Sidebar View', nls.localize('view', "View")); +registry.registerWorkbenchAction(new SyncActionDescriptor(PreviousSideBarViewAction, PreviousSideBarViewAction.ID, PreviousSideBarViewAction.LABEL), 'View: Open Previous Side Bar View', nls.localize('view', "View")); +registry.registerWorkbenchAction(new SyncActionDescriptor(NextSideBarViewAction, NextSideBarViewAction.ID, NextSideBarViewAction.LABEL), 'View: Open Next Side Bar View', nls.localize('view', "View")); registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 9b868aedf..ed31d13c4 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -30,24 +30,30 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { URI } from 'vs/base/common/uri'; import { ToggleCompositePinnedAction, ICompositeBarColors } from 'vs/workbench/browser/parts/compositeBarActions'; 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'; -interface IPlaceholderComposite { +const SCM_VIEWLET_ID = 'workbench.view.scm'; + +interface ICachedViewlet { id: string; iconUrl: URI; + views?: { when: string }[]; } export class ActivitybarPart extends Part { private static readonly ACTION_HEIGHT = 50; private static readonly PINNED_VIEWLETS = 'workbench.activity.pinnedViewlets'; - private static readonly PLACEHOLDER_VIEWLETS = 'workbench.activity.placeholderViewlets'; + private static readonly CACHED_VIEWLETS = 'workbench.activity.placeholderViewlets'; private dimension: Dimension; private globalActionBar: ActionBar; private globalActivityIdToActions: { [globalActivityId: string]: GlobalActivityAction; } = Object.create(null); - private placeholderComposites: IPlaceholderComposite[] = []; + private cachedViewlets: ICachedViewlet[] = []; private compositeBar: CompositeBar; private compositeActions: { [compositeId: string]: { activityAction: ViewletActivityAction, pinnedAction: ToggleCompositePinnedAction } } = Object.create(null); @@ -59,7 +65,9 @@ export class ActivitybarPart extends Part { @IThemeService themeService: IThemeService, @ILifecycleService private lifecycleService: ILifecycleService, @IStorageService private storageService: IStorageService, - @IExtensionService private extensionService: IExtensionService + @IExtensionService private extensionService: IExtensionService, + @IViewsService private viewsService: IViewsService, + @IContextKeyService private contextKeyService: IContextKeyService ) { super(id, { hasTitle: false }, themeService, storageService); @@ -79,25 +87,23 @@ export class ActivitybarPart extends Part { overflowActionSize: ActivitybarPart.ACTION_HEIGHT })); - const previousState = this.storageService.get(ActivitybarPart.PLACEHOLDER_VIEWLETS, StorageScope.GLOBAL, '[]'); - this.placeholderComposites = JSON.parse(previousState); - this.placeholderComposites.forEach((s) => { - if (typeof s.iconUrl === 'object') { - s.iconUrl = URI.revive(s.iconUrl); - } else { - s.iconUrl = void 0; + const previousState = this.storageService.get(ActivitybarPart.CACHED_VIEWLETS, StorageScope.GLOBAL, '[]'); + this.cachedViewlets = (JSON.parse(previousState)).map(({ id, iconUrl, views }) => ({ id, views, iconUrl: typeof iconUrl === 'object' ? URI.revive(iconUrl) : void 0 })); + for (const cachedViewlet of this.cachedViewlets) { + if (this.shouldBeHidden(cachedViewlet.id, cachedViewlet)) { + this.compositeBar.hideComposite(cachedViewlet.id); } - }); + } this.registerListeners(); - this.updateCompositebar(); + this.onDidRegisterViewlets(viewletService.getAllViewlets()); } private registerListeners(): void { - this._register(this.viewletService.onDidViewletRegister(() => this.updateCompositebar())); + this._register(this.viewletService.onDidViewletRegister(viewlet => this.onDidRegisterViewlets([viewlet]))); // Activate viewlet action on opening of a viewlet - this._register(this.viewletService.onDidViewletOpen(viewlet => this.compositeBar.activateComposite(viewlet.getId()))); + this._register(this.viewletService.onDidViewletOpen(viewlet => this.onDidViewletOpen(viewlet))); // Deactivate viewlet action on close this._register(this.viewletService.onDidViewletClose(viewlet => this.compositeBar.deactivateComposite(viewlet.getId()))); @@ -114,7 +120,28 @@ export class ActivitybarPart extends Part { private onDidRegisterExtensions(): void { this.removeNotExistingComposites(); - this.updateCompositebar(); + for (const viewlet of this.viewletService.getAllViewlets()) { + this.enableCompositeActions(viewlet); + const viewContainer = this.getViewContainer(viewlet.id); + if (viewContainer) { + const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer); + this.onDidChangeActiveViews(viewlet, viewDescriptors); + viewDescriptors.onDidChangeActiveViews(() => this.onDidChangeActiveViews(viewlet, viewDescriptors)); + } + } + } + + private onDidChangeActiveViews(viewlet: ViewletDescriptor, viewDescriptors: IViewDescriptorCollection): void { + if (viewDescriptors.activeViewDescriptors.length) { + this.compositeBar.addComposite(viewlet); + } else { + this.removeComposite(viewlet.id, true); + } + } + + private onDidViewletOpen(viewlet: IViewlet): void { + this.compositeBar.addComposite(this.viewletService.getViewlet(viewlet.getId())); + this.compositeBar.activateComposite(viewlet.getId()); } showActivity(viewletOrActionId: string, badge: IBadge, clazz?: string, priority?: number): IDisposable { @@ -239,9 +266,9 @@ export class ActivitybarPart extends Part { pinnedAction: new ToggleCompositePinnedAction(viewlet, this.compositeBar) }; } else { - const placeHolderComposite = this.placeholderComposites.filter(c => c.id === compositeId)[0]; + const cachedComposite = this.cachedViewlets.filter(c => c.id === compositeId)[0]; compositeActions = { - activityAction: this.instantiationService.createInstance(PlaceHolderViewletActivityAction, compositeId, placeHolderComposite && placeHolderComposite.iconUrl), + activityAction: this.instantiationService.createInstance(PlaceHolderViewletActivityAction, compositeId, cachedComposite && cachedComposite.iconUrl), pinnedAction: new PlaceHolderToggleCompositePinnedAction(compositeId, this.compositeBar) }; } @@ -252,25 +279,33 @@ export class ActivitybarPart extends Part { return compositeActions; } - private updateCompositebar(): void { - const viewlets = this.viewletService.getViewlets(); + private onDidRegisterViewlets(viewlets: ViewletDescriptor[]): void { for (const viewlet of viewlets) { - this.compositeBar.addComposite(viewlet); + const cachedViewlet = this.cachedViewlets.filter(({ id }) => id === viewlet.id)[0]; + const activeViewlet = this.viewletService.getActiveViewlet(); + const isActive = activeViewlet && activeViewlet.getId() === viewlet.id; - // Pin it by default if it is new => it does not has a placeholder - if (this.placeholderComposites.every(c => c.id !== viewlet.id)) { - this.compositeBar.pin(viewlet.id); - } + if (isActive || !this.shouldBeHidden(viewlet.id, cachedViewlet)) { + this.compositeBar.addComposite(viewlet); - this.enableCompositeActions(viewlet); - const activeViewlet = this.viewletService.getActiveViewlet(); - if (activeViewlet && activeViewlet.getId() === viewlet.id) { - this.compositeBar.pin(viewlet.id); - this.compositeBar.activateComposite(viewlet.id); + // Pin it by default if it is new + if (!cachedViewlet) { + this.compositeBar.pin(viewlet.id); + } + + if (isActive) { + this.compositeBar.activateComposite(viewlet.id); + } } } } + private shouldBeHidden(viewletId: string, cachedViewlet: ICachedViewlet): boolean { + 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 */; + } + private removeNotExistingComposites(): void { const viewlets = this.viewletService.getAllViewlets(); for (const { id } of this.compositeBar.getComposites()) { @@ -333,9 +368,28 @@ export class ActivitybarPart extends Part { } protected saveState(): void { - const state = this.viewletService.getAllViewlets().map(({ id, iconUrl }) => ({ id, iconUrl })); - this.storageService.store(ActivitybarPart.PLACEHOLDER_VIEWLETS, JSON.stringify(state), StorageScope.GLOBAL); + const state: ICachedViewlet[] = []; + for (const { id, iconUrl } of this.viewletService.getAllViewlets()) { + const viewContainer = this.getViewContainer(id); + const views: { when: string }[] = []; + if (viewContainer) { + for (const { when } of this.viewsService.getViewDescriptors(viewContainer).allViewDescriptors) { + views.push({ when: when ? when.serialize() : void 0 }); + } + } + state.push({ id, iconUrl, views }); + } + this.storageService.store(ActivitybarPart.CACHED_VIEWLETS, JSON.stringify(state), StorageScope.GLOBAL); super.saveState(); } + + private getViewContainer(viewletId: string): ViewContainer | undefined { + // TODO: @Joao Remove this after moving SCM Viewlet to ViewContainerViewlet - https://github.com/Microsoft/vscode/issues/49054 + if (viewletId === SCM_VIEWLET_ID) { + return null; + } + const viewContainerRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); + return viewContainerRegistry.get(viewletId); + } } diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts index 93f552147..310c06c14 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -13,7 +13,6 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ActionBar, ActionsOrientation, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { CompositeActionItem, CompositeOverflowActivityAction, ICompositeActivity, CompositeOverflowActivityActionItem, ActivityAction, ICompositeBar, ICompositeBarColors, DraggedCompositeIdentifier } from 'vs/workbench/browser/parts/compositeBarActions'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Dimension, $, addDisposableListener, EventType, EventHelper } from 'vs/base/browser/dom'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -33,9 +32,9 @@ export interface ICompositeBarOptions { getCompositePinnedAction: (compositeId: string) => Action; getOnCompositeClickAction: (compositeId: string) => Action; getContextMenuActions: () => Action[]; - openComposite: (compositeId: string) => TPromise; + openComposite: (compositeId: string) => Thenable; getDefaultCompositeId: () => string; - hidePart: () => TPromise; + hidePart: () => void; } export class CompositeBar extends Widget implements ICompositeBar { @@ -67,7 +66,7 @@ export class CompositeBar extends Widget implements ICompositeBar { } getComposites(): ICompositeBarItem[] { - return this.model.items; + return [...this.model.items]; } getPinnedComposites(): ICompositeBarItem[] { @@ -401,7 +400,7 @@ export class CompositeBar extends Widget implements ICompositeBar { const event = new StandardMouseEvent(e); this.contextMenuService.showContextMenu({ getAnchor: () => { return { x: event.posx, y: event.posy }; }, - getActions: () => Promise.resolve(this.getContextMenuActions()) + getActions: () => this.getContextMenuActions() }); } diff --git a/src/vs/workbench/browser/parts/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts index f05c43363..a694d8971 100644 --- a/src/vs/workbench/browser/parts/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -373,7 +373,7 @@ export class CompositeOverflowActivityActionItem extends ActivityActionItem { this.contextMenuService.showContextMenu({ getAnchor: () => this.element, - getActions: () => Promise.resolve(this.actions), + getActions: () => this.actions, onHide: () => dispose(this.actions) }); } @@ -598,7 +598,7 @@ export class CompositeActionItem extends ActivityActionItem { this.contextMenuService.showContextMenu({ getAnchor: () => anchor, getActionsContext: () => this.activity.id, - getActions: () => Promise.resolve(actions) + getActions: () => actions }); } diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index 4ff3b172f..8fd845740 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -6,11 +6,9 @@ import 'vs/css!./media/compositepart'; import * as nls from 'vs/nls'; import { defaultGenerator } from 'vs/base/common/idGenerator'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; import { Emitter } from 'vs/base/common/event'; -import * as types from 'vs/base/common/types'; import * as errors from 'vs/base/common/errors'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IActionItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -33,6 +31,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; 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'; export interface ICompositeTitleLabel { @@ -96,7 +95,7 @@ export abstract class CompositePart extends Part { this.lastActiveCompositeId = storageService.get(activeCompositeSettingsKey, StorageScope.WORKSPACE, this.defaultCompositeId); } - protected openComposite(id: string, focus?: boolean): TPromise { + protected openComposite(id: string, focus?: boolean): Composite { // Check if composite already visible and just focus in that case if (this.activeComposite && this.activeComposite.getId() === id) { if (focus) { @@ -104,66 +103,57 @@ export abstract class CompositePart extends Part { } // Fullfill promise with composite that is being opened - return Promise.resolve(this.activeComposite); + return this.activeComposite; } // Open return this.doOpenComposite(id, focus); } - private doOpenComposite(id: string, focus?: boolean): TPromise { + private doOpenComposite(id: string, focus?: boolean): Composite { // Use a generated token to avoid race conditions from long running promises const currentCompositeOpenToken = defaultGenerator.nextId(); this.currentCompositeOpenToken = currentCompositeOpenToken; // Hide current - let hidePromise: TPromise; if (this.activeComposite) { - hidePromise = this.hideActiveComposite(); - } else { - hidePromise = Promise.resolve(null); + this.hideActiveComposite(); } - return hidePromise.then(() => { + // Update Title + this.updateTitle(id); - // Update Title - this.updateTitle(id); + // Create composite + const composite = this.createComposite(id, true); - // Create composite - const composite = this.createComposite(id, true); + // Check if another composite opened meanwhile and return in that case + if ((this.currentCompositeOpenToken !== currentCompositeOpenToken) || (this.activeComposite && this.activeComposite.getId() !== composite.getId())) { + return undefined; + } - // Check if another composite opened meanwhile and return in that case - if ((this.currentCompositeOpenToken !== currentCompositeOpenToken) || (this.activeComposite && this.activeComposite.getId() !== composite.getId())) { - return Promise.resolve(null); + // Check if composite already visible and just focus in that case + if (this.activeComposite && this.activeComposite.getId() === composite.getId()) { + if (focus) { + composite.focus(); } - // Check if composite already visible and just focus in that case - if (this.activeComposite && this.activeComposite.getId() === composite.getId()) { - if (focus) { - composite.focus(); - } + this._onDidCompositeOpen.fire({ composite, focus }); + return composite; + } - // Fullfill promise with composite that is being opened - return Promise.resolve(composite); - } + // Show Composite and Focus + this.showComposite(composite); + if (focus) { + composite.focus(); + } - // Show Composite and Focus - return this.showComposite(composite).then(() => { - if (focus) { - composite.focus(); - } - - // Fullfill promise with composite that is being opened - return composite; - }); - }).then(composite => { - if (composite) { - this._onDidCompositeOpen.fire({ composite, focus }); - } + // Return with the composite that is being opened + if (composite) { + this._onDidCompositeOpen.fire({ composite, focus }); + } - return composite; - }); + return composite; } protected createComposite(id: string, isActive?: boolean): Composite { @@ -196,7 +186,7 @@ export abstract class CompositePart extends Part { throw new Error(strings.format('Unable to find composite with id {0}', id)); } - protected showComposite(composite: Composite): TPromise { + protected showComposite(composite: Composite): void { // Remember Composite this.activeComposite = composite; @@ -287,18 +277,17 @@ export abstract class CompositePart extends Part { }); // Indicate to composite that it is now visible - return composite.setVisible(true).then(() => { + composite.setVisible(true); - // Make sure that the user meanwhile did not open another composite or closed the part containing the composite - if (!this.activeComposite || composite.getId() !== this.activeComposite.getId()) { - return; - } + // Make sure that the user meanwhile did not open another composite or closed the part containing the composite + if (!this.activeComposite || composite.getId() !== this.activeComposite.getId()) { + return; + } - // Make sure the composite is layed out - if (this.contentAreaSize) { - composite.layout(this.contentAreaSize); - } - }, error => this.onError(error)); + // Make sure the composite is layed out + if (this.contentAreaSize) { + composite.layout(this.contentAreaSize); + } } protected onTitleAreaUpdate(compositeId: string): void { @@ -360,9 +349,9 @@ export abstract class CompositePart extends Part { return this.lastActiveCompositeId; } - protected hideActiveComposite(): TPromise { + protected hideActiveComposite(): Composite { if (!this.activeComposite) { - return Promise.resolve(null); // Nothing to do + return undefined; // Nothing to do } const composite = this.activeComposite; @@ -371,21 +360,20 @@ export abstract class CompositePart extends Part { const compositeContainer = this.mapCompositeToCompositeContainer[composite.getId()]; // Indicate to Composite - return composite.setVisible(false).then(() => { + composite.setVisible(false); - // Take Container Off-DOM and hide - compositeContainer.remove(); - hide(compositeContainer); + // Take Container Off-DOM and hide + compositeContainer.remove(); + hide(compositeContainer); - // Clear any running Progress - this.progressBar.stop().hide(); + // Clear any running Progress + this.progressBar.stop().hide(); - // Empty Actions - this.toolBar.setActions([])(); - this._onDidCompositeClose.fire(composite); + // Empty Actions + this.toolBar.setActions([])(); + this._onDidCompositeClose.fire(composite); - return composite; - }); + return composite; } createTitleArea(parent: HTMLElement): HTMLElement { @@ -404,7 +392,8 @@ export abstract class CompositePart extends Part { this.toolBar = this._register(new ToolBar(titleActionsContainer, this.contextMenuService, { actionItemProvider: action => this.actionItemProvider(action as Action), orientation: ActionsOrientation.HORIZONTAL, - getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id) + getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id), + anchorAlignmentProvider: () => this.getTitleAreaDropDownAnchorAlignment() })); return titleArea; @@ -454,10 +443,6 @@ export abstract class CompositePart extends Part { return contentContainer; } - private onError(error: any): void { - this.notificationService.error(types.isString(error) ? new Error(error) : error); - } - getProgressIndicator(id: string): IProgressService { return this.mapProgressServiceToComposite[id]; } @@ -470,6 +455,10 @@ export abstract class CompositePart extends Part { return []; } + protected getTitleAreaDropDownAnchorAlignment(): AnchorAlignment { + return AnchorAlignment.RIGHT; + } + layout(dimension: Dimension): Dimension[] { // Pass to super diff --git a/src/vs/workbench/browser/parts/editor/baseEditor.ts b/src/vs/workbench/browser/parts/editor/baseEditor.ts index 5b3c99d4c..91ea40626 100644 --- a/src/vs/workbench/browser/parts/editor/baseEditor.ts +++ b/src/vs/workbench/browser/parts/editor/baseEditor.ts @@ -117,15 +117,10 @@ export abstract class BaseEditor extends Panel implements IEditor { */ protected abstract createEditor(parent: HTMLElement): void; - setVisible(visible: boolean, group?: IEditorGroup): void; // setVisible is sync for editors - setVisible(visible: boolean, group?: IEditorGroup): Promise; - setVisible(visible: boolean, group?: IEditorGroup): Promise { - const promise = super.setVisible(visible); - + setVisible(visible: boolean, group?: IEditorGroup): void { + super.setVisible(visible); // Propagate to Editor this.setEditorVisible(visible, group); - - return promise; } /** diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index cfe0453ff..cde62d614 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -39,12 +39,12 @@ import { FileLabel } from 'vs/workbench/browser/labels'; import { BreadcrumbsConfig, IBreadcrumbsService } from 'vs/workbench/browser/parts/editor/breadcrumbs'; import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel'; import { BreadcrumbsPicker, createBreadcrumbsPicker } from 'vs/workbench/browser/parts/editor/breadcrumbsPicker'; -import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView'; import { SideBySideEditorInput } from 'vs/workbench/common/editor'; import { ACTIVE_GROUP, ACTIVE_GROUP_TYPE, IEditorService, SIDE_GROUP, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor'; class Item extends BreadcrumbsItem { @@ -152,7 +152,7 @@ export class BreadcrumbsControl { constructor( container: HTMLElement, private readonly _options: IBreadcrumbsControlOptions, - private readonly _editorGroup: EditorGroupView, + private readonly _editorGroup: IEditorGroupView, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IContextViewService private readonly _contextViewService: IContextViewService, @IEditorService private readonly _editorService: IEditorService, @@ -183,6 +183,7 @@ export class BreadcrumbsControl { this._cfUseQuickPick = BreadcrumbsConfig.UseQuickPick.bindTo(_configurationService); this._disposables.push(breadcrumbsService.register(this._editorGroup.id, this._widget)); + this._disposables.push(_fileService.onDidChangeFileSystemProviderRegistrations(this.update, this)); } dispose(): void { diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index eaa16b8ca..3ab4eb91b 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -333,7 +333,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(JoinTwoGroupsAction, J registry.registerWorkbenchAction(new SyncActionDescriptor(JoinAllGroupsAction, JoinAllGroupsAction.ID, JoinAllGroupsAction.LABEL), 'View: Join Editors of All Groups', category); registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateBetweenGroupsAction, NavigateBetweenGroupsAction.ID, NavigateBetweenGroupsAction.LABEL), 'View: Navigate Between Editor Groups', category); registry.registerWorkbenchAction(new SyncActionDescriptor(ResetGroupSizesAction, ResetGroupSizesAction.ID, ResetGroupSizesAction.LABEL), 'View: Reset Editor Group Sizes', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(MaximizeGroupAction, MaximizeGroupAction.ID, MaximizeGroupAction.LABEL), 'View: Maximize Editor Group and Hide Sidebar', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(MaximizeGroupAction, MaximizeGroupAction.ID, MaximizeGroupAction.LABEL), 'View: Maximize Editor Group and Hide Side Bar', category); registry.registerWorkbenchAction(new SyncActionDescriptor(MinimizeOtherGroupsAction, MinimizeOtherGroupsAction.ID, MinimizeOtherGroupsAction.LABEL), 'View: Maximize Editor Group', category); registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorLeftInGroupAction, MoveEditorLeftInGroupAction.ID, MoveEditorLeftInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageUp, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow) } }), 'View: Move Editor Left', category); registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorRightInGroupAction, MoveEditorRightInGroupAction.ID, MoveEditorRightInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageDown, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow) } }), 'View: Move Editor Right', category); diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index ed046115e..bcf9cf0dc 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -897,8 +897,7 @@ export class MaximizeGroupAction extends Action { run(): Thenable { if (this.editorService.activeEditor) { this.editorGroupService.arrangeGroups(GroupsArrangement.MINIMIZE_OTHERS); - - return this.partService.setSideBarHidden(true); + this.partService.setSideBarHidden(true); } return Promise.resolve(false); diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index 0dee127b2..26332bd20 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -22,7 +22,6 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { INotificationService } from 'vs/platform/notification/common/notification'; export const CLOSE_SAVED_EDITORS_COMMAND_ID = 'workbench.action.closeUnmodifiedEditors'; export const CLOSE_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeEditorsInGroup'; @@ -273,13 +272,6 @@ function registerDiffEditorCommands(): void { handler: accessor => toggleDiffSideBySide(accessor) }); - // TODO@Ben remove me after a while - CommandsRegistry.registerCommand('toggle.diff.editorMode', accessor => { - toggleDiffSideBySide(accessor); - - accessor.get(INotificationService).warn(nls.localize('diffCommandDeprecation', "Command 'toggle.diff.editorMode' has been deprecated. Please use '{0}' instead.", TOGGLE_DIFF_SIDE_BY_SIDE)); - }); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: TOGGLE_DIFF_SIDE_BY_SIDE, diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 2fb789ef7..97b736fb0 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -308,7 +308,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Show it this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => TPromise.as(actions), + getActions: () => actions, onHide: () => this.focus() }); } @@ -763,13 +763,21 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.accessor.activateGroup(this); } + // Actually move the editor if a specific index is provided and we figure + // out that the the editor is already opened at a different index. This + // ensures the right set of events are fired to the outside. + if (typeof openEditorOptions.index === 'number') { + const indexOfEditor = this._group.indexOf(editor); + if (indexOfEditor !== -1 && indexOfEditor !== openEditorOptions.index) { + this.doMoveEditorInsideGroup(editor, openEditorOptions); + } + } + // Update model this._group.openEditor(editor, openEditorOptions); // Show editor - const showEditorResult = this.doShowEditor(editor, openEditorOptions.active, options); - - return showEditorResult; + return this.doShowEditor(editor, openEditorOptions.active, options); } private doShowEditor(editor: EditorInput, active: boolean, options?: EditorOptions): TPromise { diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 8b01a98aa..e94317b65 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -877,7 +877,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor // Create new const groupViews: IEditorGroupView[] = []; const gridWidget = SerializableGrid.deserialize(serializedGrid, { - fromJSON: (serializedEditorGroup: ISerializedEditorGroup) => { + fromJSON: (serializedEditorGroup: ISerializedEditorGroup | null) => { let groupView: IEditorGroupView; if (reuseGroupViews.length > 0) { groupView = reuseGroupViews.shift(); diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 1545851c2..a2f95c7b9 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -15,7 +15,6 @@ import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar'; import { Action } from 'vs/base/common/actions'; import { language, LANGUAGE_DEFAULT, AccessibilitySupport } from 'vs/base/common/platform'; import * as browser from 'vs/base/browser/browser'; -import { IMode } from 'vs/editor/common/modes'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput, IEditor as IBaseEditor, IEditorInput } from 'vs/workbench/common/editor'; import { IDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -32,7 +31,7 @@ import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { SUPPORTED_ENCODINGS, IFileService, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IModeService } from 'vs/editor/common/services/modeService'; +import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; @@ -977,16 +976,16 @@ export class ChangeModeAction extends Action { } // Find mode - let mode: Promise; + let languageSelection: ILanguageSelection; if (pick === autoDetectMode) { - mode = this.modeService.getOrCreateModeByFilepathOrFirstLine(toResource(activeEditor, { supportSideBySide: true }).fsPath, textModel.getLineContent(1)); + languageSelection = this.modeService.createByFilepathOrFirstLine(toResource(activeEditor, { supportSideBySide: true }).fsPath, textModel.getLineContent(1)); } else { - mode = this.modeService.getOrCreateModeByLanguageName(pick.label); + languageSelection = this.modeService.createByLanguageName(pick.label); } // Change mode models.forEach(textModel => { - this.modelService.setMode(textModel, mode); + this.modelService.setMode(textModel, languageSelection); }); }); } @@ -1237,13 +1236,14 @@ export class ChangeEncodingAction extends Action { return { id: key, label: SUPPORTED_ENCODINGS[key].labelLong, description: key }; }); + const items = picks.slice() as IQuickPickItem[]; + // If we have a guessed encoding, show it first unless it matches the configured encoding if (guessedEncoding && configuredEncoding !== guessedEncoding && SUPPORTED_ENCODINGS[guessedEncoding]) { picks.unshift({ type: 'separator' }); picks.unshift({ id: guessedEncoding, label: SUPPORTED_ENCODINGS[guessedEncoding].labelLong, description: nls.localize('guessedEncoding', "Guessed from content") }); } - const items = picks.filter(p => p.type !== 'separator') as IQuickPickItem[]; return this.quickInputService.pick(picks, { placeHolder: isReopenWithEncoding ? nls.localize('pickEncodingForReopen', "Select File Encoding to Reopen File") : nls.localize('pickEncodingForSave', "Select File Encoding to Save with"), activeItem: items[typeof directMatchIndex === 'number' ? directMatchIndex : typeof aliasMatchIndex === 'number' ? aliasMatchIndex : -1] diff --git a/src/vs/workbench/browser/parts/editor/editorWidgets.ts b/src/vs/workbench/browser/parts/editor/editorWidgets.ts index ef3a85448..adf02391d 100644 --- a/src/vs/workbench/browser/parts/editor/editorWidgets.ts +++ b/src/vs/workbench/browser/parts/editor/editorWidgets.ts @@ -64,8 +64,15 @@ export class FloatingClickWidget extends Widget implements IOverlayWidget { this._domNode = $('.floating-click-widget'); this._register(attachStylerCallback(this.themeService, { buttonBackground, buttonForeground, editorBackground, editorForeground, contrastBorder }, colors => { - this._domNode.style.backgroundColor = colors.buttonBackground ? colors.buttonBackground.toString() : colors.editorBackground.toString(); - this._domNode.style.color = colors.buttonForeground ? colors.buttonForeground.toString() : colors.editorForeground.toString(); + const backgroundColor = colors.buttonBackground ? colors.buttonBackground : colors.editorBackground; + if (backgroundColor) { + this._domNode.style.backgroundColor = backgroundColor.toString(); + } + + const foregroundColor = colors.buttonForeground ? colors.buttonForeground : colors.editorForeground; + if (foregroundColor) { + this._domNode.style.color = foregroundColor.toString(); + } const borderColor = colors.contrastBorder ? colors.contrastBorder.toString() : null; this._domNode.style.borderWidth = borderColor ? '1px' : null; @@ -149,7 +156,12 @@ export class OpenWorkspaceButtonContribution extends Disposable implements IEdit private createOpenWorkspaceWidgetRenderer(): void { if (!this.openWorkspaceButton) { this.openWorkspaceButton = this.instantiationService.createInstance(FloatingClickWidget, this.editor, localize('openWorkspace', "Open Workspace"), null); - this._register(this.openWorkspaceButton.onClick(() => this.windowService.openWindow([this.editor.getModel().uri]))); + this._register(this.openWorkspaceButton.onClick(() => { + const model = this.editor.getModel(); + if (model) { + this.windowService.openWindow([model.uri]); + } + })); this.openWorkspaceButton.render(); } diff --git a/src/vs/workbench/browser/parts/editor/media/back-tb.png b/src/vs/workbench/browser/parts/editor/media/back-tb.png index 1f075e67e..88041b756 100644 Binary files a/src/vs/workbench/browser/parts/editor/media/back-tb.png and b/src/vs/workbench/browser/parts/editor/media/back-tb.png differ diff --git a/src/vs/workbench/browser/parts/editor/media/forward-tb.png b/src/vs/workbench/browser/parts/editor/media/forward-tb.png index 8949fe7f2..03327b968 100644 Binary files a/src/vs/workbench/browser/parts/editor/media/forward-tb.png and b/src/vs/workbench/browser/parts/editor/media/forward-tb.png differ diff --git a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts index 6dbede3ee..7eddf53b4 100644 --- a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts @@ -66,7 +66,7 @@ export class NoTabsTitleControl extends TitleControl { this._register(addDisposableListener(this.titleContainer, EventType.DBLCLICK, (e: MouseEvent) => this.onTitleDoubleClick(e))); // Detect mouse click - this._register(addDisposableListener(this.titleContainer, EventType.CLICK, (e: MouseEvent) => this.onTitleClick(e))); + this._register(addDisposableListener(this.titleContainer, EventType.MOUSE_UP, (e: MouseEvent) => this.onTitleClick(e))); // Detect touch this._register(addDisposableListener(this.titleContainer, TouchEventType.Tap, (e: GestureEvent) => this.onTitleClick(e))); diff --git a/src/vs/workbench/browser/parts/editor/resourceViewer.ts b/src/vs/workbench/browser/parts/editor/resourceViewer.ts index 239cae041..57bd8008a 100644 --- a/src/vs/workbench/browser/parts/editor/resourceViewer.ts +++ b/src/vs/workbench/browser/parts/editor/resourceViewer.ts @@ -284,7 +284,7 @@ class ZoomStatusbarItem extends Themable implements IStatusbarItem { DOM.addDisposableListener(this.statusBarItem, DOM.EventType.CLICK, () => { this.contextMenuService.showContextMenu({ getAnchor: () => container, - getActions: () => Promise.resolve(this.zoomActions) + getActions: () => this.zoomActions }); }); } diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index 210381d9a..aa3275999 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -37,6 +37,7 @@ import { EditorCommandsContextActionRunner, IEditorCommandsContext, IEditorInput import { ResourceContextKey } from 'vs/workbench/common/resources'; 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'; export interface IToolbarActions { primary: IAction[]; @@ -119,7 +120,8 @@ export abstract class TitleControl extends Themable { orientation: ActionsOrientation.HORIZONTAL, ariaLabel: localize('araLabelEditorActions', "Editor actions"), getKeyBinding: action => this.getKeybinding(action), - actionRunner: this._register(new EditorCommandsContextActionRunner(context)) + actionRunner: this._register(new EditorCommandsContextActionRunner(context)), + anchorAlignmentProvider: () => AnchorAlignment.RIGHT })); // Context @@ -288,7 +290,7 @@ export abstract class TitleControl extends Themable { // Show it this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => Promise.resolve(actions), + getActions: () => actions, getActionsContext: () => ({ groupId: this.group.id, editorIndex: this.group.getIndexOfEditor(editor) } as IEditorCommandsContext), getKeyBinding: (action) => this.getKeybinding(action), onHide: () => { diff --git a/src/vs/workbench/browser/parts/notifications/notificationsList.ts b/src/vs/workbench/browser/parts/notifications/notificationsList.ts index ea830ef9c..76dc13247 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsList.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsList.ts @@ -86,7 +86,7 @@ export class NotificationsList extends Themable { this._register((this.list.onContextMenu(e => { this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, - getActions: () => Promise.resolve([copyAction]), + getActions: () => [copyAction], getActionsContext: () => e.element, actionRunner }); diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index 151df6f7c..a5daf01b9 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -211,7 +211,10 @@ export class NotificationsToasts extends Themable { // Install Timers to Purge Notification let purgeTimeoutHandle: any; + let listener: IDisposable; + const hideAfterTimeout = () => { + purgeTimeoutHandle = setTimeout(() => { // If the notification is sticky or prompting and the window does not have @@ -220,15 +223,18 @@ export class NotificationsToasts extends Themable { // could immediately hide the notification because the timeout was triggered // again. if ((item.sticky || item.hasPrompt()) && !this.windowHasFocus) { - disposables.push(this.windowService.onDidChangeFocus(focus => { - if (focus) { - hideAfterTimeout(); - } - })); + if (!listener) { + listener = this.windowService.onDidChangeFocus(focus => { + if (focus) { + hideAfterTimeout(); + } + }); + disposables.push(listener); + } } // Otherwise... - if ( + else if ( item.sticky || // never hide sticky notifications notificationList.hasFocus() || // never hide notifications with focus isMouseOverToast // never hide notifications under mouse @@ -509,4 +515,4 @@ export class NotificationsToasts extends Themable { private isVisible(toast: INotificationToast): boolean { return !!toast.container.parentElement; } -} \ No newline at end of file +} diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index 7433c1b15..f9fa7fd84 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -30,7 +30,8 @@ export class ClosePanelAction extends Action { } run(): Thenable { - return this.partService.setPanelHidden(true); + this.partService.setPanelHidden(true); + return Promise.resolve(null); } } @@ -48,7 +49,8 @@ export class TogglePanelAction extends Action { } run(): Thenable { - return this.partService.setPanelHidden(this.partService.isVisible(Parts.PANEL_PART)); + this.partService.setPanelHidden(this.partService.isVisible(Parts.PANEL_PART)); + return Promise.resolve(null); } } @@ -70,7 +72,8 @@ class FocusPanelAction extends Action { // Show panel if (!this.partService.isVisible(Parts.PANEL_PART)) { - return this.partService.setPanelHidden(false); + this.partService.setPanelHidden(false); + return Promise.resolve(null); } // Focus into active panel @@ -79,7 +82,7 @@ class FocusPanelAction extends Action { panel.focus(); } - return Promise.resolve(true); + return Promise.resolve(null); } } @@ -116,7 +119,8 @@ export class TogglePanelPositionAction extends Action { run(): Thenable { const position = this.partService.getPanelPosition(); - return this.partService.setPanelPosition(position === Position.BOTTOM ? Position.RIGHT : Position.BOTTOM); + this.partService.setPanelPosition(position === Position.BOTTOM ? Position.RIGHT : Position.BOTTOM); + return Promise.resolve(null); } dispose(): void { @@ -153,9 +157,12 @@ export class ToggleMaximizedPanelAction extends Action { } run(): Thenable { - const promise: Thenable = !this.partService.isVisible(Parts.PANEL_PART) ? this.partService.setPanelHidden(false) : Promise.resolve(null); + if (!this.partService.isVisible(Parts.PANEL_PART)) { + this.partService.setPanelHidden(false); + } - return promise.then(() => this.partService.toggleMaximizedPanel()); + this.partService.toggleMaximizedPanel(); + return Promise.resolve(null); } dispose(): void { @@ -175,7 +182,9 @@ export class PanelActivityAction extends ActivityAction { } run(event: any): Thenable { - return this.panelService.openPanel(this.activity.id, true).then(() => this.activate()); + this.panelService.openPanel(this.activity.id, true); + this.activate(); + return Promise.resolve(null); } } @@ -202,7 +211,8 @@ export class SwitchPanelViewAction extends Action { break; } } - return this.panelService.openPanel(targetPanelId, true); + this.panelService.openPanel(targetPanelId, true); + return Promise.resolve(null); } } diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index fc7562b14..fa2ace5f8 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/panelpart'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IAction } from 'vs/base/common/actions'; import { Event, mapEvent } from 'vs/base/common/event'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -86,7 +85,7 @@ export class PanelPart extends CompositePart implements IPanelService { icon: false, storageId: PanelPart.PINNED_PANELS, orientation: ActionsOrientation.HORIZONTAL, - openComposite: (compositeId: string) => this.openPanel(compositeId, true), + 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)), @@ -180,23 +179,22 @@ export class PanelPart extends CompositePart implements IPanelService { title.style.borderTopColor = this.getColor(PANEL_BORDER) || this.getColor(contrastBorder); } - openPanel(id: string, focus?: boolean): TPromise { + openPanel(id: string, focus?: boolean): Panel { if (this.blockOpeningPanel) { - return Promise.resolve(null); // Workaround against a potential race condition + return null; // Workaround against a potential race condition } // First check if panel is hidden and show if so - let promise = TPromise.wrap(null); if (!this.partService.isVisible(Parts.PANEL_PART)) { try { this.blockOpeningPanel = true; - promise = this.partService.setPanelHidden(false); + this.partService.setPanelHidden(false); } finally { this.blockOpeningPanel = false; } } - return promise.then(() => this.openComposite(id, focus)); + return this.openComposite(id, focus); } showActivity(panelId: string, badge: IBadge, clazz?: string): IDisposable { @@ -247,8 +245,8 @@ export class PanelPart extends CompositePart implements IPanelService { return this.getLastActiveCompositetId(); } - hideActivePanel(): TPromise { - return this.hideActiveComposite().then(composite => void 0); + hideActivePanel(): void { + this.hideActiveComposite(); } protected createTitleLabel(parent: HTMLElement): ICompositeTitleLabel { diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts index b08a87ffb..99b4fe1d5 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts @@ -13,7 +13,6 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { contrastBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; import { SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND } from 'vs/workbench/common/theme'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; -import { TPromise } from 'vs/base/common/winjs.base'; import { CancellationToken } from 'vs/base/common/cancellation'; import { QuickInputList } from './quickInputList'; import { QuickInputBox } from './quickInputBox'; @@ -1041,8 +1040,8 @@ export class QuickInputService extends Component implements IQuickInputService { this.updateStyles(); } - pick>(picks: TPromise[]> | QuickPickInput[], options: O = {}, token: CancellationToken = CancellationToken.None): TPromise { - return new TPromise((doResolve, reject) => { + pick>(picks: Promise[]> | QuickPickInput[], options: O = {}, token: CancellationToken = CancellationToken.None): Promise { + return new Promise((doResolve, reject) => { let resolve = (result: any) => { resolve = doResolve; if (options.onKeyMods) { @@ -1117,7 +1116,7 @@ export class QuickInputService extends Component implements IQuickInputService { input.quickNavigate = options.quickNavigate; input.contextKey = options.contextKey; input.busy = true; - TPromise.join([picks, options.activeItem]) + Promise.all([picks, options.activeItem]) .then(([items, _activeItem]) => { activeItem = _activeItem; input.busy = false; @@ -1130,29 +1129,29 @@ export class QuickInputService extends Component implements IQuickInputService { } }); input.show(); - TPromise.wrap(picks).then(null, err => { + Promise.resolve(picks).then(null, err => { reject(err); input.hide(); }); }); } - input(options: IInputOptions = {}, token: CancellationToken = CancellationToken.None): TPromise { - return new TPromise((resolve, reject) => { + input(options: IInputOptions = {}, token: CancellationToken = CancellationToken.None): Promise { + return new Promise((resolve, reject) => { if (token.isCancellationRequested) { resolve(undefined); return; } const input = this.createInputBox(); - const validateInput = options.validateInput || (() => TPromise.as(undefined)); + const validateInput = options.validateInput || (() => >Promise.resolve(undefined)); const onDidValueChange = debounceEvent(input.onDidChangeValue, (last, cur) => cur, 100); let validationValue = options.value || ''; - let validation = TPromise.wrap(validateInput(validationValue)); + let validation = Promise.resolve(validateInput(validationValue)); const disposables = [ input, onDidValueChange(value => { if (value !== validationValue) { - validation = TPromise.wrap(validateInput(value)); + validation = Promise.resolve(validateInput(value)); validationValue = value; } validation.then(result => { @@ -1164,7 +1163,7 @@ export class QuickInputService extends Component implements IQuickInputService { input.onDidAccept(() => { const value = input.value; if (value !== validationValue) { - validation = TPromise.wrap(validateInput(value)); + validation = Promise.resolve(validateInput(value)); validationValue = value; } validation.then(result => { @@ -1335,17 +1334,17 @@ export class QuickInputService extends Component implements IQuickInputService { accept() { this.onDidAcceptEmitter.fire(); - return TPromise.as(undefined); + return Promise.resolve(undefined); } back() { this.onDidTriggerButtonEmitter.fire(this.backButton); - return TPromise.as(undefined); + return Promise.resolve(undefined); } cancel() { this.hide(); - return TPromise.as(undefined); + return Promise.resolve(undefined); } layout(dimension: dom.Dimension): void { @@ -1411,8 +1410,8 @@ export class BackAction extends Action { super(id, label); } - public run(): TPromise { + public run(): Promise { this.quickInputService.back(); - return TPromise.as(null); + return Promise.resolve(null); } } diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts index 21eef9b50..399829c93 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts @@ -116,7 +116,7 @@ class ListElementRenderer implements IListRenderer element.label, openController: { shouldOpen: () => false }, // Workaround #58124 + setRowLineHeight: false, multipleSelectionSupport: false }) as WorkbenchList; this.list.getHTMLElement().id = id; diff --git a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts index 678a224a1..fa22428e5 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/quickopen'; -import { TPromise, ValueCallback } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import * as browser from 'vs/base/browser/browser'; import * as strings from 'vs/base/common/strings'; @@ -54,6 +53,8 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; const HELP_PREFIX = '?'; +type ValueCallback = (value: T | Thenable) => void; + export class QuickOpenController extends Component implements IQuickOpenService { private static readonly MAX_SHORT_RESPONSE_TIME = 500; @@ -73,7 +74,7 @@ export class QuickOpenController extends Component implements IQuickOpenService private lastSubmittedInputValue: string; private quickOpenWidget: QuickOpenWidget; private dimension: Dimension; - private mapResolvedHandlersToPrefix: { [prefix: string]: TPromise; } = Object.create(null); + private mapResolvedHandlersToPrefix: { [prefix: string]: Promise; } = Object.create(null); private mapContextKeyToContext: { [id: string]: IContextKey; } = Object.create(null); private handlerOnOpenCalled: { [prefix: string]: boolean; } = Object.create(null); private promisesToCompleteOnHide: ValueCallback[] = []; @@ -153,12 +154,12 @@ export class QuickOpenController extends Component implements IQuickOpenService } } - show(prefix?: string, options?: IShowOptions): TPromise { + show(prefix?: string, options?: IShowOptions): Promise { let quickNavigateConfiguration = options ? options.quickNavigateConfiguration : void 0; let inputSelection = options ? options.inputSelection : void 0; let autoFocus = options ? options.autoFocus : void 0; - const promiseCompletedOnHide = new TPromise(c => { + const promiseCompletedOnHide = new Promise(c => { this.promisesToCompleteOnHide.push(c); }); @@ -381,7 +382,7 @@ export class QuickOpenController extends Component implements IQuickOpenService return; } - let resultPromise: TPromise; + let resultPromise: Promise; let resultPromiseDone = false; if (handlerDescriptor) { @@ -426,7 +427,7 @@ export class QuickOpenController extends Component implements IQuickOpenService }); } - private handleDefaultHandler(handler: QuickOpenHandlerDescriptor, value: string, token: CancellationToken): TPromise { + private handleDefaultHandler(handler: QuickOpenHandlerDescriptor, value: string, token: CancellationToken): Promise { // Fill in history results if matching and we are configured to search in history let matchingHistoryEntries: QuickOpenEntry[]; @@ -514,7 +515,7 @@ export class QuickOpenController extends Component implements IQuickOpenService } } - private handleSpecificHandler(handlerDescriptor: QuickOpenHandlerDescriptor, value: string, token: CancellationToken): TPromise { + private handleSpecificHandler(handlerDescriptor: QuickOpenHandlerDescriptor, value: string, token: CancellationToken): Promise { return this.resolveHandler(handlerDescriptor).then((resolvedHandler: QuickOpenHandler) => { // Remove handler prefix from search value @@ -585,7 +586,7 @@ export class QuickOpenController extends Component implements IQuickOpenService return mapEntryToPath; } - private resolveHandler(handler: QuickOpenHandlerDescriptor): TPromise { + private resolveHandler(handler: QuickOpenHandlerDescriptor): Promise { let result = this._resolveHandler(handler); const id = handler.getId(); @@ -603,11 +604,11 @@ export class QuickOpenController extends Component implements IQuickOpenService return result.then(null, (error) => { delete this.mapResolvedHandlersToPrefix[id]; - return TPromise.wrapError(new Error(`Unable to instantiate quick open handler ${handler.getId()}: ${JSON.stringify(error)}`)); + return Promise.reject(new Error(`Unable to instantiate quick open handler ${handler.getId()}: ${JSON.stringify(error)}`)); }); } - private _resolveHandler(handler: QuickOpenHandlerDescriptor): TPromise { + private _resolveHandler(handler: QuickOpenHandlerDescriptor): Promise { const id = handler.getId(); // Return Cached @@ -835,7 +836,7 @@ export class RemoveFromEditorHistoryAction extends Action { super(id, label); } - run(): TPromise { + run(): Thenable { interface IHistoryPickEntry extends IQuickPickItem { input: IEditorInput | IResourceInput; } diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index 16afa6abb..160f54c4f 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -29,15 +29,16 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { Dimension, EventType, addDisposableListener, trackFocus } from 'vs/base/browser/dom'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; -const SidebarFocusContextId = 'sidebarFocus'; -export const SidebarFocusContext = new RawContextKey(SidebarFocusContextId, false); +const SideBarFocusContextId = 'sideBarFocus'; +export const SidebarFocusContext = new RawContextKey(SideBarFocusContextId, false); export class SidebarPart extends CompositePart { static readonly activeViewletSettingsKey = 'workbench.sidebar.activeviewletid'; - private sidebarFocusContextKey: IContextKey; + private sideBarFocusContextKey: IContextKey; private blockOpeningViewlet: boolean; constructor( @@ -71,7 +72,7 @@ export class SidebarPart extends CompositePart { { hasTitle: true, borderWidth: () => (this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder)) ? 1 : 0 } ); - this.sidebarFocusContextKey = SidebarFocusContext.bindTo(contextKeyService); + this.sideBarFocusContextKey = SidebarFocusContext.bindTo(contextKeyService); } get onDidViewletOpen(): Event { @@ -88,10 +89,10 @@ export class SidebarPart extends CompositePart { const focusTracker = trackFocus(parent); focusTracker.onDidFocus(() => { - this.sidebarFocusContextKey.set(true); + this.sideBarFocusContextKey.set(true); }); focusTracker.onDidBlur(() => { - this.sidebarFocusContextKey.set(false); + this.sideBarFocusContextKey.set(false); }); } @@ -124,23 +125,22 @@ export class SidebarPart extends CompositePart { container.style.borderLeftColor = !isPositionLeft ? borderColor : null; } - openViewlet(id: string, focus?: boolean): TPromise { + openViewlet(id: string, focus?: boolean): Viewlet { if (this.blockOpeningViewlet) { - return Promise.resolve(null); // Workaround against a potential race condition + return null; // Workaround against a potential race condition } // First check if sidebar is hidden and show if so - let promise = TPromise.wrap(null); if (!this.partService.isVisible(Parts.SIDEBAR_PART)) { try { this.blockOpeningViewlet = true; - promise = this.partService.setSideBarHidden(false); + this.partService.setSideBarHidden(false); } finally { this.blockOpeningViewlet = false; } } - return promise.then(() => this.openComposite(id, focus)) as TPromise; + return this.openComposite(id, focus) as Viewlet; } getActiveViewlet(): IViewlet { @@ -151,8 +151,8 @@ export class SidebarPart extends CompositePart { return this.getLastActiveCompositetId(); } - hideActiveViewlet(): TPromise { - return this.hideActiveComposite().then(composite => void 0); + hideActiveViewlet(): void { + this.hideActiveComposite(); } layout(dimension: Dimension): Dimension[] { @@ -163,6 +163,10 @@ export class SidebarPart extends CompositePart { return super.layout(dimension); } + protected getTitleAreaDropDownAnchorAlignment(): AnchorAlignment { + return this.partService.getSideBarPosition() === SideBarPosition.LEFT ? AnchorAlignment.LEFT : AnchorAlignment.RIGHT; + } + private onTitleAreaContextMenu(event: StandardMouseEvent): void { const activeViewlet = this.getActiveViewlet() as Viewlet; if (activeViewlet) { @@ -171,7 +175,7 @@ export class SidebarPart extends CompositePart { const anchor: { x: number, y: number } = { x: event.posx, y: event.posy }; this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => Promise.resolve(contextMenuActions), + getActions: () => contextMenuActions, getActionItem: action => this.actionItemProvider(action as Action), actionRunner: activeViewlet.getActionRunner() }); @@ -198,7 +202,7 @@ class FocusSideBarAction extends Action { // Show side bar if (!this.partService.isVisible(Parts.SIDEBAR_PART)) { - return this.partService.setSideBarHidden(false); + return Promise.resolve(this.partService.setSideBarHidden(false)); } // Focus into active viewlet diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index eb4c55133..48e617f2c 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -288,7 +288,7 @@ class StatusBarEntryItem implements IStatusbarItem { this.contextMenuService.showContextMenu({ getAnchor: () => el, getActionsContext: () => this.entry.extensionId, - getActions: () => Promise.resolve([manageExtensionAction]) + getActions: () => [manageExtensionAction] }); })); } diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index aae7522e5..f399f2888 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -101,6 +101,7 @@ -webkit-app-region: no-drag; height: 100%; width: 138px; + margin-left: auto; } .monaco-workbench.fullscreen > .part.titlebar > .window-controls-container { @@ -192,7 +193,7 @@ } .monaco-workbench .menubar .menubar-menu-items-holder.monaco-menu-container { - font-family: "Segoe WPC", "Segoe UI", ".SFNSDisplay-Light", "SFUIText-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback"; + font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif; outline: 0; border: none; } diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index 41b6122cb..4f586e543 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -9,13 +9,13 @@ import * as strings from 'vs/base/common/strings'; import { IMenubarMenu, IMenubarMenuItemAction, IMenubarMenuItemSubmenu, IMenubarKeybinding, IMenubarService, IMenubarData } from 'vs/platform/menubar/common/menubar'; import { IMenuService, MenuId, IMenu, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { registerThemingParticipant, ITheme, ICssStyleCollector, IThemeService } from 'vs/platform/theme/common/themeService'; -import { IWindowService, MenuBarVisibility, IWindowsService } from 'vs/platform/windows/common/windows'; +import { IWindowService, MenuBarVisibility, IWindowsService, getTitleBarStyle } from 'vs/platform/windows/common/windows'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ActionRunner, IActionRunner, IAction, Action } from 'vs/base/common/actions'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import * as DOM from 'vs/base/browser/dom'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { isMacintosh } from 'vs/base/common/platform'; +import { isMacintosh, isLinux } from 'vs/base/common/platform'; import { Menu, IMenuOptions, SubmenuAction, MENU_MNEMONIC_REGEX, cleanMnemonic, MENU_ESCAPED_MNEMONIC_REGEX } from 'vs/base/browser/ui/menu/menu'; import { KeyCode, KeyCodeUtils } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -32,6 +32,10 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { IUpdateService, StateType } from 'vs/platform/update/common/update'; import { Gesture, EventType, GestureEvent } from 'vs/base/browser/touch'; import { attachMenuStyler } from 'vs/platform/theme/common/styler'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; const $ = DOM.$; @@ -124,7 +128,11 @@ export class MenubarControl extends Disposable { @IKeybindingService private keybindingService: IKeybindingService, @IConfigurationService private configurationService: IConfigurationService, @ILabelService private labelService: ILabelService, - @IUpdateService private updateService: IUpdateService + @IUpdateService private updateService: IUpdateService, + @IStorageService private storageService: IStorageService, + @INotificationService private notificationService: INotificationService, + @IPreferencesService private preferencesService: IPreferencesService, + @IEnvironmentService private environmentService: IEnvironmentService ) { super(); @@ -167,6 +175,8 @@ export class MenubarControl extends Disposable { this.recentlyOpened = recentlyOpened; }); + this.detectAndRecommendCustomTitlebar(); + this.registerListeners(); } @@ -206,7 +216,7 @@ export class MenubarControl extends Disposable { } private get currentTitlebarStyleSetting(): string { - return this.configurationService.getValue('window.titleBarStyle'); + return getTitleBarStyle(this.configurationService, this.environmentService); } private get focusState(): MenubarState { @@ -348,6 +358,7 @@ export class MenubarControl extends Disposable { if (event.affectsConfiguration('window.menuBarVisibility')) { this.setUnfocusedState(); + this.detectAndRecommendCustomTitlebar(); } } @@ -366,13 +377,17 @@ export class MenubarControl extends Disposable { } private hideMenubar(): void { - this.container.style.display = 'none'; - this._onVisibilityChange.fire(false); + if (this.container.style.display !== 'none') { + this.container.style.display = 'none'; + this._onVisibilityChange.fire(false); + } } private showMenubar(): void { - this.container.style.display = 'flex'; - this._onVisibilityChange.fire(true); + if (this.container.style.display !== 'flex') { + this.container.style.display = 'flex'; + this._onVisibilityChange.fire(true); + } } private onModifierKeyToggled(modifierKeyStatus: IModifierKeyStatus): void { @@ -432,6 +447,41 @@ export class MenubarControl extends Disposable { }); } + private detectAndRecommendCustomTitlebar(): void { + if (!isLinux) { + return; + } + + if (!this.storageService.getBoolean('menubar/electronFixRecommended', StorageScope.GLOBAL, false)) { + if (this.currentMenubarVisibility === 'hidden' || this.currentTitlebarStyleSetting === 'custom') { + // Issue will not arise for user, abort notification + return; + } + + const message = nls.localize('menubar.electronFixRecommendation', "If you experience hard to read text in the menu bar, we recommend trying out the custom title bar."); + this.notificationService.prompt(Severity.Info, message, [ + { + label: nls.localize('goToSetting', "Open Settings"), + run: () => { + return this.preferencesService.openGlobalSettings(undefined, { query: 'window.titleBarStyle' }); + } + }, + { + label: nls.localize('moreInfo', "More Info"), + run: () => { + window.open('https://go.microsoft.com/fwlink/?linkid=2038566'); + } + }, + { + label: nls.localize('neverShowAgain', "Don't Show Again"), + run: () => { + this.storageService.store('menubar/electronFixRecommended', true, StorageScope.GLOBAL); + } + } + ]); + } + } + private registerListeners(): void { // Update when config changes this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e))); @@ -697,6 +747,7 @@ export class MenubarControl extends Disposable { const submenuActions: SubmenuAction[] = []; updateActions(submenu, submenuActions); target.push(new SubmenuAction(action.label, submenuActions)); + submenu.dispose(); } else { action.label = this.calculateActionLabel(action); target.push(action); @@ -838,8 +889,8 @@ export class MenubarControl extends Disposable { } })); - this._register(DOM.addDisposableListener(window, DOM.EventType.KEY_DOWN, (e) => { - if (!this.currentEnableMenuBarMnemonics || !e.altKey || e.ctrlKey) { + this._register(DOM.addDisposableListener(window, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { + if (!this.currentEnableMenuBarMnemonics || !e.altKey || e.ctrlKey || e.defaultPrevented) { return; } @@ -950,7 +1001,8 @@ export class MenubarControl extends Disposable { if (menuItem instanceof SubmenuItemAction) { const submenu = { items: [] }; - this.populateMenuItems(this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService), submenu, keybindings); + const menuToDispose = this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService); + this.populateMenuItems(menuToDispose, submenu, keybindings); let menubarSubmenuItem: IMenubarMenuItemSubmenu = { id: menuItem.id, @@ -959,6 +1011,7 @@ export class MenubarControl extends Disposable { }; menuToPopulate.items.push(menubarSubmenuItem); + menuToDispose.dispose(); } else { let menubarMenuItem: IMenubarMenuItemAction = { id: menuItem.id, diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 6682bffba..4fae98b4b 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -8,7 +8,7 @@ import * as paths from 'vs/base/common/paths'; import { Part } from 'vs/workbench/browser/part'; import { ITitleService, ITitleProperties } from 'vs/workbench/services/title/common/titleService'; import { getZoomFactor } from 'vs/base/browser/browser'; -import { IWindowService, IWindowsService, MenuBarVisibility } from 'vs/platform/windows/common/windows'; +import { IWindowService, IWindowsService, MenuBarVisibility, getTitleBarStyle } from 'vs/platform/windows/common/windows'; import * as errors from 'vs/base/common/errors'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; @@ -26,7 +26,7 @@ import { isMacintosh, isWindows, isLinux } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { Color } from 'vs/base/common/color'; import { trim } from 'vs/base/common/strings'; -import { EventType, EventHelper, Dimension, isAncestor, hide, show, removeClass, addClass, append, $, addDisposableListener, getComputedStyle } from 'vs/base/browser/dom'; +import { EventType, EventHelper, Dimension, isAncestor, hide, show, removeClass, addClass, append, $, addDisposableListener } from 'vs/base/browser/dom'; import { MenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { template, getBaseLabel } from 'vs/base/common/labels'; @@ -57,14 +57,6 @@ export class TitlebarPart extends Part implements ITitleService { private pendingTitle: string; private representedFileName: string; - private initialSizing: { - titleFontSize?: number; - titlebarHeight?: number; - controlsWidth?: number; - appIconSize?: number; - appIconWidth?: number; - } = Object.create(null); - private isInactive: boolean; private properties: ITitleProperties; @@ -395,6 +387,8 @@ export class TitlebarPart extends Part implements ITitleService { }, 0 /* need a timeout because we are in capture phase */); }, true /* use capture to know the currently active element properly */)); + this.updateStyles(); + return this.titleContainer; } @@ -462,7 +456,7 @@ export class TitlebarPart extends Part implements ITitleService { if (actions.length) { this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => Promise.resolve(actions), + getActions: () => actions, onHide: () => actions.forEach(a => a.dispose()) }); } @@ -499,93 +493,45 @@ export class TitlebarPart extends Part implements ITitleService { private adjustTitleMarginToCenter(): void { setTimeout(() => { - // Center the title in the window - const currentAppIconWidth = this.appIcon ? parseInt(getComputedStyle(this.appIcon).width, 10) : 0; - let currentMenubarWidth = parseInt(getComputedStyle(this.menubar).width, 10); - currentMenubarWidth = isNaN(currentMenubarWidth) ? 0 : currentMenubarWidth; - const currentTotalWidth = parseInt(getComputedStyle(document.body).width, 10); - const currentTitleWidth = parseInt(getComputedStyle(this.title).width, 10); - const currentWindowControlsWidth = this.windowControls ? parseInt(getComputedStyle(this.windowControls).width, 10) : 0; - - let leftMargin = (currentTotalWidth / 2) - (currentTitleWidth / 2) - (currentMenubarWidth + currentAppIconWidth); - let rightMargin = currentTotalWidth - (currentAppIconWidth + currentMenubarWidth + leftMargin + currentTitleWidth + currentWindowControlsWidth); - - // Center if we can, leaving some space on both sides - if (leftMargin >= 20 && rightMargin >= 20) { - this.title.style.marginLeft = `${leftMargin}px`; + // Cannot center + if (!isMacintosh && + (this.appIcon.clientWidth + this.menubar.clientWidth + 10 > (this.titleContainer.clientWidth - this.title.clientWidth) / 2 || + this.titleContainer.clientWidth - this.windowControls.clientWidth - 10 < (this.titleContainer.clientWidth + this.title.clientWidth) / 2)) { + this.title.style.position = null; + this.title.style.left = null; + this.title.style.transform = null; } else { - this.title.style.marginLeft = null; + this.title.style.position = 'absolute'; + this.title.style.left = '50%'; + this.title.style.transform = 'translate(-50%, 0)'; } - }, 0); // delay so that we can get accurate information about the widths + }, 0); // delay so that we can get accurate information about widths } - private updateLayout(dimension: Dimension) { - // Store initital title sizing if we need to prevent zooming - if (typeof this.initialSizing.titleFontSize !== 'number') { - this.initialSizing.titleFontSize = parseInt(getComputedStyle(this.title).fontSize, 10); - } - - if (typeof this.initialSizing.titlebarHeight !== 'number') { - this.initialSizing.titlebarHeight = parseInt(getComputedStyle(this.title).height, 10); - } - - // Only prevent zooming behavior on macOS or when the menubar is not visible - if (isMacintosh || this.configurationService.getValue('window.menuBarVisibility') === 'hidden') { - // To prevent zooming we need to adjust the font size with the zoom factor - const newHeight = this.initialSizing.titlebarHeight / getZoomFactor(); - this.title.style.fontSize = `${this.initialSizing.titleFontSize / getZoomFactor()}px`; - this.title.style.lineHeight = `${newHeight}px`; - - // Windows/Linux specific layout - if (isWindows || isLinux) { - if (typeof this.initialSizing.controlsWidth !== 'number') { - this.initialSizing.controlsWidth = parseInt(getComputedStyle(this.windowControls).width, 10); - } - - const appIconComputedStyles = getComputedStyle(this.appIcon); - if (typeof this.initialSizing.appIconWidth !== 'number') { - this.initialSizing.appIconWidth = parseInt(appIconComputedStyles.width, 10); + layout(dimension: Dimension): Dimension[] { + if (getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') { + // Only prevent zooming behavior on macOS or when the menubar is not visible + if (isMacintosh || this.configurationService.getValue('window.menuBarVisibility') === 'hidden') { + this.title.style.zoom = `${1.0 / getZoomFactor()}`; + if (isWindows || isLinux) { + this.appIcon.style.zoom = `${1.0 / getZoomFactor()}`; + this.windowControls.style.zoom = `${1.0 / getZoomFactor()}`; } - - if (typeof this.initialSizing.appIconSize !== 'number') { - this.initialSizing.appIconSize = parseInt(appIconComputedStyles.backgroundSize, 10); + } else { + this.title.style.zoom = null; + if (isWindows || isLinux) { + this.appIcon.style.zoom = null; + this.windowControls.style.zoom = null; } - - const currentAppIconHeight = parseInt(appIconComputedStyles.height, 10); - const newControlsWidth = this.initialSizing.controlsWidth / getZoomFactor(); - const newAppIconWidth = this.initialSizing.appIconWidth / getZoomFactor(); - const newAppIconSize = this.initialSizing.appIconSize / getZoomFactor(); - - // Adjust app icon mimic menubar - this.appIcon.style.width = `${newAppIconWidth}px`; - this.appIcon.style.backgroundSize = `${newAppIconSize}px`; - this.appIcon.style.paddingTop = `${(newHeight - currentAppIconHeight) / 2.0}px`; - this.appIcon.style.paddingBottom = `${(newHeight - currentAppIconHeight) / 2.0}px`; - - // Adjust windows controls - this.windowControls.style.width = `${newControlsWidth}px`; } - } else { - // We need to undo zoom prevention - this.title.style.fontSize = null; - this.title.style.lineHeight = null; - - this.appIcon.style.width = null; - this.appIcon.style.backgroundSize = null; - this.appIcon.style.paddingTop = null; - this.appIcon.style.paddingBottom = null; - this.windowControls.style.width = null; - } + this.adjustTitleMarginToCenter(); - if (this.menubarPart) { - const menubarDimension = new Dimension(undefined, dimension.height); - this.menubarPart.layout(menubarDimension); + if (this.menubarPart) { + const menubarDimension = new Dimension(undefined, dimension.height); + this.menubarPart.layout(menubarDimension); + } } - } - - layout(dimension: Dimension): Dimension[] { - this.updateLayout(dimension); return super.layout(dimension); } diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index 8da31e121..f7cf92bff 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -7,14 +7,13 @@ import 'vs/css!./media/views'; import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { IAction, IActionItem, ActionRunner } from 'vs/base/common/actions'; +import { IAction, IActionItem, ActionRunner, Action } from 'vs/base/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; import { ContextAwareMenuItemActionItem, fillInActionBarActions, fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IViewsService, ITreeViewer, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ICustomViewDescriptor, ViewsRegistry, ViewContainer, ITreeItemLabel } from 'vs/workbench/common/views'; +import { IViewsService, ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ICustomViewDescriptor, ViewsRegistry, ViewContainer, ITreeItemLabel } from 'vs/workbench/common/views'; import { IViewletViewOptions, FileIconThemableWorkbenchTree } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -23,70 +22,78 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ICommandService } from 'vs/platform/commands/common/commands'; import * as DOM from 'vs/base/browser/dom'; -import { isUndefinedOrNull } from 'vs/base/common/types'; import { IDataSource, ITree, IRenderer, ContextMenuEvent } from 'vs/base/parts/tree/browser/tree'; import { ResourceLabel } from 'vs/workbench/browser/labels'; import { ActionBar, IActionItemProvider, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { URI } from 'vs/base/common/uri'; import { basename } from 'vs/base/common/paths'; -import { LIGHT, FileThemeIcon, FolderThemeIcon } from 'vs/platform/theme/common/themeService'; +import { LIGHT, FileThemeIcon, FolderThemeIcon, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { FileKind } from 'vs/platform/files/common/files'; import { WorkbenchTreeController } from 'vs/platform/list/browser/listService'; import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { localize } from 'vs/nls'; import { timeout } from 'vs/base/common/async'; +import { CollapseAllAction } from 'vs/base/parts/tree/browser/treeDefaults'; +import { editorFindMatchHighlight, editorFindMatchHighlightBorder } from 'vs/platform/theme/common/colorRegistry'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { isString } from 'vs/base/common/types'; +import { renderMarkdown, RenderOptions } from 'vs/base/browser/htmlContentRenderer'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IMarkdownRenderResult } from 'vs/editor/contrib/markdown/markdownRenderer'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { dirname } from 'vs/base/common/resources'; export class CustomTreeViewPanel extends ViewletPanel { - private menus: TitleMenus; - private treeViewer: ITreeViewer; + private treeView: ITreeView; constructor( options: IViewletViewOptions, @INotificationService private notificationService: INotificationService, @IKeybindingService keybindingService: IKeybindingService, @IContextMenuService contextMenuService: IContextMenuService, - @IInstantiationService private instantiationService: IInstantiationService, @IConfigurationService configurationService: IConfigurationService, @IViewsService viewsService: IViewsService, ) { super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService); - this.treeViewer = (ViewsRegistry.getView(options.id)).treeViewer; - this.disposables.push(toDisposable(() => this.treeViewer.setVisibility(false))); - this.menus = this.instantiationService.createInstance(TitleMenus, this.id); - this.menus.onDidChangeTitle(() => this.updateActions(), this, this.disposables); + const { treeView } = (ViewsRegistry.getView(options.id)); + this.treeView = treeView; + this.treeView.onDidChangeActions(() => this.updateActions(), this, this.disposables); + this.disposables.push(toDisposable(() => this.treeView.setVisibility(false))); this.updateTreeVisibility(); } - setVisible(visible: boolean): TPromise { - return super.setVisible(visible).then(() => this.updateTreeVisibility()); + setVisible(visible: boolean): void { + super.setVisible(visible); + this.updateTreeVisibility(); } focus(): void { super.focus(); - this.treeViewer.focus(); + this.treeView.focus(); } renderBody(container: HTMLElement): void { - this.treeViewer.show(container); + this.treeView.show(container); } setExpanded(expanded: boolean): void { - this.treeViewer.setVisibility(this.isVisible() && expanded); + this.treeView.setVisibility(this.isVisible() && expanded); super.setExpanded(expanded); } layoutBody(size: number): void { - this.treeViewer.layout(size); + this.treeView.layout(size); } getActions(): IAction[] { - return [...this.menus.getTitleActions()]; + return [...this.treeView.getPrimaryActions()]; } getSecondaryActions(): IAction[] { - return this.menus.getTitleSecondaryActions(); + return [...this.treeView.getSecondaryActions()]; } getActionItem(action: IAction): IActionItem { @@ -94,11 +101,11 @@ export class CustomTreeViewPanel extends ViewletPanel { } getOptimalWidth(): number { - return this.treeViewer.getOptimalWidth(); + return this.treeView.getOptimalWidth(); } private updateTreeVisibility(): void { - this.treeViewer.setVisibility(this.isVisible() && this.isExpanded()); + this.treeView.setVisibility(this.isVisible() && this.isExpanded()); } dispose(): void { @@ -173,21 +180,25 @@ class Root implements ITreeItem { const noDataProviderMessage = localize('no-dataprovider', "There is no data provider registered that can provide view data."); -export class CustomTreeViewer extends Disposable implements ITreeViewer { +export class CustomTreeView extends Disposable implements ITreeView { private isVisible: boolean = false; private activated: boolean = false; private _hasIconForParentNode = false; private _hasIconForLeafNode = false; + private _showCollapseAllAction = false; private domNode: HTMLElement; private treeContainer: HTMLElement; - private message: HTMLDivElement; + private _messageValue: string | IMarkdownString | undefined; + private messageElement: HTMLDivElement; private tree: FileIconThemableWorkbenchTree; private root: ITreeItem; private elementsToRefresh: ITreeItem[] = []; + private menus: TitleMenus; - private _dataProvider: ITreeViewDataProvider; + private markdownRenderer: MarkdownRenderer; + private markdownResult: IMarkdownRenderResult; private _onDidExpandItem: Emitter = this._register(new Emitter()); readonly onDidExpandItem: Event = this._onDidExpandItem.event; @@ -201,6 +212,9 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { private _onDidChangeVisibility: Emitter = this._register(new Emitter()); readonly onDidChangeVisibility: Event = this._onDidChangeVisibility.event; + private _onDidChangeActions: Emitter = this._register(new Emitter()); + readonly onDidChangeActions: Event = this._onDidChangeActions.event; + constructor( private id: string, private container: ViewContainer, @@ -213,6 +227,8 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { ) { super(); this.root = new Root(); + this.menus = this._register(instantiationService.createInstance(TitleMenus, this.id)); + this._register(this.menus.onDidChangeTitle(() => this._onDidChangeActions.fire())); this._register(this.themeService.onDidFileIconThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/)); this._register(this.themeService.onThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/)); this._register(this.configurationService.onDidChangeConfiguration(e => { @@ -220,10 +236,16 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { this.doRefresh([this.root]); /** soft refresh **/ } })); - + this.markdownRenderer = instantiationService.createInstance(MarkdownRenderer); + this._register(toDisposable(() => { + if (this.markdownResult) { + this.markdownResult.dispose(); + } + })); this.create(); } + private _dataProvider: ITreeViewDataProvider; get dataProvider(): ITreeViewDataProvider { return this._dataProvider; } @@ -231,7 +253,7 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { set dataProvider(dataProvider: ITreeViewDataProvider) { if (dataProvider) { this._dataProvider = new class implements ITreeViewDataProvider { - getChildren(node?: ITreeItem): TPromise { + getChildren(node?: ITreeItem): Promise { if (node && node.children) { return Promise.resolve(node.children); } @@ -242,14 +264,24 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { }); } }; - this.hideMessage(); + this.updateMessage(); this.refresh(); } else { this._dataProvider = null; - this.showMessage(noDataProviderMessage); + this.updateMessage(); } } + private _message: string | IMarkdownString | undefined; + get message(): string | IMarkdownString | undefined { + return this._message; + } + + set message(message: string | IMarkdownString | undefined) { + this._message = message; + this.updateMessage(); + } + get hasIconForParentNode(): boolean { return this._hasIconForParentNode; } @@ -262,6 +294,30 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { return this.isVisible; } + get showCollapseAllAction(): boolean { + return this._showCollapseAllAction; + } + + set showCollapseAllAction(showCollapseAllAction: boolean) { + if (this._showCollapseAllAction !== !!showCollapseAllAction) { + this._showCollapseAllAction = !!showCollapseAllAction; + this._onDidChangeActions.fire(); + } + } + + getPrimaryActions(): IAction[] { + if (this.showCollapseAllAction) { + const collapseAllAction = new Action('vs.tree.collapse', localize('collapse', "Collapse"), 'monaco-tree-action collapse-all', true, () => this.tree ? new CollapseAllAction(this.tree, true).run() : Promise.resolve()); + return [...this.menus.getTitleActions(), collapseAllAction]; + } else { + return this.menus.getTitleActions(); + } + } + + getSecondaryActions(): IAction[] { + return this.menus.getTitleSecondaryActions(); + } + setVisibility(isVisible: boolean): void { isVisible = !!isVisible; if (this.isVisible === isVisible) { @@ -316,9 +372,8 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { } private create() { - this.domNode = DOM.$('.tree-explorer-viewlet-tree-view.message'); - this.message = DOM.append(this.domNode, DOM.$('.customview-message')); - this.message.innerText = noDataProviderMessage; + this.domNode = DOM.$('.tree-explorer-viewlet-tree-view'); + this.messageElement = DOM.append(this.domNode, DOM.$('.message')); this.treeContainer = DOM.append(this.domNode, DOM.$('.customview-tree')); } @@ -335,22 +390,57 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { this._register(this.tree.onDidExpandItem(e => this._onDidExpandItem.fire(e.item.getElement()))); this._register(this.tree.onDidCollapseItem(e => this._onDidCollapseItem.fire(e.item.getElement()))); this._register(this.tree.onDidChangeSelection(e => this._onDidChangeSelection.fire(e.selection))); - this.tree.setInput(this.root); } - private showMessage(message: string): void { - DOM.addClass(this.domNode, 'message'); - this.message.innerText = message; + private updateMessage(): void { + if (this._message) { + this.showMessage(this._message); + } else if (!this.dataProvider) { + this.showMessage(noDataProviderMessage); + } else { + this.hideMessage(); + } + } + + private showMessage(message: string | IMarkdownString): void { + DOM.removeClass(this.messageElement, 'hide'); + if (this._messageValue !== message) { + this.resetMessageElement(); + this._messageValue = message; + if (isString(this._messageValue)) { + this.messageElement.innerText = this._messageValue; + } else { + this.markdownResult = this.markdownRenderer.render(this._messageValue); + DOM.append(this.messageElement, this.markdownResult.element); + } + this.layout(this._size); + } } private hideMessage(): void { - DOM.removeClass(this.domNode, 'message'); + this.resetMessageElement(); + DOM.addClass(this.messageElement, 'hide'); + this.layout(this._size); + } + + private resetMessageElement(): void { + if (this.markdownResult) { + this.markdownResult.dispose(); + this.markdownResult = null; + } + DOM.clearNode(this.messageElement); } + private _size: number; layout(size: number) { - this.domNode.style.height = size + 'px'; - if (this.tree) { - this.tree.layout(size); + if (size) { + this._size = size; + this.domNode.style.height = size + 'px'; + const treeSize = size - DOM.getTotalHeight(this.messageElement); + this.treeContainer.style.height = treeSize + 'px'; + if (this.tree) { + this.tree.layout(treeSize); + } } } @@ -363,7 +453,7 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { return 0; } - refresh(elements?: ITreeItem[]): TPromise { + refresh(elements?: ITreeItem[]): Promise { if (this.dataProvider && this.tree) { elements = elements || [this.root]; for (const element of elements) { @@ -378,49 +468,37 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { return Promise.resolve(null); } - reveal(item: ITreeItem, parentChain: ITreeItem[], options?: { select?: boolean, focus?: boolean }): TPromise { - if (this.dataProvider && this.tree && this.isVisible) { - options = options ? options : { select: false, focus: false }; - const select = isUndefinedOrNull(options.select) ? false : options.select; - const focus = isUndefinedOrNull(options.focus) ? false : options.focus; + expand(itemOrItems: ITreeItem | ITreeItem[]): Thenable { + itemOrItems = Array.isArray(itemOrItems) ? itemOrItems : [itemOrItems]; + return this.tree.expandAll(itemOrItems); + } - const root: Root = this.tree.getInput(); - const promise: Thenable = root.children ? Promise.resolve(null) : this.refresh(); // Refresh if root is not populated - return promise.then(() => { - var result = Promise.resolve(null); - parentChain.forEach((e) => { - result = result.then(() => this.tree.expand(e)); - }); - return result.then(() => this.tree.reveal(item)) - .then(() => { - if (select) { - this.tree.setSelection([item], { source: 'api' }); - } - if (focus) { - this.focus(); - this.tree.setFocus(item); - } - }); - }); - } - return Promise.resolve(null); + setSelection(items: ITreeItem[]): void { + this.tree.setSelection(items, { source: 'api' }); + } + + setFocus(item: ITreeItem): void { + this.focus(); + this.tree.setFocus(item); + } + + reveal(item: ITreeItem): Thenable { + return this.tree.reveal(item); } private activate() { - this.hideMessage(); if (!this.activated) { + this.tree.setInput(this.root); this.progressService.withProgress({ location: this.container.id }, () => this.extensionService.activateByEvent(`onView:${this.id}`)) .then(() => timeout(2000)) .then(() => { - if (!this.dataProvider) { - this.showMessage(noDataProviderMessage); - } + this.updateMessage(); }); this.activated = true; } } - private doRefresh(elements: ITreeItem[]): TPromise { + private doRefresh(elements: ITreeItem[]): Promise { if (this.tree) { return Promise.all(elements.map(e => this.tree.refresh(e))).then(() => null); } @@ -449,7 +527,7 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { class TreeDataSource implements IDataSource { constructor( - private treeView: ITreeViewer, + private treeView: ITreeView, private container: ViewContainer, @IProgressService2 private progressService: IProgressService2 ) { @@ -463,7 +541,7 @@ class TreeDataSource implements IDataSource { return this.treeView.dataProvider && node.collapsibleState !== TreeItemCollapsibleState.None; } - getChildren(tree: ITree, node: ITreeItem): TPromise { + getChildren(tree: ITree, node: ITreeItem): Promise { if (this.treeView.dataProvider) { return this.progressService.withProgress({ location: this.container.id }, () => this.treeView.dataProvider.getChildren(node)); } @@ -474,7 +552,7 @@ class TreeDataSource implements IDataSource { return node.collapsibleState === TreeItemCollapsibleState.Expanded; } - getParent(tree: ITree, node: any): TPromise { + getParent(tree: ITree, node: any): Promise { return Promise.resolve(null); } } @@ -486,6 +564,19 @@ interface ITreeExplorerTemplateData { aligner: Aligner; } +// todo@joh,sandy make this proper and contributable from extensions +registerThemingParticipant((theme, collector) => { + + const findMatchHighlightColor = theme.getColor(editorFindMatchHighlight); + if (findMatchHighlightColor) { + collector.addRule(`.file-icon-themable-tree .monaco-tree-row .content .monaco-highlighted-label .highlight { color: unset !important; background-color: ${findMatchHighlightColor}; }`); + } + const findMatchHighlightColorBorder = theme.getColor(editorFindMatchHighlightBorder); + if (findMatchHighlightColorBorder) { + collector.addRule(`.file-icon-themable-tree .monaco-tree-row .content .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${findMatchHighlightColorBorder}; box-sizing: border-box; }`); + } +}); + class TreeRenderer implements IRenderer { private static readonly ITEM_HEIGHT = 22; @@ -498,6 +589,7 @@ class TreeRenderer implements IRenderer { @IInstantiationService private instantiationService: IInstantiationService, @IWorkbenchThemeService private themeService: IWorkbenchThemeService, @IConfigurationService private configurationService: IConfigurationService, + @ILabelService private labelService: ILabelService ) { } @@ -513,7 +605,7 @@ class TreeRenderer implements IRenderer { DOM.addClass(container, 'custom-view-tree-node-item'); const icon = DOM.append(container, DOM.$('.custom-view-tree-node-item-icon')); - const resourceLabel = this.instantiationService.createInstance(ResourceLabel, container, { supportHighlights: true }); + const resourceLabel = this.instantiationService.createInstance(ResourceLabel, container, { supportHighlights: true, donotSupportOcticons: true }); DOM.addClass(resourceLabel.element, 'custom-view-tree-node-item-resourceLabel'); const actionsContainer = DOM.append(resourceLabel.element, DOM.$('.actions')); const actionBar = new ActionBar(actionsContainer, { @@ -527,6 +619,7 @@ class TreeRenderer implements IRenderer { renderElement(tree: ITree, node: ITreeItem, templateId: string, templateData: ITreeExplorerTemplateData): void { const resource = node.resourceUri ? URI.revive(node.resourceUri) : null; const treeItemLabel: ITreeItemLabel = node.label ? node.label : resource ? { label: basename(resource.path) } : void 0; + const description = isString(node.description) ? node.description : resource && node.description === true ? this.labelService.getUriLabel(dirname(resource), { relative: true }) : void 0; const label = treeItemLabel ? treeItemLabel.label : void 0; const matches = treeItemLabel && treeItemLabel.highlights ? treeItemLabel.highlights.map(([start, end]) => ({ start, end })) : void 0; const icon = this.themeService.getTheme().type === LIGHT ? node.icon : node.iconDark; @@ -539,9 +632,9 @@ class TreeRenderer implements IRenderer { if (resource || node.themeIcon) { const fileDecorations = this.configurationService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations'); - templateData.resourceLabel.setLabel({ name: label, resource: resource ? resource : URI.parse('missing:_icon_resource') }, { fileKind: this.getFileKind(node), title, hideIcon: !!iconUrl, fileDecorations, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches }); + templateData.resourceLabel.setLabel({ 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 }); } else { - templateData.resourceLabel.setLabel({ name: label }, { title, hideIcon: true, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches }); + templateData.resourceLabel.setLabel({ name: label, description }, { title, hideIcon: true, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches }); } templateData.icon.style.backgroundImage = iconUrl ? `url('${iconUrl.toString(true)}')` : ''; @@ -656,9 +749,7 @@ class TreeController extends WorkbenchTreeController { this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => { - return Promise.resolve(actions); - }, + getActions: () => actions, getActionItem: (action) => { const keybinding = this._keybindingService.lookupKeybinding(action.id); @@ -689,7 +780,7 @@ class MultipleSelectionActionRunner extends ActionRunner { super(); } - runAction(action: IAction, context: any): TPromise { + runAction(action: IAction, context: any): Thenable { if (action instanceof MenuItemAction) { const selection = this.getSelectedResources(); const filteredSelection = selection.filter(s => s !== context); @@ -741,3 +832,39 @@ class TreeMenus extends Disposable implements IDisposable { return result; } } + +class MarkdownRenderer { + + constructor( + @IOpenerService private readonly _openerService: IOpenerService + ) { + } + + private getOptions(disposeables: IDisposable[]): RenderOptions { + return { + actionHandler: { + callback: (content) => { + let uri: URI | undefined; + try { + uri = URI.parse(content); + } catch { + // ignore + } + if (uri && this._openerService) { + this._openerService.open(uri).catch(onUnexpectedError); + } + }, + disposeables + } + }; + } + + render(markdown: IMarkdownString): IMarkdownRenderResult { + let disposeables: IDisposable[] = []; + const element: HTMLElement = markdown ? renderMarkdown(markdown, this.getOptions(disposeables)) : document.createElement('span'); + return { + element, + dispose: () => dispose(disposeables) + }; + } +} \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/views/media/views.css b/src/vs/workbench/browser/parts/views/media/views.css index 4b388524a..602de4016 100644 --- a/src/vs/workbench/browser/parts/views/media/views.css +++ b/src/vs/workbench/browser/parts/views/media/views.css @@ -50,17 +50,18 @@ display: none; } -.monaco-workbench .tree-explorer-viewlet-tree-view .customview-message { - display: none; - padding: 10px 22px 0 22px; - opacity: 0.5; +.monaco-workbench .tree-explorer-viewlet-tree-view .message { + padding: 4px 0px 0px 18px; + user-select: text } -.monaco-workbench .tree-explorer-viewlet-tree-view.message .customview-message { - display: inherit; +.monaco-workbench .tree-explorer-viewlet-tree-view .message p { + margin-top: 0px; + margin-bottom: 0px; + padding-bottom: 4px; } -.monaco-workbench .tree-explorer-viewlet-tree-view.message .customview-tree { +.monaco-workbench .tree-explorer-viewlet-tree-view .message.hide { display: none; } diff --git a/src/vs/workbench/browser/parts/views/panelViewlet.ts b/src/vs/workbench/browser/parts/views/panelViewlet.ts index afd9c52bf..dca476911 100644 --- a/src/vs/workbench/browser/parts/views/panelViewlet.ts +++ b/src/vs/workbench/browser/parts/views/panelViewlet.ts @@ -5,7 +5,6 @@ import 'vs/css!./media/panelviewlet'; import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Event, Emitter, filterEvent } from 'vs/base/common/event'; import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; import { attachStyler, IColorMapping } from 'vs/platform/theme/common/styler'; @@ -77,12 +76,10 @@ export abstract class ViewletPanel extends Panel implements IView { this.actionRunner = options.actionRunner; } - setVisible(visible: boolean): TPromise { + setVisible(visible: boolean): void { if (this._isVisible !== visible) { this._isVisible = visible; } - - return TPromise.wrap(null); } isVisible(): boolean { @@ -232,7 +229,7 @@ export class PanelViewlet extends Viewlet { let anchor: { x: number, y: number } = { x: event.posx, y: event.posy }; this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => Promise.resolve(this.getContextMenuActions()) + getActions: () => this.getContextMenuActions() }); } diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index 96cdcc5ad..0c660036c 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -5,12 +5,9 @@ import 'vs/css!./media/views'; import { Disposable } from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { IViewsService, ViewsRegistry, IViewsViewlet, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, TEST_VIEW_CONTAINER_ID, IView, IViewDescriptorCollection } from 'vs/workbench/common/views'; +import { IViewsService, ViewsRegistry, IViewsViewlet, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, IView, IViewDescriptorCollection } from 'vs/workbench/common/views'; import { Registry } from 'vs/platform/registry/common/platform'; -import { ViewletRegistry, Extensions as ViewletExtensions } from 'vs/workbench/browser/viewlet'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IContextKeyService, IContextKeyChangeEvent, IReadableSet, IContextKey, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Event, chain, filterEvent, Emitter } from 'vs/base/common/event'; @@ -81,6 +78,10 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl .map(i => i.viewDescriptor); } + get allViewDescriptors(): IViewDescriptor[] { + return this.items.map(i => i.viewDescriptor); + } + constructor( container: ViewContainer, @IContextKeyService private contextKeyService: IContextKeyService @@ -508,8 +509,6 @@ export class PersistentContributableViewsModel extends ContributableViewsModel { } } -const SCM_VIEWLET_ID = 'workbench.view.scm'; - export class ViewsService extends Disposable implements IViewsService { _serviceBrand: any; @@ -518,9 +517,7 @@ export class ViewsService extends Disposable implements IViewsService { private readonly activeViewContextKeys: Map>; constructor( - @ILifecycleService private lifecycleService: ILifecycleService, @IViewletService private viewletService: IViewletService, - @IStorageService private storageService: IStorageService, @IContextKeyService private contextKeyService: IContextKeyService ) { super(); @@ -534,19 +531,18 @@ export class ViewsService extends Disposable implements IViewsService { const viewContainersRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); viewContainersRegistry.all.forEach(viewContainer => this.onDidRegisterViewContainer(viewContainer)); this._register(viewContainersRegistry.onDidRegister(viewContainer => this.onDidRegisterViewContainer(viewContainer))); - this._register(Registry.as(ViewletExtensions.Viewlets).onDidRegister(viewlet => this.viewletService.setViewletEnablement(viewlet.id, this.storageService.getBoolean(`viewservice.${viewlet.id}.enablement`, StorageScope.WORKSPACE, viewlet.id !== TEST_VIEW_CONTAINER_ID)))); } getViewDescriptors(container: ViewContainer): IViewDescriptorCollection { return this.viewDescriptorCollections.get(container); } - openView(id: string, focus: boolean): TPromise { + openView(id: string, focus: boolean): Thenable { const viewDescriptor = ViewsRegistry.getView(id); if (viewDescriptor) { const viewletDescriptor = this.viewletService.getViewlet(viewDescriptor.container.id); if (viewletDescriptor) { - return this.viewletService.openViewlet(viewletDescriptor.id) + return this.viewletService.openViewlet(viewletDescriptor.id, focus) .then((viewlet: IViewsViewlet) => { if (viewlet && viewlet.openView) { return viewlet.openView(id, focus); @@ -559,23 +555,12 @@ export class ViewsService extends Disposable implements IViewsService { } private onDidRegisterViewContainer(viewContainer: ViewContainer): void { - const viewDescriptorCollection = this.registerViewDescriptorCollection(viewContainer); - - // TODO: @Joao Remove this after moving SCM Viewlet to ViewContainerViewlet - https://github.com/Microsoft/vscode/issues/49054 - if (viewContainer.id !== SCM_VIEWLET_ID) { - this._register(viewDescriptorCollection.onDidChangeActiveViews(() => this.updateViewletEnablement(viewContainer, viewDescriptorCollection))); - this.lifecycleService.when(LifecyclePhase.Eventually).then(() => this.updateViewletEnablement(viewContainer, viewDescriptorCollection)); - } - } - - private registerViewDescriptorCollection(viewContainer: ViewContainer): IViewDescriptorCollection { const viewDescriptorCollection = this._register(new ViewDescriptorCollection(viewContainer, this.contextKeyService)); this.onDidChangeActiveViews({ added: viewDescriptorCollection.activeViewDescriptors, removed: [] }); this._register(viewDescriptorCollection.onDidChangeActiveViews(changed => this.onDidChangeActiveViews(changed))); this.viewDescriptorCollections.set(viewContainer, viewDescriptorCollection); - return viewDescriptorCollection; } private onDidChangeActiveViews({ added, removed }: { added: IViewDescriptor[], removed: IViewDescriptor[] }): void { @@ -624,10 +609,4 @@ export class ViewsService extends Disposable implements IViewsService { } return contextKey; } - - private updateViewletEnablement(viewContainer: ViewContainer, viewDescriptorCollection: IViewDescriptorCollection): void { - const enabled = viewDescriptorCollection.activeViewDescriptors.length > 0; - this.viewletService.setViewletEnablement(viewContainer.id, enabled); - this.storageService.store(`viewservice.${viewContainer.id}.enablement`, enabled, StorageScope.WORKSPACE); - } } diff --git a/src/vs/workbench/browser/parts/views/viewsViewlet.ts b/src/vs/workbench/browser/parts/views/viewsViewlet.ts index 0f9fd76dc..551cc7d80 100644 --- a/src/vs/workbench/browser/parts/views/viewsViewlet.ts +++ b/src/vs/workbench/browser/parts/views/viewsViewlet.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import * as DOM from 'vs/base/browser/dom'; import { dispose, IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IAction } from 'vs/base/common/actions'; @@ -42,12 +41,11 @@ export abstract class TreeViewsViewletPanel extends ViewletPanel { } } - setVisible(visible: boolean): TPromise { + setVisible(visible: boolean): void { if (this.isVisible() !== visible) { - return super.setVisible(visible) - .then(() => this.updateTreeVisibility(this.tree, visible && this.isExpanded())); + super.setVisible(visible); + this.updateTreeVisibility(this.tree, visible && this.isExpanded()); } - return TPromise.wrap(null); } focus(): void { @@ -87,7 +85,7 @@ export abstract class TreeViewsViewletPanel extends ViewletPanel { // Make sure the current selected element is revealed const selectedElement = this.tree.getSelection()[0]; if (selectedElement) { - this.tree.reveal(selectedElement, 0.5); + this.tree.reveal(selectedElement); } // Pass Focus to Viewer @@ -189,14 +187,13 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView return result; } - setVisible(visible: boolean): Promise { - return super.setVisible(visible) - .then(() => Promise.all(this.panels.filter(view => view.isVisible() !== visible) - .map((view) => view.setVisible(visible)))) - .then(() => void 0); + setVisible(visible: boolean): void { + super.setVisible(visible); + this.panels.filter(view => view.isVisible() !== visible) + .map((view) => view.setVisible(visible)); } - openView(id: string, focus?: boolean): TPromise { + openView(id: string, focus?: boolean): IView { if (focus) { this.focus(); } @@ -209,7 +206,7 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView if (focus) { view.focus(); } - return Promise.resolve(view); + return view; } movePanel(from: ViewletPanel, to: ViewletPanel): void { @@ -321,7 +318,7 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView let anchor: { x: number, y: number } = { x: event.posx, y: event.posy }; this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => Promise.resolve(actions) + getActions: () => actions }); } diff --git a/src/vs/workbench/browser/quickopen.ts b/src/vs/workbench/browser/quickopen.ts index da2678228..72f74e980 100644 --- a/src/vs/workbench/browser/quickopen.ts +++ b/src/vs/workbench/browser/quickopen.ts @@ -326,7 +326,7 @@ export class QuickOpenAction extends Action { this.enabled = !!this.quickOpenService; } - run(context?: any): TPromise { + run(context?: any): Promise { // Show with prefix this.quickOpenService.show(this.prefix); diff --git a/src/vs/workbench/common/editor/textEditorModel.ts b/src/vs/workbench/common/editor/textEditorModel.ts index 6ae29b06e..db7432b7f 100644 --- a/src/vs/workbench/common/editor/textEditorModel.ts +++ b/src/vs/workbench/common/editor/textEditorModel.ts @@ -5,11 +5,10 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { ITextModel, ITextBufferFactory } from 'vs/editor/common/model'; -import { IMode } from 'vs/editor/common/modes'; import { EditorModel } from 'vs/workbench/common/editor'; import { URI } from 'vs/base/common/uri'; import { ITextEditorModel } from 'vs/editor/common/services/resolverService'; -import { IModeService } from 'vs/editor/common/services/modeService'; +import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IDisposable } from 'vs/base/common/lifecycle'; import { ITextSnapshot } from 'vs/platform/files/common/files'; @@ -72,22 +71,22 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd */ protected createTextEditorModel(value: ITextBufferFactory, resource?: URI, modeId?: string): TPromise { const firstLineText = this.getFirstLineText(value); - const mode = this.getOrCreateMode(this.modeService, modeId, firstLineText); + const languageSelection = this.getOrCreateMode(this.modeService, modeId, firstLineText); - return TPromise.as(this.doCreateTextEditorModel(value, mode, resource)); + return TPromise.as(this.doCreateTextEditorModel(value, languageSelection, resource)); } - private doCreateTextEditorModel(value: ITextBufferFactory, mode: Promise, resource: URI): EditorModel { + private doCreateTextEditorModel(value: ITextBufferFactory, languageSelection: ILanguageSelection, resource: URI): EditorModel { let model = resource && this.modelService.getModel(resource); if (!model) { - model = this.modelService.createModel(value, mode, resource); + model = this.modelService.createModel(value, languageSelection, resource); this.createdEditorModel = true; // Make sure we clean up when this model gets disposed this.registerModelDisposeListener(model); } else { this.modelService.updateModel(model, value); - this.modelService.setMode(model, mode); + this.modelService.setMode(model, languageSelection); } this.textEditorModelHandle = model.uri; @@ -113,8 +112,8 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd * * @param firstLineText optional first line of the text buffer to set the mode on. This can be used to guess a mode from content. */ - protected getOrCreateMode(modeService: IModeService, modeId: string, firstLineText?: string): Promise { - return modeService.getOrCreateMode(modeId); + protected getOrCreateMode(modeService: IModeService, modeId: string, firstLineText?: string): ILanguageSelection { + return modeService.create(modeId); } /** diff --git a/src/vs/workbench/common/editor/untitledEditorModel.ts b/src/vs/workbench/common/editor/untitledEditorModel.ts index 936c47329..91c66b68e 100644 --- a/src/vs/workbench/common/editor/untitledEditorModel.ts +++ b/src/vs/workbench/common/editor/untitledEditorModel.ts @@ -9,9 +9,8 @@ import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel' import { URI } from 'vs/base/common/uri'; import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { CONTENT_CHANGE_EVENT_BUFFER_DELAY } from 'vs/platform/files/common/files'; -import { IModeService } from 'vs/editor/common/services/modeService'; +import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { IMode } from 'vs/editor/common/modes'; import { Event, Emitter } from 'vs/base/common/event'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; @@ -58,9 +57,9 @@ export class UntitledEditorModel extends BaseTextEditorModel implements IEncodin this.registerListeners(); } - protected getOrCreateMode(modeService: IModeService, modeId: string, firstLineText?: string): Promise { + protected getOrCreateMode(modeService: IModeService, modeId: string, firstLineText?: string): ILanguageSelection { if (!modeId || modeId === PLAINTEXT_MODE_ID) { - return modeService.getOrCreateModeByFilepathOrFirstLine(this.resource.fsPath, firstLineText); // lookup mode via resource path if the provided modeId is unspecific + return modeService.createByFilepathOrFirstLine(this.resource.fsPath, firstLineText); // lookup mode via resource path if the provided modeId is unspecific } return super.getOrCreateMode(modeService, modeId, firstLineText); diff --git a/src/vs/workbench/common/notifications.ts b/src/vs/workbench/common/notifications.ts index 04e7e4252..27d472b8a 100644 --- a/src/vs/workbench/common/notifications.ts +++ b/src/vs/workbench/common/notifications.ts @@ -203,7 +203,7 @@ export interface INotificationViewItem { close(): void; - equals(item: INotificationViewItem); + equals(item: INotificationViewItem): boolean; } export function isNotificationViewItem(obj: any): obj is INotificationViewItem { diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index ab8d9857d..fd08fbce9 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { Command } from 'vs/editor/common/modes'; import { UriComponents } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; @@ -17,6 +16,8 @@ import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { values } from 'vs/base/common/map'; import { Registry } from 'vs/platform/registry/common/platform'; import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IAction } from 'vs/base/common/actions'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; export const TEST_VIEW_CONTAINER_ID = 'workbench.view.extension.test'; @@ -119,6 +120,7 @@ export interface IViewDescriptor { export interface IViewDescriptorCollection { readonly onDidChangeActiveViews: Event<{ added: IViewDescriptor[], removed: IViewDescriptor[] }>; readonly activeViewDescriptors: IViewDescriptor[]; + readonly allViewDescriptors: IViewDescriptor[]; } export interface IViewsRegistry { @@ -218,7 +220,7 @@ export interface IView { export interface IViewsViewlet extends IViewlet { - openView(id: string, focus?: boolean): TPromise; + openView(id: string, focus?: boolean): IView; } @@ -227,17 +229,23 @@ export const IViewsService = createDecorator('viewsService'); export interface IViewsService { _serviceBrand: any; - openView(id: string, focus?: boolean): TPromise; + openView(id: string, focus?: boolean): Thenable; getViewDescriptors(container: ViewContainer): IViewDescriptorCollection; } // Custom views -export interface ITreeViewer extends IDisposable { +export interface ITreeView extends IDisposable { dataProvider: ITreeViewDataProvider; + showCollapseAllAction: boolean; + + message: string | IMarkdownString; + + readonly visible: boolean; + readonly onDidExpandItem: Event; readonly onDidCollapseItem: Event; @@ -246,9 +254,9 @@ export interface ITreeViewer extends IDisposable { readonly onDidChangeVisibility: Event; - readonly visible: boolean; + readonly onDidChangeActions: Event; - refresh(treeItems?: ITreeItem[]): TPromise; + refresh(treeItems?: ITreeItem[]): Promise; setVisibility(visible: boolean): void; @@ -260,12 +268,32 @@ export interface ITreeViewer extends IDisposable { getOptimalWidth(): number; - reveal(item: ITreeItem, parentChain: ITreeItem[], options: { select?: boolean }): TPromise; + reveal(item: ITreeItem): Thenable; + + expand(itemOrItems: ITreeItem | ITreeItem[]): Thenable; + + setSelection(items: ITreeItem[]): void; + + setFocus(item: ITreeItem): void; + + getPrimaryActions(): IAction[]; + + getSecondaryActions(): IAction[]; +} + +export interface IRevealOptions { + + select?: boolean; + + focus?: boolean; + + expand?: boolean | number; + } export interface ICustomViewDescriptor extends IViewDescriptor { - readonly treeViewer: ITreeViewer; + readonly treeView: ITreeView; } @@ -298,6 +326,8 @@ export interface ITreeItem { label?: ITreeItemLabel; + description?: string | boolean; + icon?: UriComponents; iconDark?: UriComponents; @@ -317,6 +347,6 @@ export interface ITreeItem { export interface ITreeViewDataProvider { - getChildren(element?: ITreeItem): TPromise; + getChildren(element?: ITreeItem): Promise; -} \ No newline at end of file +} diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index 4a99d73fc..41c99e534 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -735,7 +735,7 @@ export abstract class BaseNavigationAction extends Action { } if (isSidebarFocus) { - return this.navigateOnSidebarFocus(isSidebarPositionLeft, isPanelPositionDown); + return Promise.resolve(this.navigateOnSidebarFocus(isSidebarPositionLeft, isPanelPositionDown)); } return Promise.resolve(false); @@ -749,13 +749,13 @@ export abstract class BaseNavigationAction extends Action { return Promise.resolve(true); } - protected navigateOnSidebarFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Thenable { - return Promise.resolve(true); + protected navigateOnSidebarFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): boolean | IViewlet { + return true; } - protected navigateToPanel(): Thenable { + protected navigateToPanel(): IPanel | boolean { if (!this.partService.isVisible(Parts.PANEL_PART)) { - return Promise.resolve(false); + return false; } const activePanelId = this.panelService.getActivePanel().getId(); @@ -773,23 +773,23 @@ export abstract class BaseNavigationAction extends Action { return this.viewletService.openViewlet(activeViewletId, true); } - protected navigateAcrossEditorGroup(direction: GroupDirection): Promise { + protected navigateAcrossEditorGroup(direction: GroupDirection): boolean { return this.doNavigateToEditorGroup({ direction }); } - protected navigateToEditorGroup(location: GroupLocation): Promise { + protected navigateToEditorGroup(location: GroupLocation): boolean { return this.doNavigateToEditorGroup({ location }); } - private doNavigateToEditorGroup(scope: IFindGroupScope): Promise { + private doNavigateToEditorGroup(scope: IFindGroupScope): boolean { const targetGroup = this.editorGroupService.findGroup(scope, this.editorGroupService.activeGroup); if (targetGroup) { targetGroup.focus(); - return Promise.resolve(true); + return true; } - return Promise.resolve(false); + return false; } } @@ -809,19 +809,17 @@ export class NavigateLeftAction extends BaseNavigationAction { super(id, label, editorGroupService, panelService, partService, viewletService); } - protected navigateOnEditorFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { - return this.navigateAcrossEditorGroup(GroupDirection.LEFT) - .then(didNavigate => { - if (didNavigate) { - return Promise.resolve(true); - } + protected navigateOnEditorFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Thenable { + const didNavigate = this.navigateAcrossEditorGroup(GroupDirection.LEFT); + if (didNavigate) { + return Promise.resolve(true); + } - if (isSidebarPositionLeft) { - return this.navigateToSidebar(); - } + if (isSidebarPositionLeft) { + return this.navigateToSidebar(); + } - return Promise.resolve(false); - }); + return Promise.resolve(false); } protected navigateOnPanelFocus(isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Thenable { @@ -830,18 +828,18 @@ export class NavigateLeftAction extends BaseNavigationAction { } if (!isPanelPositionDown) { - return this.navigateToEditorGroup(GroupLocation.LAST); + return Promise.resolve(this.navigateToEditorGroup(GroupLocation.LAST)); } return Promise.resolve(false); } - protected navigateOnSidebarFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { + protected navigateOnSidebarFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): boolean { if (!isSidebarPositionLeft) { return this.navigateToEditorGroup(GroupLocation.LAST); } - return Promise.resolve(false); + return false; } } @@ -861,23 +859,21 @@ export class NavigateRightAction extends BaseNavigationAction { super(id, label, editorGroupService, panelService, partService, viewletService); } - protected navigateOnEditorFocus(isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Promise { - return this.navigateAcrossEditorGroup(GroupDirection.RIGHT) - .then(didNavigate => { - if (didNavigate) { - return Promise.resolve(true); - } + protected navigateOnEditorFocus(isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Thenable { + const didNavigate = this.navigateAcrossEditorGroup(GroupDirection.RIGHT); + if (didNavigate) { + return Promise.resolve(true); + } - if (!isPanelPositionDown) { - return this.navigateToPanel(); - } + if (!isPanelPositionDown) { + return Promise.resolve(this.navigateToPanel()); + } - if (!isSidebarPositionLeft) { - return this.navigateToSidebar(); - } + if (!isSidebarPositionLeft) { + return this.navigateToSidebar(); + } - return Promise.resolve(false); - }); + return Promise.resolve(false); } protected navigateOnPanelFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Thenable { @@ -888,12 +884,12 @@ export class NavigateRightAction extends BaseNavigationAction { return Promise.resolve(false); } - protected navigateOnSidebarFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { + protected navigateOnSidebarFocus(isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): boolean { if (isSidebarPositionLeft) { return this.navigateToEditorGroup(GroupLocation.FIRST); } - return Promise.resolve(false); + return false; } } @@ -914,12 +910,12 @@ export class NavigateUpAction extends BaseNavigationAction { } protected navigateOnEditorFocus(_isSidebarPositionLeft: boolean, _isPanelPositionDown: boolean): Promise { - return this.navigateAcrossEditorGroup(GroupDirection.UP); + return Promise.resolve(this.navigateAcrossEditorGroup(GroupDirection.UP)); } protected navigateOnPanelFocus(_isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Promise { if (isPanelPositionDown) { - return this.navigateToEditorGroup(GroupLocation.LAST); + return Promise.resolve(this.navigateToEditorGroup(GroupLocation.LAST)); } return Promise.resolve(false); @@ -943,18 +939,16 @@ export class NavigateDownAction extends BaseNavigationAction { } protected navigateOnEditorFocus(_isSidebarPositionLeft: boolean, isPanelPositionDown: boolean): Promise { - return this.navigateAcrossEditorGroup(GroupDirection.DOWN) - .then(didNavigate => { - if (didNavigate) { - return Promise.resolve(true); - } + const didNavigate = this.navigateAcrossEditorGroup(GroupDirection.DOWN); + if (didNavigate) { + return Promise.resolve(true); + } - if (isPanelPositionDown) { - return this.navigateToPanel(); - } + if (isPanelPositionDown) { + return Promise.resolve(this.navigateToPanel()); + } - return Promise.resolve(false); - }); + return Promise.resolve(false); } } @@ -1142,7 +1136,7 @@ export class ToggleWindowTabsBar extends Action { export class OpenTwitterUrlAction extends Action { static readonly ID = 'workbench.action.openTwitterUrl'; - static LABEL = nls.localize('openTwitterUrl', "Join us on Twitter", product.applicationName); + static LABEL = nls.localize('openTwitterUrl', "Join Us on Twitter", product.applicationName); constructor( id: string, diff --git a/src/vs/workbench/electron-browser/commands.ts b/src/vs/workbench/electron-browser/commands.ts index aa0364cd9..880dbe5f5 100644 --- a/src/vs/workbench/electron-browser/commands.ts +++ b/src/vs/workbench/electron-browser/commands.ts @@ -61,7 +61,9 @@ export function registerCommands(): void { else if (focused instanceof ObjectTree) { const list = focused; - list.focusNext(count); + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + list.focusNext(count, false, fakeKeyboardEvent); + const listFocus = list.getFocus(); if (listFocus.length) { list.reveal(listFocus[0]); @@ -110,10 +112,12 @@ export function registerCommands(): void { const focus = list.getFocus() ? list.getFocus()[0] : void 0; const selection = list.getSelection(); + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + if (selection && selection.indexOf(focus) >= 0) { - list.setSelection(selection.filter(s => s !== previousFocus)); + list.setSelection(selection.filter(s => s !== previousFocus), fakeKeyboardEvent); } else { - list.setSelection(selection.concat(focus)); + list.setSelection(selection.concat(focus), fakeKeyboardEvent); } } @@ -140,19 +144,7 @@ export function registerCommands(): void { const focused = accessor.get(IListService).lastFocusedList; // List - if (focused instanceof List || focused instanceof PagedList) { - const list = focused; - - // Focus down first - const previousFocus = list.getFocus() ? list.getFocus()[0] : void 0; - focusDown(accessor, arg2); - - // Then adjust selection - expandMultiSelection(focused, previousFocus); - } - - // ObjectTree - else if (focused instanceof ObjectTree) { + if (focused instanceof List || focused instanceof PagedList || focused instanceof ObjectTree) { const list = focused; // Focus down first @@ -199,7 +191,9 @@ export function registerCommands(): void { else if (focused instanceof ObjectTree) { const list = focused; - list.focusPrevious(count); + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + list.focusPrevious(count, false, fakeKeyboardEvent); + const listFocus = list.getFocus(); if (listFocus.length) { list.reveal(listFocus[0]); @@ -289,7 +283,8 @@ export function registerCommands(): void { const parent = tree.getParentElement(focus); if (parent) { - tree.setFocus([parent]); + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + tree.setFocus([parent], fakeKeyboardEvent); tree.reveal(parent); } } @@ -335,7 +330,8 @@ export function registerCommands(): void { const child = tree.getFirstElementChild(focus); if (child) { - tree.setFocus([child]); + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + tree.setFocus([child], fakeKeyboardEvent); tree.reveal(child); } } @@ -380,7 +376,8 @@ export function registerCommands(): void { else if (focused instanceof ObjectTree) { const list = focused; - list.focusPreviousPage(); + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + list.focusPreviousPage(fakeKeyboardEvent); list.reveal(list.getFocus()[0]); } @@ -417,7 +414,8 @@ export function registerCommands(): void { else if (focused instanceof ObjectTree) { const list = focused; - list.focusNextPage(); + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + list.focusNextPage(fakeKeyboardEvent); list.reveal(list.getFocus()[0]); } @@ -470,7 +468,8 @@ export function registerCommands(): void { return; } - list.setFocus([first]); + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + list.setFocus([first], fakeKeyboardEvent); list.reveal(first); } @@ -522,7 +521,8 @@ export function registerCommands(): void { return; } - list.setFocus([last]); + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + list.setFocus([last], fakeKeyboardEvent); list.reveal(last); } @@ -557,7 +557,8 @@ export function registerCommands(): void { // ObjectTree else if (focused instanceof ObjectTree) { const list = focused; - list.setSelection(list.getFocus()); + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + list.setSelection(list.getFocus(), fakeKeyboardEvent); list.open(list.getFocus()); } @@ -642,11 +643,12 @@ export function registerCommands(): void { // ObjectTree else if (focused instanceof ObjectTree) { const list = focused; + const fakeKeyboardEvent = new KeyboardEvent('keydown'); if (list.getSelection().length > 0) { - list.setSelection([]); + list.setSelection([], fakeKeyboardEvent); } else if (list.getFocus().length > 0) { - list.setFocus([]); + list.setFocus([], fakeKeyboardEvent); } } diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 6e144e570..727780a67 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -66,7 +66,7 @@ if (OpenTipsAndTricksUrlAction.AVAILABLE) { workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenTipsAndTricksUrlAction, OpenTipsAndTricksUrlAction.ID, OpenTipsAndTricksUrlAction.LABEL), 'Help: Tips and Tricks', helpCategory); } -workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenTwitterUrlAction, OpenTwitterUrlAction.ID, OpenTwitterUrlAction.LABEL), 'Help: Join us on Twitter', helpCategory); +workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenTwitterUrlAction, OpenTwitterUrlAction.ID, OpenTwitterUrlAction.LABEL), 'Help: Join Us on Twitter', helpCategory); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenRequestFeatureUrlAction, OpenRequestFeatureUrlAction.ID, OpenRequestFeatureUrlAction.LABEL), 'Help: Search Feature Requests', helpCategory); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenLicenseUrlAction, OpenLicenseUrlAction.ID, OpenLicenseUrlAction.LABEL), 'Help: View License', helpCategory); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenPrivacyStatementUrlAction, OpenPrivacyStatementUrlAction.ID, OpenPrivacyStatementUrlAction.LABEL), 'Help: Privacy Statement', helpCategory); @@ -307,6 +307,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { id: ToggleMenuBarAction.ID, title: nls.localize({ key: 'miToggleMenuBar', comment: ['&& denotes a mnemonic'] }, "Toggle Menu &&Bar") }, + when: IsMacContext.toNegated(), order: 4 }); @@ -392,7 +393,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { group: '3_feedback', command: { id: 'workbench.action.openTwitterUrl', - title: nls.localize({ key: 'miTwitter', comment: ['&& denotes a mnemonic'] }, "&&Join us on Twitter") + title: nls.localize({ key: 'miTwitter', comment: ['&& denotes a mnemonic'] }, "&&Join Us on Twitter") }, order: 1 }); @@ -677,6 +678,12 @@ configurationRegistry.registerConfiguration({ 'description': nls.localize('workbench.enableExperiments', "Fetches experiments to run from a Microsoft online service."), 'default': true, 'tags': ['usesOnlineServices'] + }, + //TODO@Ben remove ('enableLegacyStorage') after a while + 'workbench.enableLegacyStorage': { + 'type': 'boolean', + 'description': nls.localize('workbench.enableLegacyStorage', "Switches back to the previous storage implementation. Only change this setting if advised to do so."), + 'default': false } } }); @@ -807,7 +814,7 @@ configurationRegistry.registerConfiguration({ 'window.titleBarStyle': { 'type': 'string', 'enum': ['native', 'custom'], - 'default': isLinux ? 'native' : 'custom', + 'default': 'custom', 'scope': ConfigurationScope.APPLICATION, 'description': nls.localize('titleBarStyle', "Adjust the appearance of the window title bar. Changes require a full restart to apply.") }, @@ -818,6 +825,19 @@ configurationRegistry.registerConfiguration({ '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) }, + 'window.nativeFullScreen': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('window.nativeFullScreen', "Controls if native full-screen should be used on macOS. Disable this option to prevent macOS from creating a new space when going full-screen."), + 'included': isMacintosh + }, + 'window.smoothScrollingWorkaround': { // TODO@Ben remove once https://github.com/Microsoft/vscode/issues/61824 settles + 'type': 'boolean', + 'default': false, + 'scope': ConfigurationScope.APPLICATION, + 'markdownDescription': nls.localize('window.smoothScrollingWorkaround', "Enable this workaround if scrolling is no longer smooth after restoring a minimized VS Code window. This is a workaround for an issue (https://github.com/Microsoft/vscode/issues/13612) where scrolling starts to lag on devices with precision trackpads like the Surface devices from Microsoft. Enabling this workaround can result in a little bit of layout flickering after restoring the window from minimized state but is otherwise harmless."), + 'included': isWindows + }, 'window.clickThroughInactive': { 'type': 'boolean', 'default': true, diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index be41a2eee..afd936e86 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -46,10 +46,11 @@ import { Schemas } from 'vs/base/common/network'; import { sanitizeFilePath, mkdirp } from 'vs/base/node/extfs'; import { basename, join } from 'path'; import { createHash } from 'crypto'; -import { parseStorage, StorageObject } from 'vs/platform/storage/common/storageLegacyMigration'; +import { StorageObject, parseFolderStorage, parseMultiRootStorage, parseNoWorkspaceStorage, parseEmptyStorage } from 'vs/platform/storage/common/storageLegacyMigration'; import { StorageScope } from 'vs/platform/storage/common/storage'; import { endsWith } from 'vs/base/common/strings'; import { IdleValue } from 'vs/base/common/async'; +import { setGlobalLeakWarningThreshold } from 'vs/base/common/event'; gracefulFs.gracefulify(fs); // enable gracefulFs @@ -61,6 +62,9 @@ export function startup(configuration: IWindowConfiguration): Promise { // Setup perf perf.importEntries(configuration.perfEntries); + // Configure emitter leak warning threshold + setGlobalLeakWarningThreshold(500); + // Browser config browser.setZoomFactor(webFrame.getZoomFactor()); // Ensure others can listen to zoom level changes browser.setZoomLevel(webFrame.getZoomLevel(), true /* isTrusted */); // Can be trusted because we are not setting it ourselves (https://github.com/Microsoft/vscode/issues/26151) @@ -112,52 +116,49 @@ function openWorkbench(configuration: IWindowConfiguration): Promise { // Resolve a workspace payload that we can get the workspace ID from return createWorkspaceInitializationPayload(configuration, environmentService).then(payload => { - // Prepare the workspace storage folder - return prepareWorkspaceStorageFolder(payload, environmentService).then(workspaceStoragePath => { - return Promise.all([ - - // Create and initialize workspace/configuration service - createWorkspaceService(payload, environmentService, logService), - - // Create and initialize storage service - createStorageService(workspaceStoragePath, payload, environmentService, logService) - ]).then(services => { - const workspaceService = services[0]; - const storageService = new DelegatingStorageService(services[1], createStorageLegacyService(workspaceService, environmentService), logService); - - return domContentLoaded().then(() => { - perf.mark('willStartWorkbench'); - - // Create Shell - const shell = new WorkbenchShell(document.body, { - contextService: workspaceService, - configurationService: workspaceService, - environmentService, - logService, - storageService - }, mainServices, mainProcessClient, configuration); - - // Store meta file in workspace storage after workbench is running - shell.onRunning(() => { - ensureWorkspaceStorageFolderMeta(workspaceStoragePath, workspaceService); - }); - - // Gracefully Shutdown Storage - shell.onShutdown(event => { - event.join(storageService.close()); - }); - - // Open Shell - shell.open(); - - // Inform user about loading issues from the loader - (self).require.config({ - onError: err => { - if (err.errorCode === 'load') { - shell.onUnexpectedError(new Error(nls.localize('loaderErrorNative', "Failed to load a required file. Please restart the application to try again. Details: {0}", JSON.stringify(err)))); - } + return Promise.all([ + + // Create and initialize workspace/configuration service + createWorkspaceService(payload, environmentService, logService), + + // Create and initialize storage service + createStorageService(payload, environmentService, logService) + ]).then(services => { + const workspaceService = services[0]; + const storageService = new DelegatingStorageService(services[1], createStorageLegacyService(workspaceService, environmentService), logService, workspaceService); + + return domContentLoaded().then(() => { + perf.mark('willStartWorkbench'); + + // Create Shell + const shell = new WorkbenchShell(document.body, { + contextService: workspaceService, + configurationService: workspaceService, + environmentService, + logService, + storageService + }, mainServices, mainProcessClient, configuration); + + // Store meta file in workspace storage after workbench is running + shell.onRunning(() => { + ensureWorkspaceStorageFolderMeta(payload, workspaceService, environmentService); + }); + + // Gracefully Shutdown Storage + shell.onShutdown(event => { + event.join(storageService.close()); + }); + + // Open Shell + shell.open(); + + // Inform user about loading issues from the loader + (self).require.config({ + onError: err => { + if (err.errorCode === 'load') { + shell.onUnexpectedError(new Error(nls.localize('loaderErrorNative', "Failed to load a required file. Please restart the application to try again. Details: {0}", JSON.stringify(err)))); } - }); + } }); }); }); @@ -234,40 +235,6 @@ function resolveSingleFolderWorkspaceInitializationPayload(folderUri: ISingleFol }, error => onUnexpectedError(error)); } -function prepareWorkspaceStorageFolder(payload: IWorkspaceInitializationPayload, environmentService: IEnvironmentService): Thenable { - const workspaceStoragePath = join(environmentService.workspaceStorageHome, payload.id); // workspace home + workspace id - - return exists(workspaceStoragePath).then(exists => { - if (exists) { - return workspaceStoragePath; - } - - return mkdirp(workspaceStoragePath).then(() => workspaceStoragePath); - }); -} - -function ensureWorkspaceStorageFolderMeta(workspaceStoragePath: string, workspaceService: IWorkspaceContextService): void { - const state = workspaceService.getWorkbenchState(); - if (state === WorkbenchState.EMPTY) { - return; // no storage meta for empty workspaces - } - - const workspaceStorageMetaPath = join(workspaceStoragePath, 'workspace.json'); - - exists(workspaceStorageMetaPath).then(exists => { - if (exists) { - return void 0; // already existing - } - - const workspace = workspaceService.getWorkspace(); - - return writeFile(workspaceStorageMetaPath, JSON.stringify({ - configuration: workspace.configuration ? uri.revive(workspace.configuration).toString() : void 0, - folder: state === WorkbenchState.FOLDER ? uri.revive(workspace.folders[0].uri).toString() : void 0 - }, undefined, 2)); - }).then(null, error => onUnexpectedError(error)); -} - function createWorkspaceService(payload: IWorkspaceInitializationPayload, environmentService: IEnvironmentService, logService: ILogService): Promise { const workspaceService = new WorkspaceService(environmentService); @@ -279,150 +246,197 @@ function createWorkspaceService(payload: IWorkspaceInitializationPayload, enviro }); } -function createStorageService(workspaceStorageFolder: string, payload: IWorkspaceInitializationPayload, environmentService: IEnvironmentService, logService: ILogService): Thenable { +function createStorageService(payload: IWorkspaceInitializationPayload, environmentService: IEnvironmentService, logService: ILogService): Thenable { - // Return early if we are using in-memory storage - const useInMemoryStorage = !!environmentService.extensionTestsPath; /* never keep any state when running extension tests */ - if (useInMemoryStorage) { - const storageService = new StorageService(StorageService.IN_MEMORY_PATH, logService, environmentService); + // Prepare the workspace storage folder + return prepareWorkspaceStorageFolder(payload, environmentService).then(result => { - return storageService.init().then(() => storageService); - } + // Return early if we are using in-memory storage + const useInMemoryStorage = !!environmentService.extensionTestsPath; /* never keep any state when running extension tests */ + if (useInMemoryStorage) { + const storageService = new StorageService(StorageService.IN_MEMORY_PATH, true, logService); - // Otherwise do a migration of previous workspace data if the DB does not exist yet - // TODO@Ben remove me after one milestone - const workspaceStorageDBPath = join(workspaceStorageFolder, 'storage.db'); - return exists(workspaceStorageDBPath).then(exists => { - const storageService = new StorageService(workspaceStorageDBPath, logService, environmentService); + return storageService.init().then(() => storageService); + } - return storageService.init().then(() => { - if (exists) { - return storageService; // return early if DB was already there - } + // Otherwise do a migration of previous workspace data if the DB does not exist yet + // TODO@Ben remove me after some milestones + const workspaceStorageDBPath = join(result.path, 'storage.db'); + perf.mark('willCheckWorkspaceStorageExists'); + return exists(workspaceStorageDBPath).then(exists => { + perf.mark('didCheckWorkspaceStorageExists'); - return readdir(environmentService.extensionsPath).then(extensions => { + const storageService = new StorageService(workspaceStorageDBPath, true, logService); - // Otherwise, we migrate data from window.localStorage over - try { - const parsedStorage = parseStorage(window.localStorage); + return storageService.init().then(() => { + if (exists) { + return storageService; // return early if DB was already there + } - let workspaceItems: StorageObject; - if (isWorkspaceIdentifier(payload)) { - workspaceItems = parsedStorage.multiRoot.get(`root:${payload.id}`); - } else if (isSingleFolderWorkspaceInitializationPayload(payload)) { - workspaceItems = parsedStorage.folder.get(payload.folder.toString()); - } else { - if (payload.id === 'ext-dev') { - workspaceItems = parsedStorage.noWorkspace; - } else { - workspaceItems = parsedStorage.empty.get(`empty:${payload.id}`); - } - } + perf.mark('willMigrateWorkspaceStorageKeys'); + return readdir(environmentService.extensionsPath).then(extensions => { - const supportedKeys = new Map(); - [ - 'workbench.search.history', - 'history.entries', - 'ignoreNetVersionError', - 'ignoreEnospcError', - 'extensionUrlHandler.urlToHandle', - 'terminal.integrated.isWorkspaceShellAllowed', - 'workbench.tasks.ignoreTask010Shown', - 'workbench.tasks.recentlyUsedTasks', - 'workspaces.dontPromptToOpen', - 'output.activechannel', - 'outline/state', - 'extensionsAssistant/workspaceRecommendationsIgnore', - 'extensionsAssistant/dynamicWorkspaceRecommendations', - 'debug.repl.history', - 'editor.matchCase', - 'editor.wholeWord', - 'editor.isRegex', - 'lifecyle.lastShutdownReason', - 'debug.selectedroot', - 'debug.selectedconfigname', - 'debug.breakpoint', - 'debug.breakpointactivated', - 'debug.functionbreakpoint', - 'debug.exceptionbreakpoint', - 'debug.watchexpressions', - 'workbench.sidebar.activeviewletid', - 'workbench.panelpart.activepanelid', - 'workbench.zenmode.active', - 'workbench.centerededitorlayout.active', - 'workbench.sidebar.restore', - 'workbench.sidebar.hidden', - 'workbench.panel.hidden', - 'workbench.panel.location', - 'extensionsIdentifiers/disabled', - 'extensionsIdentifiers/enabled', - 'scm.views', - 'suggest/memories/first', - 'suggest/memories/recentlyUsed', - 'suggest/memories/recentlyUsedByPrefix', - 'workbench.view.explorer.numberOfVisibleViews', - 'workbench.view.extensions.numberOfVisibleViews', - 'workbench.view.debug.numberOfVisibleViews', - 'workbench.explorer.views.state', - 'workbench.view.extensions.state', - 'workbench.view.debug.state', - 'memento/workbench.editor.walkThroughPart', - 'memento/workbench.editor.settings2', - 'memento/workbench.editor.htmlPreviewPart', - 'memento/workbench.editor.defaultPreferences', - 'memento/workbench.editors.files.textFileEditor', - 'memento/workbench.editors.logViewer', - 'memento/workbench.editors.textResourceEditor', - 'memento/workbench.panel.output' - ].forEach(key => supportedKeys.set(key.toLowerCase(), key)); - - // Support extension storage as well (always the ID of the extension) - extensions.forEach(extension => { - let extensionId: string; - if (extension.indexOf('-') >= 0) { - extensionId = extension.substring(0, extension.lastIndexOf('-')); // convert "author.extension-0.2.5" => "author.extension" + // Otherwise, we migrate data from window.localStorage over + try { + let workspaceItems: StorageObject; + if (isWorkspaceIdentifier(payload)) { + workspaceItems = parseMultiRootStorage(window.localStorage, `root:${payload.id}`); + } else if (isSingleFolderWorkspaceInitializationPayload(payload)) { + workspaceItems = parseFolderStorage(window.localStorage, payload.folder.toString()); } else { - extensionId = extension; + if (payload.id === 'ext-dev') { + workspaceItems = parseNoWorkspaceStorage(window.localStorage); + } else { + workspaceItems = parseEmptyStorage(window.localStorage, `${payload.id}`); + } } - if (extensionId) { - supportedKeys.set(extensionId.toLowerCase(), extensionId); + const workspaceItemsKeys = workspaceItems ? Object.keys(workspaceItems) : []; + if (workspaceItemsKeys.length > 0) { + const supportedKeys = new Map(); + [ + 'workbench.search.history', + 'history.entries', + 'ignoreNetVersionError', + 'ignoreEnospcError', + 'extensionUrlHandler.urlToHandle', + 'terminal.integrated.isWorkspaceShellAllowed', + 'workbench.tasks.ignoreTask010Shown', + 'workbench.tasks.recentlyUsedTasks', + 'workspaces.dontPromptToOpen', + 'output.activechannel', + 'outline/state', + 'extensionsAssistant/workspaceRecommendationsIgnore', + 'extensionsAssistant/dynamicWorkspaceRecommendations', + 'debug.repl.history', + 'editor.matchCase', + 'editor.wholeWord', + 'editor.isRegex', + 'lifecyle.lastShutdownReason', + 'debug.selectedroot', + 'debug.selectedconfigname', + 'debug.breakpoint', + 'debug.breakpointactivated', + 'debug.functionbreakpoint', + 'debug.exceptionbreakpoint', + 'debug.watchexpressions', + 'workbench.sidebar.activeviewletid', + 'workbench.panelpart.activepanelid', + 'workbench.zenmode.active', + 'workbench.centerededitorlayout.active', + 'workbench.sidebar.restore', + 'workbench.sidebar.hidden', + 'workbench.panel.hidden', + 'workbench.panel.location', + 'extensionsIdentifiers/disabled', + 'extensionsIdentifiers/enabled', + 'scm.views', + 'suggest/memories/first', + 'suggest/memories/recentlyUsed', + 'suggest/memories/recentlyUsedByPrefix', + 'workbench.view.explorer.numberOfVisibleViews', + 'workbench.view.extensions.numberOfVisibleViews', + 'workbench.view.debug.numberOfVisibleViews', + 'workbench.explorer.views.state', + 'workbench.view.extensions.state', + 'workbench.view.debug.state', + 'memento/workbench.editor.walkThroughPart', + 'memento/workbench.editor.settings2', + 'memento/workbench.editor.htmlPreviewPart', + 'memento/workbench.editor.defaultPreferences', + 'memento/workbench.editors.files.textFileEditor', + 'memento/workbench.editors.logViewer', + 'memento/workbench.editors.textResourceEditor', + 'memento/workbench.panel.output' + ].forEach(key => supportedKeys.set(key.toLowerCase(), key)); + + // Support extension storage as well (always the ID of the extension) + extensions.forEach(extension => { + let extensionId: string; + if (extension.indexOf('-') >= 0) { + extensionId = extension.substring(0, extension.lastIndexOf('-')); // convert "author.extension-0.2.5" => "author.extension" + } else { + extensionId = extension; + } + + if (extensionId) { + supportedKeys.set(extensionId.toLowerCase(), extensionId); + } + }); + + workspaceItemsKeys.forEach(key => { + const value = workspaceItems[key]; + + // first check for a well known supported key and store with realcase value + const supportedKey = supportedKeys.get(key); + if (supportedKey) { + storageService.store(supportedKey, value, StorageScope.WORKSPACE); + } + + // fix lowercased ".numberOfVisibleViews" + else if (endsWith(key, '.numberOfVisibleViews'.toLowerCase())) { + const normalizedKey = key.substring(0, key.length - '.numberOfVisibleViews'.length) + '.numberOfVisibleViews'; + storageService.store(normalizedKey, value, StorageScope.WORKSPACE); + } + + // support dynamic keys + else if (key.indexOf('memento/') === 0 || key.indexOf('viewservice.') === 0 || endsWith(key, '.state')) { + storageService.store(key, value, StorageScope.WORKSPACE); + } + }); } - }); + } catch (error) { + onUnexpectedError(error); + logService.error(error); + } - if (workspaceItems) { - Object.keys(workspaceItems).forEach(key => { - const value = workspaceItems[key]; + perf.mark('didMigrateWorkspaceStorageKeys'); - // first check for a well known supported key and store with realcase value - const supportedKey = supportedKeys.get(key); - if (supportedKey) { - storageService.store(supportedKey, value, StorageScope.WORKSPACE); - } + return storageService; + }); + }); + }); + }); +} - // fix lowercased ".numberOfVisibleViews" - else if (endsWith(key, '.numberOfVisibleViews'.toLowerCase())) { - const normalizedKey = key.substring(0, key.length - '.numberOfVisibleViews'.length) + '.numberOfVisibleViews'; - storageService.store(normalizedKey, value, StorageScope.WORKSPACE); - } +function getWorkspaceStoragePath(payload: IWorkspaceInitializationPayload, environmentService: IEnvironmentService): string { + return join(environmentService.workspaceStorageHome, payload.id); // workspace home + workspace id; +} - // support dynamic keys - else if (key.indexOf('memento/') === 0 || key.indexOf('viewservice.') === 0 || endsWith(key, '.state')) { - storageService.store(key, value, StorageScope.WORKSPACE); - } - }); - } - } catch (error) { - onUnexpectedError(error); - logService.error(error); - } +function prepareWorkspaceStorageFolder(payload: IWorkspaceInitializationPayload, environmentService: IEnvironmentService): Thenable<{ path: string, wasCreated: boolean }> { + const workspaceStoragePath = getWorkspaceStoragePath(payload, environmentService); - return storageService; - }); - }); + return exists(workspaceStoragePath).then(exists => { + if (exists) { + return { path: workspaceStoragePath, wasCreated: false }; + } + + return mkdirp(workspaceStoragePath).then(() => ({ path: workspaceStoragePath, wasCreated: true })); }); } +function ensureWorkspaceStorageFolderMeta(payload: IWorkspaceInitializationPayload, workspaceService: IWorkspaceContextService, environmentService: IEnvironmentService): void { + const state = workspaceService.getWorkbenchState(); + if (state === WorkbenchState.EMPTY) { + return; // no storage meta for empty workspaces + } + + const workspaceStorageMetaPath = join(getWorkspaceStoragePath(payload, environmentService), 'workspace.json'); + + exists(workspaceStorageMetaPath).then(exists => { + if (exists) { + return void 0; // already existing + } + + const workspace = workspaceService.getWorkspace(); + + return writeFile(workspaceStorageMetaPath, JSON.stringify({ + configuration: workspace.configuration ? uri.revive(workspace.configuration).toString() : void 0, + folder: state === WorkbenchState.FOLDER ? uri.revive(workspace.folders[0].uri).toString() : void 0 + }, undefined, 2)); + }).then(null, error => onUnexpectedError(error)); +} + function createStorageLegacyService(workspaceService: IWorkspaceContextService, environmentService: IEnvironmentService): IStorageLegacyService { let workspaceId: string; @@ -479,7 +493,7 @@ function createMainProcessServices(mainProcessClient: ElectronIPCClient, configu serviceCollection.set(IWindowsService, new WindowsChannelClient(windowsChannel)); const updateChannel = mainProcessClient.getChannel('update'); - serviceCollection.set(IUpdateService, new SyncDescriptor(UpdateChannelClient, updateChannel)); + serviceCollection.set(IUpdateService, new SyncDescriptor(UpdateChannelClient, [updateChannel])); const urlChannel = mainProcessClient.getChannel('url'); const mainUrlService = new URLServiceChannelClient(urlChannel); @@ -490,10 +504,10 @@ function createMainProcessServices(mainProcessClient: ElectronIPCClient, configu mainProcessClient.registerChannel('urlHandler', urlHandlerChannel); const issueChannel = mainProcessClient.getChannel('issue'); - serviceCollection.set(IIssueService, new SyncDescriptor(IssueChannelClient, issueChannel)); + serviceCollection.set(IIssueService, new SyncDescriptor(IssueChannelClient, [issueChannel])); const menubarChannel = mainProcessClient.getChannel('menubar'); - serviceCollection.set(IMenubarService, new SyncDescriptor(MenubarChannelClient, menubarChannel)); + serviceCollection.set(IMenubarService, new SyncDescriptor(MenubarChannelClient, [menubarChannel])); const workspacesChannel = mainProcessClient.getChannel('workspaces'); serviceCollection.set(IWorkspacesService, new WorkspacesChannelClient(workspacesChannel)); diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index ab04c60f4..536a89312 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -17,7 +17,7 @@ import pkg from 'vs/platform/node/package'; import { Workbench, IWorkbenchStartedInfo } from 'vs/workbench/electron-browser/workbench'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService, configurationTelemetry, combinedAppender, LogAppender } from 'vs/platform/telemetry/common/telemetryUtils'; -import { ITelemetryAppenderChannel, TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc'; +import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc'; import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; import ErrorTelemetry from 'vs/platform/telemetry/browser/errorTelemetry'; import { ElectronWindow } from 'vs/workbench/electron-browser/window'; @@ -56,7 +56,7 @@ import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/serv import { ICrashReporterService, NullCrashReporterService, CrashReporterService } from 'vs/workbench/services/crashReporter/electron-browser/crashReporterService'; import { getDelayedChannel, IPCClient } from 'vs/base/parts/ipc/node/ipc'; import { connect as connectNet } from 'vs/base/parts/ipc/node/ipc.net'; -import { IExtensionManagementChannel, ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/node/extensionManagementIpc'; +import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/node/extensionManagementIpc'; import { IExtensionManagementService, IExtensionEnablementService, IExtensionManagementServerService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; @@ -78,7 +78,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { DelegatingStorageService } from 'vs/platform/storage/node/storageService'; import { Event, Emitter } from 'vs/base/common/event'; import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme'; -import { ILocalizationsChannel, LocalizationsChannelClient } from 'vs/platform/localizations/node/localizationsIpc'; +import { LocalizationsChannelClient } from 'vs/platform/localizations/node/localizationsIpc'; import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; import { WorkbenchIssueService } from 'vs/workbench/services/issue/electron-browser/workbenchIssueService'; @@ -87,18 +87,24 @@ import { NotificationService } from 'vs/workbench/services/notification/common/n import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { DialogService } from 'vs/workbench/services/dialogs/electron-browser/dialogService'; import { DialogChannel } from 'vs/platform/dialogs/node/dialogIpc'; -import { EventType, addDisposableListener, addClass } from 'vs/base/browser/dom'; +import { EventType, addDisposableListener, addClass, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/node/remoteAgentService'; +import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { OpenerService } from 'vs/editor/browser/services/openerService'; import { SearchHistoryService } from 'vs/workbench/services/search/node/searchHistoryService'; import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/node/extensionManagementServerService'; -import { DefaultURITransformer } from 'vs/base/common/uriIpc'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; +import { LogLevelSetterChannel } from 'vs/platform/log/node/logIpc'; import { ILabelService, LabelService } from 'vs/platform/label/common/label'; import { IDownloadService } from 'vs/platform/download/common/download'; import { DownloadService } from 'vs/platform/download/node/downloadService'; +import { DownloadServiceChannel } from 'vs/platform/download/node/downloadIpc'; import { runWhenIdle } from 'vs/base/common/async'; import { TextResourcePropertiesService } from 'vs/workbench/services/textfile/electron-browser/textResourcePropertiesService'; +import { MulitExtensionManagementService } from 'vs/platform/extensionManagement/node/multiExtensionManagement'; +import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { RemoteAuthorityResolverChannelClient } from 'vs/platform/remote/node/remoteAuthorityResolverChannel'; /** * Services that we require for the Shell @@ -285,13 +291,17 @@ export class WorkbenchShell extends Disposable { } private logStorageTelemetry(): void { - const globalStorageInitDuration = perf.getDuration('willInitGlobalStorage', 'didInitGlobalStorage'); + const initialStartup = !!this.configuration.isInitialStartup; + + const appReadyDuration = initialStartup ? perf.getDuration('main:started', 'main:appReady') : 0; + const workbenchReadyDuration = perf.getDuration(initialStartup ? 'main:started' : 'main:loadWindow', 'didStartWorkbench'); + const workspaceStorageRequireDuration = perf.getDuration('willRequireSQLite', 'didRequireSQLite'); + const workspaceStorageSchemaDuration = perf.getDuration('willSetupSQLiteSchema', 'didSetupSQLiteSchema'); const workspaceStorageInitDuration = perf.getDuration('willInitWorkspaceStorage', 'didInitWorkspaceStorage'); + const workspaceStorageFileExistsDuration = perf.getDuration('willCheckWorkspaceStorageExists', 'didCheckWorkspaceStorageExists'); + const workspaceStorageMigrationDuration = perf.getDuration('willMigrateWorkspaceStorageKeys', 'didMigrateWorkspaceStorageKeys'); const workbenchLoadDuration = perf.getDuration('willLoadWorkbenchMain', 'didLoadWorkbenchMain'); - const localStorageAccessDuration = perf.getDuration('willAccessLocalStorage', 'didAccessLocalStorage'); - const localStorageReadDuration = perf.getDuration('willReadLocalStorage', 'didReadLocalStorage'); - - let workspaceIntegrity: string; + const localStorageDuration = perf.getDuration('willReadLocalStorage', 'didReadLocalStorage'); // Handle errors (avoid duplicates to reduce spam) const loggedStorageErrors = new Set(); @@ -302,67 +312,75 @@ export class WorkbenchShell extends Disposable { loggedStorageErrors.add(errorStr); /* __GDPR__ - "sqliteStorageError2" : { - "globalReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "sqliteStorageError5" : { + "appReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workbenchReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspaceExistsTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspaceMigrationTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspaceRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspaceSchemaTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspaceReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "localStorageAccessTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "localStorageReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "localStorageTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workbenchRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "globalKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspaceKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "startupKind": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceIntegrity" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "storageError": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + "storageError": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ - this.telemetryService.publicLog('sqliteStorageError2', { - 'globalReadTime': globalStorageInitDuration, + this.telemetryService.publicLog('sqliteStorageError5', { + 'appReadyTime': appReadyDuration, + 'workbenchReadyTime': workbenchReadyDuration, + 'workspaceExistsTime': workspaceStorageFileExistsDuration, + 'workspaceMigrationTime': workspaceStorageMigrationDuration, + 'workspaceRequireTime': workspaceStorageRequireDuration, + 'workspaceSchemaTime': workspaceStorageSchemaDuration, 'workspaceReadTime': workspaceStorageInitDuration, - 'localStorageAccessTime': localStorageAccessDuration, - 'localStorageReadTime': localStorageReadDuration, + 'localStorageTime': localStorageDuration, 'workbenchRequireTime': workbenchLoadDuration, - 'globalKeys': this.storageService.storage.getSize(StorageScope.GLOBAL), 'workspaceKeys': this.storageService.storage.getSize(StorageScope.WORKSPACE), 'startupKind': this.lifecycleService.startupKind, - 'workspaceIntegrity': workspaceIntegrity, 'storageError': errorStr }); } })); - perf.mark('willCheckWorkspaceStorageIntegrity'); - this.storageService.storage.checkIntegrity(StorageScope.WORKSPACE, false).then(integrity => { - perf.mark('didCheckWorkspaceStorageIntegrity'); - - workspaceIntegrity = integrity; - - /* __GDPR__ - "sqliteStorageTimers2" : { - "globalReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "localStorageAccessTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "localStorageReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceIntegrity" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceIntegrityCheckTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workbenchRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "globalKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "startupKind": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ - this.telemetryService.publicLog('sqliteStorageTimers2', { - 'globalReadTime': globalStorageInitDuration, - 'workspaceReadTime': workspaceStorageInitDuration, - 'localStorageAccessTime': localStorageAccessDuration, - 'localStorageReadTime': localStorageReadDuration, - 'workspaceIntegrity': workspaceIntegrity, - 'workspaceIntegrityCheckTime': perf.getDuration('willCheckWorkspaceStorageIntegrity', 'didCheckWorkspaceStorageIntegrity'), - 'workbenchRequireTime': workbenchLoadDuration, - 'globalKeys': this.storageService.storage.getSize(StorageScope.GLOBAL), - 'workspaceKeys': this.storageService.storage.getSize(StorageScope.WORKSPACE), - 'startupKind': this.lifecycleService.startupKind - }); - }, error => errors.onUnexpectedError(error)); + + if (this.storageService.storage.hasErrors) { + return; // do not log performance numbers when errors occured + } + + if (this.environmentService.verbose) { + return; // do not log when running in verbose mode + } + + /* __GDPR__ + "sqliteStorageTimers5" : { + "appReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workbenchReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspaceExistsTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspaceMigrationTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspaceRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspaceSchemaTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspaceReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "localStorageTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workbenchRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspaceKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "startupKind": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ + this.telemetryService.publicLog('sqliteStorageTimers5', { + 'appReadyTime': appReadyDuration, + 'workbenchReadyTime': workbenchReadyDuration, + 'workspaceExistsTime': workspaceStorageFileExistsDuration, + 'workspaceMigrationTime': workspaceStorageMigrationDuration, + 'workspaceRequireTime': workspaceStorageRequireDuration, + 'workspaceSchemaTime': workspaceStorageSchemaDuration, + 'workspaceReadTime': workspaceStorageInitDuration, + 'localStorageTime': localStorageDuration, + 'workbenchRequireTime': workbenchLoadDuration, + 'workspaceKeys': this.storageService.storage.getSize(StorageScope.WORKSPACE), + 'startupKind': this.lifecycleService.startupKind + }); } private initServiceCollection(container: HTMLElement): [IInstantiationService, ServiceCollection] { @@ -386,7 +404,7 @@ export class WorkbenchShell extends Disposable { this.broadcastService = instantiationService.createInstance(BroadcastService, this.configuration.windowId); serviceCollection.set(IBroadcastService, this.broadcastService); - serviceCollection.set(IWindowService, new SyncDescriptor(WindowService, this.configuration.windowId, this.configuration)); + serviceCollection.set(IWindowService, new SyncDescriptor(WindowService, [this.configuration.windowId, this.configuration])); const sharedProcess = (serviceCollection.get(IWindowsService)).whenSharedProcessReady() .then(() => connectNet(this.environmentService.sharedIPCHandle, `window:${this.configuration.windowId}`)); @@ -400,7 +418,7 @@ export class WorkbenchShell extends Disposable { // Telemetry if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) { - const channel = getDelayedChannel(sharedProcess.then(c => c.getChannel('telemetryAppender'))); + const channel = getDelayedChannel(sharedProcess.then(c => c.getChannel('telemetryAppender'))); const config: ITelemetryServiceConfig = { appender: combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(this.logService)), commonProperties: resolveWorkbenchCommonProperties(this.storageService, product.commit, pkg.version, this.configuration.machineId, this.environmentService.installSourcePath), @@ -437,10 +455,24 @@ export class WorkbenchShell extends Disposable { serviceCollection.set(IDownloadService, new SyncDescriptor(DownloadService)); serviceCollection.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); - const extensionManagementChannel = getDelayedChannel(sharedProcess.then(c => c.getChannel('extensions'))); - const extensionManagementChannelClient = new ExtensionManagementChannelClient(extensionManagementChannel, DefaultURITransformer); - serviceCollection.set(IExtensionManagementServerService, new SyncDescriptor(ExtensionManagementServerService, extensionManagementChannelClient)); - serviceCollection.set(IExtensionManagementService, extensionManagementChannelClient); + const remoteAuthorityResolverChannel = getDelayedChannel(sharedProcess.then(c => c.getChannel('remoteAuthorityResolver'))); + const remoteAuthorityResolverService = new RemoteAuthorityResolverChannelClient(remoteAuthorityResolverChannel); + serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService); + + const remoteAgentService = new RemoteAgentService(this.configuration, this.notificationService, this.environmentService, remoteAuthorityResolverService); + serviceCollection.set(IRemoteAgentService, remoteAgentService); + + const remoteAgentConnection = remoteAgentService.getConnection(); + if (remoteAgentConnection) { + remoteAgentConnection.registerChannel('dialog', instantiationService.createInstance(DialogChannel)); + remoteAgentConnection.registerChannel('download', new DownloadServiceChannel()); + remoteAgentConnection.registerChannel('loglevel', new LogLevelSetterChannel(this.logService)); + } + + const extensionManagementChannel = getDelayedChannel(sharedProcess.then(c => c.getChannel('extensions'))); + const extensionManagementChannelClient = new ExtensionManagementChannelClient(extensionManagementChannel); + serviceCollection.set(IExtensionManagementServerService, new SyncDescriptor(ExtensionManagementServerService, [extensionManagementChannelClient])); + serviceCollection.set(IExtensionManagementService, new SyncDescriptor(MulitExtensionManagementService)); const extensionEnablementService = this._register(instantiationService.createInstance(ExtensionEnablementService)); serviceCollection.set(IExtensionEnablementService, extensionEnablementService); @@ -480,8 +512,8 @@ export class WorkbenchShell extends Disposable { serviceCollection.set(IIntegrityService, new SyncDescriptor(IntegrityServiceImpl)); - const localizationsChannel = getDelayedChannel(sharedProcess.then(c => c.getChannel('localizations'))); - serviceCollection.set(ILocalizationsService, new SyncDescriptor(LocalizationsChannelClient, localizationsChannel)); + const localizationsChannel = getDelayedChannel(sharedProcess.then(c => c.getChannel('localizations'))); + serviceCollection.set(ILocalizationsService, new SyncDescriptor(LocalizationsChannelClient, [localizationsChannel])); return [instantiationService, serviceCollection]; } @@ -517,13 +549,25 @@ export class WorkbenchShell extends Disposable { } private registerListeners(): void { + this._register(addDisposableListener(window, EventType.RESIZE, e => this.onWindowResize(e, true))); + } - // Resize - this._register(addDisposableListener(window, EventType.RESIZE, e => { - if (e.target === window) { - this.layout(); + private onWindowResize(e: any, retry: boolean): void { + if (e.target === window) { + if (window.document && window.document.body && window.document.body.clientWidth === 0) { + // TODO@Ben this is an electron issue on macOS when simple fullscreen is enabled + // where for some reason the window clientWidth is reported as 0 when switching + // between simple fullscreen and normal screen. In that case we schedule the layout + // call at the next animation frame once, in the hope that the dimensions are + // proper then. + if (retry) { + scheduleAtNextAnimationFrame(() => this.onWindowResize(e, false)); + } + return; } - })); + + this.layout(); + } } onUnexpectedError(error: any): void { diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index c4873f273..0df78cd5c 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -237,7 +237,7 @@ export class ElectronWindow extends Themable { this.contextMenuService.showContextMenu({ getAnchor: () => e, - getActions: () => TPromise.as(TextInputActions), + getActions: () => TextInputActions, onHide: () => target.focus() // fixes https://github.com/Microsoft/vscode/issues/52948 }); } diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 29158f4ab..c29aaadbe 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -75,10 +75,10 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; import { LifecycleService } from 'vs/platform/lifecycle/electron-browser/lifecycleService'; -import { IWindowService, IWindowConfiguration as IWindowSettings, IWindowConfiguration, IPath, MenuBarVisibility } from 'vs/platform/windows/common/windows'; +import { IWindowService, IWindowConfiguration, IPath, MenuBarVisibility, getTitleBarStyle } from 'vs/platform/windows/common/windows'; import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; import { IMenuService, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { MenuService } from 'vs/workbench/services/actions/common/menuService'; +import { MenuService } from 'vs/platform/actions/common/menuService'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; @@ -357,8 +357,8 @@ export class Workbench extends Disposable implements IPartService { serviceCollection.set(IContextViewService, this.contextViewService); // Use themable context menus when custom titlebar is enabled to match custom menubar - if (!isMacintosh && this.getCustomTitleBarStyle() === 'custom') { - serviceCollection.set(IContextMenuService, new SyncDescriptor(HTMLContextMenuService, null)); + if (!isMacintosh && this.useCustomTitleBarStyle()) { + serviceCollection.set(IContextMenuService, new SyncDescriptor(HTMLContextMenuService, [null])); } else { serviceCollection.set(IContextMenuService, new SyncDescriptor(NativeContextMenuService)); } @@ -444,7 +444,7 @@ export class Workbench extends Disposable implements IPartService { serviceCollection.set(IKeybindingEditingService, this.instantiationService.createInstance(KeybindingsEditingService)); // Configuration Resolver - serviceCollection.set(IConfigurationResolverService, new SyncDescriptor(ConfigurationResolverService, process.env)); + serviceCollection.set(IConfigurationResolverService, new SyncDescriptor(ConfigurationResolverService, [process.env])); // Quick open service (quick open controller) this.quickOpen = this.instantiationService.createInstance(QuickOpenController); @@ -522,8 +522,7 @@ export class Workbench extends Disposable implements IPartService { } // Changing fullscreen state of the window has an impact on custom title bar visibility, so we need to update - const hasCustomTitle = this.getCustomTitleBarStyle() === 'custom'; - if (hasCustomTitle) { + if (this.useCustomTitleBarStyle()) { this._onTitleBarVisibilityChange.fire(); this.layout(); // handle title bar when fullscreen changes } @@ -533,11 +532,8 @@ export class Workbench extends Disposable implements IPartService { if (visible !== this.menubarToggled) { this.menubarToggled = visible; - if (this.menubarVisibility === 'toggle' || (browser.isFullscreen() && this.menubarVisibility === 'default')) { - if (browser.isFullscreen() && this.menubarVisibility === 'default') { - this._onTitleBarVisibilityChange.fire(); - } - + if (browser.isFullscreen() && (this.menubarVisibility === 'toggle' || this.menubarVisibility === 'default')) { + this._onTitleBarVisibilityChange.fire(); this.layout(); } } @@ -757,7 +753,8 @@ export class Workbench extends Disposable implements IPartService { perf.mark('willRestorePanel'); const isPanelToRestoreEnabled = !!this.panelPart.getPanels().filter(p => p.id === panelId).length; const panelIdToRestore = isPanelToRestoreEnabled ? panelId : panelRegistry.getDefaultPanelId(); - restorePromises.push(this.panelPart.openPanel(panelIdToRestore, false).then(() => perf.mark('didRestorePanel'))); + this.panelPart.openPanel(panelIdToRestore, false); + perf.mark('didRestorePanel'); } // Restore Zen Mode if active @@ -935,26 +932,8 @@ export class Workbench extends Disposable implements IPartService { this.panelPosition = (panelPosition === 'right') ? Position.RIGHT : Position.BOTTOM; } - private getCustomTitleBarStyle(): 'custom' { - const isDev = !this.environmentService.isBuilt || this.environmentService.isExtensionDevelopment; - if (isMacintosh && isDev) { - return null; // not enabled when developing due to https://github.com/electron/electron/issues/3647 - } - - const windowConfig = this.configurationService.getValue(); - if (windowConfig && windowConfig.window) { - const useNativeTabs = windowConfig.window.nativeTabs; - if (useNativeTabs) { - return null; // native tabs on sierra do not work with custom title style - } - - const style = windowConfig.window.titleBarStyle; - if (style === 'custom') { - return style; - } - } - - return null; + private useCustomTitleBarStyle(): boolean { + return getTitleBarStyle(this.configurationService, this.environmentService) === 'custom'; } private setStatusBarHidden(hidden: boolean, skipLayout?: boolean): void { @@ -1041,7 +1020,7 @@ export class Workbench extends Disposable implements IPartService { // Menubar visibility changes - if ((isWindows || isLinux) && this.getCustomTitleBarStyle() === 'custom') { + if ((isWindows || isLinux) && this.useCustomTitleBarStyle()) { this.titlebarPart.onMenubarVisibilityChange()(e => this.onMenubarToggled(e)); } @@ -1207,7 +1186,7 @@ export class Workbench extends Disposable implements IPartService { isVisible(part: Parts): boolean { switch (part) { case Parts.TITLEBAR_PART: - if (this.getCustomTitleBarStyle() !== 'custom') { + if (!this.useCustomTitleBarStyle()) { return false; } else if (!browser.isFullscreen()) { return true; @@ -1371,7 +1350,7 @@ export class Workbench extends Disposable implements IPartService { } } - setSideBarHidden(hidden: boolean, skipLayout?: boolean): TPromise { + setSideBarHidden(hidden: boolean, skipLayout?: boolean): void { this.sideBarHidden = hidden; this.sideBarVisibleContext.set(!hidden); @@ -1383,47 +1362,45 @@ export class Workbench extends Disposable implements IPartService { } // If sidebar becomes hidden, also hide the current active Viewlet if any - let promise = TPromise.wrap(null); if (hidden && this.sidebarPart.getActiveViewlet()) { - promise = this.sidebarPart.hideActiveViewlet().then(() => { - const activePanel = this.panelPart.getActivePanel(); - - // Pass Focus to Editor or Panel if Sidebar is now hidden - if (this.hasFocus(Parts.PANEL_PART) && activePanel) { - activePanel.focus(); - } else { - this.editorGroupService.activeGroup.focus(); - } - }); + this.sidebarPart.hideActiveViewlet(); + const activePanel = this.panelPart.getActivePanel(); + + // Pass Focus to Editor or Panel if Sidebar is now hidden + if (this.hasFocus(Parts.PANEL_PART) && activePanel) { + activePanel.focus(); + } else { + this.editorGroupService.activeGroup.focus(); + } } // If sidebar becomes visible, show last active Viewlet or default viewlet else if (!hidden && !this.sidebarPart.getActiveViewlet()) { const viewletToOpen = this.sidebarPart.getLastActiveViewletId(); if (viewletToOpen) { - promise = this.viewletService.openViewlet(viewletToOpen, true) - .then(viewlet => viewlet || this.viewletService.openViewlet(this.viewletService.getDefaultViewletId(), true)); + const viewlet = this.viewletService.openViewlet(viewletToOpen, true); + if (!viewlet) { + this.viewletService.openViewlet(this.viewletService.getDefaultViewletId(), true); + } } } - return promise.then(() => { - // Remember in settings - const defaultHidden = this.contextService.getWorkbenchState() === WorkbenchState.EMPTY; - if (hidden !== defaultHidden) { - this.storageService.store(Workbench.sidebarHiddenStorageKey, hidden ? 'true' : 'false', StorageScope.WORKSPACE); - } else { - this.storageService.remove(Workbench.sidebarHiddenStorageKey, StorageScope.WORKSPACE); - } + // Remember in settings + const defaultHidden = this.contextService.getWorkbenchState() === WorkbenchState.EMPTY; + if (hidden !== defaultHidden) { + this.storageService.store(Workbench.sidebarHiddenStorageKey, hidden ? 'true' : 'false', StorageScope.WORKSPACE); + } else { + this.storageService.remove(Workbench.sidebarHiddenStorageKey, StorageScope.WORKSPACE); + } - // Layout - if (!skipLayout) { - this.workbenchLayout.layout(); - } - }); + // Layout + if (!skipLayout) { + this.workbenchLayout.layout(); + } } - setPanelHidden(hidden: boolean, skipLayout?: boolean): TPromise { + setPanelHidden(hidden: boolean, skipLayout?: boolean): void { this.panelHidden = hidden; // Adjust CSS @@ -1434,35 +1411,31 @@ export class Workbench extends Disposable implements IPartService { } // If panel part becomes hidden, also hide the current active panel if any - let promise = TPromise.wrap(null); if (hidden && this.panelPart.getActivePanel()) { - promise = this.panelPart.hideActivePanel().then(() => { - this.editorGroupService.activeGroup.focus(); // Pass focus to editor group if panel part is now hidden - }); + this.panelPart.hideActivePanel(); + this.editorGroupService.activeGroup.focus(); // Pass focus to editor group if panel part is now hidden } // If panel part becomes visible, show last active panel or default panel else if (!hidden && !this.panelPart.getActivePanel()) { const panelToOpen = this.panelPart.getLastActivePanelId(); if (panelToOpen) { - promise = this.panelPart.openPanel(panelToOpen, true); + this.panelPart.openPanel(panelToOpen, true); } } - return promise.then(() => { - // Remember in settings - if (!hidden) { - this.storageService.store(Workbench.panelHiddenStorageKey, 'false', StorageScope.WORKSPACE); - } else { - this.storageService.remove(Workbench.panelHiddenStorageKey, StorageScope.WORKSPACE); - } + // Remember in settings + if (!hidden) { + this.storageService.store(Workbench.panelHiddenStorageKey, 'false', StorageScope.WORKSPACE); + } else { + this.storageService.remove(Workbench.panelHiddenStorageKey, StorageScope.WORKSPACE); + } - // Layout - if (!skipLayout) { - this.workbenchLayout.layout(); - } - }); + // Layout + if (!skipLayout) { + this.workbenchLayout.layout(); + } } toggleMaximizedPanel(): void { @@ -1518,23 +1491,25 @@ export class Workbench extends Disposable implements IPartService { return this.panelPosition; } - setPanelPosition(position: Position): TPromise { - return (this.panelHidden ? this.setPanelHidden(false, true /* Skip Layout */) : TPromise.as(undefined)).then(() => { - const newPositionValue = (position === Position.BOTTOM) ? 'bottom' : 'right'; - const oldPositionValue = (this.panelPosition === Position.BOTTOM) ? 'bottom' : 'right'; - this.panelPosition = position; - this.storageService.store(Workbench.panelPositionStorageKey, PositionToString(this.panelPosition).toLowerCase(), StorageScope.WORKSPACE); + setPanelPosition(position: Position): void { + if (this.panelHidden) { + this.setPanelHidden(false, true /* Skip Layout */); + } + + const newPositionValue = (position === Position.BOTTOM) ? 'bottom' : 'right'; + const oldPositionValue = (this.panelPosition === Position.BOTTOM) ? 'bottom' : 'right'; + this.panelPosition = position; + this.storageService.store(Workbench.panelPositionStorageKey, PositionToString(this.panelPosition).toLowerCase(), StorageScope.WORKSPACE); - // Adjust CSS - DOM.removeClass(this.panelPart.getContainer(), oldPositionValue); - DOM.addClass(this.panelPart.getContainer(), newPositionValue); + // Adjust CSS + DOM.removeClass(this.panelPart.getContainer(), oldPositionValue); + DOM.addClass(this.panelPart.getContainer(), newPositionValue); - // Update Styles - this.panelPart.updateStyles(); + // Update Styles + this.panelPart.updateStyles(); - // Layout - this.workbenchLayout.layout(); - }); + // Layout + this.workbenchLayout.layout(); } //#endregion diff --git a/src/vs/workbench/node/extensionHostMain.ts b/src/vs/workbench/node/extensionHostMain.ts index a644e6369..914e5b815 100644 --- a/src/vs/workbench/node/extensionHostMain.ts +++ b/src/vs/workbench/node/extensionHostMain.ts @@ -11,7 +11,7 @@ import * as errors from 'vs/base/common/errors'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Counter } from 'vs/base/common/numbers'; import { URI, setUriThrowOnMissingScheme } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { IURITransformer } from 'vs/base/common/uriIpc'; import * as pfs from 'vs/base/node/pfs'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; import { IEnvironment, IInitData, IWorkspaceData, MainContext, MainThreadWorkspaceShape } from 'vs/workbench/api/node/extHost.protocol'; @@ -20,7 +20,6 @@ import { ExtensionActivatedByEvent } from 'vs/workbench/api/node/extHostExtensio import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; -import { connectProxyResolver } from 'vs/workbench/node/proxyResolver'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol'; @@ -69,7 +68,8 @@ export class ExtensionHostMain { private _mainThreadWorkspace: MainThreadWorkspaceShape; constructor(protocol: IMessagePassingProtocol, initData: IInitData) { - const rpcProtocol = new RPCProtocol(protocol); + const uriTransformer: IURITransformer = null; + const rpcProtocol = new RPCProtocol(protocol, null, uriTransformer); // ensure URIs are transformed and revived initData = this.transform(initData, rpcProtocol); @@ -91,7 +91,6 @@ export class ExtensionHostMain { this._extHostConfiguration = new ExtHostConfiguration(rpcProtocol.getProxy(MainContext.MainThreadConfiguration), extHostWorkspace, initData.configuration); const mainThreadTelemetry = rpcProtocol.getProxy(MainContext.MainThreadTelemetry); - connectProxyResolver(extHostWorkspace, this._extHostConfiguration, this._extHostLogService, mainThreadTelemetry); this._extensionService = new ExtHostExtensionService(initData, rpcProtocol, extHostWorkspace, this._extHostConfiguration, this._extHostLogService, mainThreadTelemetry); // error forwarding and stack trace scanning @@ -130,7 +129,7 @@ export class ExtensionHostMain { this._mainThreadWorkspace = rpcProtocol.getProxy(MainContext.MainThreadWorkspace); } - start(): TPromise { + start(): Thenable { return this._extensionService.onExtensionAPIReady() .then(() => this.handleEagerExtensions()) .then(() => this.handleExtensionTests()) @@ -152,7 +151,7 @@ export class ExtensionHostMain { // TODO: write to log once we have one }); - let allPromises: TPromise[] = []; + let allPromises: Thenable[] = []; try { const allExtensions = this._extensionService.getAllExtensionDescriptions(); const allExtensionsIds = allExtensions.map(ext => ext.id); @@ -165,7 +164,7 @@ export class ExtensionHostMain { // TODO: write to log once we have one } - const extensionsDeactivated = TPromise.join(allPromises).then(() => void 0); + const extensionsDeactivated = Promise.all(allPromises).then(() => void 0); // Give extensions 1 second to wrap up any async dispose, then exit setTimeout(() => { @@ -174,7 +173,7 @@ export class ExtensionHostMain { } // Handle "eager" activation extensions - private handleEagerExtensions(): TPromise { + private handleEagerExtensions(): Promise { this._extensionService.activateByEvent('*', true).then(null, (err) => { console.error(err); }); @@ -182,22 +181,22 @@ export class ExtensionHostMain { return this.handleWorkspaceContainsEagerExtensions(); } - private handleWorkspaceContainsEagerExtensions(): TPromise { + private handleWorkspaceContainsEagerExtensions(): Promise { if (!this._workspace || this._workspace.folders.length === 0) { - return TPromise.as(null); + return Promise.resolve(null); } - return TPromise.join( + return Promise.all( this._extensionService.getAllExtensionDescriptions().map((desc) => { return this.handleWorkspaceContainsEagerExtension(desc); }) ).then(() => { }); } - private handleWorkspaceContainsEagerExtension(desc: IExtensionDescription): TPromise { + private handleWorkspaceContainsEagerExtension(desc: IExtensionDescription): Promise { const activationEvents = desc.activationEvents; if (!activationEvents) { - return TPromise.as(void 0); + return Promise.resolve(void 0); } const fileNames: string[] = []; @@ -215,13 +214,13 @@ export class ExtensionHostMain { } if (fileNames.length === 0 && globPatterns.length === 0) { - return TPromise.as(void 0); + return Promise.resolve(void 0); } - const fileNamePromise = TPromise.join(fileNames.map((fileName) => this.activateIfFileName(desc.id, fileName))).then(() => { }); + const fileNamePromise = Promise.all(fileNames.map((fileName) => this.activateIfFileName(desc.id, fileName))).then(() => { }); const globPatternPromise = this.activateIfGlobPatterns(desc.id, globPatterns); - return TPromise.join([fileNamePromise, globPatternPromise]).then(() => { }); + return Promise.all([fileNamePromise, globPatternPromise]).then(() => { }); } private async activateIfFileName(extensionId: string, fileName: string): Promise { @@ -244,7 +243,7 @@ export class ExtensionHostMain { this._extHostLogService.trace(`extensionHostMain#activateIfGlobPatterns: fileSearch, extension: ${extensionId}, entryPoint: workspaceContains`); if (globPatterns.length === 0) { - return TPromise.as(void 0); + return Promise.resolve(void 0); } const tokenSource = new CancellationTokenSource(); @@ -276,12 +275,12 @@ export class ExtensionHostMain { ); } - return TPromise.as(void 0); + return Promise.resolve(void 0); } - private handleExtensionTests(): TPromise { + private handleExtensionTests(): Promise { if (!this._environment.extensionTestsPath || !this._environment.extensionDevelopmentLocationURI) { - return TPromise.as(null); + return Promise.resolve(null); } // Require the test runner via node require from the provided path @@ -295,7 +294,7 @@ export class ExtensionHostMain { // Execute the runner if it follows our spec if (testRunner && typeof testRunner.run === 'function') { - return new TPromise((c, e) => { + return new Promise((c, e) => { testRunner.run(this._environment.extensionTestsPath, (error, failures) => { if (error) { e(error.toString()); @@ -314,15 +313,15 @@ export class ExtensionHostMain { this.gracefulExit(1 /* ERROR */); } - return TPromise.wrapError(new Error(requireError ? requireError.toString() : nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", this._environment.extensionTestsPath))); + return Promise.reject(new Error(requireError ? requireError.toString() : nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", this._environment.extensionTestsPath))); } private transform(initData: IInitData, rpcProtocol: RPCProtocol): IInitData { - initData.extensions.forEach((ext) => (ext).extensionLocation = URI.revive(ext.extensionLocation)); - initData.environment.appRoot = URI.revive(initData.environment.appRoot); - initData.environment.appSettingsHome = URI.revive(initData.environment.appSettingsHome); - initData.environment.extensionDevelopmentLocationURI = URI.revive(initData.environment.extensionDevelopmentLocationURI); - initData.logsLocation = URI.revive(initData.logsLocation); + initData.extensions.forEach((ext) => (ext).extensionLocation = URI.revive(rpcProtocol.transformIncomingURIs(ext.extensionLocation))); + initData.environment.appRoot = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.appRoot)); + initData.environment.appSettingsHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.appSettingsHome)); + initData.environment.extensionDevelopmentLocationURI = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.extensionDevelopmentLocationURI)); + initData.logsLocation = URI.revive(rpcProtocol.transformIncomingURIs(initData.logsLocation)); initData.workspace = rpcProtocol.transformIncomingURIs(initData.workspace); return initData; } diff --git a/src/vs/workbench/node/proxyResolver.ts b/src/vs/workbench/node/proxyResolver.ts index 45795dbaa..82f19e4fb 100644 --- a/src/vs/workbench/node/proxyResolver.ts +++ b/src/vs/workbench/node/proxyResolver.ts @@ -3,16 +3,37 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; -import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; import * as http from 'http'; import * as https from 'https'; -import ElectronProxyAgent = require('electron-proxy-agent'); +import * as nodeurl from 'url'; + +import { assign } from 'vs/base/common/objects'; +import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; +import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; +import { ProxyAgent } from 'vscode-proxy-agent'; import { MainThreadTelemetryShape } from 'vs/workbench/api/node/extHost.protocol'; import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; import { toErrorMessage } from 'vs/base/common/errorMessage'; +import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; +import { URI } from 'vs/base/common/uri'; + +export function connectProxyResolver( + extHostWorkspace: ExtHostWorkspace, + extHostConfiguration: ExtHostConfiguration, + extensionService: ExtHostExtensionService, + extHostLogService: ExtHostLogService, + mainThreadTelemetry: MainThreadTelemetryShape +) { + const agent = createProxyAgent(extHostWorkspace, extHostLogService, mainThreadTelemetry); + const lookup = createPatchedModules(extHostConfiguration, agent); + return configureModuleLoading(extensionService, lookup); +} -export function connectProxyResolver(extHostWorkspace: ExtHostWorkspace, extHostConfiguration: ExtHostConfiguration, extHostLogService: ExtHostLogService, mainThreadTelemetry: MainThreadTelemetryShape) { +function createProxyAgent( + extHostWorkspace: ExtHostWorkspace, + extHostLogService: ExtHostLogService, + mainThreadTelemetry: MainThreadTelemetryShape +) { let timeout: NodeJS.Timer | undefined; let count = 0; let duration = 0; @@ -39,6 +60,7 @@ export function connectProxyResolver(extHostWorkspace: ExtHostWorkspace, extHost extHostWorkspace.resolveProxy(url) .then(proxy => { callback(proxy); + extHostLogService.info('ProxyResolver#resolveProxy', url, proxy); }).then(() => { count++; duration = Date.now() - start + duration; @@ -49,19 +71,46 @@ export function connectProxyResolver(extHostWorkspace: ExtHostWorkspace, extHost }); } - const agent = new ElectronProxyAgent({ resolveProxy }); + return new ProxyAgent({ resolveProxy }); +} - let config = extHostConfiguration.getConfiguration('http').get('systemProxy') || 'off'; +function createPatchedModules(extHostConfiguration: ExtHostConfiguration, agent: http.Agent) { + const setting = { + config: extHostConfiguration.getConfiguration('http') + .get('systemProxy') || 'off' + }; extHostConfiguration.onDidChangeConfiguration(e => { - config = extHostConfiguration.getConfiguration('http').get('systemProxy') || 'off'; + setting.config = extHostConfiguration.getConfiguration('http') + .get('systemProxy') || 'off'; }); + return { + http: { + off: assign({}, http, patches(http, agent, { config: 'off' }, true)), + on: assign({}, http, patches(http, agent, { config: 'on' }, true)), + force: assign({}, http, patches(http, agent, { config: 'force' }, true)), + onRequest: assign({}, http, patches(http, agent, setting, true)), + default: assign(http, patches(http, agent, setting, false)) // run last + }, + https: { + off: assign({}, https, patches(https, agent, { config: 'off' }, true)), + on: assign({}, https, patches(https, agent, { config: 'on' }, true)), + force: assign({}, https, patches(https, agent, { config: 'force' }, true)), + onRequest: assign({}, https, patches(https, agent, setting, true)), + default: assign(https, patches(https, agent, setting, false)) // run last + } + }; +} + +function patches(originals: typeof http | typeof https, agent: http.Agent, setting: { config: string; }, onRequest: boolean) { + + return { + get: patch(originals.get), + request: patch(originals.request) + }; + function patch(original: typeof http.get) { function patched(url: string | URL, options?: http.RequestOptions, callback?: (res: http.IncomingMessage) => void): http.ClientRequest { - if (config === 'off') { - return original.apply(null, arguments); - } - if (typeof url !== 'string' && !(url && (url).searchParams)) { callback = options; options = url; @@ -73,9 +122,14 @@ export function connectProxyResolver(extHostWorkspace: ExtHostWorkspace, extHost } options = options || {}; - if (config === 'force' || config === 'on' && !options.agent) { + const config = onRequest && (options)._vscodeSystemProxy || setting.config; + if (config === 'off') { + return original.apply(null, arguments); + } + + if (!options.socketPath && (config === 'force' || config === 'on' && !options.agent)) { if (url) { - const parsed = typeof url === 'string' ? new URL(url) : url; + const parsed = typeof url === 'string' ? nodeurl.parse(url) : url; options = { protocol: parsed.protocol, hostname: parsed.hostname, @@ -92,9 +146,24 @@ export function connectProxyResolver(extHostWorkspace: ExtHostWorkspace, extHost } return patched; } +} - (http).get = patch(http.get); - (http).request = patch(http.request); - (https).get = patch(https.get); - (https).request = patch(https.request); +function configureModuleLoading(extensionService: ExtHostExtensionService, lookup: ReturnType): Promise { + return extensionService.getExtensionPathIndex() + .then(extensionPaths => { + const node_module = require.__$__nodeRequire('module'); + const original = node_module._load; + node_module._load = function load(request: string, parent: any, isMain: any) { + if (request !== 'http' && request !== 'https') { + return original.apply(this, arguments); + } + + const modules = lookup[request]; + const ext = extensionPaths.findSubstr(URI.file(parent.filename).fsPath); + if (ext && ext.enableProposedApi) { + return modules[(ext).systemProxy] || modules.onRequest; + } + return modules.default; + }; + }); } \ No newline at end of file diff --git a/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts b/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts index 60bff5108..b96f1b758 100644 --- a/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts +++ b/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts @@ -20,7 +20,7 @@ import Severity from 'vs/base/common/severity'; import { ILogService } from 'vs/platform/log/common/log'; import { getPathFromAmdModule } from 'vs/base/common/amd'; -function ignore(code: string, value: T | null = null): (err: any) => Promise { +function ignore(code: string, value: T): (err: any) => Promise { return err => err.code === code ? Promise.resolve(value) : Promise.reject(err); } @@ -70,7 +70,7 @@ class InstallAction extends Action { return Promise.resolve(null); } else { return pfs.unlink(this.target) - .then(null, ignore('ENOENT')) + .then(null, ignore('ENOENT', null)) .then(() => pfs.symlink(getSource(), this.target)) .then(null, err => { if (err.code === 'EACCES' || err.code === 'ENOENT') { @@ -147,7 +147,7 @@ class UninstallAction extends Action { const uninstall = () => { return pfs.unlink(this.target) - .then(null, ignore('ENOENT')); + .then(null, ignore('ENOENT', null)); }; return uninstall().then(null, err => { diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts b/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts index e88626845..0b266d0ee 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts @@ -15,6 +15,7 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { IFileService } from 'vs/platform/files/common/files'; import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ITextMateService } from 'vs/workbench/services/textMate/electron-browser/textMateService'; interface IRegExp { @@ -67,12 +68,19 @@ export class LanguageConfigurationFileHandler { constructor( @ITextMateService textMateService: ITextMateService, @IModeService private readonly _modeService: IModeService, - @IFileService private readonly _fileService: IFileService + @IFileService private readonly _fileService: IFileService, + @IExtensionService private readonly _extensionService: IExtensionService ) { this._done = []; // Listen for hints that a language configuration is needed/usefull and then load it once - this._modeService.onDidCreateMode((mode) => this._loadConfigurationsForMode(mode.getLanguageIdentifier())); + this._modeService.onDidCreateMode((mode) => { + const languageIdentifier = mode.getLanguageIdentifier(); + // Modes can be instantiated before the extension points have finished registering + this._extensionService.whenInstalledExtensionsRegistered().then(() => { + this._loadConfigurationsForMode(languageIdentifier); + }); + }); textMateService.onDidEncounterLanguage((languageId) => { this._loadConfigurationsForMode(this._modeService.getLanguageIdentifier(languageId)); }); @@ -510,7 +518,7 @@ const schema: IJSONSchema = { }, decreaseIndentPattern: { type: ['string', 'object'], - description: nls.localize('schema.indentationRules.decreaseIndentPattern', 'If a line matches this pattern, then all the lines after it should be unindendented once (until another rule matches).'), + description: nls.localize('schema.indentationRules.decreaseIndentPattern', 'If a line matches this pattern, then all the lines after it should be unindented once (until another rule matches).'), properties: { pattern: { type: 'string', diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.ts b/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.ts index 6816a6bc1..3d1cb7944 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.ts @@ -11,7 +11,6 @@ import { Color } from 'vs/base/common/color'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; import { escape } from 'vs/base/common/strings'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ContentWidgetPositionPreference, IActiveCodeEditor, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; @@ -180,7 +179,7 @@ class InspectTMScopesWidget extends Disposable implements IContentWidget { private readonly _notificationService: INotificationService; private readonly _model: ITextModel; private readonly _domNode: HTMLElement; - private readonly _grammar: TPromise; + private readonly _grammar: Promise; constructor( editor: IActiveCodeEditor, diff --git a/src/vs/workbench/parts/comments/common/commentModel.ts b/src/vs/workbench/parts/comments/common/commentModel.ts index 64d0efd2c..1d22f17c6 100644 --- a/src/vs/workbench/parts/comments/common/commentModel.ts +++ b/src/vs/workbench/parts/comments/common/commentModel.ts @@ -10,6 +10,10 @@ import { groupBy, firstIndex, flatten } from 'vs/base/common/arrays'; import { localize } from 'vs/nls'; import { values } from 'vs/base/common/map'; +export interface ICommentThreadChangedEvent extends CommentThreadChangedEvent { + owner: string; +} + export class CommentNode { threadId: string; range: IRange; @@ -53,19 +57,19 @@ export class ResourceWithCommentThreads { export class CommentsModel { resourceCommentThreads: ResourceWithCommentThreads[]; - commentThreadsMap: Map; + commentThreadsMap: Map; constructor() { this.resourceCommentThreads = []; - this.commentThreadsMap = new Map(); + this.commentThreadsMap = new Map(); } - public setCommentThreads(owner: number, commentThreads: CommentThread[]): void { + public setCommentThreads(owner: string, commentThreads: CommentThread[]): void { this.commentThreadsMap.set(owner, this.groupByResource(commentThreads)); this.resourceCommentThreads = flatten(values(this.commentThreadsMap)); } - public updateCommentThreads(event: CommentThreadChangedEvent): boolean { + public updateCommentThreads(event: ICommentThreadChangedEvent): boolean { const { owner, removed, changed, added } = event; if (!this.commentThreadsMap.has(owner)) { return false; diff --git a/src/vs/workbench/parts/comments/electron-browser/commentNode.ts b/src/vs/workbench/parts/comments/electron-browser/commentNode.ts index ed0f88781..40da787fd 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentNode.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentNode.ts @@ -56,7 +56,7 @@ export class CommentNode extends Disposable { constructor( public comment: modes.Comment, - private owner: number, + private owner: string, private resource: URI, private markdownRenderer: MarkdownRenderer, private themeService: IThemeService, @@ -75,6 +75,7 @@ export class CommentNode extends Disposable { if (comment.userIconPath) { const img = dom.append(avatar, dom.$('img.avatar')); img.src = comment.userIconPath.toString(); + img.onerror = _ => img.remove(); } const commentDetailsContainer = dom.append(this._domNode, dom.$('.review-comment-contents')); @@ -123,7 +124,7 @@ export class CommentNode extends Disposable { const container = dom.append(this._commentEditContainer, dom.$('.edit-textarea')); this._commentEditor = this.instantiationService.createInstance(SimpleCommentEditor, container, SimpleCommentEditor.getEditorOptions()); const resource = URI.parse(`comment:commentinput-${this.comment.commentId}-${Date.now()}.md`); - this._commentEditorModel = this.modelService.createModel('', this.modeService.getOrCreateModeByFilepathOrFirstLine(resource.path), resource, true); + this._commentEditorModel = this.modelService.createModel('', this.modeService.createByFilepathOrFirstLine(resource.path), resource, true); this._commentEditor.setModel(this._commentEditorModel); this._commentEditor.setValue(this.comment.body.value); @@ -185,7 +186,6 @@ export class CommentNode extends Disposable { private createDeleteAction(): Action { return new Action('comment.delete', nls.localize('label.delete', "Delete"), 'octicon octicon-x', true, () => { return this.dialogService.confirm({ - title: nls.localize('deleteCommentTitle', "Delete Comment"), message: nls.localize('confirmDelete', "Delete comment?"), type: 'question', primaryButton: nls.localize('label.delete', "Delete") diff --git a/src/vs/workbench/parts/comments/electron-browser/commentService.ts b/src/vs/workbench/parts/comments/electron-browser/commentService.ts index e802d8117..8b937d8ae 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentService.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentService.ts @@ -11,16 +11,23 @@ import { URI } from 'vs/base/common/uri'; import { Range } from 'vs/editor/common/core/range'; import { keys } from 'vs/base/common/map'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { ExtensionCommentProviderHandler } from 'vs/workbench/api/electron-browser/mainThreadComments'; +import { assign } from 'vs/base/common/objects'; +import { ICommentThreadChangedEvent } from 'vs/workbench/parts/comments/common/commentModel'; export const ICommentService = createDecorator('commentService'); export interface IResourceCommentThreadEvent { resource: URI; - commentInfos: CommentInfo[]; + commentInfos: ICommentInfo[]; +} + +export interface ICommentInfo extends CommentInfo { + owner: string; } export interface IWorkspaceCommentThreadsEvent { - ownerId: number; + ownerId: string; commentThreads: CommentThread[]; } @@ -28,20 +35,20 @@ export interface ICommentService { _serviceBrand: any; readonly onDidSetResourceCommentInfos: Event; readonly onDidSetAllCommentThreads: Event; - readonly onDidUpdateCommentThreads: Event; + readonly onDidUpdateCommentThreads: Event; readonly onDidSetDataProvider: Event; - readonly onDidDeleteDataProvider: Event; - setDocumentComments(resource: URI, commentInfos: CommentInfo[]): void; - setWorkspaceComments(owner: number, commentsByResource: CommentThread[]): void; - removeWorkspaceComments(owner: number): void; - registerDataProvider(owner: number, commentProvider: DocumentCommentProvider): void; - unregisterDataProvider(owner: number): void; - updateComments(event: CommentThreadChangedEvent): void; - createNewCommentThread(owner: number, resource: URI, range: Range, text: string): Promise; - replyToCommentThread(owner: number, resource: URI, range: Range, thread: CommentThread, text: string): Promise; - editComment(owner: number, resource: URI, comment: Comment, text: string): Promise; - deleteComment(owner: number, resource: URI, comment: Comment): Promise; - getComments(resource: URI): Promise; + readonly onDidDeleteDataProvider: Event; + setDocumentComments(resource: URI, commentInfos: ICommentInfo[]): void; + setWorkspaceComments(owner: string, commentsByResource: CommentThread[]): void; + removeWorkspaceComments(owner: string): void; + registerDataProvider(owner: string, commentProvider: ExtensionCommentProviderHandler): void; + unregisterDataProvider(owner: string): void; + updateComments(ownerId: string, event: CommentThreadChangedEvent): void; + createNewCommentThread(owner: string, resource: URI, range: Range, text: string): Promise; + replyToCommentThread(owner: string, resource: URI, range: Range, thread: CommentThread, text: string): Promise; + editComment(owner: string, resource: URI, comment: Comment, text: string): Promise; + deleteComment(owner: string, resource: URI, comment: Comment): Promise; + getComments(resource: URI): Promise; } export class CommentService extends Disposable implements ICommentService { @@ -50,8 +57,8 @@ export class CommentService extends Disposable implements ICommentService { private readonly _onDidSetDataProvider: Emitter = this._register(new Emitter()); readonly onDidSetDataProvider: Event = this._onDidSetDataProvider.event; - private readonly _onDidDeletetDataProvider: Emitter = this._register(new Emitter()); - readonly onDidDeleteDataProvider: Event = this._onDidDeletetDataProvider.event; + private readonly _onDidDeleteDataProvider: Emitter = this._register(new Emitter()); + readonly onDidDeleteDataProvider: Event = this._onDidDeleteDataProvider.event; private readonly _onDidSetResourceCommentInfos: Emitter = this._register(new Emitter()); readonly onDidSetResourceCommentInfos: Event = this._onDidSetResourceCommentInfos.event; @@ -59,72 +66,73 @@ export class CommentService extends Disposable implements ICommentService { private readonly _onDidSetAllCommentThreads: Emitter = this._register(new Emitter()); readonly onDidSetAllCommentThreads: Event = this._onDidSetAllCommentThreads.event; - private readonly _onDidUpdateCommentThreads: Emitter = this._register(new Emitter()); - readonly onDidUpdateCommentThreads: Event = this._onDidUpdateCommentThreads.event; + private readonly _onDidUpdateCommentThreads: Emitter = this._register(new Emitter()); + readonly onDidUpdateCommentThreads: Event = this._onDidUpdateCommentThreads.event; - private _commentProviders = new Map(); + private _commentProviders = new Map(); constructor() { super(); } - setDocumentComments(resource: URI, commentInfos: CommentInfo[]): void { + setDocumentComments(resource: URI, commentInfos: ICommentInfo[]): void { this._onDidSetResourceCommentInfos.fire({ resource, commentInfos }); } - setWorkspaceComments(owner: number, commentsByResource: CommentThread[]): void { + setWorkspaceComments(owner: string, commentsByResource: CommentThread[]): void { this._onDidSetAllCommentThreads.fire({ ownerId: owner, commentThreads: commentsByResource }); } - removeWorkspaceComments(owner: number): void { + removeWorkspaceComments(owner: string): void { this._onDidSetAllCommentThreads.fire({ ownerId: owner, commentThreads: [] }); } - registerDataProvider(owner: number, commentProvider: DocumentCommentProvider) { + registerDataProvider(owner: string, commentProvider: DocumentCommentProvider) { this._commentProviders.set(owner, commentProvider); this._onDidSetDataProvider.fire(); } - unregisterDataProvider(owner: number): void { + unregisterDataProvider(owner: string): void { this._commentProviders.delete(owner); - this._onDidDeletetDataProvider.fire(owner); + this._onDidDeleteDataProvider.fire(owner); } - updateComments(event: CommentThreadChangedEvent): void { - this._onDidUpdateCommentThreads.fire(event); + updateComments(ownerId: string, event: CommentThreadChangedEvent): void { + const evt: ICommentThreadChangedEvent = assign({}, event, { owner: ownerId }); + this._onDidUpdateCommentThreads.fire(evt); } - createNewCommentThread(owner: number, resource: URI, range: Range, text: string): Promise { + async createNewCommentThread(owner: string, resource: URI, range: Range, text: string): Promise { const commentProvider = this._commentProviders.get(owner); if (commentProvider) { - return commentProvider.createNewCommentThread(resource, range, text, CancellationToken.None); + return await commentProvider.createNewCommentThread(resource, range, text, CancellationToken.None); } return null; } - replyToCommentThread(owner: number, resource: URI, range: Range, thread: CommentThread, text: string): Promise { + async replyToCommentThread(owner: string, resource: URI, range: Range, thread: CommentThread, text: string): Promise { const commentProvider = this._commentProviders.get(owner); if (commentProvider) { - return commentProvider.replyToCommentThread(resource, range, thread, text, CancellationToken.None); + return await commentProvider.replyToCommentThread(resource, range, thread, text, CancellationToken.None); } return null; } - editComment(owner: number, resource: URI, comment: Comment, text: string): Promise { + editComment(owner: string, resource: URI, comment: Comment, text: string): Promise { const commentProvider = this._commentProviders.get(owner); if (commentProvider) { return commentProvider.editComment(resource, comment, text, CancellationToken.None); } - return null; + return Promise.resolve(void 0); } - deleteComment(owner: number, resource: URI, comment: Comment): Promise { + deleteComment(owner: string, resource: URI, comment: Comment): Promise { const commentProvider = this._commentProviders.get(owner); if (commentProvider) { @@ -134,12 +142,23 @@ export class CommentService extends Disposable implements ICommentService { return Promise.resolve(false); } - getComments(resource: URI): Promise { - const result: Promise[] = []; - for (const handle of keys(this._commentProviders)) { - const provider = this._commentProviders.get(handle); - if ((provider).provideDocumentComments) { - result.push((provider).provideDocumentComments(resource, CancellationToken.None)); + getComments(resource: URI): Promise { + const result: Promise[] = []; + for (const owner of keys(this._commentProviders)) { + const provider = this._commentProviders.get(owner); + if (provider.provideDocumentComments) { + result.push(provider.provideDocumentComments(resource, CancellationToken.None).then(commentInfo => { + if (commentInfo) { + return { + owner: owner, + threads: commentInfo.threads, + commentingRanges: commentInfo.commentingRanges, + reply: commentInfo.reply + }; + } else { + return null; + } + })); } } diff --git a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts index ad58f7bb5..ebc848b4e 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts @@ -63,14 +63,15 @@ export class ReviewZoneWidget extends ZoneWidget { private _collapseAction: Action; private _commentThread: modes.CommentThread; private _commentGlyph: CommentGlyphWidget; - private _owner: number; + private _owner: string; + private _pendingComment: string; private _localToDispose: IDisposable[]; private _globalToDispose: IDisposable[]; private _markdownRenderer: MarkdownRenderer; private _styleElement: HTMLStyleElement; private _error: HTMLElement; - public get owner(): number { + public get owner(): string { return this._owner; } public get commentThread(): modes.CommentThread { @@ -87,14 +88,16 @@ export class ReviewZoneWidget extends ZoneWidget { private dialogService: IDialogService, private notificationService: INotificationService, editor: ICodeEditor, - owner: number, + owner: string, commentThread: modes.CommentThread, + pendingComment: string, options: IOptions = {} ) { super(editor, options); this._resizeObserver = null; this._owner = owner; this._commentThread = commentThread; + this._pendingComment = pendingComment; this._isCollapsed = commentThread.collapsibleState !== modes.CommentThreadCollapsibleState.Expanded; this._globalToDispose = []; this._localToDispose = []; @@ -120,6 +123,16 @@ export class ReviewZoneWidget extends ZoneWidget { return this._onDidCreateThread.event; } + public getPosition(): IPosition | undefined { + let position: IPosition = this.position; + if (position) { + return position; + } + + position = this._commentGlyph.getPosition().position; + return position; + } + protected revealLine(lineNumber: number) { // we don't do anything here as we always do the reveal ourselves. } @@ -144,6 +157,18 @@ export class ReviewZoneWidget extends ZoneWidget { this.editor.revealRangeInCenter(this._commentThread.range); } + public getPendingComment(): string { + if (this._commentEditor) { + let model = this._commentEditor.getModel(); + + if (model && model.getValueLength() > 0) { // checking length is cheap + return model.getValue(); + } + } + + return null; + } + protected _fillContainer(container: HTMLElement): void { this.setCssClass('review-widget'); this._headElement = dom.$('.head'); @@ -264,7 +289,7 @@ export class ReviewZoneWidget extends ZoneWidget { this._commentEditor = this.instantiationService.createInstance(SimpleCommentEditor, this._commentForm, SimpleCommentEditor.getEditorOptions()); const modeId = hasExistingComments ? this._commentThread.threadId : ++INMEM_MODEL_ID; const resource = URI.parse(`${COMMENT_SCHEME}:commentinput-${modeId}.md`); - const model = this.modelService.createModel('', this.modeService.getOrCreateModeByFilepathOrFirstLine(resource.path), resource, true); + const model = this.modelService.createModel(this._pendingComment || '', this.modeService.createByFilepathOrFirstLine(resource.path), resource, true); this._localToDispose.push(model); this._commentEditor.setModel(model); this._localToDispose.push(this._commentEditor); @@ -309,7 +334,7 @@ export class ReviewZoneWidget extends ZoneWidget { attachButtonStyler(button, this.themeService); button.label = 'Add comment'; - button.enabled = false; + button.enabled = model.getValueLength() > 0; this._localToDispose.push(this._commentEditor.onDidChangeModelContent(_ => { if (this._commentEditor.getValue()) { button.enabled = true; @@ -339,6 +364,11 @@ export class ReviewZoneWidget extends ZoneWidget { // If there are no existing comments, place focus on the text area. This must be done after show, which also moves focus. if (this._commentThread.reply && !this._commentThread.comments.length) { this._commentEditor.focus(); + } else if (this._commentEditor.getModel().getValueLength() > 0) { + if (!dom.hasClass(this._commentForm, 'expand')) { + dom.addClass(this._commentForm, 'expand'); + } + this._commentEditor.focus(); } } @@ -408,6 +438,7 @@ export class ReviewZoneWidget extends ZoneWidget { if (newCommentThread) { this._commentEditor.setValue(''); + this._pendingComment = ''; if (dom.hasClass(this._commentForm, 'expand')) { dom.removeClass(this._commentForm, 'expand'); } @@ -442,17 +473,20 @@ export class ReviewZoneWidget extends ZoneWidget { this._headingLabel.setAttribute('aria-label', label); } + private expandReplyArea() { + if (!dom.hasClass(this._commentForm, 'expand')) { + dom.addClass(this._commentForm, 'expand'); + this._commentEditor.focus(); + } + } + private createReplyButton() { this._reviewThreadReplyButton = dom.append(this._commentForm, dom.$('button.review-thread-reply-button')); this._reviewThreadReplyButton.title = nls.localize('reply', "Reply..."); this._reviewThreadReplyButton.textContent = nls.localize('reply', "Reply..."); // bind click/escape actions for reviewThreadReplyButton and textArea - this._reviewThreadReplyButton.onclick = () => { - if (!dom.hasClass(this._commentForm, 'expand')) { - dom.addClass(this._commentForm, 'expand'); - this._commentEditor.focus(); - } - }; + this._localToDispose.push(dom.addDisposableListener(this._reviewThreadReplyButton, 'click', _ => this.expandReplyArea())); + this._localToDispose.push(dom.addDisposableListener(this._reviewThreadReplyButton, 'focus', _ => this.expandReplyArea())); this._commentEditor.onDidBlurEditorWidget(() => { if (this._commentEditor.getModel().getValueLength() === 0 && dom.hasClass(this._commentForm, 'expand')) { diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts b/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts index 09437912c..6d7ef69d2 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts @@ -13,7 +13,7 @@ import { ICodeEditor, IEditorMouseEvent, IViewZone, MouseTargetType } from 'vs/e import { registerEditorContribution, EditorAction, registerEditorAction } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; -import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { IEditorContribution, IModelChangedEvent } from 'vs/editor/common/editorCommon'; import { IRange } from 'vs/editor/common/core/range'; import * as modes from 'vs/editor/common/modes'; import { peekViewResultsBackground, peekViewResultsSelectionBackground, peekViewTitleBackground } from 'vs/editor/contrib/referenceSearch/referencesWidget'; @@ -24,7 +24,7 @@ import { editorForeground, registerColor } from 'vs/platform/theme/common/colorR import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { CommentThreadCollapsibleState } from 'vs/workbench/api/node/extHostTypes'; import { ReviewZoneWidget, COMMENTEDITOR_DECORATION_KEY } from 'vs/workbench/parts/comments/electron-browser/commentThreadWidget'; -import { ICommentService } from 'vs/workbench/parts/comments/electron-browser/commentService'; +import { ICommentService, ICommentInfo } from 'vs/workbench/parts/comments/electron-browser/commentService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; @@ -34,6 +34,7 @@ import { Color, RGBA } from 'vs/base/common/color'; import { IMarginData } from 'vs/editor/browser/controller/mouseTarget'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; export const ctxReviewPanelVisible = new RawContextKey('reviewPanelVisible', false); @@ -63,11 +64,11 @@ export const overviewRulerCommentingRangeForeground = registerColor('editorGutte class CommentingRangeDecoration { private _decorationId: string; - get id(): string { + public get id(): string { return this._decorationId; } - constructor(private _editor: ICodeEditor, private _ownerId: number, private _range: IRange, private _reply: modes.Command, commentingOptions: ModelDecorationOptions) { + constructor(private _editor: ICodeEditor, private _ownerId: string, private _range: IRange, private _reply: modes.Command, commentingOptions: ModelDecorationOptions) { const startLineNumber = _range.startLineNumber; const endLineNumber = _range.endLineNumber; let commentingRangeDecorations = [{ @@ -84,18 +85,18 @@ class CommentingRangeDecoration { } } - getCommentAction(): { replyCommand: modes.Command, ownerId: number } { + public getCommentAction(): { replyCommand: modes.Command, ownerId: string } { return { replyCommand: this._reply, ownerId: this._ownerId }; } - getOriginalRange() { + public getOriginalRange() { return this._range; } - getActiveRange() { + public getActiveRange() { return this._editor.getModel().getDecorationRange(this._decorationId); } } @@ -122,7 +123,7 @@ class CommentingRangeDecorator { this.commentsOptions = CommentingRangeDecorator.createDecoration('comment-thread', overviewRulerCommentingRangeForeground, options); } - update(editor: ICodeEditor, commentInfos: modes.CommentInfo[]) { + public update(editor: ICodeEditor, commentInfos: ICommentInfo[]) { let model = editor.getModel(); if (!model) { return; @@ -142,7 +143,7 @@ class CommentingRangeDecorator { this.commentingRangeDecorations = commentingRangeDecorations; } - getMatchedCommentAction(line: number) { + public getMatchedCommentAction(line: number) { for (let i = 0; i < this.commentingRangeDecorations.length; i++) { let range = this.commentingRangeDecorations[i].getActiveRange(); @@ -154,7 +155,7 @@ class CommentingRangeDecorator { return null; } - dispose(): void { + public dispose(): void { this.disposables = dispose(this.disposables); this.commentingRangeDecorations = []; } @@ -167,11 +168,14 @@ export class ReviewController implements IEditorContribution { private _newCommentWidget: ReviewZoneWidget; private _commentWidgets: ReviewZoneWidget[]; private _reviewPanelVisible: IContextKey; - private _commentInfos: modes.CommentInfo[]; + private _commentInfos: ICommentInfo[]; private _commentingRangeDecorator: CommentingRangeDecorator; private mouseDownInfo: { lineNumber: number } | null = null; private _commentingRangeSpaceReserved = false; + private _computePromise: CancelablePromise | null; + private _pendingCommentCache: { [key: number]: { [key: string]: string } }; + private _pendingNewCommentCache: { [key: string]: { lineNumber: number, replyCommand: modes.Command, ownerId: string, pendingComment: string } }; constructor( editor: ICodeEditor, @@ -191,20 +195,25 @@ export class ReviewController implements IEditorContribution { this.localToDispose = []; this._commentInfos = []; this._commentWidgets = []; + this._pendingCommentCache = {}; + this._pendingNewCommentCache = {}; this._newCommentWidget = null; + this._computePromise = null; this._reviewPanelVisible = ctxReviewPanelVisible.bindTo(contextKeyService); this._commentingRangeDecorator = new CommentingRangeDecorator(); - this.globalToDispose.push(this.commentService.onDidDeleteDataProvider(e => { + this.globalToDispose.push(this.commentService.onDidDeleteDataProvider(ownerId => { // Remove new comment widget and glyph, refresh comments - if (this._newCommentWidget) { + if (this._newCommentWidget && this._newCommentWidget.owner === ownerId) { this._newCommentWidget.dispose(); this._newCommentWidget = null; } - this.getComments(); + delete this._pendingCommentCache[ownerId]; + this.beginCompute(); })); + this.globalToDispose.push(this.commentService.onDidSetDataProvider(_ => this.beginCompute())); this.globalToDispose.push(this.commentService.onDidSetResourceCommentInfos(e => { const editorURI = this.editor && this.editor.getModel() && this.editor.getModel().uri; @@ -213,30 +222,40 @@ export class ReviewController implements IEditorContribution { } })); - this.globalToDispose.push(this.commentService.onDidSetDataProvider(_ => this.getComments())); - - this.globalToDispose.push(this.editor.onDidChangeModel(() => this.onModelChanged())); + this.globalToDispose.push(this.editor.onDidChangeModel(e => this.onModelChanged(e))); this.codeEditorService.registerDecorationType(COMMENTEDITOR_DECORATION_KEY, {}); + this.beginCompute(); } - private getComments(): void { - const editorURI = this.editor && this.editor.getModel() && this.editor.getModel().uri; + private beginCompute(): Promise { + this._computePromise = createCancelablePromise(token => { + const editorURI = this.editor && this.editor.getModel() && this.editor.getModel().uri; - if (editorURI) { - this.commentService.getComments(editorURI).then(commentInfos => { - this.setComments(commentInfos.filter(commentInfo => commentInfo !== null)); - }, error => console.log(error)); - } + if (editorURI) { + return this.commentService.getComments(editorURI); + } + + return Promise.resolve([]); + }); + + return this._computePromise.then(commentInfos => { + this.setComments(commentInfos.filter(commentInfo => commentInfo !== null)); + this._computePromise = null; + }, error => console.log(error)); } public static get(editor: ICodeEditor): ReviewController { return editor.getContribution(ID); } - public revealCommentThread(threadId: string, commentId?: string): void { + public revealCommentThread(threadId: string, commentId: string, fetchOnceIfNotExist: boolean): void { const commentThreadWidget = this._commentWidgets.filter(widget => widget.commentThread.threadId === threadId); if (commentThreadWidget.length === 1) { commentThreadWidget[0].reveal(commentId); + } else if (fetchOnceIfNotExist) { + this.beginCompute().then(_ => { + this.revealCommentThread(threadId, commentId, false); + }); } } @@ -290,11 +309,11 @@ export class ReviewController implements IEditorContribution { } } - getId(): string { + public getId(): string { return ID; } - dispose(): void { + public dispose(): void { this.globalToDispose = dispose(this.globalToDispose); this.localToDispose = dispose(this.localToDispose); @@ -307,18 +326,44 @@ export class ReviewController implements IEditorContribution { this.editor = null; } - public onModelChanged(): void { + public onModelChanged(e: IModelChangedEvent): void { this.localToDispose = dispose(this.localToDispose); if (this._newCommentWidget) { - // todo@peng store view state. + let pendingNewComment = this._newCommentWidget.getPendingComment(); + + if (e.oldModelUrl) { + if (pendingNewComment) { + // we can't fetch zone widget's position as the model is already gone + const position = this._newCommentWidget.getPosition(); + if (position) { + this._pendingNewCommentCache[e.oldModelUrl.toString()] = { + lineNumber: position.lineNumber, + ownerId: this._newCommentWidget.owner, + replyCommand: this._newCommentWidget.commentThread.reply, + pendingComment: pendingNewComment + }; + } + } else { + // clear cache if it is empty + delete this._pendingNewCommentCache[e.oldModelUrl.toString()]; + } + } + this._newCommentWidget.dispose(); this._newCommentWidget = null; + } else { + if (e.oldModelUrl) { + // remove pending new comment cache as there is no newCommentWidget anymore + delete this._pendingNewCommentCache[e.oldModelUrl.toString()]; + } } - this._commentWidgets.forEach(zone => { - zone.dispose(); - }); - this._commentWidgets = []; + this.removeCommentWidgetsAndStoreCache(); + + if (e.newModelUrl && this._pendingNewCommentCache[e.newModelUrl.toString()]) { + let newCommentCache = this._pendingNewCommentCache[e.newModelUrl.toString()]; + this.addComment(newCommentCache.lineNumber, newCommentCache.replyCommand, newCommentCache.ownerId, newCommentCache.pendingComment); + } this.localToDispose.push(this.editor.onMouseDown(e => this.onEditorMouseDown(e))); this.localToDispose.push(this.editor.onMouseUp(e => this.onEditorMouseUp(e))); @@ -355,28 +400,24 @@ export class ReviewController implements IEditorContribution { } }); added.forEach(thread => { - let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.notificationService, this.editor, e.owner, thread, {}); + let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.notificationService, this.editor, e.owner, thread, null, {}); zoneWidget.display(thread.range.startLineNumber, this._commentingRangeDecorator.commentsOptions); this._commentWidgets.push(zoneWidget); this._commentInfos.filter(info => info.owner === e.owner)[0].threads.push(thread); }); })); + + this.beginCompute(); } - private addComment(lineNumber: number) { + private addComment(lineNumber: number, replyCommand: modes.Command, ownerId: string, pendingComment: string) { if (this._newCommentWidget !== null) { this.notificationService.warn(`Please submit the comment at line ${this._newCommentWidget.position.lineNumber} before creating a new one.`); return; } - let newCommentInfo = this._commentingRangeDecorator.getMatchedCommentAction(lineNumber); - if (!newCommentInfo) { - return; - } - // add new comment this._reviewPanelVisible.set(true); - const { replyCommand, ownerId } = newCommentInfo; this._newCommentWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.notificationService, this.editor, ownerId, { threadId: null, resource: null, @@ -389,7 +430,7 @@ export class ReviewController implements IEditorContribution { }, reply: replyCommand, collapsibleState: CommentThreadCollapsibleState.Expanded, - }, {}); + }, pendingComment, {}); this.localToDispose.push(this._newCommentWidget.onDidClose(e => { this._newCommentWidget = null; @@ -457,11 +498,21 @@ export class ReviewController implements IEditorContribution { if (e.target.element.className.indexOf('comment-diff-added') >= 0) { const lineNumber = e.target.position.lineNumber; - this.addComment(lineNumber); + let newCommentInfo = this._commentingRangeDecorator.getMatchedCommentAction(lineNumber); + if (!newCommentInfo) { + return; + } + const { replyCommand, ownerId } = newCommentInfo; + this.addComment(lineNumber, replyCommand, ownerId, null); } } - setComments(commentInfos: modes.CommentInfo[]): void { + + private setComments(commentInfos: ICommentInfo[]): void { + if (!this.editor) { + return; + } + this._commentInfos = commentInfos; let lineDecorationsWidth: number = this.editor.getConfiguration().layoutInfo.decorationsWidth; @@ -494,15 +545,21 @@ export class ReviewController implements IEditorContribution { } // create viewzones - this._commentWidgets.forEach(zone => { - zone.dispose(); - }); - - this._commentWidgets = []; + this.removeCommentWidgetsAndStoreCache(); this._commentInfos.forEach(info => { + let providerCacheStore = this._pendingCommentCache[info.owner]; info.threads.forEach(thread => { - let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.notificationService, this.editor, info.owner, thread, {}); + let pendingComment: string = null; + if (providerCacheStore) { + pendingComment = providerCacheStore[thread.threadId]; + } + + if (pendingComment) { + thread.collapsibleState = modes.CommentThreadCollapsibleState.Expanded; + } + + let zoneWidget = new ReviewZoneWidget(this.instantiationService, this.modeService, this.modelService, this.themeService, this.commentService, this.openerService, this.dialogService, this.notificationService, this.editor, info.owner, thread, pendingComment, {}); zoneWidget.display(thread.range.startLineNumber, this._commentingRangeDecorator.commentsOptions); this._commentWidgets.push(zoneWidget); }); @@ -530,6 +587,31 @@ export class ReviewController implements IEditorContribution { this.editor.focus(); this.editor.revealRangeInCenter(this.editor.getSelection()); } + + private removeCommentWidgetsAndStoreCache() { + if (this._commentWidgets) { + this._commentWidgets.forEach(zone => { + let pendingComment = zone.getPendingComment(); + let providerCacheStore = this._pendingCommentCache[zone.owner]; + + if (pendingComment) { + if (!providerCacheStore) { + this._pendingCommentCache[zone.owner] = {}; + } + + this._pendingCommentCache[zone.owner][zone.commentThread.threadId] = pendingComment; + } else { + if (providerCacheStore) { + delete providerCacheStore[zone.commentThread.threadId]; + } + } + + zone.dispose(); + }); + } + + this._commentWidgets = []; + } } export class NextCommentThreadAction extends EditorAction { diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts b/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts index 84e80059d..213a134d4 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts @@ -9,13 +9,12 @@ import { IAction } from 'vs/base/common/actions'; import { debounceEvent } from 'vs/base/common/event'; import { CollapseAllAction, DefaultAccessibilityProvider, DefaultController, DefaultDragAndDrop } from 'vs/base/parts/tree/browser/treeDefaults'; import { isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; -import { CommentThreadChangedEvent } from 'vs/editor/common/modes'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TreeResourceNavigator, WorkbenchTree } from 'vs/platform/list/browser/listService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { Panel } from 'vs/workbench/browser/panel'; -import { CommentNode, CommentsModel, ResourceWithCommentThreads } from 'vs/workbench/parts/comments/common/commentModel'; +import { CommentNode, CommentsModel, ResourceWithCommentThreads, ICommentThreadChangedEvent } from 'vs/workbench/parts/comments/common/commentModel'; import { ReviewController } from 'vs/workbench/parts/comments/electron-browser/commentsEditorContribution'; import { CommentsDataFilter, CommentsDataSource, CommentsModelRenderer } from 'vs/workbench/parts/comments/electron-browser/commentsTreeViewer'; import { ICommentService, IWorkspaceCommentThreadsEvent } from 'vs/workbench/parts/comments/electron-browser/commentService'; @@ -167,29 +166,18 @@ export class CommentsPanel extends Panel { const control = this.editorService.activeTextEditorWidget; if (threadToReveal && isCodeEditor(control)) { const controller = ReviewController.get(control); - controller.revealCommentThread(threadToReveal, commentToReveal); + controller.revealCommentThread(threadToReveal, commentToReveal, false); } return true; } - let setCommentsForFile = new Promise((resolve, reject) => { - this.commentService.onDidSetResourceCommentInfos(e => { - if (e.resource.toString() === element.resource.toString()) { - resolve(); - } - }); - }); - const threadToReveal = element instanceof ResourceWithCommentThreads ? element.commentThreads[0].threadId : element.threadId; const commentToReveal = element instanceof ResourceWithCommentThreads ? element.commentThreads[0].comment : element.comment; if (commentToReveal.command) { - Promise.all([ - this.commandService.executeCommand(commentToReveal.command.id, ...commentToReveal.command.arguments), - setCommentsForFile - ]).then(_ => { + this.commandService.executeCommand(commentToReveal.command.id, ...commentToReveal.command.arguments).then(_ => { let activeWidget = this.editorService.activeTextEditorWidget; if (isDiffEditor(activeWidget)) { const originalEditorWidget = activeWidget.getOriginalEditor(); @@ -203,7 +191,7 @@ export class CommentsPanel extends Panel { } if (controller) { - controller.revealCommentThread(threadToReveal, commentToReveal.commentId); + controller.revealCommentThread(threadToReveal, commentToReveal.commentId, true); } } else { let activeEditor = this.editorService.activeEditor; @@ -212,7 +200,7 @@ export class CommentsPanel extends Panel { const control = this.editorService.activeTextEditorWidget; if (threadToReveal && isCodeEditor(control)) { const controller = ReviewController.get(control); - controller.revealCommentThread(threadToReveal, commentToReveal.commentId); + controller.revealCommentThread(threadToReveal, commentToReveal.commentId, true); } } } @@ -220,38 +208,33 @@ export class CommentsPanel extends Panel { return true; }); } else { - Promise.all([this.editorService.openEditor({ + this.editorService.openEditor({ resource: element.resource, options: { pinned: pinned, preserveFocus: preserveFocus, selection: range } - }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP), setCommentsForFile]).then(vals => { - let editor = vals[0]; + }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => { const control = editor.getControl(); if (threadToReveal && isCodeEditor(control)) { const controller = ReviewController.get(control); - controller.revealCommentThread(threadToReveal, commentToReveal.commentId); + controller.revealCommentThread(threadToReveal, commentToReveal.commentId, true); } - setCommentsForFile = null; }); } - return true; } - public setVisible(visible: boolean): Promise { + public setVisible(visible: boolean): void { const wasVisible = this.isVisible(); - return super.setVisible(visible) - .then(() => { - if (this.isVisible()) { - if (!wasVisible) { - this.refresh(); - } - } - }); + super.setVisible(visible); + if (this.isVisible()) { + if (!wasVisible) { + this.refresh(); + } + } } private refresh(): void { @@ -272,7 +255,7 @@ export class CommentsPanel extends Panel { this.refresh(); } - private onCommentsUpdated(e: CommentThreadChangedEvent): void { + private onCommentsUpdated(e: ICommentThreadChangedEvent): void { const didUpdate = this.commentsModel.updateCommentThreads(e); if (didUpdate) { this.refresh(); diff --git a/src/vs/workbench/parts/debug/browser/baseDebugView.ts b/src/vs/workbench/parts/debug/browser/baseDebugView.ts index 25f5179e0..e57e1f66a 100644 --- a/src/vs/workbench/parts/debug/browser/baseDebugView.ts +++ b/src/vs/workbench/parts/debug/browser/baseDebugView.ts @@ -200,6 +200,7 @@ export class BaseDebugController extends WorkbenchTreeController { super(options, configurationService); this.contributedContextMenu = menuService.createMenu(menuId, contextKeyService); + this.disposables.push(this.contributedContextMenu); } public onContextMenu(tree: ITree, element: IEnablement, event: ContextMenuEvent, focusElement = true): boolean { @@ -218,10 +219,11 @@ export class BaseDebugController extends WorkbenchTreeController { const anchor = { x: event.posx, y: event.posy }; this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => this.actionProvider.getSecondaryActions(tree, element).then(actions => { + getActions: () => { + const actions = this.actionProvider.getSecondaryActions(tree, element); fillInContextMenuActions(this.contributedContextMenu, { arg: this.getContext(element) }, actions, this.contextMenuService); return actions; - }), + }, onHide: (wasCancelled?: boolean) => { if (wasCancelled) { tree.domFocus(); diff --git a/src/vs/workbench/parts/debug/browser/breakpointsView.ts b/src/vs/workbench/parts/debug/browser/breakpointsView.ts index 3c6480706..c74d9c262 100644 --- a/src/vs/workbench/parts/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/parts/debug/browser/breakpointsView.ts @@ -16,7 +16,6 @@ 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 { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IListVirtualDelegate, IListContextMenuEvent, IListRenderer } from 'vs/base/browser/ui/list/list'; import { IEditor } from 'vs/workbench/common/editor'; @@ -172,7 +171,7 @@ export class BreakpointsView extends ViewletPanel { this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, - getActions: () => Promise.resolve(actions), + getActions: () => actions, getActionsContext: () => element }); } @@ -192,12 +191,11 @@ export class BreakpointsView extends ViewletPanel { } } - public setVisible(visible: boolean): TPromise { - return super.setVisible(visible).then(() => { - if (visible && this.needsRefresh) { - this.onBreakpointsChange(); - } - }); + public setVisible(visible: boolean): void { + super.setVisible(visible); + if (visible && this.needsRefresh) { + this.onBreakpointsChange(); + } } private onBreakpointsChange(): void { @@ -554,7 +552,7 @@ class FunctionBreakpointInputRenderer implements IListRenderer { +export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolean, preserveFocus: boolean, debugService: IDebugService, editorService: IEditorService): Thenable { if (breakpoint.uri.scheme === DEBUG_SCHEME && debugService.state === State.Inactive) { return Promise.resolve(null); } diff --git a/src/vs/workbench/parts/debug/browser/debugActions.ts b/src/vs/workbench/parts/debug/browser/debugActions.ts index ce0e0b6a9..2aa2fbc22 100644 --- a/src/vs/workbench/parts/debug/browser/debugActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugActions.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import * as lifecycle from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; @@ -44,7 +43,7 @@ export abstract class AbstractDebugAction extends Action { this.updateEnablement(); } - public run(e?: any): TPromise { + public run(e?: any): Thenable { throw new Error('implement me'); } @@ -100,7 +99,7 @@ export class ConfigureAction extends AbstractDebugAction { this.class = this.debugService.getConfigurationManager().selectedConfiguration.name ? 'debug-action configure' : 'debug-action configure notification'; } - public run(event?: any): TPromise { + public run(event?: any): Thenable { if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { this.notificationService.info(nls.localize('noFolderDebugConfig', "Please first open a folder in order to do advanced debug configuration.")); return Promise.resolve(null); @@ -134,7 +133,7 @@ export class StartAction extends AbstractDebugAction { this.toDispose.push(this.contextService.onDidChangeWorkbenchState(() => this.updateEnablement())); } - public run(): TPromise { + public run(): Thenable { const configurationManager = this.debugService.getConfigurationManager(); let launch = configurationManager.selectedConfiguration.launch; if (!launch || launch.getConfigurationNames().length === 0) { @@ -198,7 +197,7 @@ export class SelectAndStartAction extends AbstractDebugAction { super(id, label, undefined, debugService, keybindingService); } - public run(): TPromise { + public run(): Thenable { return this.quickOpenService.show('debug '); } } @@ -228,7 +227,7 @@ export class RestartAction extends AbstractDebugAction { this.updateLabel(session && session.configuration.request === 'attach' ? RestartAction.RECONNECT_LABEL : RestartAction.LABEL); } - public run(session: IDebugSession): TPromise { + public run(session: IDebugSession): Thenable { if (!session || !session.getId) { session = this.debugService.getViewModel().focusedSession; } @@ -258,7 +257,7 @@ export class StepOverAction extends AbstractDebugAction { super(id, label, 'debug-action step-over', debugService, keybindingService, 20); } - public run(thread: IThread): TPromise { + public run(thread: IThread): Promise { if (!(thread instanceof Thread)) { thread = this.debugService.getViewModel().focusedThread; } @@ -279,7 +278,7 @@ export class StepIntoAction extends AbstractDebugAction { super(id, label, 'debug-action step-into', debugService, keybindingService, 30); } - public run(thread: IThread): TPromise { + public run(thread: IThread): Promise { if (!(thread instanceof Thread)) { thread = this.debugService.getViewModel().focusedThread; } @@ -300,7 +299,7 @@ export class StepOutAction extends AbstractDebugAction { super(id, label, 'debug-action step-out', debugService, keybindingService, 40); } - public run(thread: IThread): TPromise { + public run(thread: IThread): Promise { if (!(thread instanceof Thread)) { thread = this.debugService.getViewModel().focusedThread; } @@ -321,7 +320,7 @@ export class StopAction extends AbstractDebugAction { super(id, label, 'debug-action stop', debugService, keybindingService, 80); } - public run(session: IDebugSession): TPromise { + public run(session: IDebugSession): Promise { if (!session || !session.getId) { session = this.debugService.getViewModel().focusedSession; } @@ -342,7 +341,7 @@ export class DisconnectAction extends AbstractDebugAction { super(id, label, 'debug-action disconnect', debugService, keybindingService, 80); } - public run(): TPromise { + public run(): Promise { const session = this.debugService.getViewModel().focusedSession; return this.debugService.stopSession(session); } @@ -360,7 +359,7 @@ export class ContinueAction extends AbstractDebugAction { super(id, label, 'debug-action continue', debugService, keybindingService, 10); } - public run(thread: IThread): TPromise { + public run(thread: IThread): Promise { if (!(thread instanceof Thread)) { thread = this.debugService.getViewModel().focusedThread; } @@ -381,9 +380,14 @@ export class PauseAction extends AbstractDebugAction { super(id, label, 'debug-action pause', debugService, keybindingService, 10); } - public run(thread: IThread): TPromise { + public run(thread: IThread): Promise { if (!(thread instanceof Thread)) { thread = this.debugService.getViewModel().focusedThread; + if (!thread) { + const session = this.debugService.getViewModel().focusedSession; + const threads = session && session.getAllThreads(); + thread = threads && threads.length ? threads[0] : undefined; + } } return thread ? thread.pause() : Promise.resolve(null); @@ -402,7 +406,7 @@ export class TerminateThreadAction extends AbstractDebugAction { super(id, label, undefined, debugService, keybindingService); } - public run(thread: IThread): TPromise { + public run(thread: IThread): Promise { if (!(thread instanceof Thread)) { thread = this.debugService.getViewModel().focusedThread; } @@ -423,7 +427,7 @@ export class RestartFrameAction extends AbstractDebugAction { super(id, label, undefined, debugService, keybindingService); } - public run(frame: IStackFrame): TPromise { + public run(frame: IStackFrame): Promise { if (!frame) { frame = this.debugService.getViewModel().focusedStackFrame; } @@ -440,7 +444,7 @@ export class RemoveBreakpointAction extends AbstractDebugAction { super(id, label, 'debug-action remove', debugService, keybindingService); } - public run(breakpoint: IBreakpoint): TPromise { + public run(breakpoint: IBreakpoint): Promise { return breakpoint instanceof Breakpoint ? this.debugService.removeBreakpoints(breakpoint.getId()) : this.debugService.removeFunctionBreakpoints(breakpoint.getId()); } @@ -455,7 +459,7 @@ export class RemoveAllBreakpointsAction extends AbstractDebugAction { this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); } - public run(): TPromise { + public run(): Promise { return Promise.all([this.debugService.removeBreakpoints(), this.debugService.removeFunctionBreakpoints()]); } @@ -474,7 +478,7 @@ export class EnableAllBreakpointsAction extends AbstractDebugAction { this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); } - public run(): TPromise { + public run(): Promise { return this.debugService.enableOrDisableBreakpoints(true); } @@ -493,7 +497,7 @@ export class DisableAllBreakpointsAction extends AbstractDebugAction { this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); } - public run(): TPromise { + public run(): Promise { return this.debugService.enableOrDisableBreakpoints(false); } @@ -518,7 +522,7 @@ export class ToggleBreakpointsActivatedAction extends AbstractDebugAction { })); } - public run(): TPromise { + public run(): Promise { return this.debugService.setBreakpointsActivated(!this.debugService.getModel().areBreakpointsActivated()); } @@ -536,7 +540,7 @@ export class ReapplyBreakpointsAction extends AbstractDebugAction { this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); } - public run(): TPromise { + public run(): Promise { return this.debugService.setBreakpointsActivated(true); } @@ -556,7 +560,7 @@ export class AddFunctionBreakpointAction extends AbstractDebugAction { this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.updateEnablement())); } - public run(): TPromise { + public run(): Promise { this.debugService.addFunctionBreakpoint(); return Promise.resolve(null); } @@ -575,7 +579,7 @@ export class SetValueAction extends AbstractDebugAction { super(id, label, null, debugService, keybindingService); } - public run(): TPromise { + public run(): Promise { if (this.variable instanceof Variable) { this.debugService.getViewModel().setSelectedExpression(this.variable); } @@ -599,7 +603,7 @@ export class AddWatchExpressionAction extends AbstractDebugAction { this.toDispose.push(this.debugService.getModel().onDidChangeWatchExpressions(() => this.updateEnablement())); } - public run(): TPromise { + public run(): Promise { this.debugService.addWatchExpression(); return Promise.resolve(undefined); } @@ -617,7 +621,7 @@ export class EditWatchExpressionAction extends AbstractDebugAction { super(id, label, undefined, debugService, keybindingService); } - public run(expression: Expression): TPromise { + public run(expression: Expression): Promise { this.debugService.getViewModel().setSelectedExpression(expression); return Promise.resolve(null); } @@ -632,7 +636,7 @@ export class AddToWatchExpressionsAction extends AbstractDebugAction { this.updateEnablement(); } - public run(): TPromise { + public run(): Promise { this.debugService.addWatchExpression(this.variable.evaluateName); return Promise.resolve(undefined); } @@ -650,7 +654,7 @@ export class RemoveWatchExpressionAction extends AbstractDebugAction { super(id, label, undefined, debugService, keybindingService); } - public run(expression: Expression): TPromise { + public run(expression: Expression): Promise { this.debugService.removeWatchExpressions(expression.getId()); return Promise.resolve(null); } @@ -665,7 +669,7 @@ export class RemoveAllWatchExpressionsAction extends AbstractDebugAction { this.toDispose.push(this.debugService.getModel().onDidChangeWatchExpressions(() => this.updateEnablement())); } - public run(): TPromise { + public run(): Promise { this.debugService.removeWatchExpressions(); return Promise.resolve(null); } @@ -716,8 +720,9 @@ export class FocusReplAction extends Action { super(id, label); } - public run(): TPromise { - return this.panelService.openPanel(REPL_ID, true); + public run(): Promise { + this.panelService.openPanel(REPL_ID, true); + return Promise.resolve(null); } } @@ -733,7 +738,7 @@ export class FocusSessionAction extends AbstractDebugAction { super(id, label, null, debugService, keybindingService, 100); } - public run(sessionName: string): TPromise { + public run(sessionName: string): Thenable { const session = this.debugService.getModel().getSessions().filter(p => p.getLabel() === sessionName).pop(); this.debugService.focusStackFrame(undefined, undefined, session, true); const stackFrame = this.debugService.getViewModel().focusedStackFrame; @@ -754,7 +759,7 @@ export class StepBackAction extends AbstractDebugAction { super(id, label, 'debug-action step-back', debugService, keybindingService, 50); } - public run(thread: IThread): TPromise { + public run(thread: IThread): Promise { if (!(thread instanceof Thread)) { thread = this.debugService.getViewModel().focusedThread; } @@ -777,7 +782,7 @@ export class ReverseContinueAction extends AbstractDebugAction { super(id, label, 'debug-action reverse-continue', debugService, keybindingService, 60); } - public run(thread: IThread): TPromise { + public run(thread: IThread): Promise { if (!(thread instanceof Thread)) { thread = this.debugService.getViewModel().focusedThread; } @@ -797,7 +802,7 @@ export class ReplCollapseAllAction extends CollapseAction { super(viewer, true, undefined); } - public run(event?: any): TPromise { + public run(event?: any): Thenable { return super.run(event).then(() => { this.toFocus.focus(); }); diff --git a/src/vs/workbench/parts/debug/browser/debugCommands.ts b/src/vs/workbench/parts/debug/browser/debugCommands.ts index 40f0e10a6..88d709b6e 100644 --- a/src/vs/workbench/parts/debug/browser/debugCommands.ts +++ b/src/vs/workbench/parts/debug/browser/debugCommands.ts @@ -22,6 +22,7 @@ import { openBreakpointSource } from 'vs/workbench/parts/debug/browser/breakpoin import { INotificationService } from 'vs/platform/notification/common/notification'; import { InputFocusedContext } from 'vs/platform/workbench/common/contextkeys'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { PanelFocusContext } from 'vs/workbench/browser/parts/panel/panelPart'; export const ADD_CONFIGURATION_ID = 'debug.addConfiguration'; export const TOGGLE_INLINE_BREAKPOINT_ID = 'editor.debug.action.toggleInlineBreakpoint'; @@ -180,7 +181,7 @@ export function registerCommands(): void { const manager = accessor.get(IDebugService).getConfigurationManager(); if (accessor.get(IWorkspaceContextService).getWorkbenchState() === WorkbenchState.EMPTY) { accessor.get(INotificationService).info(nls.localize('noFolderDebugConfig', "Please first open a folder in order to do advanced debug configuration.")); - return Promise.resolve(null); + return undefined; } const launch = manager.getLaunches().filter(l => l.uri.toString() === launchUri).pop() || manager.selectedConfiguration.launch; @@ -208,14 +209,14 @@ export function registerCommands(): void { .filter(bp => (bp.column === position.column || !bp.column && position.column <= 1)).pop(); if (bp) { - return Promise.resolve(null); + return undefined; } if (debugService.getConfigurationManager().canSetBreakpointsIn(widget.getModel())) { return debugService.addBreakpoints(modelUri, [{ lineNumber: position.lineNumber, column: position.column > 1 ? position.column : undefined }], 'debugCommands.inlineBreakpointCommand'); } } - return Promise.resolve(null); + return undefined; }; KeybindingsRegistry.registerCommandAndKeybindingRule({ weight: KeybindingWeight.WorkbenchContrib, @@ -237,7 +238,7 @@ export function registerCommands(): void { id: TOGGLE_INLINE_BREAKPOINT_ID, title: nls.localize('addInlineBreakpoint', "Add Inline Breakpoint") }, - when: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, EditorContextKeys.writable, EditorContextKeys.editorTextFocus), + when: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, PanelFocusContext.toNegated(), EditorContextKeys.editorTextFocus), group: 'debug', order: 1 }); @@ -258,7 +259,7 @@ export function registerCommands(): void { } } - return Promise.resolve(undefined); + return undefined; } }); } diff --git a/src/vs/workbench/parts/debug/browser/debugContentProvider.ts b/src/vs/workbench/parts/debug/browser/debugContentProvider.ts index 710c3a975..4899ab58c 100644 --- a/src/vs/workbench/parts/debug/browser/debugContentProvider.ts +++ b/src/vs/workbench/parts/debug/browser/debugContentProvider.ts @@ -5,7 +5,6 @@ import { URI as uri } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { guessMimeTypes, MIME_TEXT } from 'vs/base/common/mime'; import { ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -39,7 +38,7 @@ export class DebugContentProvider implements IWorkbenchContribution, ITextModelC textModelResolverService.registerTextModelContentProvider(DEBUG_SCHEME, this); } - public provideTextContent(resource: uri): TPromise { + provideTextContent(resource: uri): Promise { let session: IDebugSession; @@ -58,19 +57,19 @@ export class DebugContentProvider implements IWorkbenchContribution, ITextModelC } const createErrModel = (errMsg?: string) => { this.debugService.sourceIsNotAvailable(resource); - const modePromise = this.modeService.getOrCreateMode(MIME_TEXT); + const languageSelection = this.modeService.create(MIME_TEXT); const message = errMsg ? localize('canNotResolveSourceWithError', "Could not load source '{0}': {1}.", resource.path, errMsg) : localize('canNotResolveSource', "Could not load source '{0}'.", resource.path); - return this.modelService.createModel(message, modePromise, resource); + return this.modelService.createModel(message, languageSelection, resource); }; return session.loadSource(resource).then(response => { if (response && response.body) { const mime = response.body.mimeType || guessMimeTypes(resource.path)[0]; - const modePromise = this.modeService.getOrCreateMode(mime); - return this.modelService.createModel(response.body.content, modePromise, resource); + const languageSelection = this.modeService.create(mime); + return this.modelService.createModel(response.body.content, languageSelection, resource); } return createErrModel(); diff --git a/src/vs/workbench/parts/debug/browser/debugEditorActions.ts b/src/vs/workbench/parts/debug/browser/debugEditorActions.ts index 91b12ddb7..f136aaa92 100644 --- a/src/vs/workbench/parts/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugEditorActions.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { Range } from 'vs/editor/common/core/range'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; @@ -17,6 +16,8 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { openBreakpointSource } from 'vs/workbench/parts/debug/browser/breakpointsView'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { PanelFocusContext } from 'vs/workbench/browser/parts/panel/panelPart'; +import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; export const TOGGLE_BREAKPOINT_ID = 'editor.debug.action.toggleBreakpoint'; class ToggleBreakpointAction extends EditorAction { @@ -34,7 +35,7 @@ class ToggleBreakpointAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise { + public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { const debugService = accessor.get(IDebugService); const position = editor.getPosition(); @@ -98,12 +99,15 @@ class LogPointAction extends EditorAction { class RunToCursorAction extends EditorAction { + public static ID = 'editor.debug.action.runToCursor'; + public static LABEL = nls.localize('runToCursor', "Run to Cursor"); + constructor() { super({ - id: 'editor.debug.action.runToCursor', - label: nls.localize('runToCursor', "Run to Cursor"), + id: RunToCursorAction.ID, + label: RunToCursorAction.LABEL, alias: 'Debug: Run to Cursor', - precondition: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, EditorContextKeys.writable, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), EditorContextKeys.editorTextFocus), + precondition: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, PanelFocusContext.toNegated(), CONTEXT_DEBUG_STATE.isEqualTo('stopped'), EditorContextKeys.editorTextFocus), menuOpts: { group: 'debug', order: 2 @@ -111,14 +115,16 @@ class RunToCursorAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise { + public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { const debugService = accessor.get(IDebugService); - if (debugService.state !== State.Stopped) { + const focusedSession = debugService.getViewModel().focusedSession; + if (debugService.state !== State.Stopped || !focusedSession) { return Promise.resolve(null); } let breakpointToRemove: IBreakpoint; - const oneTimeListener = debugService.onDidChangeState(state => { + const oneTimeListener = focusedSession.onDidChangeState(() => { + const state = focusedSession.state; if (state === State.Stopped || state === State.Inactive) { if (breakpointToRemove) { debugService.removeBreakpoints(breakpointToRemove.getId()); @@ -154,7 +160,7 @@ class SelectionToReplAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise { + public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { const debugService = accessor.get(IDebugService); const panelService = accessor.get(IPanelService); @@ -182,7 +188,7 @@ class SelectionToWatchExpressionsAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise { + public run(accessor: ServicesAccessor, editor: ICodeEditor): Thenable { const debugService = accessor.get(IDebugService); const viewletService = accessor.get(IViewletService); @@ -207,7 +213,7 @@ class ShowDebugHoverAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise { + public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { const position = editor.getPosition(); const word = editor.getModel().getWordAtPosition(position); if (!word) { @@ -224,7 +230,7 @@ class GoToBreakpointAction extends EditorAction { super(opts); } - public run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): TPromise { + public run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): Thenable { const debugService = accessor.get(IDebugService); const editorService = accessor.get(IEditorService); const currentUri = editor.getModel().uri; @@ -290,3 +296,12 @@ registerEditorAction(SelectionToWatchExpressionsAction); registerEditorAction(ShowDebugHoverAction); registerEditorAction(GoToNextBreakpointAction); registerEditorAction(GoToPreviousBreakpointAction); +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: RunToCursorAction.ID, + title: RunToCursorAction.LABEL, + category: 'Debug' + }, + group: 'debug', + when: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE.isEqualTo('stopped')), +}); diff --git a/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts b/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts index 4efc42394..0f6aa619a 100644 --- a/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts +++ b/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts @@ -5,7 +5,6 @@ import * as nls from 'vs/nls'; import * as Filters from 'vs/base/common/filters'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as Quickopen from 'vs/workbench/browser/quickopen'; import * as QuickOpen from 'vs/base/parts/quickopen/common/quickOpen'; import * as Model from 'vs/base/parts/quickopen/browser/quickOpenModel'; @@ -93,7 +92,7 @@ export class DebugQuickOpenHandler extends Quickopen.QuickOpenHandler { return nls.localize('debugAriaLabel', "Type a name of a launch configuration to run."); } - public getResults(input: string, token: CancellationToken): TPromise { + public getResults(input: string, token: CancellationToken): Promise { const configurations: Model.QuickOpenEntry[] = []; const configManager = this.debugService.getConfigurationManager(); diff --git a/src/vs/workbench/parts/debug/browser/debugViewlet.ts b/src/vs/workbench/parts/debug/browser/debugViewlet.ts index dea2d3923..f97321a3d 100644 --- a/src/vs/workbench/parts/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/parts/debug/browser/debugViewlet.ts @@ -69,7 +69,7 @@ export class DebugViewlet extends ViewContainerViewlet { DOM.addClass(parent, 'debug-viewlet'); } - public focus(): void { + focus(): void { super.focus(); if (this.startDebugActionItem) { @@ -97,7 +97,7 @@ export class DebugViewlet extends ViewContainerViewlet { return this._register(this.instantiationService.createInstance(SelectAndStartAction, SelectAndStartAction.ID, nls.localize('startAdditionalSession', "Start Additional Session"))); } - public getActions(): IAction[] { + getActions(): IAction[] { if (this.showInitialDebugActions) { return [this.startAction, this.configureAction, this.toggleReplAction]; } @@ -105,12 +105,12 @@ export class DebugViewlet extends ViewContainerViewlet { return DebugToolbar.getActions(this.allActions, this.toDispose, this.debugService, this.keybindingService, this.instantiationService); } - public get showInitialDebugActions(): boolean { + get showInitialDebugActions(): boolean { const state = this.debugService.state; return state === State.Inactive || this.configurationService.getValue('debug').toolBarLocation !== 'docked'; } - public getSecondaryActions(): IAction[] { + getSecondaryActions(): IAction[] { if (this.showInitialDebugActions) { return []; } @@ -118,7 +118,7 @@ export class DebugViewlet extends ViewContainerViewlet { return [this.selectAndStartAction, this.configureAction, this.toggleReplAction]; } - public getActionItem(action: IAction): IActionItem { + getActionItem(action: IAction): IActionItem { if (action.id === StartAction.ID) { this.startDebugActionItem = this.instantiationService.createInstance(StartDebugActionItem, null, action); return this.startDebugActionItem; @@ -130,7 +130,7 @@ export class DebugViewlet extends ViewContainerViewlet { return null; } - public focusView(id: string): void { + focusView(id: string): void { const view = this.getView(id); if (view) { view.focus(); diff --git a/src/vs/workbench/parts/debug/browser/linkDetector.ts b/src/vs/workbench/parts/debug/browser/linkDetector.ts index dee02d384..339dbba73 100644 --- a/src/vs/workbench/parts/debug/browser/linkDetector.ts +++ b/src/vs/workbench/parts/debug/browser/linkDetector.ts @@ -34,7 +34,7 @@ export class LinkDetector { * 'onclick' event is attached to all anchored links that opens them in the editor. * If no links were detected, returns the original string. */ - public handleLinks(text: string): HTMLElement | string { + handleLinks(text: string): HTMLElement | string { if (text.length > LinkDetector.MAX_LENGTH) { return text; } diff --git a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts index f69305533..70e492072 100644 --- a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts @@ -5,7 +5,6 @@ import * as nls from 'vs/nls'; import { TreeViewsViewletPanel, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as dom from 'vs/base/browser/dom'; import { normalize, isAbsolute, sep } from 'vs/base/common/paths'; import { IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; @@ -129,7 +128,7 @@ class BaseTreeItem { } // skips intermediate single-child nodes - getChildren(): TPromise { + getChildren(): Promise { const child = this.oneChild(); if (child) { return child.getChildren(); @@ -181,7 +180,7 @@ class BaseTreeItem { } private oneChild(): BaseTreeItem { - if (SMART && !this._source && !this._showedMoreThanOne && !(this instanceof RootFolderTreeItem)) { + if (SMART && !this._source && !this._showedMoreThanOne && !(this instanceof RootFolderTreeItem) && !(this instanceof SessionTreeItem)) { const keys = Object.keys(this._children); if (keys.length === 1) { return this._children[keys[0]]; @@ -247,7 +246,7 @@ class SessionTreeItem extends BaseTreeItem { return true; } - getChildren(): TPromise { + getChildren(): Promise { if (!this._initialized) { this._initialized = true; @@ -478,11 +477,11 @@ class LoadedScriptsDataSource implements IDataSource { return element.hasChildren(); } - getChildren(tree: ITree, element: any): TPromise { + getChildren(tree: ITree, element: any): Promise { return element.getChildren(); } - getParent(tree: ITree, element: any): TPromise { + getParent(tree: ITree, element: any): Promise { return Promise.resolve(element.getParent()); } diff --git a/src/vs/workbench/parts/debug/browser/media/breakpointWidget.css b/src/vs/workbench/parts/debug/browser/media/breakpointWidget.css index 165dd70d6..f08164c75 100644 --- a/src/vs/workbench/parts/debug/browser/media/breakpointWidget.css +++ b/src/vs/workbench/parts/debug/browser/media/breakpointWidget.css @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ .monaco-editor .zone-widget .zone-widget-container.breakpoint-widget { - height: 30px !important; display: flex; border-color: #007ACC; } diff --git a/src/vs/workbench/parts/debug/browser/media/debugViewlet.css b/src/vs/workbench/parts/debug/browser/media/debugViewlet.css index 1f760b93e..3fb41aa0f 100644 --- a/src/vs/workbench/parts/debug/browser/media/debugViewlet.css +++ b/src/vs/workbench/parts/debug/browser/media/debugViewlet.css @@ -257,6 +257,11 @@ text-align: center; } +.debug-viewlet .debug-call-stack .show-more { + font-style: italic; + opacity: 0.35; +} + .monaco-workbench .debug-viewlet .monaco-tree-row .expression { white-space: pre; } diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts index 067b92f0a..a92334c55 100644 --- a/src/vs/workbench/parts/debug/common/debug.ts +++ b/src/vs/workbench/parts/debug/common/debug.ts @@ -5,7 +5,6 @@ import * as nls from 'vs/nls'; import { URI as uri } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import severity from 'vs/base/common/severity'; import { Event } from 'vs/base/common/event'; import { IJSONSchemaSnippet } from 'vs/base/common/jsonSchema'; @@ -100,7 +99,7 @@ export interface IReplElementSource { export interface IExpressionContainer extends ITreeElement { readonly hasChildren: boolean; - getChildren(): TPromise>; + getChildren(): Promise>; } export interface IExpression extends IReplElement, IExpressionContainer { @@ -111,18 +110,27 @@ export interface IExpression extends IReplElement, IExpressionContainer { } export interface IDebugger { - createDebugAdapter(session: IDebugSession, root: IWorkspaceFolder, config: IConfig, outputService: IOutputService): TPromise; - runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): TPromise; - getCustomTelemetryService(): TPromise; + createDebugAdapter(session: IDebugSession, outputService: IOutputService): Promise; + runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise; + getCustomTelemetryService(): Thenable; } -export enum State { +export const enum State { Inactive, Initializing, Stopped, Running } +export function getStateLabel(state: State): string { + switch (state) { + case State.Initializing: return 'initializing'; + case State.Stopped: return 'stopped'; + case State.Running: return 'running'; + default: return 'inactive'; + } +} + export class AdapterEndEvent { error?: Error; sessionLengthInSeconds: number; @@ -156,7 +164,7 @@ export interface IDebugSession extends ITreeElement { getReplElements(): ReadonlyArray; removeReplExpressions(): void; - addReplExpression(stackFrame: IStackFrame, name: string): TPromise; + addReplExpression(stackFrame: IStackFrame, name: string): Promise; appendToRepl(data: string | IExpression, severity: severity, source?: IReplElementSource): void; logToRepl(sev: severity, args: any[], frame?: { uri: uri, line: number, column: number }); @@ -178,37 +186,37 @@ export interface IDebugSession extends ITreeElement { // DAP request - initialize(dbgr: IDebugger): TPromise; - launchOrAttach(config: IConfig): TPromise; - restart(): TPromise; - terminate(restart?: boolean /* false */): TPromise; - disconnect(restart?: boolean /* false */): TPromise; - - sendBreakpoints(modelUri: uri, bpts: IBreakpoint[], sourceModified: boolean): TPromise; - sendFunctionBreakpoints(fbps: IFunctionBreakpoint[]): TPromise; - sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): TPromise; - - stackTrace(threadId: number, startFrame: number, levels: number): TPromise; - exceptionInfo(threadId: number): TPromise; - scopes(frameId: number): TPromise; - variables(variablesReference: number, filter: 'indexed' | 'named', start: number, count: number): TPromise; - evaluate(expression: string, frameId?: number, context?: string): TPromise; - customRequest(request: string, args: any): TPromise; - - restartFrame(frameId: number, threadId: number): TPromise; - next(threadId: number): TPromise; - stepIn(threadId: number): TPromise; - stepOut(threadId: number): TPromise; - stepBack(threadId: number): TPromise; - continue(threadId: number): TPromise; - reverseContinue(threadId: number): TPromise; - pause(threadId: number): TPromise; - terminateThreads(threadIds: number[]): TPromise; - - completions(frameId: number, text: string, position: Position, overwriteBefore: number): TPromise; - setVariable(variablesReference: number, name: string, value: string): TPromise; - loadSource(resource: uri): TPromise; - getLoadedSources(): TPromise; + initialize(dbgr: IDebugger): Thenable; + launchOrAttach(config: IConfig): Promise; + restart(): Promise; + terminate(restart?: boolean /* false */): Promise; + disconnect(restart?: boolean /* false */): Promise; + + sendBreakpoints(modelUri: uri, bpts: IBreakpoint[], sourceModified: boolean): Promise; + sendFunctionBreakpoints(fbps: IFunctionBreakpoint[]): Promise; + sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise; + + stackTrace(threadId: number, startFrame: number, levels: number): Promise; + exceptionInfo(threadId: number): Promise; + scopes(frameId: number): Promise; + variables(variablesReference: number, filter: 'indexed' | 'named', start: number, count: number): Promise; + evaluate(expression: string, frameId?: number, context?: string): Promise; + customRequest(request: string, args: any): Promise; + + restartFrame(frameId: number, threadId: number): Promise; + next(threadId: number): Promise; + stepIn(threadId: number): Promise; + stepOut(threadId: number): Promise; + stepBack(threadId: number): Promise; + continue(threadId: number): Promise; + reverseContinue(threadId: number): Promise; + pause(threadId: number): Promise; + terminateThreads(threadIds: number[]): Promise; + + completions(frameId: number, text: string, position: Position, overwriteBefore: number): Promise; + setVariable(variablesReference: number, name: string, value: string): Promise; + loadSource(resource: uri): Promise; + getLoadedSources(): Promise; } export interface IThread extends ITreeElement { @@ -236,7 +244,7 @@ export interface IThread extends ITreeElement { /** * Information about the exception if an 'exception' stopped event raised and DA supports the 'exceptionInfo' request, otherwise null. */ - readonly exceptionInfo: TPromise; + readonly exceptionInfo: Promise; /** * Gets the callstack if it has already been received from the debug @@ -255,14 +263,14 @@ export interface IThread extends ITreeElement { */ readonly stopped: boolean; - next(): TPromise; - stepIn(): TPromise; - stepOut(): TPromise; - stepBack(): TPromise; - continue(): TPromise; - pause(): TPromise; - terminate(): TPromise; - reverseContinue(): TPromise; + next(): Promise; + stepIn(): Promise; + stepOut(): Promise; + stepBack(): Promise; + continue(): Promise; + pause(): Promise; + terminate(): Promise; + reverseContinue(): Promise; } export interface IScope extends IExpressionContainer { @@ -278,12 +286,12 @@ export interface IStackFrame extends ITreeElement { readonly frameId: number; readonly range: IRange; readonly source: Source; - getScopes(): TPromise>; - getMostSpecificScopes(range: IRange): TPromise>; + getScopes(): Promise>; + getMostSpecificScopes(range: IRange): Promise>; getSpecificSourceName(): string; - restart(): TPromise; + restart(): Promise; toString(): string; - openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean): TPromise; + openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean): Thenable; } export interface IEnablement extends ITreeElement { @@ -428,7 +436,7 @@ export interface IConfig extends IEnvConfig { // fundamental attributes type: string; request: string; - name?: string; + name: string; // platform specifics windows?: IEnvConfig; @@ -452,24 +460,28 @@ export interface IDebugAdapter extends IDisposable { readonly onExit: Event; onRequest(callback: (request: DebugProtocol.Request) => void); onEvent(callback: (event: DebugProtocol.Event) => void); - startSession(): TPromise; + startSession(): Promise; sendMessage(message: DebugProtocol.ProtocolMessage): void; sendResponse(response: DebugProtocol.Response): void; sendRequest(command: string, args: any, clb: (result: DebugProtocol.Response) => void, timemout?: number): void; - stopSession(): TPromise; + stopSession(): Promise; +} + +export interface IDebugAdapterFactory extends ITerminalLauncher { + createDebugAdapter(session: IDebugSession): IDebugAdapter; + substituteVariables(folder: IWorkspaceFolder, config: IConfig): Promise; } -export interface IDebugAdapterProvider extends ITerminalLauncher { - createDebugAdapter(session: IDebugSession, folder: IWorkspaceFolder, config: IConfig): IDebugAdapter; - substituteVariables(folder: IWorkspaceFolder, config: IConfig): TPromise; +export interface IDebugAdapterExecutableOptions { + cwd?: string; + env?: { [key: string]: string }; } export interface IDebugAdapterExecutable { readonly type: 'executable'; readonly command: string; readonly args: string[]; - readonly cwd?: string; - readonly env?: { [key: string]: string }; + readonly options?: IDebugAdapterExecutableOptions; } export interface IDebugAdapterServer { @@ -519,15 +531,19 @@ export interface IDebuggerContribution extends IPlatformSpecificAdapterContribut export interface IDebugConfigurationProvider { readonly type: string; - handle: number; - resolveDebugConfiguration?(folderUri: uri | undefined, debugConfiguration: IConfig): TPromise; - provideDebugConfigurations?(folderUri: uri | undefined): TPromise; - provideDebugAdapter?(session: IDebugSession, folderUri: uri | undefined, config: IConfig): TPromise; + resolveDebugConfiguration?(folderUri: uri | undefined, debugConfiguration: IConfig): Promise; + provideDebugConfigurations?(folderUri: uri | undefined): Promise; + debugAdapterExecutable?(folderUri: uri | undefined): Promise; // TODO@AW legacy hasTracker: boolean; } +export interface IDebugAdapterProvider { + readonly type: string; + provideDebugAdapter(session: IDebugSession): Promise; +} + export interface ITerminalLauncher { - runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise; + runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise; } export interface ITerminalSettings { @@ -570,19 +586,25 @@ export interface IConfigurationManager { */ onDidSelectConfiguration: Event; + activateDebuggers(activationEvent: string, debugType?: string): Thenable; + needsToRunInExtHost(debugType: string): boolean; + hasDebugConfigurationProvider(debugType: string): boolean; + + registerDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): IDisposable; + unregisterDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): void; - registerDebugConfigurationProvider(handle: number, debugConfigurationProvider: IDebugConfigurationProvider): void; - unregisterDebugConfigurationProvider(handle: number): void; + registerDebugAdapterProvider(debugConfigurationProvider: IDebugAdapterProvider): IDisposable; + unregisterDebugAdapterProvider(debugConfigurationProvider: IDebugAdapterProvider): void; - resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: any): TPromise; - provideDebugAdapter(session: IDebugSession, folderUri: uri | undefined, config: IConfig): TPromise; + resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: any): Thenable; + provideDebugAdapter(session: IDebugSession): Promise; - registerDebugAdapterProvider(debugTypes: string[], debugAdapterLauncher: IDebugAdapterProvider): IDisposable; - createDebugAdapter(session: IDebugSession, folder: IWorkspaceFolder, config: IConfig): IDebugAdapter; + registerDebugAdapterFactory(debugTypes: string[], debugAdapterFactory: IDebugAdapterFactory): IDisposable; + createDebugAdapter(session: IDebugSession): IDebugAdapter; - substituteVariables(debugType: string, folder: IWorkspaceFolder, config: IConfig): TPromise; - runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise; + substituteVariables(debugType: string, folder: IWorkspaceFolder, config: IConfig): Promise; + runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise; } export interface ILaunch { @@ -628,7 +650,7 @@ export interface ILaunch { /** * Opens the launch.json file. Creates if it does not exist. */ - openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): TPromise<{ editor: IEditor, created: boolean }>; + openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): Thenable<{ editor: IEditor, created: boolean }>; } // Debug service interfaces @@ -676,7 +698,7 @@ export interface IDebugService { /** * Adds new breakpoints to the model for the file specified with the uri. Notifies debug adapter of breakpoint changes. */ - addBreakpoints(uri: uri, rawBreakpoints: IBreakpointData[], context: string): TPromise; + addBreakpoints(uri: uri, rawBreakpoints: IBreakpointData[], context: string): Promise; /** * Updates the breakpoints. @@ -687,19 +709,19 @@ export interface IDebugService { * Enables or disables all breakpoints. If breakpoint is passed only enables or disables the passed breakpoint. * Notifies debug adapter of breakpoint changes. */ - enableOrDisableBreakpoints(enable: boolean, breakpoint?: IEnablement): TPromise; + enableOrDisableBreakpoints(enable: boolean, breakpoint?: IEnablement): Promise; /** * Sets the global activated property for all breakpoints. * Notifies debug adapter of breakpoint changes. */ - setBreakpointsActivated(activated: boolean): TPromise; + setBreakpointsActivated(activated: boolean): Promise; /** * Removes all breakpoints. If id is passed only removes the breakpoint associated with that id. * Notifies debug adapter of breakpoint changes. */ - removeBreakpoints(id?: string): TPromise; + removeBreakpoints(id?: string): Promise; /** * Adds a new function breakpoint for the given name. @@ -710,19 +732,19 @@ export interface IDebugService { * Renames an already existing function breakpoint. * Notifies debug adapter of breakpoint changes. */ - renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise; + renameFunctionBreakpoint(id: string, newFunctionName: string): Promise; /** * Removes all function breakpoints. If id is passed only removes the function breakpoint with the passed id. * Notifies debug adapter of breakpoint changes. */ - removeFunctionBreakpoints(id?: string): TPromise; + removeFunctionBreakpoints(id?: string): Promise; /** * Sends all breakpoints to the passed session. * If session is not passed, sends all breakpoints to each session. */ - sendAllBreakpoints(session?: IDebugSession): TPromise; + sendAllBreakpoints(session?: IDebugSession): Promise; /** * Adds a new watch expression and evaluates it against the debug adapter. @@ -752,17 +774,17 @@ export interface IDebugService { * Returns true if the start debugging was successfull. For compound launches, all configurations have to start successfuly for it to return success. * On errors the startDebugging will throw an error, however some error and cancelations are handled and in that case will simply return false. */ - startDebugging(launch: ILaunch, configOrName?: IConfig | string, noDebug?: boolean): TPromise; + startDebugging(launch: ILaunch, configOrName?: IConfig | string, noDebug?: boolean): Thenable; /** * Restarts a session or creates a new one if there is no active session. */ - restartSession(session: IDebugSession, restartData?: any): TPromise; + restartSession(session: IDebugSession, restartData?: any): Thenable; /** * Stops the session. If the session does not exist then stops all sessions. */ - stopSession(session: IDebugSession): TPromise; + stopSession(session: IDebugSession): Promise; /** * Makes unavailable all sources with the passed uri. Source will appear as grayed out in callstack view. @@ -788,8 +810,8 @@ export const enum BreakpointWidgetContext { } export interface IDebugEditorContribution extends IEditorContribution { - showHover(range: Range, focus: boolean): TPromise; + showHover(range: Range, focus: boolean): Promise; showBreakpointWidget(lineNumber: number, column: number, context?: BreakpointWidgetContext): void; closeBreakpointWidget(): void; - addLaunchConfiguration(): TPromise; + addLaunchConfiguration(): Promise; } diff --git a/src/vs/workbench/parts/debug/common/debugModel.ts b/src/vs/workbench/parts/debug/common/debugModel.ts index f6e408239..57bb5ad39 100644 --- a/src/vs/workbench/parts/debug/common/debugModel.ts +++ b/src/vs/workbench/parts/debug/common/debugModel.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import { URI as uri } from 'vs/base/common/uri'; import * as resources from 'vs/base/common/resources'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as lifecycle from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { generateUuid } from 'vs/base/common/uuid'; @@ -81,7 +80,7 @@ export class RawObjectReplElement extends AbstractReplElement implements IExpres return (Array.isArray(this.valueObj) && this.valueObj.length > 0) || (isObject(this.valueObj) && Object.getOwnPropertyNames(this.valueObj).length > 0); } - public getChildren(): TPromise { + public getChildren(): Promise { let result: IExpression[] = []; if (Array.isArray(this.valueObj)) { result = (this.valueObj).slice(0, RawObjectReplElement.MAX_CHILDREN) @@ -107,7 +106,7 @@ export class ExpressionContainer implements IExpressionContainer { public valueChanged: boolean; private _value: string; - protected children: TPromise; + protected children: Promise; constructor( protected session: IDebugSession, @@ -127,7 +126,7 @@ export class ExpressionContainer implements IExpressionContainer { this.children = undefined; // invalidate children cache } - public getChildren(): TPromise { + public getChildren(): Promise { if (!this.children) { this.children = this.doGetChildren(); } @@ -135,7 +134,7 @@ export class ExpressionContainer implements IExpressionContainer { return this.children; } - private doGetChildren(): TPromise { + private doGetChildren(): Promise { if (!this.hasChildren) { return Promise.resolve([]); } @@ -145,7 +144,7 @@ export class ExpressionContainer implements IExpressionContainer { } // Check if object has named variables, fetch them independent from indexed variables #9670 - const childrenThenable: Thenable = !!this.namedVariables ? this.fetchVariables(undefined, undefined, 'named') : Promise.resolve([]); + const childrenThenable = !!this.namedVariables ? this.fetchVariables(undefined, undefined, 'named') : Promise.resolve([]); return childrenThenable.then(childrenArray => { // Use a dynamic chunk size based on the number of elements #9774 let chunkSize = ExpressionContainer.BASE_CHUNK_SIZE; @@ -183,7 +182,7 @@ export class ExpressionContainer implements IExpressionContainer { return this.reference > 0; } - private fetchVariables(start: number, count: number, filter: 'indexed' | 'named'): TPromise { + private fetchVariables(start: number, count: number, filter: 'indexed' | 'named'): Promise { return this.session.variables(this.reference, filter, start, count).then(response => { return response && response.body && response.body.variables ? distinct(response.body.variables.filter(v => !!v && isString(v.name)), v => v.name).map( @@ -225,9 +224,9 @@ export class Expression extends ExpressionContainer implements IExpression { } } - public evaluate(session: IDebugSession, stackFrame: IStackFrame, context: string): TPromise { + public evaluate(session: IDebugSession, stackFrame: IStackFrame, context: string): Promise { if (!session || (!stackFrame && context !== 'repl')) { - this.value = context === 'repl' ? nls.localize('startDebugFirst', "Please start a debug session to evaluate") : Expression.DEFAULT_VALUE; + this.value = context === 'repl' ? nls.localize('startDebugFirst', "Please start a debug session to evaluate expressions") : Expression.DEFAULT_VALUE; this.available = false; this.reference = 0; @@ -279,7 +278,7 @@ export class Variable extends ExpressionContainer implements IExpression { this.value = value; } - public setVariable(value: string): TPromise { + public setVariable(value: string): Promise { return this.session.setVariable((this.parent).reference, this.name, value).then(response => { if (response && response.body) { this.value = response.body.value; @@ -316,7 +315,7 @@ export class Scope extends ExpressionContainer implements IScope { export class StackFrame implements IStackFrame { - private scopes: TPromise; + private scopes: Promise; constructor( public thread: IThread, @@ -334,7 +333,7 @@ export class StackFrame implements IStackFrame { return `stackframe:${this.thread.getId()}:${this.frameId}:${this.index}`; } - public getScopes(): TPromise { + public getScopes(): Promise { if (!this.scopes) { this.scopes = this.thread.session.scopes(this.frameId).then(response => { return response && response.body && response.body.scopes ? @@ -347,7 +346,11 @@ export class StackFrame implements IStackFrame { } public getSpecificSourceName(): string { - const otherSources = this.thread.getCallStack().map(sf => sf.source).filter(s => s !== this.source); + // To reduce flashing of the path name and the way we fetch stack frames + // We need to compute the source name based on the other frames in the stale call stack + let callStack = (this.thread).getStaleCallStack(); + callStack = callStack.length > 0 ? callStack : this.thread.getCallStack(); + const otherSources = callStack.map(sf => sf.source).filter(s => s !== this.source); let suffixLength = 0; otherSources.forEach(s => { if (s.name === this.source.name) { @@ -362,7 +365,7 @@ export class StackFrame implements IStackFrame { return (from > 0 ? '...' : '') + this.source.uri.path.substr(from); } - public getMostSpecificScopes(range: IRange): TPromise { + public getMostSpecificScopes(range: IRange): Promise { return this.getScopes().then(scopes => { scopes = scopes.filter(s => !s.expensive); const haveRangeInfo = scopes.some(s => !!s.range); @@ -376,7 +379,7 @@ export class StackFrame implements IStackFrame { }); } - public restart(): TPromise { + public restart(): Promise { return this.thread.session.restartFrame(this.frameId, this.thread.threadId); } @@ -384,7 +387,7 @@ export class StackFrame implements IStackFrame { return `${this.name} (${this.source.inMemory ? this.source.name : this.source.uri.fsPath}:${this.range.startLineNumber})`; } - public openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): TPromise { + public openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Thenable { return !this.source.available ? Promise.resolve(null) : this.source.openInEditor(editorService, this.range, preserveFocus, sideBySide, pinned); } @@ -418,7 +421,7 @@ export class Thread implements IThread { return this.callStack; } - public getStaleCallStack(): IStackFrame[] { + public getStaleCallStack(): ReadonlyArray { return this.staleCallStack; } @@ -429,7 +432,7 @@ export class Thread implements IThread { * Only fetches the first stack frame for performance reasons. Calling this method consecutive times * gets the remainder of the call stack. */ - public fetchCallStack(levels = 20): TPromise { + public fetchCallStack(levels = 20): Promise { if (!this.stopped) { return Promise.resolve(null); } @@ -444,7 +447,7 @@ export class Thread implements IThread { }); } - private getCallStackImpl(startFrame: number, levels: number): TPromise { + private getCallStackImpl(startFrame: number, levels: number): Promise { return this.session.stackTrace(this.threadId, startFrame, levels).then(response => { if (!response || !response.body) { return []; @@ -476,7 +479,7 @@ export class Thread implements IThread { /** * Returns exception info promise if the exception was thrown, otherwise null */ - public get exceptionInfo(): TPromise { + public get exceptionInfo(): Promise { if (this.stoppedDetails && this.stoppedDetails.reason === 'exception') { if (this.session.capabilities.supportsExceptionInfoRequest) { return this.session.exceptionInfo(this.threadId); @@ -489,35 +492,35 @@ export class Thread implements IThread { return Promise.resolve(null); } - public next(): TPromise { + public next(): Promise { return this.session.next(this.threadId); } - public stepIn(): TPromise { + public stepIn(): Promise { return this.session.stepIn(this.threadId); } - public stepOut(): TPromise { + public stepOut(): Promise { return this.session.stepOut(this.threadId); } - public stepBack(): TPromise { + public stepBack(): Promise { return this.session.stepBack(this.threadId); } - public continue(): TPromise { + public continue(): Promise { return this.session.continue(this.threadId); } - public pause(): TPromise { + public pause(): Promise { return this.session.pause(this.threadId); } - public terminate(): TPromise { + public terminate(): Promise { return this.session.terminateThreads([this.threadId]); } - public reverseContinue(): TPromise { + public reverseContinue(): Promise { return this.session.reverseContinue(this.threadId); } } @@ -758,9 +761,18 @@ export class DebugModel implements IDebugModel { } public addSession(session: IDebugSession): void { - // Make sure to remove all inactive sessions once a new session is started - // Also make sure to de-dupe if a session is re-intialized. In case of EH debugging we are adding a session again after an attach. - this.sessions = this.sessions.filter(s => s.state !== State.Inactive && s.getId() !== session.getId()); + this.sessions = this.sessions.filter(s => { + if (s.getId() === session.getId()) { + // Make sure to de-dupe if a session is re-intialized. In case of EH debugging we are adding a session again after an attach. + return false; + } + if (s.state === State.Inactive && s.getLabel() === session.getLabel()) { + // Make sure to remove all inactive sessions that are using the same configuration as the new session + return false; + } + + return true; + }); this.sessions.push(session); this._onDidChangeCallStack.fire(); } @@ -796,7 +808,7 @@ export class DebugModel implements IDebugModel { } } - public fetchCallStack(thread: Thread): TPromise { + public fetchCallStack(thread: Thread): Promise { if (thread.session.capabilities.supportsDelayedStackTraceLoading) { // For improved performance load the first stack frame and then load the rest async. return thread.fetchCallStack(1).then(() => { diff --git a/src/vs/workbench/parts/debug/common/debugProtocol.d.ts b/src/vs/workbench/parts/debug/common/debugProtocol.d.ts index 5bec6d838..e085183cf 100644 --- a/src/vs/workbench/parts/debug/common/debugProtocol.d.ts +++ b/src/vs/workbench/parts/debug/common/debugProtocol.d.ts @@ -191,7 +191,7 @@ declare module DebugProtocol { Values: 'changed', 'new', 'removed', etc. */ reason: string; - /** The breakpoint. */ + /** The 'id' attribute is used to find the target breakpoint and the other attributes are used as the new values. */ breakpoint: Breakpoint; }; } @@ -284,6 +284,8 @@ declare module DebugProtocol { body: { /** The process ID. */ processId?: number; + /** The process ID of the terminal shell. */ + shellProcessId?: number; }; } @@ -1458,7 +1460,7 @@ declare module DebugProtocol { /** Information about a Breakpoint created in setBreakpoints or setFunctionBreakpoints. */ export interface Breakpoint { - /** An optional unique identifier for the breakpoint. */ + /** An optional identifier for the breakpoint. It is needed if breakpoint events are used to update or remove breakpoints. */ id?: number; /** If true breakpoint could be set (but not necessarily at the desired location). */ verified: boolean; diff --git a/src/vs/workbench/parts/debug/common/debugSource.ts b/src/vs/workbench/parts/debug/common/debugSource.ts index d776521a9..434c78d67 100644 --- a/src/vs/workbench/parts/debug/common/debugSource.ts +++ b/src/vs/workbench/parts/debug/common/debugSource.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { URI as uri } from 'vs/base/common/uri'; import * as paths from 'vs/base/common/paths'; import * as resources from 'vs/base/common/resources'; @@ -64,27 +63,27 @@ export class Source { } } - public get name() { + get name() { return this.raw.name || resources.basenameOrAuthority(this.uri); } - public get origin() { + get origin() { return this.raw.origin; } - public get presentationHint() { + get presentationHint() { return this.raw.presentationHint; } - public get reference() { + get reference() { return this.raw.sourceReference; } - public get inMemory() { + get inMemory() { return this.uri.scheme === DEBUG_SCHEME; } - public openInEditor(editorService: IEditorService, selection: IRange, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): TPromise { + openInEditor(editorService: IEditorService, selection: IRange, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Thenable { return !this.available ? Promise.resolve(null) : editorService.openEditor({ resource: this.uri, description: this.origin, @@ -98,7 +97,7 @@ export class Source { }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); } - public static getEncodedDebugData(modelUri: uri): { name: string, path: string, sessionId: string, sourceReference: number } { + static getEncodedDebugData(modelUri: uri): { name: string, path: string, sessionId: string, sourceReference: number } { let path: string; let sourceReference: number; let sessionId: string; diff --git a/src/vs/workbench/parts/debug/common/debugUtils.ts b/src/vs/workbench/parts/debug/common/debugUtils.ts index 24091e118..a12f19a08 100644 --- a/src/vs/workbench/parts/debug/common/debugUtils.ts +++ b/src/vs/workbench/parts/debug/common/debugUtils.ts @@ -7,6 +7,7 @@ import { equalsIgnoreCase } from 'vs/base/common/strings'; import { IConfig } from 'vs/workbench/parts/debug/common/debug'; import { URI as uri } from 'vs/base/common/uri'; import { isAbsolute_posix, isAbsolute_win32 } from 'vs/base/common/paths'; +import { deepClone } from 'vs/base/common/objects'; const _formatPIIRegexp = /{([^}]+)}/g; @@ -106,20 +107,30 @@ export function uriToString(source: DebugProtocol.Source): void { // path hooks helpers -export function convertToDAPaths(msg: DebugProtocol.ProtocolMessage, fixSourcePaths: (source: DebugProtocol.Source) => void): void { +export function convertToDAPaths(message: DebugProtocol.ProtocolMessage, fixSourcePaths: (source: DebugProtocol.Source) => void): DebugProtocol.ProtocolMessage { + + // since we modify Source.paths in the message in place, we need to make a copy of it (see #61129) + const msg = deepClone(message); + convertPaths(msg, (toDA: boolean, source: DebugProtocol.Source | undefined) => { if (toDA && source) { fixSourcePaths(source); } }); + return msg; } -export function convertToVSCPaths(msg: DebugProtocol.ProtocolMessage, fixSourcePaths: (source: DebugProtocol.Source) => void): void { +export function convertToVSCPaths(message: DebugProtocol.ProtocolMessage, fixSourcePaths: (source: DebugProtocol.Source) => void): DebugProtocol.ProtocolMessage { + + // since we modify Source.paths in the message in place, we need to make a copy of it (see #61129) + const msg = deepClone(message); + convertPaths(msg, (toDA: boolean, source: DebugProtocol.Source | undefined) => { if (!toDA && source) { fixSourcePaths(source); } }); + return msg; } function convertPaths(msg: DebugProtocol.ProtocolMessage, fixSourcePaths: (toDA: boolean, source: DebugProtocol.Source | undefined) => void): void { diff --git a/src/vs/workbench/parts/debug/common/replModel.ts b/src/vs/workbench/parts/debug/common/replModel.ts index 983204bfa..6d4f0fef5 100644 --- a/src/vs/workbench/parts/debug/common/replModel.ts +++ b/src/vs/workbench/parts/debug/common/replModel.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import severity from 'vs/base/common/severity'; import { IReplElement, IStackFrame, IExpression, IReplElementSource, IDebugSession } from 'vs/workbench/parts/debug/common/debug'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Expression, SimpleReplElement, RawObjectReplElement } from 'vs/workbench/parts/debug/common/debugModel'; import { isUndefinedOrNull, isObject } from 'vs/base/common/types'; import { basenameOrAuthority } from 'vs/base/common/resources'; @@ -19,17 +18,17 @@ export class ReplModel { constructor(private session: IDebugSession) { } - public getReplElements(): ReadonlyArray { + getReplElements(): ReadonlyArray { return this.replElements; } - public addReplExpression(stackFrame: IStackFrame, name: string): TPromise { + addReplExpression(stackFrame: IStackFrame, name: string): Promise { const expression = new Expression(name); this.addReplElements([expression]); return expression.evaluate(this.session, stackFrame, 'repl'); } - public appendToRepl(data: string | IExpression, sev: severity, source?: IReplElementSource): void { + appendToRepl(data: string | IExpression, sev: severity, source?: IReplElementSource): void { const clearAnsiSequence = '\u001b[2J'; if (typeof data === 'string' && data.indexOf(clearAnsiSequence) >= 0) { // [2J is the ansi escape sequence for clearing the display http://ascii-table.com/ansi-escape-sequences.php @@ -64,7 +63,7 @@ export class ReplModel { } } - public logToRepl(sev: severity, args: any[], frame?: { uri: URI, line: number, column: number }) { + logToRepl(sev: severity, args: any[], frame?: { uri: URI, line: number, column: number }) { let source: IReplElementSource; if (frame) { @@ -142,5 +141,4 @@ export class ReplModel { this.replElements = []; } } - } diff --git a/src/vs/workbench/parts/debug/electron-browser/breakpointWidget.ts b/src/vs/workbench/parts/debug/electron-browser/breakpointWidget.ts index c07e33b7b..4f32cee0f 100644 --- a/src/vs/workbench/parts/debug/electron-browser/breakpointWidget.ts +++ b/src/vs/workbench/parts/debug/electron-browser/breakpointWidget.ts @@ -9,7 +9,7 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; import * as lifecycle from 'vs/base/common/lifecycle'; import * as dom from 'vs/base/browser/dom'; -import { Position } from 'vs/editor/common/core/position'; +import { Position, IPosition } from 'vs/editor/common/core/position'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; @@ -34,7 +34,7 @@ import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { getSimpleCodeEditorWidgetOptions } from 'vs/workbench/parts/codeEditor/electron-browser/simpleEditorOptions'; import { getSimpleEditorOptions } from 'vs/workbench/parts/codeEditor/browser/simpleEditorOptions'; -import { Range } from 'vs/editor/common/core/range'; +import { IRange, Range } from 'vs/editor/common/core/range'; const $ = dom.$; const IPrivateBreakpointWidgetService = createDecorator('privateBreakopintWidgetService'); @@ -127,6 +127,16 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi } } + public show(rangeOrPos: IRange | IPosition, heightInLines: number) { + const lineNum = this.input.getModel().getLineCount(); + super.show(rangeOrPos, lineNum + 1); + } + + public fitHeightToContent() { + const lineNum = this.input.getModel().getLineCount(); + this._relayout(lineNum + 1); + } + protected _fillContainer(container: HTMLElement): void { this.setCssClass('breakpoint-widget'); const selectBox = new SelectBox([nls.localize('expression', "Expression"), nls.localize('hitCount', "Hit Count"), nls.localize('logMessage', "Log Message")], this.context, this.contextViewService, null, { ariaLabel: nls.localize('breakpointType', 'Breakpoint Type') }); @@ -144,6 +154,9 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi this.createBreakpointInput(dom.append(container, $('.inputContainer'))); this.input.getModel().setValue(this.getInputValue(this.breakpoint)); + this.toDispose.push(this.input.getModel().onDidChangeContent(() => { + this.fitHeightToContent(); + })); this.input.setPosition({ lineNumber: 1, column: this.input.getModel().getLineMaxColumn(1) }); // Due to an electron bug we have to do the timeout, otherwise we do not get focus setTimeout(() => this.input.focus(), 100); @@ -191,7 +204,7 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi } protected _doLayout(heightInPixel: number, widthInPixel: number): void { - this.input.layout({ height: 18, width: widthInPixel - 113 }); + this.input.layout({ height: heightInPixel, width: widthInPixel - 113 }); } private createBreakpointInput(container: HTMLElement): void { diff --git a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts index 16663836f..0f3e0896a 100644 --- a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import { RunOnceScheduler } from 'vs/base/common/async'; import * as dom from 'vs/base/browser/dom'; -import { TPromise } from 'vs/base/common/winjs.base'; import { TreeViewsViewletPanel, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IDebugService, State, IStackFrame, IDebugSession, IThread, CONTEXT_CALLSTACK_ITEM_TYPE } from 'vs/workbench/parts/debug/common/debug'; import { Thread, StackFrame, ThreadAndSessionIds, DebugModel } from 'vs/workbench/parts/debug/common/debugModel'; @@ -39,6 +38,7 @@ export class CallStackView extends TreeViewsViewletPanel { private ignoreSelectionChangedEvent: boolean; private treeContainer: HTMLElement; private callStackItemType: IContextKey; + private dataSource: CallStackDataSource; constructor( private options: IViewletViewOptions, @@ -67,9 +67,7 @@ export class CallStackView extends TreeViewsViewletPanel { // Otherwise there will be a pause message per thread and there is no need for a global one. if (newTreeInput instanceof Thread && newTreeInput.stoppedDetails) { this.pauseMessageLabel.textContent = newTreeInput.stoppedDetails.description || nls.localize('debugStopped', "Paused on {0}", newTreeInput.stoppedDetails.reason); - if (newTreeInput.stoppedDetails.text) { - this.pauseMessageLabel.title = newTreeInput.stoppedDetails.text; - } + this.pauseMessageLabel.title = newTreeInput.stoppedDetails.text; dom.toggleClass(this.pauseMessageLabel, 'exception', newTreeInput.stoppedDetails.reason === 'exception'); this.pauseMessage.hidden = false; } else { @@ -77,6 +75,7 @@ export class CallStackView extends TreeViewsViewletPanel { } this.needsRefresh = false; + this.dataSource.deemphasizedStackFramesToShow = []; (this.tree.getInput() === newTreeInput ? this.tree.refresh() : this.tree.setInput(newTreeInput)) .then(() => this.updateTreeSelection()); }, 50); @@ -91,14 +90,15 @@ export class CallStackView extends TreeViewsViewletPanel { this.pauseMessageLabel = dom.append(this.pauseMessage, $('span.label')); } - public renderBody(container: HTMLElement): void { + renderBody(container: HTMLElement): void { dom.addClass(container, 'debug-call-stack'); this.treeContainer = renderViewTree(container); const actionProvider = new CallStackActionProvider(this.debugService, this.keybindingService, this.instantiationService); const controller = this.instantiationService.createInstance(CallStackController, actionProvider, MenuId.DebugCallStackContext, {}); - + this.disposables.push(controller); + this.dataSource = new CallStackDataSource(); this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, { - dataSource: new CallStackDataSource(), + dataSource: this.dataSource, renderer: this.instantiationService.createInstance(CallStackRenderer), accessibilityProvider: this.instantiationService.createInstance(CallstackAccessibilityProvider), controller @@ -133,6 +133,10 @@ export class CallStackView extends TreeViewsViewletPanel { .then(() => this.tree.refresh()); } } + if (element instanceof Array) { + this.dataSource.deemphasizedStackFramesToShow.push(...element); + this.tree.refresh(); + } })); this.disposables.push(this.tree.onDidChangeFocus(() => { const focus = this.tree.getFocus(); @@ -179,7 +183,7 @@ export class CallStackView extends TreeViewsViewletPanel { super.layoutBody(size); } - private updateTreeSelection(): TPromise { + private updateTreeSelection(): Thenable { if (!this.tree.getInput()) { // Tree not initialized yet return Promise.resolve(null); @@ -217,12 +221,11 @@ export class CallStackView extends TreeViewsViewletPanel { }); } - public setVisible(visible: boolean): TPromise { - return super.setVisible(visible).then(() => { - if (visible && this.needsRefresh) { - this.onCallStackChangeScheduler.schedule(); - } - }); + setVisible(visible: boolean): void { + super.setVisible(visible); + if (visible && this.needsRefresh) { + this.onCallStackChangeScheduler.schedule(); + } } } @@ -248,19 +251,19 @@ class CallStackActionProvider implements IActionProvider { // noop } - public hasActions(tree: ITree, element: any): boolean { + hasActions(tree: ITree, element: any): boolean { return false; } - public getActions(tree: ITree, element: any): TPromise { - return Promise.resolve([]); + getActions(tree: ITree, element: any): IAction[] { + return []; } - public hasSecondaryActions(tree: ITree, element: any): boolean { + hasSecondaryActions(tree: ITree, element: any): boolean { return element !== tree.getInput(); } - public getSecondaryActions(tree: ITree, element: any): TPromise { + getSecondaryActions(tree: ITree, element: any): IAction[] { const actions: IAction[] = []; if (element instanceof DebugSession) { actions.push(this.instantiationService.createInstance(RestartAction, RestartAction.ID, RestartAction.LABEL)); @@ -285,31 +288,62 @@ class CallStackActionProvider implements IActionProvider { actions.push(new CopyStackTraceAction(CopyStackTraceAction.ID, CopyStackTraceAction.LABEL)); } - return Promise.resolve(actions); + return actions; } - public getActionItem(tree: ITree, element: any, action: IAction): IActionItem { + getActionItem(tree: ITree, element: any, action: IAction): IActionItem { return null; } } class CallStackDataSource implements IDataSource { - public getId(tree: ITree, element: any): string { + deemphasizedStackFramesToShow: IStackFrame[]; + + getId(tree: ITree, element: any): string { if (typeof element === 'string') { return element; } + if (element instanceof Array) { + return `showMore ${element[0].getId()}`; + } return element.getId(); } - public hasChildren(tree: ITree, element: any): boolean { + hasChildren(tree: ITree, element: any): boolean { return element instanceof DebugModel || element instanceof DebugSession || (element instanceof Thread && (element).stopped); } - public getChildren(tree: ITree, element: any): TPromise { + getChildren(tree: ITree, element: any): Promise { if (element instanceof Thread) { - return this.getThreadChildren(element); + return this.getThreadChildren(element).then(children => { + // Check if some stack frames should be hidden under a parent element since they are deemphasized + const result = []; + children.forEach((child, index) => { + if (child instanceof StackFrame && child.source && child.source.presentationHint === 'deemphasize') { + // Check if the user clicked to show the deemphasized source + if (this.deemphasizedStackFramesToShow.indexOf(child) === -1) { + if (result.length && result[result.length - 1] instanceof Array) { + // Collect all the stackframes that will be "collapsed" + result[result.length - 1].push(child); + return; + } + + const nextChild = index < children.length - 1 ? children[index + 1] : undefined; + if (nextChild instanceof StackFrame && nextChild.source && nextChild.source.presentationHint === 'deemphasize') { + // Start collecting stackframes that will be "collapsed" + result.push([child]); + return; + } + } + } + + result.push(child); + }); + + return result; + }); } if (element instanceof DebugModel) { return Promise.resolve(element.getSessions()); @@ -319,15 +353,15 @@ class CallStackDataSource implements IDataSource { return Promise.resolve(session.getAllThreads()); } - private getThreadChildren(thread: Thread): TPromise { + private getThreadChildren(thread: Thread): Promise<(IStackFrame | string | ThreadAndSessionIds)[]> { let callStack: any[] = thread.getCallStack(); - let callStackPromise: TPromise = Promise.resolve(null); + let callStackPromise: Promise = Promise.resolve(null); if (!callStack || !callStack.length) { callStackPromise = thread.fetchCallStack().then(() => callStack = thread.getCallStack()); } return callStackPromise.then(() => { - if (callStack.length === 1 && thread.session.capabilities.supportsDelayedStackTraceLoading) { + if (callStack.length === 1 && thread.session.capabilities.supportsDelayedStackTraceLoading && thread.stoppedDetails && thread.stoppedDetails.totalFrames > 1) { // To reduce flashing of the call stack view simply append the stale call stack // once we have the correct data the tree will refresh and we will no longer display it. callStack = callStack.concat(thread.getStaleCallStack().slice(1)); @@ -344,7 +378,7 @@ class CallStackDataSource implements IDataSource { }); } - public getParent(tree: ITree, element: any): TPromise { + getParent(tree: ITree, element: any): Promise { return Promise.resolve(null); } } @@ -367,7 +401,7 @@ interface IErrorTemplateData { label: HTMLElement; } -interface ILoadMoreTemplateData { +interface ILabelTemplateData { label: HTMLElement; } @@ -385,6 +419,7 @@ class CallStackRenderer implements IRenderer { private static readonly STACK_FRAME_TEMPLATE_ID = 'stackFrame'; private static readonly ERROR_TEMPLATE_ID = 'error'; private static readonly LOAD_MORE_TEMPLATE_ID = 'loadMore'; + private static readonly SHOW_MORE_TEMPLATE_ID = 'showMore'; private static readonly SESSION_TEMPLATE_ID = 'session'; constructor( @@ -393,11 +428,11 @@ class CallStackRenderer implements IRenderer { // noop } - public getHeight(tree: ITree, element: any): number { + getHeight(tree: ITree, element: any): number { return 22; } - public getTemplateId(tree: ITree, element: any): string { + getTemplateId(tree: ITree, element: any): string { if (element instanceof DebugSession) { return CallStackRenderer.SESSION_TEMPLATE_ID; } @@ -410,11 +445,17 @@ class CallStackRenderer implements IRenderer { if (typeof element === 'string') { return CallStackRenderer.ERROR_TEMPLATE_ID; } + if (element instanceof ThreadAndSessionIds) { + return CallStackRenderer.LOAD_MORE_TEMPLATE_ID; + } + if (element instanceof Array) { + return CallStackRenderer.SHOW_MORE_TEMPLATE_ID; + } - return CallStackRenderer.LOAD_MORE_TEMPLATE_ID; + return undefined; } - public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any { + renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any { if (templateId === CallStackRenderer.SESSION_TEMPLATE_ID) { let data: ISessionTemplateData = Object.create(null); data.session = dom.append(container, $('.session')); @@ -426,13 +467,19 @@ class CallStackRenderer implements IRenderer { } if (templateId === CallStackRenderer.LOAD_MORE_TEMPLATE_ID) { - let data: ILoadMoreTemplateData = Object.create(null); + let data: ILabelTemplateData = Object.create(null); data.label = dom.append(container, $('.load-more')); return data; } + if (templateId === CallStackRenderer.SHOW_MORE_TEMPLATE_ID) { + let data: ILabelTemplateData = Object.create(null); + data.label = dom.append(container, $('.show-more')); + + return data; + } if (templateId === CallStackRenderer.ERROR_TEMPLATE_ID) { - let data: ILoadMoreTemplateData = Object.create(null); + let data: ILabelTemplateData = Object.create(null); data.label = dom.append(container, $('.error')); return data; @@ -458,7 +505,7 @@ class CallStackRenderer implements IRenderer { return data; } - public renderElement(tree: ITree, element: any, templateId: string, templateData: any): void { + renderElement(tree: ITree, element: any, templateId: string, templateData: any): void { if (templateId === CallStackRenderer.SESSION_TEMPLATE_ID) { this.renderSession(element, templateData); } else if (templateId === CallStackRenderer.THREAD_TEMPLATE_ID) { @@ -469,6 +516,8 @@ class CallStackRenderer implements IRenderer { this.renderError(element, templateData); } else if (templateId === CallStackRenderer.LOAD_MORE_TEMPLATE_ID) { this.renderLoadMore(templateData); + } else if (templateId === CallStackRenderer.SHOW_MORE_TEMPLATE_ID) { + this.renderShowMore(templateData, element); } } @@ -498,12 +547,20 @@ class CallStackRenderer implements IRenderer { data.label.title = element; } - private renderLoadMore(data: ILoadMoreTemplateData): void { + private renderLoadMore(data: ILabelTemplateData): void { data.label.textContent = nls.localize('loadMoreStackFrames', "Load More Stack Frames"); } + private renderShowMore(data: ILabelTemplateData, element: IStackFrame[]): void { + if (element.every(sf => sf.source && sf.source.origin && sf.source.origin === element[0].source.origin)) { + data.label.textContent = nls.localize('showMoreAndOrigin', "Show {0} More: {1}", element.length, element[0].source.origin); + } else { + data.label.textContent = nls.localize('showMoreStackFrames', "Show {0} More Stack Frames", element.length); + } + } + private renderStackFrame(stackFrame: IStackFrame, data: IStackFrameTemplateData): void { - dom.toggleClass(data.stackFrame, 'disabled', !stackFrame.source.available || stackFrame.source.presentationHint === 'deemphasize'); + dom.toggleClass(data.stackFrame, 'disabled', !stackFrame.source || !stackFrame.source.available || stackFrame.source.presentationHint === 'deemphasize'); dom.toggleClass(data.stackFrame, 'label', stackFrame.presentationHint === 'label'); dom.toggleClass(data.stackFrame, 'subtle', stackFrame.presentationHint === 'subtle'); @@ -525,7 +582,7 @@ class CallStackRenderer implements IRenderer { } } - public disposeTemplate(tree: ITree, templateId: string, templateData: any): void { + disposeTemplate(tree: ITree, templateId: string, templateData: any): void { // noop } } @@ -536,7 +593,7 @@ class CallstackAccessibilityProvider implements IAccessibilityProvider { // noop } - public getAriaLabel(tree: ITree, element: any): string { + getAriaLabel(tree: ITree, element: any): string { if (element instanceof Thread) { return nls.localize('threadAriaLabel', "Thread {0}, callstack, debug", (element).name); } diff --git a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts index 17fe3ee55..6ca468713 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts @@ -46,7 +46,7 @@ import { isMacintosh } from 'vs/base/common/platform'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { URI } from 'vs/base/common/uri'; import { DebugViewlet } from 'vs/workbench/parts/debug/browser/debugViewlet'; -import { Repl } from 'vs/workbench/parts/debug/electron-browser/repl'; +import { Repl, ClearReplAction } from 'vs/workbench/parts/debug/electron-browser/repl'; import { DebugQuickOpenHandler } from 'vs/workbench/parts/debug/browser/debugQuickOpen'; import { DebugStatus } from 'vs/workbench/parts/debug/browser/debugStatus'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; @@ -149,6 +149,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(EnableAllBreakpointsAc registry.registerWorkbenchAction(new SyncActionDescriptor(DisableAllBreakpointsAction, DisableAllBreakpointsAction.ID, DisableAllBreakpointsAction.LABEL), 'Debug: Disable All Breakpoints', debugCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(FocusReplAction, FocusReplAction.ID, FocusReplAction.LABEL), 'Debug: Focus on Debug Console View', debugCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(SelectAndStartAction, SelectAndStartAction.ID, SelectAndStartAction.LABEL), 'Debug: Select and Start Debugging', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL), 'Debug: Clear Console', debugCategory); // Register Quick Open (Registry.as(QuickOpenExtensions.Quickopen)).registerQuickOpenHandler( diff --git a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts index 42d478e77..76f6e931e 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as strings from 'vs/base/common/strings'; import * as objects from 'vs/base/common/objects'; import { URI as uri } from 'vs/base/common/uri'; @@ -22,7 +21,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IDebugConfigurationProvider, ICompound, IDebugConfiguration, IConfig, IGlobalConfig, IConfigurationManager, ILaunch, IDebugAdapterProvider, IDebugAdapter, ITerminalSettings, ITerminalLauncher, IDebugSession, IAdapterDescriptor, CONTEXT_DEBUG_CONFIGURATION_TYPE } from 'vs/workbench/parts/debug/common/debug'; +import { IDebugConfigurationProvider, ICompound, IDebugConfiguration, IConfig, IGlobalConfig, IConfigurationManager, ILaunch, IDebugAdapterProvider, IDebugAdapter, ITerminalSettings, ITerminalLauncher, IDebugSession, IAdapterDescriptor, CONTEXT_DEBUG_CONFIGURATION_TYPE, IDebugAdapterFactory } from 'vs/workbench/parts/debug/common/debug'; import { Debugger } from 'vs/workbench/parts/debug/node/debugger'; import { IEditorService, ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -49,8 +48,9 @@ export class ConfigurationManager implements IConfigurationManager { private selectedLaunch: ILaunch; private toDispose: IDisposable[]; private _onDidSelectConfigurationName = new Emitter(); - private providers: IDebugConfigurationProvider[]; - private debugAdapterProviders: Map; + private configProviders: IDebugConfigurationProvider[]; + private adapterProviders: IDebugAdapterProvider[]; + private debugAdapterFactories: Map; private terminalLauncher: ITerminalLauncher; private debugConfigurationTypeContext: IContextKey; @@ -66,7 +66,8 @@ export class ConfigurationManager implements IConfigurationManager { @IExtensionService private extensionService: IExtensionService, @IContextKeyService contextKeyService: IContextKeyService ) { - this.providers = []; + this.configProviders = []; + this.adapterProviders = []; this.debuggers = []; this.toDispose = []; this.registerListeners(lifecycleService); @@ -74,112 +75,146 @@ export class ConfigurationManager implements IConfigurationManager { const previousSelectedRoot = this.storageService.get(DEBUG_SELECTED_ROOT, StorageScope.WORKSPACE); const previousSelectedLaunch = this.launches.filter(l => l.uri.toString() === previousSelectedRoot).pop(); this.debugConfigurationTypeContext = CONTEXT_DEBUG_CONFIGURATION_TYPE.bindTo(contextKeyService); - this.debugAdapterProviders = new Map(); + this.debugAdapterFactories = new Map(); if (previousSelectedLaunch) { this.selectConfiguration(previousSelectedLaunch, this.storageService.get(DEBUG_SELECTED_CONFIG_NAME_KEY, StorageScope.WORKSPACE)); } } - public registerDebugConfigurationProvider(handle: number, debugConfigurationProvider: IDebugConfigurationProvider): void { - if (!debugConfigurationProvider) { - return; - } + // debuggers - debugConfigurationProvider.handle = handle; - this.providers = this.providers.filter(p => p.handle !== handle); - this.providers.push(debugConfigurationProvider); - const dbg = this.getDebugger(debugConfigurationProvider.type); - // Check if the provider contributes provideDebugConfigurations method - if (dbg && debugConfigurationProvider.provideDebugConfigurations) { - dbg.hasConfigurationProvider = true; + public registerDebugAdapterFactory(debugTypes: string[], debugAdapterLauncher: IDebugAdapterFactory): IDisposable { + debugTypes.forEach(debugType => this.debugAdapterFactories.set(debugType, debugAdapterLauncher)); + return { + dispose: () => { + debugTypes.forEach(debugType => this.debugAdapterFactories.delete(debugType)); + } + }; + } + + public createDebugAdapter(session: IDebugSession): IDebugAdapter { + let dap = this.debugAdapterFactories.get(session.configuration.type); + if (dap) { + return dap.createDebugAdapter(session); } + return undefined; } - public needsToRunInExtHost(debugType: string): boolean { - // if the given debugType matches any registered provider that has a provideTracker method, we need to run the DA in the EH - const providers = this.providers.filter(p => p.hasTracker && (p.type === debugType || p.type === '*')); - return providers.length > 0; + public substituteVariables(debugType: string, folder: IWorkspaceFolder, config: IConfig): Promise { + let dap = this.debugAdapterFactories.get(debugType); + if (dap) { + return dap.substituteVariables(folder, config); + } + return Promise.resolve(config); } - public unregisterDebugConfigurationProvider(handle: number): void { - this.providers = this.providers.filter(p => p.handle !== handle); + public runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise { + let tl: ITerminalLauncher = this.debugAdapterFactories.get(debugType); + if (!tl) { + if (!this.terminalLauncher) { + this.terminalLauncher = this.instantiationService.createInstance(TerminalLauncher); + } + tl = this.terminalLauncher; + } + return tl.runInTerminal(args, config); } - public resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: IConfig): TPromise { - return this.activateDebuggers(`onDebugResolve:${type}`).then(() => { - // pipe the config through the promises sequentially. append at the end the '*' types - const providers = this.providers.filter(p => p.type === type && p.resolveDebugConfiguration) - .concat(this.providers.filter(p => p.type === '*' && p.resolveDebugConfiguration)); + // debug adapter - return providers.reduce((promise, provider) => { - return promise.then(config => { - if (config) { - return provider.resolveDebugConfiguration(folderUri, config); - } else { - return Promise.resolve(config); - } - }); - }, Promise.resolve(debugConfiguration)); - }); + public registerDebugAdapterProvider(debugAdapterProvider: IDebugAdapterProvider): IDisposable { + this.adapterProviders.push(debugAdapterProvider); + return { + dispose: () => { + this.unregisterDebugAdapterProvider(debugAdapterProvider); + } + }; } - public provideDebugConfigurations(folderUri: uri | undefined, type: string): TPromise { - return this.activateDebuggers('onDebugInitialConfigurations') - .then(() => Promise.all(this.providers.filter(p => p.type === type && p.provideDebugConfigurations).map(p => p.provideDebugConfigurations(folderUri))) - .then(results => results.reduce((first, second) => first.concat(second), []))); + public unregisterDebugAdapterProvider(debugAdapterProvider: IDebugAdapterProvider): void { + const ix = this.adapterProviders.indexOf(debugAdapterProvider); + if (ix >= 0) { + this.configProviders.splice(ix, 1); + } } - public provideDebugAdapter(session: IDebugSession, folderUri: uri | undefined, config: IConfig): TPromise { - const providers = this.providers.filter(p => p.type === config.type && p.provideDebugAdapter); + public provideDebugAdapter(session: IDebugSession): Promise { + + const config = session.configuration; + + // first try legacy proposed API: DebugConfigurationProvider.debugAdapterExecutable + const providers0 = this.configProviders.filter(p => p.type === config.type && p.debugAdapterExecutable); + if (providers0.length === 1) { + return providers0[0].debugAdapterExecutable(session.root ? session.root.uri : undefined); + } else { + // TODO@AW handle n > 1 case + } + + // try new proposed API + const providers = this.adapterProviders.filter(p => p.type === config.type && p.provideDebugAdapter); if (providers.length === 1) { - return providers[0].provideDebugAdapter(session, folderUri, config); + return providers[0].provideDebugAdapter(session); } else { // TODO@AW handle n > 1 case } return Promise.resolve(undefined); } - public registerDebugAdapterProvider(debugTypes: string[], debugAdapterLauncher: IDebugAdapterProvider): IDisposable { - debugTypes.forEach(debugType => this.debugAdapterProviders.set(debugType, debugAdapterLauncher)); + // debug configurations + + public registerDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): IDisposable { + this.configProviders.push(debugConfigurationProvider); return { dispose: () => { - debugTypes.forEach(debugType => this.debugAdapterProviders.delete(debugType)); + this.unregisterDebugConfigurationProvider(debugConfigurationProvider); } }; } - private getDebugAdapterProvider(type: string): IDebugAdapterProvider | undefined { - return this.debugAdapterProviders.get(type); + public unregisterDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): void { + const ix = this.configProviders.indexOf(debugConfigurationProvider); + if (ix >= 0) { + this.configProviders.splice(ix, 1); + } } - public createDebugAdapter(session: IDebugSession, folder: IWorkspaceFolder, config: IConfig): IDebugAdapter { - let dap = this.getDebugAdapterProvider(config.type); - if (dap) { - return dap.createDebugAdapter(session, folder, config); - } - return undefined; + public hasDebugConfigurationProvider(debugType: string): boolean { + // check if there are providers for the given type that contribute a provideDebugConfigurations method + const providers = this.configProviders.filter(p => p.provideDebugConfigurations && (p.type === debugType)); + return providers.length > 0; } - public substituteVariables(debugType: string, folder: IWorkspaceFolder, config: IConfig): TPromise { - let dap = this.getDebugAdapterProvider(debugType); - if (dap) { - return dap.substituteVariables(folder, config); - } - return Promise.resolve(config); + public needsToRunInExtHost(debugType: string): boolean { + // if the given debugType matches any registered provider that has a provideTracker method, we need to run the DA in the EH + const providers = this.configProviders.filter(p => p.hasTracker && (p.type === debugType || p.type === '*')); + return providers.length > 0; } - public runInTerminal(debugType: string, args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise { + public resolveConfigurationByProviders(folderUri: uri | undefined, type: string | undefined, debugConfiguration: IConfig): Thenable { + return this.activateDebuggers('onDebugResolve', type).then(() => { + // pipe the config through the promises sequentially. append at the end the '*' types + const providers = this.configProviders.filter(p => p.type === type && p.resolveDebugConfiguration) + .concat(this.configProviders.filter(p => p.type === '*' && p.resolveDebugConfiguration)); - let tl: ITerminalLauncher = this.getDebugAdapterProvider(debugType); - if (!tl) { - if (!this.terminalLauncher) { - this.terminalLauncher = this.instantiationService.createInstance(TerminalLauncher); - } - tl = this.terminalLauncher; - } - return tl.runInTerminal(args, config); + return providers.reduce((promise, provider) => { + return promise.then(config => { + if (config) { + return provider.resolveDebugConfiguration(folderUri, config); + } else { + return Promise.resolve(config); + } + }); + }, Promise.resolve(debugConfiguration)); + }); + } + + public provideDebugConfigurations(folderUri: uri | undefined, type: string): Thenable { + return this.activateDebuggers('onDebugInitialConfigurations') + .then(() => Promise.all(this.configProviders.filter(p => p.type === type && p.provideDebugConfigurations).map(p => p.provideDebugConfigurations(folderUri))) + .then(results => results.reduce((first, second) => first.concat(second), []))); } + /////////////////////////////////////////////////////////// + private registerListeners(lifecycleService: ILifecycleService): void { debuggersExtPoint.setHandler((extensions) => { extensions.forEach(extension => { @@ -338,14 +373,14 @@ export class ConfigurationManager implements IConfigurationManager { return this.debuggers.filter(dbg => strings.equalsIgnoreCase(dbg.type, type)).pop(); } - public guessDebugger(type?: string): TPromise { + public guessDebugger(type?: string): Thenable { if (type) { const adapter = this.getDebugger(type); return Promise.resolve(adapter); } const activeTextEditorWidget = this.editorService.activeTextEditorWidget; - let candidates: TPromise; + let candidates: Thenable; if (isCodeEditor(activeTextEditorWidget)) { const model = activeTextEditorWidget.getModel(); const language = model ? model.getLanguageIdentifier().language : undefined; @@ -359,7 +394,7 @@ export class ConfigurationManager implements IConfigurationManager { } if (!candidates) { - candidates = this.activateDebuggers('onDebugInitialConfigurations').then(() => this.debuggers.filter(a => a.hasInitialConfiguration() || a.hasConfigurationProvider)); + candidates = this.activateDebuggers('onDebugInitialConfigurations').then(() => this.debuggers.filter(dbg => dbg.hasInitialConfiguration() || dbg.hasConfigurationProvider())); } return candidates.then(debuggers => { @@ -378,8 +413,17 @@ export class ConfigurationManager implements IConfigurationManager { }); } - private activateDebuggers(activationEvent: string): TPromise { - return this.extensionService.activateByEvent(activationEvent).then(() => this.extensionService.activateByEvent('onDebug')); + public activateDebuggers(activationEvent: string, debugType?: string): Thenable { + const thenables: Thenable[] = [ + this.extensionService.activateByEvent(activationEvent), + this.extensionService.activateByEvent('onDebug') + ]; + if (debugType) { + thenables.push(this.extensionService.activateByEvent(`${activationEvent}:${debugType}`)); + } + return Promise.all(thenables).then(_ => { + return void 0; + }); } private saveState(): void { @@ -459,7 +503,7 @@ class Launch implements ILaunch { return config.configurations.filter(config => config && config.name === name).shift(); } - public openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): TPromise<{ editor: IEditor, created: boolean }> { + public openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): Thenable<{ editor: IEditor, created: boolean }> { const resource = this.uri; let created = false; @@ -537,7 +581,7 @@ class WorkspaceLaunch extends Launch implements ILaunch { return this.configurationService.inspect('launch').workspace; } - openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): TPromise<{ editor: IEditor, created: boolean }> { + openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): Thenable<{ editor: IEditor, created: boolean }> { return this.editorService.openEditor({ resource: this.contextService.getWorkspace().configuration, options: { preserveFocus } @@ -574,7 +618,7 @@ class UserLaunch extends Launch implements ILaunch { return this.configurationService.inspect('launch').user; } - openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): TPromise<{ editor: IEditor, created: boolean }> { + openConfigFile(sideBySide: boolean, preserveFocus: boolean, type?: string): Thenable<{ editor: IEditor, created: boolean }> { return this.preferencesService.openGlobalSettings(false, { preserveFocus }).then(editor => ({ editor, created: false })); } } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts b/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts index f8e40ed8b..8445d347a 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { RunOnceScheduler } from 'vs/base/common/async'; import * as lifecycle from 'vs/base/common/lifecycle'; import * as env from 'vs/base/common/platform'; @@ -95,7 +94,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { this.toggleExceptionWidget(); } - private getContextMenuActions(breakpoints: ReadonlyArray, uri: uri, lineNumber: number): TPromise<(IAction | ContextSubMenu)[]> { + private getContextMenuActions(breakpoints: ReadonlyArray, uri: uri, lineNumber: number): (IAction | ContextSubMenu)[] { const actions: (IAction | ContextSubMenu)[] = []; if (breakpoints.length === 1) { const breakpointType = breakpoints[0].logMessage ? nls.localize('logPoint', "Logpoint") : nls.localize('breakpoint', "Breakpoint"); @@ -166,7 +165,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { )); } - return Promise.resolve(actions); + return actions; } private registerListeners(): void { @@ -321,7 +320,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { return EDITOR_CONTRIBUTION_ID; } - public showHover(range: Range, focus: boolean): TPromise { + public showHover(range: Range, focus: boolean): Promise { const sf = this.debugService.getViewModel().focusedStackFrame; const model = this.editor.getModel(); if (sf && model && sf.source.uri.toString() === model.uri.toString()) { @@ -524,14 +523,14 @@ export class DebugEditorContribution implements IDebugEditorContribution { if (this.configurationWidget) { this.configurationWidget.dispose(); } - if (model && LAUNCH_JSON_REGEX.test(model.uri.toString())) { + if (model && LAUNCH_JSON_REGEX.test(model.uri.toString()) && !this.editor.getConfiguration().readOnly) { this.configurationWidget = this.instantiationService.createInstance(FloatingClickWidget, this.editor, nls.localize('addConfiguration', "Add Configuration..."), null); this.configurationWidget.render(); this.toDispose.push(this.configurationWidget.onClick(() => this.addLaunchConfiguration())); } } - public addLaunchConfiguration(): TPromise { + public addLaunchConfiguration(): Promise { /* __GDPR__ "debug/addLaunchConfiguration" : {} */ @@ -561,7 +560,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { return Promise.resolve(undefined); } - const insertLine = (position: Position): TPromise => { + const insertLine = (position: Position): Promise => { // Check if there are more characters on a line after a "configurations": [, if yes enter a newline if (this.editor.getModel().getLineLastNonWhitespaceColumn(position.lineNumber) > position.column) { this.editor.setPosition(position); diff --git a/src/vs/workbench/parts/debug/electron-browser/debugHover.ts b/src/vs/workbench/parts/debug/electron-browser/debugHover.ts index 89c071e90..682074fdc 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugHover.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugHover.ts @@ -5,7 +5,6 @@ import * as nls from 'vs/nls'; import * as lifecycle from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; import { KeyCode } from 'vs/base/common/keyCodes'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import * as dom from 'vs/base/browser/dom'; @@ -140,7 +139,7 @@ export class DebugHoverWidget implements IContentWidget { return this.domNode; } - public showAt(range: Range, focus: boolean): TPromise { + public showAt(range: Range, focus: boolean): Promise { const pos = range.getStartPosition(); const session = this.debugService.getViewModel().focusedSession; @@ -152,7 +151,7 @@ export class DebugHoverWidget implements IContentWidget { return Promise.resolve(this.hide()); } - let promise: TPromise; + let promise: Promise; if (session.capabilities.supportsEvaluateForHovers) { const result = new Expression(matchingExpression); promise = result.evaluate(session, this.debugService.getViewModel().focusedStackFrame, 'hover').then(() => result); @@ -179,7 +178,7 @@ export class DebugHoverWidget implements IContentWidget { className: 'hoverHighlight' }); - private doFindExpression(container: IExpressionContainer, namesToFind: string[]): TPromise { + private doFindExpression(container: IExpressionContainer, namesToFind: string[]): Promise { if (!container) { return Promise.resolve(null); } @@ -199,7 +198,7 @@ export class DebugHoverWidget implements IContentWidget { }); } - private findExpressionInStackFrame(namesToFind: string[]): TPromise { + private findExpressionInStackFrame(namesToFind: string[]): Promise { return this.debugService.getViewModel().focusedStackFrame.getScopes() .then(scopes => scopes.filter(s => !s.expensive)) .then(scopes => Promise.all(scopes.map(scope => this.doFindExpression(scope, namesToFind)))) @@ -208,7 +207,7 @@ export class DebugHoverWidget implements IContentWidget { .then(expressions => (expressions.length > 0 && expressions.every(e => e.value === expressions[0].value)) ? expressions[0] : null); } - private doShow(position: Position, expression: IExpression, focus: boolean, forceValueHover = false): TPromise { + private doShow(position: Position, expression: IExpression, focus: boolean, forceValueHover = false): Thenable { if (!this.domNode) { this.create(); } @@ -262,7 +261,7 @@ export class DebugHoverWidget implements IContentWidget { if (visibleElementsCount === 0) { this.doShow(this.showAtPosition, this.tree.getInput(), false, true); } else { - const height = Math.min(visibleElementsCount, MAX_ELEMENTS_SHOWN) * 18; + const height = Math.min(visibleElementsCount, MAX_ELEMENTS_SHOWN) * 18 + 10; // add 10 px for the horizontal scroll bar if (this.treeContainer.clientHeight !== height) { this.treeContainer.style.height = `${height}px`; diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index c09277276..377734065 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -9,7 +9,6 @@ import { URI as uri } from 'vs/base/common/uri'; import { first, distinct } from 'vs/base/common/arrays'; import * as errors from 'vs/base/common/errors'; import severity from 'vs/base/common/severity'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IMarkerService } from 'vs/platform/markers/common/markers'; @@ -44,7 +43,7 @@ import { IAction, Action } from 'vs/base/common/actions'; import { deepClone, equals } from 'vs/base/common/objects'; import { DebugSession } from 'vs/workbench/parts/debug/electron-browser/debugSession'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, REPL_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IGlobalConfig, IStackFrame, AdapterEndEvent } from 'vs/workbench/parts/debug/common/debug'; +import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, REPL_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IGlobalConfig, IStackFrame, AdapterEndEvent, getStateLabel } from 'vs/workbench/parts/debug/common/debug'; import { isExtensionHostDebugging } from 'vs/workbench/parts/debug/common/debugUtils'; import { RunOnceScheduler } from 'vs/base/common/async'; import { isErrorWithActions, createErrorWithActions } from 'vs/base/common/errorsWithActions'; @@ -227,11 +226,8 @@ export class DebugService implements IDebugService { private onStateChange(): void { const state = this.state; if (this.previousState !== state) { - const stateLabel = State[state]; - if (stateLabel) { - this.debugState.set(stateLabel.toLowerCase()); - this.inDebugMode.set(state !== State.Inactive); - } + this.debugState.set(getStateLabel(state)); + this.inDebugMode.set(state !== State.Inactive); this.previousState = state; this._onDidChangeState.fire(state); } @@ -259,13 +255,13 @@ export class DebugService implements IDebugService { * main entry point * properly manages compounds, checks for errors and handles the initializing state. */ - startDebugging(launch: ILaunch, configOrName?: IConfig | string, noDebug = false, unresolvedConfig?: IConfig, ): TPromise { + startDebugging(launch: ILaunch, configOrName?: IConfig | string, noDebug = false, unresolvedConfig?: IConfig, ): Thenable { this.startInitializingState(); // make sure to save all files and that the configuration is up to date - return this.extensionService.activateByEvent('onDebug').then(() => - this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration(launch ? launch.workspace : undefined).then(() => - this.extensionService.whenInstalledExtensionsRegistered().then(() => { + return this.extensionService.activateByEvent('onDebug').then(() => { + return this.textFileService.saveAll().then(() => this.configurationService.reloadConfiguration(launch ? launch.workspace : undefined).then(() => { + return this.extensionService.whenInstalledExtensionsRegistered().then(() => { let config: IConfig, compound: ICompound; if (!configOrName) { @@ -332,21 +328,22 @@ export class DebugService implements IDebugService { } return this.createSession(launch, config, unresolvedConfig, noDebug); - }) - ))).then(success => { - // make sure to get out of initializing state, and propagate the result - this.endInitializingState(); - return success; - }, err => { - this.endInitializingState(); - return Promise.reject(err); - }); + }); + })); + }).then(success => { + // 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, config: IConfig, unresolvedConfig: IConfig, noDebug: boolean): TPromise { + private createSession(launch: ILaunch, config: IConfig, unresolvedConfig: IConfig, noDebug: boolean): Thenable { // 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; @@ -420,10 +417,10 @@ export class DebugService implements IDebugService { /** * instantiates the new session, initializes the session, registers session listeners and reports telemetry */ - private doCreateSession(root: IWorkspaceFolder, configuration: { resolved: IConfig, unresolved: IConfig }): TPromise { + private doCreateSession(root: IWorkspaceFolder, configuration: { resolved: IConfig, unresolved: IConfig }): Thenable { const session = this.instantiationService.createInstance(DebugSession, configuration, root, this.model); - + this.model.addSession(session); // register listeners as the very first thing! this.registerSessionListeners(session); @@ -477,7 +474,7 @@ export class DebugService implements IDebugService { }); } - private launchOrAttachToSession(session: IDebugSession, focus = true): TPromise { + private launchOrAttachToSession(session: IDebugSession, focus = true): Thenable { const dbgr = this.configurationManager.getDebugger(session.configuration.type); return session.initialize(dbgr).then(() => { return session.launchOrAttach(session.configuration).then(() => { @@ -503,6 +500,9 @@ export class DebugService implements IDebugService { if (session.state === State.Running && this.viewModel.focusedSession === session) { sessionRunningScheduler.schedule(); } + if (session === this.viewModel.focusedSession) { + this.onStateChange(); + } })); this.toDispose.push(session.onDidEndAdapter(adapterExitEvent => { @@ -546,7 +546,7 @@ export class DebugService implements IDebugService { })); } - restartSession(session: IDebugSession, restartData?: any): TPromise { + restartSession(session: IDebugSession, restartData?: any): Thenable { return this.textFileService.saveAll().then(() => { const isAutoRestart = !!restartData; const runTasks: () => Thenable = () => { @@ -616,17 +616,21 @@ export class DebugService implements IDebugService { }); } - stopSession(session: IDebugSession): TPromise { + stopSession(session: IDebugSession): Promise { if (session) { return session.terminate(); } const sessions = this.model.getSessions(); + if (sessions.length === 0) { + this.endInitializingState(); + } + return Promise.all(sessions.map(s => s.terminate())); } - private substituteVariables(launch: ILaunch | undefined, config: IConfig): TPromise { + private substituteVariables(launch: ILaunch | undefined, config: IConfig): Thenable { const dbg = this.configurationManager.getDebugger(config.type); if (dbg) { let folder: IWorkspaceFolder | undefined = undefined; @@ -648,7 +652,7 @@ export class DebugService implements IDebugService { return Promise.resolve(config); } - private showError(message: string, actions: IAction[] = []): TPromise { + private showError(message: string, actions: IAction[] = []): Thenable { const configureAction = this.instantiationService.createInstance(debugactions.ConfigureAction, debugactions.ConfigureAction.ID, debugactions.ConfigureAction.LABEL); actions.push(configureAction); return this.dialogService.show(severity.Error, message, actions.map(a => a.label).concat(nls.localize('cancel', "Cancel")), { cancelId: actions.length }).then(choice => { @@ -662,7 +666,7 @@ export class DebugService implements IDebugService { //---- task management - private runTaskAndCheckErrors(root: IWorkspaceFolder, taskId: string | TaskIdentifier): TPromise { + private runTaskAndCheckErrors(root: IWorkspaceFolder, taskId: string | TaskIdentifier): Thenable { const debugAnywayAction = new Action('debug.debugAnyway', nls.localize('debugAnyway', "Debug Anyway"), undefined, true, () => Promise.resolve(TaskRunResult.Success)); return this.runTask(root, taskId).then((taskSummary: ITaskSummary) => { @@ -681,7 +685,8 @@ export class DebugService implements IDebugService { : nls.localize('preLaunchTaskExitCode', "The preLaunchTask '{0}' terminated with exit code {1}.", taskLabel, taskSummary.exitCode); const showErrorsAction = new Action('debug.showErrors', nls.localize('showErrors', "Show Errors"), undefined, true, () => { - return this.panelService.openPanel(Constants.MARKERS_PANEL_ID).then(() => TaskRunResult.Failure); + this.panelService.openPanel(Constants.MARKERS_PANEL_ID); + return Promise.resolve(TaskRunResult.Failure); }); return this.showError(message, [debugAnywayAction, showErrorsAction]); @@ -690,7 +695,7 @@ export class DebugService implements IDebugService { }); } - private runTask(root: IWorkspaceFolder, taskId: string | TaskIdentifier): TPromise { + private runTask(root: IWorkspaceFolder, taskId: string | TaskIdentifier): Thenable { if (!taskId) { return Promise.resolve(null); } @@ -779,7 +784,7 @@ export class DebugService implements IDebugService { if (stackFrame) { stackFrame.openInEditor(this.editorService, true).then(undefined, errors.onUnexpectedError); - aria.alert(nls.localize('debuggingPaused', "Debugging paused, reason {0}, {1} {2}", thread.stoppedDetails.reason, stackFrame.source ? stackFrame.source.name : '', stackFrame.range.startLineNumber)); + aria.alert(nls.localize('debuggingPaused', "Debugging paused {0}, {1} {2}", thread.stoppedDetails ? `, reason ${thread.stoppedDetails.reason}` : '', stackFrame.source ? stackFrame.source.name : '', stackFrame.range.startLineNumber)); } this.viewModel.setFocus(stackFrame, thread, session, explicit); @@ -806,7 +811,7 @@ export class DebugService implements IDebugService { //---- breakpoints - enableOrDisableBreakpoints(enable: boolean, breakpoint?: IEnablement): TPromise { + enableOrDisableBreakpoints(enable: boolean, breakpoint?: IEnablement): Promise { if (breakpoint) { this.model.setEnablement(breakpoint, enable); if (breakpoint instanceof Breakpoint) { @@ -822,7 +827,7 @@ export class DebugService implements IDebugService { return this.sendAllBreakpoints(); } - addBreakpoints(uri: uri, rawBreakpoints: IBreakpointData[], context: string): TPromise { + addBreakpoints(uri: uri, rawBreakpoints: IBreakpointData[], context: string): Promise { const breakpoints = this.model.addBreakpoints(uri, rawBreakpoints); breakpoints.forEach(bp => aria.status(nls.localize('breakpointAdded', "Added breakpoint, line {0}, file {1}", bp.lineNumber, uri.fsPath))); breakpoints.forEach(bp => this.telemetryDebugAddBreakpoint(bp, context)); @@ -839,7 +844,7 @@ export class DebugService implements IDebugService { } } - removeBreakpoints(id?: string): TPromise { + removeBreakpoints(id?: string): Promise { const toRemove = this.model.getBreakpoints().filter(bp => !id || bp.getId() === id); toRemove.forEach(bp => aria.status(nls.localize('breakpointRemoved', "Removed breakpoint, line {0}, file {1}", bp.lineNumber, bp.uri.fsPath))); const urisToClear = distinct(toRemove, bp => bp.uri.toString()).map(bp => bp.uri); @@ -849,7 +854,7 @@ export class DebugService implements IDebugService { return Promise.all(urisToClear.map(uri => this.sendBreakpoints(uri))); } - setBreakpointsActivated(activated: boolean): TPromise { + setBreakpointsActivated(activated: boolean): Promise { this.model.setBreakpointsActivated(activated); return this.sendAllBreakpoints(); } @@ -859,24 +864,24 @@ export class DebugService implements IDebugService { this.viewModel.setSelectedFunctionBreakpoint(newFunctionBreakpoint); } - renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise { + renameFunctionBreakpoint(id: string, newFunctionName: string): Promise { this.model.renameFunctionBreakpoint(id, newFunctionName); return this.sendFunctionBreakpoints(); } - removeFunctionBreakpoints(id?: string): TPromise { + removeFunctionBreakpoints(id?: string): Promise { this.model.removeFunctionBreakpoints(id); return this.sendFunctionBreakpoints(); } - sendAllBreakpoints(session?: IDebugSession): TPromise { + 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)); } - private sendBreakpoints(modelUri: uri, sourceModified = false, session?: IDebugSession): TPromise { + private sendBreakpoints(modelUri: uri, sourceModified = false, session?: IDebugSession): Promise { const breakpointsToSend = this.model.getBreakpoints({ uri: modelUri, enabledOnly: true }); @@ -885,7 +890,7 @@ export class DebugService implements IDebugService { ); } - private sendFunctionBreakpoints(session?: IDebugSession): TPromise { + private sendFunctionBreakpoints(session?: IDebugSession): Promise { const breakpointsToSend = this.model.getFunctionBreakpoints().filter(fbp => fbp.enabled && this.model.areBreakpointsActivated()); @@ -894,7 +899,7 @@ export class DebugService implements IDebugService { }); } - private sendExceptionBreakpoints(session?: IDebugSession): TPromise { + private sendExceptionBreakpoints(session?: IDebugSession): Promise { const enabledExceptionBps = this.model.getExceptionBreakpoints().filter(exb => exb.enabled); @@ -903,7 +908,7 @@ export class DebugService implements IDebugService { }); } - private sendToOneOrAllSessions(session: IDebugSession, send: (session: IDebugSession) => TPromise): TPromise { + private sendToOneOrAllSessions(session: IDebugSession, send: (session: IDebugSession) => Promise): Promise { if (session) { return send(session); } @@ -1007,7 +1012,7 @@ export class DebugService implements IDebugService { //---- telemetry - private telemetryDebugSessionStart(root: IWorkspaceFolder, type: string): TPromise { + private telemetryDebugSessionStart(root: IWorkspaceFolder, type: string): Thenable { const extension = this.configurationManager.getDebugger(type).extensionDescription; /* __GDPR__ "debugSessionStart" : { @@ -1031,7 +1036,7 @@ export class DebugService implements IDebugService { }); } - private telemetryDebugSessionStop(session: IDebugSession, adapterExitEvent: AdapterEndEvent): TPromise { + private telemetryDebugSessionStop(session: IDebugSession, adapterExitEvent: AdapterEndEvent): Thenable { const breakpoints = this.model.getBreakpoints(); @@ -1053,7 +1058,7 @@ export class DebugService implements IDebugService { }); } - private telemetryDebugMisconfiguration(debugType: string, message: string): TPromise { + private telemetryDebugMisconfiguration(debugType: string, message: string): Thenable { /* __GDPR__ "debugMisconfiguration" : { "type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, @@ -1066,13 +1071,13 @@ export class DebugService implements IDebugService { }); } - private telemetryDebugAddBreakpoint(breakpoint: IBreakpoint, context: string): TPromise { + private telemetryDebugAddBreakpoint(breakpoint: IBreakpoint, context: string): Thenable { /* __GDPR__ "debugAddBreakpoint" : { "context": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "hasCondition": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "hasHitCondition": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "hasLogMessage": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + "hasCondition": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "hasHitCondition": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "hasLogMessage": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } } */ diff --git a/src/vs/workbench/parts/debug/electron-browser/debugSession.ts b/src/vs/workbench/parts/debug/electron-browser/debugSession.ts index 2b6e6a94d..2597e28a2 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugSession.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugSession.ts @@ -8,7 +8,6 @@ import * as resources from 'vs/base/common/resources'; import * as nls from 'vs/nls'; import * as platform from 'vs/base/common/platform'; import severity from 'vs/base/common/severity'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Event, Emitter } from 'vs/base/common/event'; import { CompletionItem, completionKindFromLegacyString } from 'vs/editor/common/modes'; import { Position } from 'vs/editor/common/core/position'; @@ -19,7 +18,6 @@ import { mixin } from 'vs/base/common/objects'; import { Thread, ExpressionContainer, DebugModel } from 'vs/workbench/parts/debug/common/debugModel'; import { RawDebugSession } from 'vs/workbench/parts/debug/electron-browser/rawDebugSession'; import product from 'vs/platform/node/product'; -import { INotificationService } from 'vs/platform/notification/common/notification'; import { IWorkspaceFolder, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -32,6 +30,7 @@ import { Range } from 'vs/editor/common/core/range'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ReplModel } from 'vs/workbench/parts/debug/common/replModel'; +import { onUnexpectedError } from 'vs/base/common/errors'; export class DebugSession implements IDebugSession { private id: string; @@ -55,7 +54,6 @@ export class DebugSession implements IDebugSession { private _configuration: { resolved: IConfig, unresolved: IConfig }, public root: IWorkspaceFolder, private model: DebugModel, - @INotificationService private notificationService: INotificationService, @IDebugService private debugService: IDebugService, @ITelemetryService private telemetryService: ITelemetryService, @IOutputService private outputService: IOutputService, @@ -137,7 +135,7 @@ export class DebugSession implements IDebugSession { /** * create and initialize a new debug adapter for this session */ - initialize(dbgr: IDebugger): TPromise { + initialize(dbgr: IDebugger): Thenable { if (this.raw) { // if there was already a connection make sure to remove old listeners @@ -146,7 +144,7 @@ export class DebugSession implements IDebugSession { return dbgr.getCustomTelemetryService().then(customTelemetryService => { - return dbgr.createDebugAdapter(this, this.root, this._configuration.resolved, this.outputService).then(debugAdapter => { + return dbgr.createDebugAdapter(this, this.outputService).then(debugAdapter => { this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService); @@ -166,7 +164,6 @@ export class DebugSession implements IDebugSession { supportsRunInTerminalRequest: true, // #10574 locale: platform.locale }).then(() => { - this.model.addSession(this); this._onDidChangeState.fire(); this.model.setExceptionBreakpoints(this.raw.capabilities.exceptionBreakpointFilters); }); @@ -178,7 +175,7 @@ export class DebugSession implements IDebugSession { /** * launch or attach to the debuggee */ - launchOrAttach(config: IConfig): TPromise { + launchOrAttach(config: IConfig): Promise { if (this.raw) { // __sessionID only used for EH debugging (but we add it always for now...) @@ -194,7 +191,7 @@ export class DebugSession implements IDebugSession { /** * end the current debug adapter session */ - terminate(restart = false): TPromise { + terminate(restart = false): Promise { if (this.raw) { if (this.raw.capabilities.supportsTerminateRequest && this._configuration.resolved.request === 'launch') { return this.raw.terminate(restart).then(response => { @@ -211,7 +208,7 @@ export class DebugSession implements IDebugSession { /** * end the current debug adapter session */ - disconnect(restart = false): TPromise { + disconnect(restart = false): Promise { if (this.raw) { return this.raw.disconnect(restart).then(response => { return void 0; @@ -223,14 +220,14 @@ export class DebugSession implements IDebugSession { /** * restart debug adapter session */ - restart(): TPromise { + restart(): Promise { if (this.raw) { return this.raw.restart().then(() => undefined); } return Promise.reject(new Error('no debug adapter')); } - sendBreakpoints(modelUri: URI, breakpointsToSend: IBreakpoint[], sourceModified: boolean): TPromise { + sendBreakpoints(modelUri: URI, breakpointsToSend: IBreakpoint[], sourceModified: boolean): Promise { if (!this.raw) { return Promise.reject(new Error('no debug adapter')); @@ -272,7 +269,7 @@ export class DebugSession implements IDebugSession { }); } - sendFunctionBreakpoints(fbpts: IFunctionBreakpoint[]): TPromise { + sendFunctionBreakpoints(fbpts: IFunctionBreakpoint[]): Promise { if (this.raw) { if (this.raw.readyForBreakpoints) { return this.raw.setFunctionBreakpoints({ breakpoints: fbpts }).then(response => { @@ -286,13 +283,13 @@ export class DebugSession implements IDebugSession { }); } - return TPromise.as(undefined); + return Promise.resolve(undefined); } return Promise.reject(new Error('no debug adapter')); } - sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): TPromise { + sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise { if (this.raw) { if (this.raw.readyForBreakpoints) { return this.raw.setExceptionBreakpoints({ filters: exbpts.map(exb => exb.filter) }).then(() => undefined); @@ -302,21 +299,21 @@ export class DebugSession implements IDebugSession { return Promise.reject(new Error('no debug adapter')); } - customRequest(request: string, args: any): TPromise { + customRequest(request: string, args: any): Promise { if (this.raw) { return this.raw.custom(request, args); } return Promise.reject(new Error('no debug adapter')); } - stackTrace(threadId: number, startFrame: number, levels: number): TPromise { + stackTrace(threadId: number, startFrame: number, levels: number): Promise { if (this.raw) { return this.raw.stackTrace({ threadId, startFrame, levels }); } return Promise.reject(new Error('no debug adapter')); } - exceptionInfo(threadId: number): TPromise { + exceptionInfo(threadId: number): Promise { if (this.raw) { return this.raw.exceptionInfo({ threadId }).then(response => { if (response) { @@ -333,98 +330,98 @@ export class DebugSession implements IDebugSession { return Promise.reject(new Error('no debug adapter')); } - scopes(frameId: number): TPromise { + scopes(frameId: number): Promise { if (this.raw) { return this.raw.scopes({ frameId }); } return Promise.reject(new Error('no debug adapter')); } - variables(variablesReference: number, filter: 'indexed' | 'named', start: number, count: number): TPromise { + variables(variablesReference: number, filter: 'indexed' | 'named', start: number, count: number): Promise { if (this.raw) { return this.raw.variables({ variablesReference, filter, start, count }); } return Promise.resolve(undefined); } - evaluate(expression: string, frameId: number, context?: string): TPromise { + evaluate(expression: string, frameId: number, context?: string): Promise { if (this.raw) { return this.raw.evaluate({ expression, frameId, context }); } return Promise.reject(new Error('no debug adapter')); } - restartFrame(frameId: number, threadId: number): TPromise { + restartFrame(frameId: number, threadId: number): Promise { if (this.raw) { return this.raw.restartFrame({ frameId }, threadId).then(() => undefined); } return Promise.reject(new Error('no debug adapter')); } - next(threadId: number): TPromise { + next(threadId: number): Promise { if (this.raw) { return this.raw.next({ threadId }).then(() => undefined); } return Promise.reject(new Error('no debug adapter')); } - stepIn(threadId: number): TPromise { + stepIn(threadId: number): Promise { if (this.raw) { return this.raw.stepIn({ threadId }).then(() => undefined); } return Promise.reject(new Error('no debug adapter')); } - stepOut(threadId: number): TPromise { + stepOut(threadId: number): Promise { if (this.raw) { return this.raw.stepOut({ threadId }).then(() => undefined); } return Promise.reject(new Error('no debug adapter')); } - stepBack(threadId: number): TPromise { + stepBack(threadId: number): Promise { if (this.raw) { return this.raw.stepBack({ threadId }).then(() => undefined); } return Promise.reject(new Error('no debug adapter')); } - continue(threadId: number): TPromise { + continue(threadId: number): Promise { if (this.raw) { return this.raw.continue({ threadId }).then(() => undefined); } return Promise.reject(new Error('no debug adapter')); } - reverseContinue(threadId: number): TPromise { + reverseContinue(threadId: number): Promise { if (this.raw) { return this.raw.reverseContinue({ threadId }).then(() => undefined); } return Promise.reject(new Error('no debug adapter')); } - pause(threadId: number): TPromise { + pause(threadId: number): Promise { if (this.raw) { return this.raw.pause({ threadId }).then(() => undefined); } return Promise.reject(new Error('no debug adapter')); } - terminateThreads(threadIds?: number[]): TPromise { + terminateThreads(threadIds?: number[]): Promise { if (this.raw) { return this.raw.terminateThreads({ threadIds }).then(() => undefined); } return Promise.reject(new Error('no debug adapter')); } - setVariable(variablesReference: number, name: string, value: string): TPromise { + setVariable(variablesReference: number, name: string, value: string): Promise { if (this.raw) { return this.raw.setVariable({ variablesReference, name, value }); } return Promise.reject(new Error('no debug adapter')); } - loadSource(resource: URI): TPromise { + loadSource(resource: URI): Promise { if (!this.raw) { return Promise.reject(new Error('no debug adapter')); @@ -452,10 +449,14 @@ export class DebugSession implements IDebugSession { return this.raw.source({ sourceReference: rawSource.sourceReference, source: rawSource }); } - getLoadedSources(): TPromise { + getLoadedSources(): Promise { if (this.raw) { return this.raw.loadedSources({}).then(response => { - return response.body.sources.map(src => this.getSource(src)); + if (response.body && response.body.sources) { + return response.body.sources.map(src => this.getSource(src)); + } else { + return []; + } }, () => { return []; }); @@ -463,7 +464,7 @@ export class DebugSession implements IDebugSession { return Promise.reject(new Error('no debug adapter')); } - completions(frameId: number, text: string, position: Position, overwriteBefore: number): TPromise { + completions(frameId: number, text: string, position: Position, overwriteBefore: number): Promise { if (this.raw) { return this.raw.completions({ frameId, @@ -560,7 +561,7 @@ export class DebugSession implements IDebugSession { } } - private fetchThreads(stoppedDetails?: IRawStoppedDetails): TPromise { + private fetchThreads(stoppedDetails?: IRawStoppedDetails): Promise { return this.raw ? this.raw.threads().then(response => { if (response && response.body && response.body.threads) { response.body.threads.forEach(thread => { @@ -572,7 +573,7 @@ export class DebugSession implements IDebugSession { }); }); } - }) : TPromise.as(undefined); + }) : Promise.resolve(undefined); } //---- private @@ -587,7 +588,9 @@ export class DebugSession implements IDebugSession { if (this.raw) { this.raw.disconnect(); } - this.notificationService.error(e.message); + if (e.command !== 'canceled' && e.message !== 'canceled') { + onUnexpectedError(e); + } }); } @@ -640,7 +643,7 @@ export class DebugSession implements IDebugSession { this.rawListeners.push(this.raw.onDidTerminateDebugee(event => { aria.status(nls.localize('debuggingStopped', "Debugging stopped.")); if (event.body && event.body.restart) { - this.debugService.restartSession(this, event.body.restart).then(null, err => this.notificationService.error(err.message)); + this.debugService.restartSession(this, event.body.restart).then(null, onUnexpectedError); } else { this.raw.disconnect(); } @@ -652,7 +655,7 @@ export class DebugSession implements IDebugSession { this._onDidChangeState.fire(); })); - let outputPromises: TPromise[] = []; + let outpuPromises: Promise[] = []; this.rawListeners.push(this.raw.onDidOutput(event => { if (!event.body) { return; @@ -671,7 +674,7 @@ export class DebugSession implements IDebugSession { } // Make sure to append output in the correct order by properly waiting on preivous promises #33822 - const waitFor = outputPromises.slice(); + const waitFor = outpuPromises.slice(); const source = event.body.source ? { lineNumber: event.body.line, column: event.body.column ? event.body.column : 1, @@ -679,7 +682,7 @@ export class DebugSession implements IDebugSession { } : undefined; if (event.body.variablesReference) { const container = new ExpressionContainer(this, event.body.variablesReference, generateUuid()); - outputPromises.push(container.getChildren().then(children => { + outpuPromises.push(container.getChildren().then(children => { return Promise.all(waitFor).then(() => children.forEach(child => { // Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names) child.name = null; @@ -689,7 +692,7 @@ export class DebugSession implements IDebugSession { } else if (typeof event.body.output === 'string') { Promise.all(waitFor).then(() => this.appendToRepl(event.body.output, outputSeverity, source)); } - Promise.all(outputPromises).then(() => outputPromises = []); + Promise.all(outpuPromises).then(() => outpuPromises = []); })); this.rawListeners.push(this.raw.onDidBreakpoint(event => { @@ -796,7 +799,7 @@ export class DebugSession implements IDebugSession { this._onDidChangeREPLElements.fire(); } - addReplExpression(stackFrame: IStackFrame, name: string): TPromise { + addReplExpression(stackFrame: IStackFrame, name: string): Promise { const viewModel = this.debugService.getViewModel(); return this.repl.addReplExpression(stackFrame, name) .then(() => this._onDidChangeREPLElements.fire()) diff --git a/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.ts b/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.ts index 86e82c534..8161f529e 100644 --- a/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.ts +++ b/src/vs/workbench/parts/debug/electron-browser/electronDebugActions.ts @@ -5,7 +5,6 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITree } from 'vs/base/parts/tree/browser/tree'; import { removeAnsiEscapeCodes } from 'vs/base/common/strings'; import { Variable } from 'vs/workbench/parts/debug/common/debugModel'; @@ -22,7 +21,7 @@ export class CopyValueAction extends Action { this._enabled = typeof this.value === 'string' || (this.value instanceof Variable && !!this.value.evaluateName); } - public run(): TPromise { + public run(): Promise { if (this.value instanceof Variable) { const frameId = this.debugService.getViewModel().focusedStackFrame.frameId; const session = this.debugService.getViewModel().focusedSession; @@ -45,7 +44,7 @@ export class CopyEvaluatePathAction extends Action { this._enabled = this.value && !!this.value.evaluateName; } - public run(): TPromise { + public run(): Promise { clipboard.writeText(this.value.evaluateName); return Promise.resolve(undefined); } @@ -55,7 +54,7 @@ export class CopyAction extends Action { static readonly ID = 'workbench.debug.action.copy'; static LABEL = nls.localize('copy', "Copy"); - public run(): TPromise { + public run(): Promise { clipboard.writeText(window.getSelection().toString()); return Promise.resolve(undefined); } @@ -70,12 +69,12 @@ export class CopyAllAction extends Action { super(id, label); } - public run(): TPromise { + public run(): Promise { let text = ''; const navigator = this.tree.getNavigator(); // skip first navigator element - the root node while (navigator.next()) { - if (text) { + if (text && text.length > 0 && text[text.length - 1] !== lineDelimiter) { text += lineDelimiter; } text += (navigator.current()).toString(); @@ -90,7 +89,7 @@ export class CopyStackTraceAction extends Action { static readonly ID = 'workbench.action.debug.copyStackTrace'; static LABEL = nls.localize('copyStackTrace', "Copy Call Stack"); - public run(frame: IStackFrame): TPromise { + public run(frame: IStackFrame): Promise { clipboard.writeText(frame.thread.getCallStack().map(sf => sf.toString()).join(lineDelimiter)); return Promise.resolve(undefined); } diff --git a/src/vs/workbench/parts/debug/electron-browser/media/continue-tb.png b/src/vs/workbench/parts/debug/electron-browser/media/continue-tb.png index 32f8acec8..c0f6e765f 100644 Binary files a/src/vs/workbench/parts/debug/electron-browser/media/continue-tb.png and b/src/vs/workbench/parts/debug/electron-browser/media/continue-tb.png differ diff --git a/src/vs/workbench/parts/debug/electron-browser/media/pause-tb.png b/src/vs/workbench/parts/debug/electron-browser/media/pause-tb.png index bc6699fb5..51089a3b6 100644 Binary files a/src/vs/workbench/parts/debug/electron-browser/media/pause-tb.png and b/src/vs/workbench/parts/debug/electron-browser/media/pause-tb.png differ diff --git a/src/vs/workbench/parts/debug/electron-browser/media/restart-tb.png b/src/vs/workbench/parts/debug/electron-browser/media/restart-tb.png index 049f8f9f5..14a11b308 100644 Binary files a/src/vs/workbench/parts/debug/electron-browser/media/restart-tb.png and b/src/vs/workbench/parts/debug/electron-browser/media/restart-tb.png differ diff --git a/src/vs/workbench/parts/debug/electron-browser/media/stepinto-tb.png b/src/vs/workbench/parts/debug/electron-browser/media/stepinto-tb.png index 9e793d5e6..3915e870e 100644 Binary files a/src/vs/workbench/parts/debug/electron-browser/media/stepinto-tb.png and b/src/vs/workbench/parts/debug/electron-browser/media/stepinto-tb.png differ diff --git a/src/vs/workbench/parts/debug/electron-browser/media/stepout-tb.png b/src/vs/workbench/parts/debug/electron-browser/media/stepout-tb.png index 67bbb10f5..7e31b956f 100644 Binary files a/src/vs/workbench/parts/debug/electron-browser/media/stepout-tb.png and b/src/vs/workbench/parts/debug/electron-browser/media/stepout-tb.png differ diff --git a/src/vs/workbench/parts/debug/electron-browser/media/stepover-tb.png b/src/vs/workbench/parts/debug/electron-browser/media/stepover-tb.png index 408a47153..e3d99e7d1 100644 Binary files a/src/vs/workbench/parts/debug/electron-browser/media/stepover-tb.png and b/src/vs/workbench/parts/debug/electron-browser/media/stepover-tb.png differ diff --git a/src/vs/workbench/parts/debug/electron-browser/media/stop-tb.png b/src/vs/workbench/parts/debug/electron-browser/media/stop-tb.png index b5d38d8ce..54235b60d 100644 Binary files a/src/vs/workbench/parts/debug/electron-browser/media/stop-tb.png and b/src/vs/workbench/parts/debug/electron-browser/media/stop-tb.png differ diff --git a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts index 5c70a9266..2d86e1b54 100644 --- a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts +++ b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts @@ -8,7 +8,6 @@ import { Event, Emitter } from 'vs/base/common/event'; import * as objects from 'vs/base/common/objects'; import { Action } from 'vs/base/common/actions'; import * as errors from 'vs/base/common/errors'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { formatPII } from 'vs/workbench/parts/debug/common/debugUtils'; import { IDebugAdapter, IConfig, AdapterEndEvent, IDebugger } from 'vs/workbench/parts/debug/common/debug'; @@ -204,7 +203,7 @@ export class RawDebugSession { /** * Starts the underlying debug adapter and tracks the session time for telemetry. */ - public start(): TPromise { + public start(): Promise { return this.debugAdapter.startSession().then(() => { this.startTime = new Date().getTime(); }, err => { @@ -215,7 +214,7 @@ export class RawDebugSession { /** * Send client capabilities to the debug adapter and receive DA capabilities in return. */ - public initialize(args: DebugProtocol.InitializeRequestArguments): TPromise { + public initialize(args: DebugProtocol.InitializeRequestArguments): Promise { return this.send('initialize', args).then((response: DebugProtocol.InitializeResponse) => { this.mergeCapabilities(response.body); return response; @@ -225,13 +224,13 @@ export class RawDebugSession { /** * Terminate the debuggee and shutdown the adapter */ - public disconnect(restart = false): TPromise { + public disconnect(restart = false): Promise { return this.shutdown(undefined, restart); } //---- DAP requests - public launchOrAttach(config: IConfig): TPromise { + public launchOrAttach(config: IConfig): Promise { return this.send(config.request, config).then(response => { this.mergeCapabilities(response.body); return response; @@ -241,7 +240,7 @@ export class RawDebugSession { /** * Try killing the debuggee softly... */ - public terminate(restart = false): TPromise { + public terminate(restart = false): Promise { if (this.capabilities.supportsTerminateRequest) { if (!this.terminated) { this.terminated = true; @@ -252,35 +251,35 @@ export class RawDebugSession { return Promise.reject(new Error('terminated not supported')); } - public restart(): TPromise { + public restart(): Promise { if (this.capabilities.supportsRestartRequest) { return this.send('restart', null); } return Promise.reject(new Error('restart not supported')); } - public next(args: DebugProtocol.NextArguments): TPromise { + public next(args: DebugProtocol.NextArguments): Promise { return this.send('next', args).then(response => { this.fireSimulatedContinuedEvent(args.threadId); return response; }); } - public stepIn(args: DebugProtocol.StepInArguments): TPromise { + public stepIn(args: DebugProtocol.StepInArguments): Promise { return this.send('stepIn', args).then(response => { this.fireSimulatedContinuedEvent(args.threadId); return response; }); } - public stepOut(args: DebugProtocol.StepOutArguments): TPromise { + public stepOut(args: DebugProtocol.StepOutArguments): Promise { return this.send('stepOut', args).then(response => { this.fireSimulatedContinuedEvent(args.threadId); return response; }); } - public continue(args: DebugProtocol.ContinueArguments): TPromise { + public continue(args: DebugProtocol.ContinueArguments): Promise { return this.send('continue', args).then(response => { if (response && response.body && response.body.allThreadsContinued !== undefined) { this.allThreadsContinued = response.body.allThreadsContinued; @@ -290,25 +289,25 @@ export class RawDebugSession { }); } - public pause(args: DebugProtocol.PauseArguments): TPromise { + public pause(args: DebugProtocol.PauseArguments): Promise { return this.send('pause', args); } - public terminateThreads(args: DebugProtocol.TerminateThreadsArguments): TPromise { + public terminateThreads(args: DebugProtocol.TerminateThreadsArguments): Promise { if (this.capabilities.supportsTerminateThreadsRequest) { return this.send('terminateThreads', args); } return Promise.reject(new Error('terminateThreads not supported')); } - public setVariable(args: DebugProtocol.SetVariableArguments): TPromise { + public setVariable(args: DebugProtocol.SetVariableArguments): Promise { if (this.capabilities.supportsSetVariable) { return this.send('setVariable', args); } return Promise.reject(new Error('setVariable not supported')); } - public restartFrame(args: DebugProtocol.RestartFrameArguments, threadId: number): TPromise { + public restartFrame(args: DebugProtocol.RestartFrameArguments, threadId: number): Promise { if (this.capabilities.supportsRestartFrame) { return this.send('restartFrame', args).then(response => { this.fireSimulatedContinuedEvent(threadId); @@ -318,74 +317,74 @@ export class RawDebugSession { return Promise.reject(new Error('restartFrame not supported')); } - public completions(args: DebugProtocol.CompletionsArguments): TPromise { + public completions(args: DebugProtocol.CompletionsArguments): Promise { if (this.capabilities.supportsCompletionsRequest) { return this.send('completions', args); } return Promise.reject(new Error('completions not supported')); } - public setBreakpoints(args: DebugProtocol.SetBreakpointsArguments): TPromise { + public setBreakpoints(args: DebugProtocol.SetBreakpointsArguments): Promise { return this.send('setBreakpoints', args); } - public setFunctionBreakpoints(args: DebugProtocol.SetFunctionBreakpointsArguments): TPromise { + public setFunctionBreakpoints(args: DebugProtocol.SetFunctionBreakpointsArguments): Promise { if (this.capabilities.supportsFunctionBreakpoints) { return this.send('setFunctionBreakpoints', args); } return Promise.reject(new Error('setFunctionBreakpoints not supported')); } - public setExceptionBreakpoints(args: DebugProtocol.SetExceptionBreakpointsArguments): TPromise { + public setExceptionBreakpoints(args: DebugProtocol.SetExceptionBreakpointsArguments): Promise { return this.send('setExceptionBreakpoints', args); } - public configurationDone(): TPromise { + public configurationDone(): Promise { if (this.capabilities.supportsConfigurationDoneRequest) { return this.send('configurationDone', null); } return Promise.reject(new Error('configurationDone not supported')); } - public stackTrace(args: DebugProtocol.StackTraceArguments): TPromise { + public stackTrace(args: DebugProtocol.StackTraceArguments): Promise { return this.send('stackTrace', args); } - public exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): TPromise { + public exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): Promise { if (this.capabilities.supportsExceptionInfoRequest) { return this.send('exceptionInfo', args); } return Promise.reject(new Error('exceptionInfo not supported')); } - public scopes(args: DebugProtocol.ScopesArguments): TPromise { + public scopes(args: DebugProtocol.ScopesArguments): Promise { return this.send('scopes', args); } - public variables(args: DebugProtocol.VariablesArguments): TPromise { + public variables(args: DebugProtocol.VariablesArguments): Promise { return this.send('variables', args); } - public source(args: DebugProtocol.SourceArguments): TPromise { + public source(args: DebugProtocol.SourceArguments): Promise { return this.send('source', args); } - public loadedSources(args: DebugProtocol.LoadedSourcesArguments): TPromise { + public loadedSources(args: DebugProtocol.LoadedSourcesArguments): Promise { if (this.capabilities.supportsLoadedSourcesRequest) { return this.send('loadedSources', args); } return Promise.reject(new Error('loadedSources not supported')); } - public threads(): TPromise { + public threads(): Promise { return this.send('threads', null); } - public evaluate(args: DebugProtocol.EvaluateArguments): TPromise { + public evaluate(args: DebugProtocol.EvaluateArguments): Promise { return this.send('evaluate', args); } - public stepBack(args: DebugProtocol.StepBackArguments): TPromise { + public stepBack(args: DebugProtocol.StepBackArguments): Promise { if (this.capabilities.supportsStepBack) { return this.send('stepBack', args).then(response => { if (response.body === undefined) { // TODO@AW why this check? @@ -397,7 +396,7 @@ export class RawDebugSession { return Promise.reject(new Error('stepBack not supported')); } - public reverseContinue(args: DebugProtocol.ReverseContinueArguments): TPromise { + public reverseContinue(args: DebugProtocol.ReverseContinueArguments): Promise { if (this.capabilities.supportsStepBack) { return this.send('reverseContinue', args).then(response => { if (response.body === undefined) { // TODO@AW why this check? @@ -409,14 +408,14 @@ export class RawDebugSession { return Promise.reject(new Error('reverseContinue not supported')); } - public custom(request: string, args: any): TPromise { + public custom(request: string, args: any): Promise { return this.send(request, args); } //---- private - private shutdown(error?: Error, restart = false): TPromise { + private shutdown(error?: Error, restart = false): Promise { if (!this.inShutdown) { this.inShutdown = true; if (this.debugAdapter) { @@ -432,7 +431,7 @@ export class RawDebugSession { return Promise.resolve(undefined); } - private stopAdapter(error?: Error): TPromise { + private stopAdapter(error?: Error): Promise { if (this.debugAdapter) { const da = this.debugAdapter; this.debugAdapter = null; @@ -476,9 +475,14 @@ export class RawDebugSession { switch (request.command) { case 'runInTerminal': - dbgr.runInTerminal(request.arguments).then(_ => { - response.body = {}; - safeSendResponse(response); + dbgr.runInTerminal(request.arguments as DebugProtocol.RunInTerminalRequestArguments).then(shellProcessId => { + const resp = response as DebugProtocol.RunInTerminalResponse; + if (typeof shellProcessId === 'number') { + resp.body = { + shellProcessId: shellProcessId + }; + } + safeSendResponse(resp); }, err => { response.success = false; response.message = err.message; diff --git a/src/vs/workbench/parts/debug/electron-browser/repl.ts b/src/vs/workbench/parts/debug/electron-browser/repl.ts index bbe682f77..7ca75bee5 100644 --- a/src/vs/workbench/parts/debug/electron-browser/repl.ts +++ b/src/vs/workbench/parts/debug/electron-browser/repl.ts @@ -6,7 +6,6 @@ import 'vs/css!vs/workbench/parts/debug/browser/media/repl'; import * as nls from 'vs/nls'; import { URI as uri } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as errors from 'vs/base/common/errors'; import { IAction, IActionItem, Action } from 'vs/base/common/actions'; import * as dom from 'vs/base/browser/dom'; @@ -50,6 +49,8 @@ import { transparent, editorForeground } from 'vs/platform/theme/common/colorReg import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { FocusSessionActionItem } from 'vs/workbench/parts/debug/browser/debugActionItems'; import { CompletionContext, CompletionList, CompletionProviderRegistry } from 'vs/editor/common/modes'; +import { first } from 'vs/base/common/arrays'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; const $ = dom.$; @@ -62,7 +63,7 @@ const HISTORY_STORAGE_KEY = 'debug.repl.history'; const IPrivateReplService = createDecorator('privateReplService'); const DECORATION_KEY = 'replinputdecoration'; -export interface IPrivateReplService { +interface IPrivateReplService { _serviceBrand: any; acceptReplInput(): void; getVisibleContent(): string; @@ -70,6 +71,7 @@ export interface IPrivateReplService { clearRepl(): void; } +const sessionsToIgnore = new Set(); export class Repl extends Panel implements IPrivateReplService, IHistoryNavigationWidget { _serviceBrand: any; @@ -113,11 +115,17 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati private registerListeners(): void { this._register(this.debugService.getViewModel().onDidFocusSession(session => { - if (this.isVisible()) { - this.selectSession(session); + sessionsToIgnore.delete(session); + this.selectSession(); + })); + this._register(this.debugService.onWillNewSession(() => { + // Need to listen to output events for sessions which are not yet fully initialised + const input: IDebugSession = this.tree.getInput(); + if (!input || input.state === State.Inactive) { + this.selectSession(); } + this.updateTitleArea(); })); - this._register(this.debugService.onDidNewSession(() => this.updateTitleArea())); this._register(this.themeService.onThemeChange(() => { if (this.isVisible()) { this.updateInputDecoration(); @@ -125,20 +133,16 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati })); } - setVisible(visible: boolean): Promise { + setVisible(visible: boolean): void { + super.setVisible(visible); if (!visible) { dispose(this.model); } else { this.model = this.modelService.createModel('', null, uri.parse(`${DEBUG_SCHEME}:replinput`), true); this.replInput.setModel(this.model); this.updateInputDecoration(); - const focusedSession = this.debugService.getViewModel().focusedSession; - if (focusedSession && this.tree.getInput() !== focusedSession) { - this.selectSession(focusedSession); - } + this.refreshReplElements(true); } - - return super.setVisible(visible); } get isReadonly(): boolean { @@ -170,11 +174,14 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati } } - selectSession(session: IDebugSession): void { - if (this.replElementsChangeListener) { - this.replElementsChangeListener.dispose(); - } + selectSession(): void { + const focusedSession = this.debugService.getViewModel().focusedSession; + // If there is a focusedSession focus on that one, otherwise just show any other not ignored session + const session = focusedSession || first(this.debugService.getModel().getSessions(true), s => !sessionsToIgnore.has(s)); if (session) { + if (this.replElementsChangeListener) { + this.replElementsChangeListener.dispose(); + } this.replElementsChangeListener = session.onDidChangeReplElements(() => { this.refreshReplElements(session.getReplElements().length === 0); }); @@ -190,7 +197,15 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati clearRepl(): void { const session: IDebugSession = this.tree.getInput(); - session.removeReplExpressions(); + if (session) { + session.removeReplExpressions(); + if (session.state === State.Inactive) { + // Ignore inactive sessions which got cleared - so they are not shown any more + sessionsToIgnore.add(session); + this.selectSession(); + this.updateTitleArea(); + } + } this.replInput.focus(); } @@ -247,7 +262,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati getActions(): IAction[] { const result: IAction[] = []; - if (this.debugService.getModel().getSessions(true).length > 1) { + if (this.debugService.getModel().getSessions(true).filter(s => !sessionsToIgnore.has(s)).length > 1) { result.push(this.selectReplAction); } result.push(this.clearReplAction); @@ -290,6 +305,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati this.renderer = this.instantiationService.createInstance(ReplExpressionsRenderer); const controller = this.instantiationService.createInstance(ReplExpressionsController, new ReplExpressionsActionProvider(this.clearReplAction, this.replInput), MenuId.DebugConsoleContext, { openMode: OpenMode.SINGLE_CLICK, clickBehavior: ClickBehavior.ON_MOUSE_UP /* do not change, to preserve focus behaviour in input field */ }); + this.toDispose.push(controller); controller.toFocusOnClick = this.replInput; this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, { @@ -298,6 +314,9 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati accessibilityProvider: new ReplExpressionsAccessibilityProvider(), controller }, replTreeOptions); + + // Make sure to select the session if debugging is already active + this.selectSession(); } private createReplInput(container: HTMLElement): void { @@ -395,7 +414,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati }, renderOptions: { after: { - contentText: nls.localize('startDebugFirst', "Please start a debug session to evaluate"), + contentText: nls.localize('startDebugFirst', "Please start a debug session to evaluate expressions"), color: transparent(editorForeground, 0.4)(this.themeService.getTheme()).toString() } } @@ -441,7 +460,7 @@ class AcceptReplInputAction extends EditorAction { }); } - run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise { + run(accessor: ServicesAccessor, editor: ICodeEditor): void | Promise { SuggestController.get(editor).acceptSelectedSuggestion(); accessor.get(IPrivateReplService).acceptReplInput(); } @@ -458,7 +477,7 @@ export class ReplCopyAllAction extends EditorAction { }); } - run(accessor: ServicesAccessor, editor: ICodeEditor): void | TPromise { + run(accessor: ServicesAccessor, editor: ICodeEditor): void | Promise { clipboard.writeText(accessor.get(IPrivateReplService).getVisibleContent()); } } @@ -480,7 +499,7 @@ registerEditorCommand(new SuggestCommand({ class SelectReplActionItem extends FocusSessionActionItem { protected getSessions(): ReadonlyArray { - return this.debugService.getModel().getSessions(true); + return this.debugService.getModel().getSessions(true).filter(s => !sessionsToIgnore.has(s)); } } @@ -496,7 +515,7 @@ class SelectReplAction extends Action { super(id, label); } - run(sessionName: string): TPromise { + run(sessionName: string): Promise { const session = this.debugService.getModel().getSessions(true).filter(p => p.getLabel() === sessionName).pop(); // 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) { @@ -509,22 +528,21 @@ class SelectReplAction extends Action { } } -class ClearReplAction extends Action { +export class ClearReplAction extends Action { static readonly ID = 'workbench.debug.panel.action.clearReplAction'; static LABEL = nls.localize('clearRepl', "Clear Console"); constructor(id: string, label: string, - @IDebugService debugService: IDebugService, - @IPrivateReplService private replService: IPrivateReplService + @IPanelService private panelService: IPanelService ) { super(id, label, 'debug-action clear-repl'); } - public run(): TPromise { - this.replService.clearRepl(); + public run(): Promise { + const repl = this.panelService.openPanel(REPL_ID); + repl.clearRepl(); aria.status(nls.localize('debugConsoleCleared', "Debug console was cleared")); - // focus back to repl return Promise.resolve(undefined); } } diff --git a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts index b4baba1d4..cc821c5a4 100644 --- a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts +++ b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IAction, Action } from 'vs/base/common/actions'; import * as lifecycle from 'vs/base/common/lifecycle'; import { isFullWidthCharacter, removeAnsiEscapeCodes, endsWith } from 'vs/base/common/strings'; @@ -38,7 +37,7 @@ export class ReplExpressionsDataSource implements IDataSource { return element instanceof DebugSession || (element).hasChildren; } - public getChildren(tree: ITree, element: any): TPromise { + public getChildren(tree: ITree, element: any): Promise { if (element instanceof DebugSession) { return Promise.resolve(element.getReplElements()); } @@ -52,7 +51,7 @@ export class ReplExpressionsDataSource implements IDataSource { return (element).getChildren(); } - public getParent(tree: ITree, element: any): TPromise { + public getParent(tree: ITree, element: any): Promise { return Promise.resolve(null); } } @@ -325,15 +324,15 @@ export class ReplExpressionsActionProvider implements IActionProvider { return false; } - public getActions(tree: ITree, element: any): TPromise { - return Promise.resolve([]); + public getActions(tree: ITree, element: any): IAction[] { + return []; } public hasSecondaryActions(tree: ITree, element: any): boolean { return true; } - public getSecondaryActions(tree: ITree, element: any): TPromise { + public getSecondaryActions(tree: ITree, element: any): IAction[] { const actions: IAction[] = []; actions.push(new CopyAction(CopyAction.ID, CopyAction.LABEL)); actions.push(new CopyAllAction(CopyAllAction.ID, CopyAllAction.LABEL, tree)); @@ -341,7 +340,7 @@ export class ReplExpressionsActionProvider implements IActionProvider { actions.push(new Separator()); actions.push(this.clearReplAction); - return Promise.resolve(actions); + return actions; } public getActionItem(tree: ITree, element: any, action: IAction): IActionItem { diff --git a/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts b/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts index 520381651..7e5aaa164 100644 --- a/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts +++ b/src/vs/workbench/parts/debug/electron-browser/terminalSupport.ts @@ -5,7 +5,6 @@ import * as nls from 'vs/nls'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITerminalService, ITerminalInstance } from 'vs/workbench/parts/terminal/common/terminal'; import { ITerminalService as IExternalTerminalService } from 'vs/workbench/parts/execution/common/execution'; import { ITerminalLauncher, ITerminalSettings } from 'vs/workbench/parts/debug/common/debug'; @@ -22,7 +21,7 @@ export class TerminalLauncher implements ITerminalLauncher { ) { } - runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise { + runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise { if (args.kind === 'external') { return this.nativeTerminalService.runInTerminal(args.title, args.cwd, args.args, args.env || {}); @@ -45,12 +44,30 @@ export class TerminalLauncher implements ITerminalLauncher { this.terminalService.setActiveInstance(t); this.terminalService.showPanel(true); - return new Promise((resolve, error) => { + return new Promise((resolve, error) => { + + if (typeof t.processId === 'number') { + // no need to wait + resolve(t.processId); + } + + // shell not ready: wait for ready event + const toDispose = t.onProcessIdReady(t => { + toDispose.dispose(); + resolve(t.processId); + }); + + // do not wait longer than 1 second setTimeout(_ => { - const command = prepareCommand(args, config); - t.sendText(command, true); - resolve(void 0); - }, 500); + error(new Error('terminal shell timeout')); + }, 1000); + + }).then(shellProcessId => { + + const command = prepareCommand(args, config); + t.sendText(command, true); + + return shellProcessId; }); } } diff --git a/src/vs/workbench/parts/debug/electron-browser/variablesView.ts b/src/vs/workbench/parts/debug/electron-browser/variablesView.ts index bd2baae2f..c676be3a3 100644 --- a/src/vs/workbench/parts/debug/electron-browser/variablesView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/variablesView.ts @@ -17,7 +17,6 @@ import { MenuId } from 'vs/platform/actions/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { twistiePixels, renderViewTree, IVariableTemplateData, BaseDebugController, renderRenameBox, renderVariable } from 'vs/workbench/parts/debug/browser/baseDebugView'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IAction, IActionItem } from 'vs/base/common/actions'; import { SetValueAction, AddToWatchExpressionsAction } from 'vs/workbench/parts/debug/browser/debugActions'; import { CopyValueAction, CopyEvaluatePathAction } from 'vs/workbench/parts/debug/electron-browser/electronDebugActions'; @@ -80,12 +79,14 @@ export class VariablesView extends TreeViewsViewletPanel { public renderBody(container: HTMLElement): void { dom.addClass(container, 'debug-variables'); this.treeContainer = renderViewTree(container); + const controller = this.instantiationService.createInstance(VariablesController, new VariablesActionProvider(this.debugService, this.keybindingService), MenuId.DebugVariablesContext, { openMode: OpenMode.SINGLE_CLICK, clickBehavior: ClickBehavior.ON_MOUSE_UP }); + this.disposables.push(controller); this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, { dataSource: new VariablesDataSource(), renderer: this.instantiationService.createInstance(VariablesRenderer), accessibilityProvider: new VariablesAccessibilityProvider(), - controller: this.instantiationService.createInstance(VariablesController, new VariablesActionProvider(this.debugService, this.keybindingService), MenuId.DebugVariablesContext, { openMode: OpenMode.SINGLE_CLICK, clickBehavior: ClickBehavior.ON_MOUSE_UP }) + controller }, { ariaLabel: nls.localize('variablesAriaTreeLabel', "Debug Variables"), twistiePixels @@ -139,12 +140,11 @@ export class VariablesView extends TreeViewsViewletPanel { } } - public setVisible(visible: boolean): TPromise { - return super.setVisible(visible).then(() => { - if (visible && this.needsRefresh) { - this.onFocusStackFrameScheduler.schedule(); - } - }); + public setVisible(visible: boolean): void { + super.setVisible(visible); + if (visible && this.needsRefresh) { + this.onFocusStackFrameScheduler.schedule(); + } } } @@ -158,8 +158,8 @@ class VariablesActionProvider implements IActionProvider { return false; } - public getActions(tree: ITree, element: any): TPromise { - return Promise.resolve([]); + public getActions(tree: ITree, element: any): IAction[] { + return []; } public hasSecondaryActions(tree: ITree, element: any): boolean { @@ -167,7 +167,7 @@ class VariablesActionProvider implements IActionProvider { return element instanceof Variable && !!element.value; } - public getSecondaryActions(tree: ITree, element: any): TPromise { + public getSecondaryActions(tree: ITree, element: any): IAction[] { const actions: IAction[] = []; const variable = element; actions.push(new SetValueAction(SetValueAction.ID, SetValueAction.LABEL, variable, this.debugService, this.keybindingService)); @@ -176,7 +176,7 @@ class VariablesActionProvider implements IActionProvider { actions.push(new Separator()); actions.push(new AddToWatchExpressionsAction(AddToWatchExpressionsAction.ID, AddToWatchExpressionsAction.LABEL, variable, this.debugService, this.keybindingService)); - return Promise.resolve(actions); + return actions; } public getActionItem(tree: ITree, element: any, action: IAction): IActionItem { @@ -199,7 +199,7 @@ export class VariablesDataSource implements IDataSource { return variable.hasChildren && !equalsIgnoreCase(variable.value, 'null'); } - public getChildren(tree: ITree, element: any): TPromise { + public getChildren(tree: ITree, element: any): Promise { if (element instanceof ViewModel) { const focusedStackFrame = (element).focusedStackFrame; return focusedStackFrame ? focusedStackFrame.getScopes() : Promise.resolve([]); @@ -209,7 +209,7 @@ export class VariablesDataSource implements IDataSource { return scope.getChildren(); } - public getParent(tree: ITree, element: any): TPromise { + public getParent(tree: ITree, element: any): Promise { return Promise.resolve(null); } } diff --git a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts index 07748d02e..25af0890d 100644 --- a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts @@ -7,7 +7,6 @@ import * as nls from 'vs/nls'; import { RunOnceScheduler } from 'vs/base/common/async'; import * as dom from 'vs/base/browser/dom'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IActionProvider, ITree, IDataSource, IRenderer, IAccessibilityProvider, IDragAndDropData, IDragOverReaction, DRAG_OVER_REJECT } from 'vs/base/parts/tree/browser/tree'; import { CollapseAction } from 'vs/workbench/browser/viewlet'; import { TreeViewsViewletPanel, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; @@ -60,11 +59,13 @@ export class WatchExpressionsView extends TreeViewsViewletPanel { this.treeContainer = renderViewTree(container); const actionProvider = new WatchExpressionsActionProvider(this.debugService, this.keybindingService); + const controller = this.instantiationService.createInstance(WatchExpressionsController, actionProvider, MenuId.DebugWatchContext, { clickBehavior: ClickBehavior.ON_MOUSE_UP /* do not change to not break DND */, openMode: OpenMode.SINGLE_CLICK }); + this.disposables.push(controller); this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, { dataSource: new WatchExpressionsDataSource(this.debugService), renderer: this.instantiationService.createInstance(WatchExpressionsRenderer), accessibilityProvider: new WatchExpressionsAccessibilityProvider(), - controller: this.instantiationService.createInstance(WatchExpressionsController, actionProvider, MenuId.DebugWatchContext, { clickBehavior: ClickBehavior.ON_MOUSE_UP /* do not change to not break DND */, openMode: OpenMode.SINGLE_CLICK }), + controller, dnd: new WatchExpressionsDragAndDrop(this.debugService) }, { ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'watchAriaTreeLabel' }, "Debug Watch Expressions"), @@ -122,12 +123,11 @@ export class WatchExpressionsView extends TreeViewsViewletPanel { } } - public setVisible(visible: boolean): TPromise { - return super.setVisible(visible).then(() => { - if (visible && this.needsRefresh) { - this.onWatchExpressionsUpdatedScheduler.schedule(); - } - }); + public setVisible(visible: boolean): void { + super.setVisible(visible); + if (visible && this.needsRefresh) { + this.onWatchExpressionsUpdatedScheduler.schedule(); + } } } @@ -146,11 +146,11 @@ class WatchExpressionsActionProvider implements IActionProvider { return true; } - public getActions(tree: ITree, element: any): TPromise { - return Promise.resolve([]); + public getActions(tree: ITree, element: any): IAction[] { + return []; } - public getSecondaryActions(tree: ITree, element: any): TPromise { + public getSecondaryActions(tree: ITree, element: any): IAction[] { const actions: IAction[] = []; if (element instanceof Expression) { const expression = element; @@ -175,7 +175,7 @@ class WatchExpressionsActionProvider implements IActionProvider { actions.push(new RemoveAllWatchExpressionsAction(RemoveAllWatchExpressionsAction.ID, RemoveAllWatchExpressionsAction.LABEL, this.debugService, this.keybindingService)); } - return Promise.resolve(actions); + return actions; } public getActionItem(tree: ITree, element: any, action: IAction): IActionItem { @@ -202,7 +202,7 @@ class WatchExpressionsDataSource implements IDataSource { return watchExpression.hasChildren && !equalsIgnoreCase(watchExpression.value, 'null'); } - public getChildren(tree: ITree, element: any): TPromise { + public getChildren(tree: ITree, element: any): Promise { if (element instanceof DebugModel) { const viewModel = this.debugService.getViewModel(); return Promise.all(element.getWatchExpressions().map(we => @@ -213,7 +213,7 @@ class WatchExpressionsDataSource implements IDataSource { return expression.getChildren(); } - public getParent(tree: ITree, element: any): TPromise { + public getParent(tree: ITree, element: any): Promise { return Promise.resolve(null); } } @@ -353,7 +353,7 @@ class WatchExpressionsDragAndDrop extends DefaultDragAndDrop { } public getDragURI(tree: ITree, element: Expression): string { - if (!(element instanceof Expression)) { + if (!(element instanceof Expression) || element === this.debugService.getViewModel().getSelectedExpression()) { return null; } diff --git a/src/vs/workbench/parts/debug/node/debugAdapter.ts b/src/vs/workbench/parts/debug/node/debugAdapter.ts index 244fd3ea2..2d798bf2d 100644 --- a/src/vs/workbench/parts/debug/node/debugAdapter.ts +++ b/src/vs/workbench/parts/debug/node/debugAdapter.ts @@ -13,7 +13,6 @@ import * as strings from 'vs/base/common/strings'; import * as objects from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; import { Emitter, Event } from 'vs/base/common/event'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ExtensionsChannelId } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { IOutputService } from 'vs/workbench/parts/output/common/output'; @@ -42,44 +41,41 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter { this._onExit = new Emitter(); } - abstract startSession(): TPromise; - abstract stopSession(): TPromise; - - public dispose(): void { - } + abstract startSession(): Promise; + abstract stopSession(): Promise; abstract sendMessage(message: DebugProtocol.ProtocolMessage): void; - public get onError(): Event { + get onError(): Event { return this._onError.event; } - public get onExit(): Event { + get onExit(): Event { return this._onExit.event; } - public onMessage(callback: (message: DebugProtocol.ProtocolMessage) => void): void { + onMessage(callback: (message: DebugProtocol.ProtocolMessage) => void): void { if (this.eventCallback) { this._onError.fire(new Error(`attempt to set more than one 'Message' callback`)); } this.messageCallback = callback; } - public onEvent(callback: (event: DebugProtocol.Event) => void): void { + onEvent(callback: (event: DebugProtocol.Event) => void): void { if (this.eventCallback) { this._onError.fire(new Error(`attempt to set more than one 'Event' callback`)); } this.eventCallback = callback; } - public onRequest(callback: (request: DebugProtocol.Request) => void): void { + onRequest(callback: (request: DebugProtocol.Request) => void): void { if (this.requestCallback) { this._onError.fire(new Error(`attempt to set more than one 'Request' callback`)); } this.requestCallback = callback; } - public sendResponse(response: DebugProtocol.Response): void { + sendResponse(response: DebugProtocol.Response): void { if (response.seq > 0) { this._onError.fire(new Error(`attempt to send more than one response for command ${response.command}`)); } else { @@ -87,7 +83,7 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter { } } - public sendRequest(command: string, args: any, clb: (result: DebugProtocol.Response) => void, timeout?: number): void { + sendRequest(command: string, args: any, clb: (result: DebugProtocol.Response) => void, timeout?: number): void { const request: any = { command: command @@ -123,7 +119,7 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter { } } - public acceptMessage(message: DebugProtocol.ProtocolMessage): void { + acceptMessage(message: DebugProtocol.ProtocolMessage): void { if (this.messageCallback) { this.messageCallback(message); } else { @@ -175,6 +171,10 @@ export abstract class AbstractDebugAdapter implements IDebugAdapter { }); }, 1000); } + + dispose(): void { + this.cancelPending(); + } } /** @@ -203,7 +203,7 @@ export abstract class StreamDebugAdapter extends AbstractDebugAdapter { readable.on('data', (data: Buffer) => this.handleData(data)); } - public sendMessage(message: DebugProtocol.ProtocolMessage): void { + sendMessage(message: DebugProtocol.ProtocolMessage): void { if (this.outputStream) { const json = JSON.stringify(message); @@ -261,7 +261,7 @@ export class SocketDebugAdapter extends StreamDebugAdapter { super(); } - startSession(): TPromise { + startSession(): Promise { return new Promise((resolve, reject) => { let connected = false; this.socket = net.createConnection(this.adapterServer.port, this.adapterServer.host || '127.0.0.1', () => { @@ -286,7 +286,7 @@ export class SocketDebugAdapter extends StreamDebugAdapter { }); } - stopSession(): TPromise { + stopSession(): Promise { // Cancel all sent promises on disconnect so debug trees are not left in a broken state #3666. this.cancelPending(); @@ -310,7 +310,7 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { super(); } - startSession(): TPromise { + startSession(): Promise { return new Promise((resolve, reject) => { @@ -336,14 +336,14 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { if (Array.isArray(this.adapterExecutable.args) && this.adapterExecutable.args.length > 0) { const isElectron = !!process.env['ELECTRON_RUN_AS_NODE'] || !!process.versions['electron']; const options: cp.ForkOptions = { - env: this.adapterExecutable.env - ? objects.mixin(objects.mixin({}, process.env), this.adapterExecutable.env) + env: this.adapterExecutable.options && this.adapterExecutable.options.env + ? objects.mixin(objects.mixin({}, process.env), this.adapterExecutable.options.env) : process.env, execArgv: isElectron ? ['-e', 'delete process.env.ELECTRON_RUN_AS_NODE;require(process.argv[1])'] : [], silent: true }; - if (this.adapterExecutable.cwd) { - options.cwd = this.adapterExecutable.cwd; + if (this.adapterExecutable.options && this.adapterExecutable.options.cwd) { + options.cwd = this.adapterExecutable.options.cwd; } const child = cp.fork(this.adapterExecutable.args[0], this.adapterExecutable.args.slice(1), options); if (!child.pid) { @@ -356,12 +356,12 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { } } else { const options: cp.SpawnOptions = { - env: this.adapterExecutable.env - ? objects.mixin(objects.mixin({}, process.env), this.adapterExecutable.env) + env: this.adapterExecutable.options && this.adapterExecutable.options.env + ? objects.mixin(objects.mixin({}, process.env), this.adapterExecutable.options.env) : process.env }; - if (this.adapterExecutable.cwd) { - options.cwd = this.adapterExecutable.cwd; + if (this.adapterExecutable.options && this.adapterExecutable.options.cwd) { + options.cwd = this.adapterExecutable.options.cwd; } this.serverProcess = cp.spawn(this.adapterExecutable.command, this.adapterExecutable.args, options); resolve(null); @@ -401,7 +401,7 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { }); } - stopSession(): TPromise { + stopSession(): Promise { // Cancel all sent promises on disconnect so debug trees are not left in a broken state #3666. this.cancelPending(); @@ -474,8 +474,8 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { return result; } - public static platformAdapterExecutable(extensionDescriptions: IExtensionDescription[], debugType: string): IDebugAdapterExecutable { - const result: IDebuggerContribution = Object.create(null); + static platformAdapterExecutable(extensionDescriptions: IExtensionDescription[], debugType: string): IDebugAdapterExecutable | undefined { + let result: IDebuggerContribution = Object.create(null); debugType = debugType.toLowerCase(); // merge all contributions into one @@ -488,7 +488,7 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { const extractedDbg = ExecutableDebugAdapter.extract(dbg, ed.extensionLocation.fsPath); // merge - objects.mixin(result, extractedDbg, ed.isBuiltin); + result = objects.mixin(result, extractedDbg, ed.isBuiltin); }); } } @@ -519,12 +519,15 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { command: runtime, args: (runtimeArgs || []).concat([program]).concat(args || []) }; - } else { + } else if (program) { return { type: 'executable', command: program, args: args || [] }; } + + // nothing found + return undefined; } } diff --git a/src/vs/workbench/parts/debug/node/debugger.ts b/src/vs/workbench/parts/debug/node/debugger.ts index 644722c03..9e7947467 100644 --- a/src/vs/workbench/parts/debug/node/debugger.ts +++ b/src/vs/workbench/parts/debug/node/debugger.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Client as TelemetryClient } from 'vs/base/parts/ipc/node/ipc.cp'; import * as strings from 'vs/base/common/strings'; import * as objects from 'vs/base/common/objects'; @@ -18,6 +17,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { IOutputService } from 'vs/workbench/parts/output/common/output'; import { ExecutableDebugAdapter, SocketDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; +import * as ConfigurationResolverUtils from 'vs/workbench/services/configurationResolver/common/configurationResolverUtils'; import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { memoize } from 'vs/base/common/decorators'; @@ -41,37 +41,52 @@ export class Debugger implements IDebugger { this.mergedExtensionDescriptions = [extensionDescription]; } - public hasConfigurationProvider = false; + public createDebugAdapter(session: IDebugSession, outputService: IOutputService): Promise { + return new Promise((resolve, reject) => { + this.configurationManager.activateDebuggers('onDebugAdapterProtocolTracker', this.type).then(_ => { + resolve(this._createDebugAdapter(session, outputService)); + }, reject); + }); + } - public createDebugAdapter(session: IDebugSession, root: IWorkspaceFolder, config: IConfig, outputService: IOutputService): TPromise { + private _createDebugAdapter(session: IDebugSession, outputService: IOutputService): Promise { if (this.inExtHost()) { - return Promise.resolve(this.configurationManager.createDebugAdapter(session, root, config)); + return Promise.resolve(this.configurationManager.createDebugAdapter(session)); } else { - return this.getAdapterDescriptor(session, root, config).then(adapterDescriptor => { + return this.getAdapterDescriptor(session).then(adapterDescriptor => { switch (adapterDescriptor.type) { - case 'server': - return new SocketDebugAdapter(adapterDescriptor); case 'executable': return new ExecutableDebugAdapter(adapterDescriptor, this.type, outputService); + case 'server': + return new SocketDebugAdapter(adapterDescriptor); + case 'implementation': + // TODO@AW: this.inExtHost() should now return true + return Promise.resolve(this.configurationManager.createDebugAdapter(session)); default: - throw new Error('Cannot create debug adapter.'); + throw new Error('unknown type'); + } + }).catch(err => { + if (err && err.message) { + throw new Error(nls.localize('cannot.create.da.with.err', "Cannot create debug adapter ({0}).", err.message)); + } else { + throw new Error(nls.localize('cannot.create.da', "Cannot create debug adapter.")); } }); } } - private getAdapterDescriptor(session: IDebugSession, root: IWorkspaceFolder, config: IConfig): TPromise { + private getAdapterDescriptor(session: IDebugSession): Promise { // a "debugServer" attribute in the launch config takes precedence - if (typeof config.debugServer === 'number') { + if (typeof session.configuration.debugServer === 'number') { return Promise.resolve({ type: 'server', - port: config.debugServer + port: session.configuration.debugServer }); } // try the proposed and the deprecated "provideDebugAdapter" API - return this.configurationManager.provideDebugAdapter(session, root ? root.uri : undefined, config).then(adapter => { + return this.configurationManager.provideDebugAdapter(session).then(adapter => { if (adapter) { return adapter; @@ -79,7 +94,7 @@ export class Debugger implements IDebugger { // try deprecated command based extension API "adapterExecutableCommand" to determine the executable if (this.debuggerContribution.adapterExecutableCommand) { - const rootFolder = root ? root.uri.toString() : undefined; + const rootFolder = session.root ? session.root.uri.toString() : undefined; return this.commandService.executeCommand(this.debuggerContribution.adapterExecutableCommand, rootFolder).then((ae: { command: string, args: string[] }) => { return { type: 'executable', @@ -90,11 +105,15 @@ export class Debugger implements IDebugger { } // fallback: use executable information from package.json - return ExecutableDebugAdapter.platformAdapterExecutable(this.mergedExtensionDescriptions, this.type); + const ae = ExecutableDebugAdapter.platformAdapterExecutable(this.mergedExtensionDescriptions, this.type); + if (ae === undefined) { + throw new Error('no executable specified in package.json'); + } + return ae; }); } - public substituteVariables(folder: IWorkspaceFolder, config: IConfig): TPromise { + substituteVariables(folder: IWorkspaceFolder, config: IConfig): Thenable { if (this.inExtHost()) { return this.configurationManager.substituteVariables(this.type, folder, config).then(config => { return this.configurationResolverService.resolveWithCommands(folder, config, this.variables); @@ -104,7 +123,7 @@ export class Debugger implements IDebugger { } } - public runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): TPromise { + runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments): Promise { const config = this.configurationService.getValue('terminal'); return this.configurationManager.runInTerminal(this.inExtHost() ? this.type : '*', args, config); } @@ -114,27 +133,27 @@ export class Debugger implements IDebugger { return debugConfigs.extensionHostDebugAdapter || this.configurationManager.needsToRunInExtHost(this.type) || this.extensionDescription.extensionLocation.scheme !== 'file'; } - public get label(): string { + get label(): string { return this.debuggerContribution.label || this.debuggerContribution.type; } - public get type(): string { + get type(): string { return this.debuggerContribution.type; } - public get variables(): { [key: string]: string } { + get variables(): { [key: string]: string } { return this.debuggerContribution.variables; } - public get configurationSnippets(): IJSONSchemaSnippet[] { + get configurationSnippets(): IJSONSchemaSnippet[] { return this.debuggerContribution.configurationSnippets; } - public get languages(): string[] { + get languages(): string[] { return this.debuggerContribution.languages; } - public merge(secondRawAdapter: IDebuggerContribution, extensionDescription: IExtensionDescription): void { + merge(secondRawAdapter: IDebuggerContribution, extensionDescription: IExtensionDescription): void { // remember all ext descriptions that are the source of this debugger this.mergedExtensionDescriptions.push(extensionDescription); @@ -146,11 +165,15 @@ export class Debugger implements IDebugger { objects.mixin(this.debuggerContribution, secondRawAdapter, extensionDescription.isBuiltin); } - public hasInitialConfiguration(): boolean { + hasInitialConfiguration(): boolean { return !!this.debuggerContribution.initialConfigurations; } - public getInitialConfigurationContent(initialConfigs?: IConfig[]): TPromise { + hasConfigurationProvider(): boolean { + return this.configurationManager.hasDebugConfigurationProvider(this.type); + } + + getInitialConfigurationContent(initialConfigs?: IConfig[]): Promise { // at this point we got some configs from the package.json and/or from registered DebugConfigurationProviders let initialConfigurations = this.debuggerContribution.initialConfigurations || []; if (initialConfigs) { @@ -183,7 +206,7 @@ export class Debugger implements IDebugger { } @memoize - public getCustomTelemetryService(): TPromise { + getCustomTelemetryService(): Thenable { if (!this.debuggerContribution.aiKey) { return Promise.resolve(undefined); } @@ -215,7 +238,7 @@ export class Debugger implements IDebugger { }); } - public getSchemaAttributes(): IJSONSchema[] { + getSchemaAttributes(): IJSONSchema[] { if (!this.debuggerContribution.configurationAttributes) { return null; } @@ -286,9 +309,7 @@ export class Debugger implements IDebugger { }; Object.keys(attributes.properties).forEach(name => { // Use schema allOf property to get independent error reporting #21113 - attributes.properties[name].pattern = attributes.properties[name].pattern || '^(?!.*\\$\\{(env|config|command)\\.)'; - attributes.properties[name].patternErrorMessage = attributes.properties[name].patternErrorMessage || - nls.localize('deprecatedVariables', "'env.', 'config.' and 'command.' are deprecated, use 'env:', 'config:' and 'command:' instead."); + ConfigurationResolverUtils.applyDeprecatedVariableMessage(attributes.properties[name]); }); return attributes; diff --git a/src/vs/workbench/parts/debug/node/telemetryApp.ts b/src/vs/workbench/parts/debug/node/telemetryApp.ts index 490dc714f..affab6855 100644 --- a/src/vs/workbench/parts/debug/node/telemetryApp.ts +++ b/src/vs/workbench/parts/debug/node/telemetryApp.ts @@ -11,5 +11,5 @@ const appender = new AppInsightsAppender(process.argv[2], JSON.parse(process.arg process.once('exit', () => appender.dispose()); const channel = new TelemetryAppenderChannel(appender); -const server = new Server(); +const server = new Server('telemetry'); server.registerChannel('telemetryAppender', channel); diff --git a/src/vs/workbench/parts/debug/node/terminals.ts b/src/vs/workbench/parts/debug/node/terminals.ts index f6cc9c3ab..5d514e483 100644 --- a/src/vs/workbench/parts/debug/node/terminals.ts +++ b/src/vs/workbench/parts/debug/node/terminals.ts @@ -8,7 +8,6 @@ import * as nls from 'vs/nls'; import * as env from 'vs/base/common/platform'; import * as pfs from 'vs/base/node/pfs'; import { assign } from 'vs/base/common/objects'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITerminalLauncher, ITerminalSettings } from 'vs/workbench/parts/debug/common/debug'; import { getPathFromAmdModule } from 'vs/base/common/amd'; @@ -29,12 +28,12 @@ export function getTerminalLauncher() { return terminalLauncher; } -let _DEFAULT_TERMINAL_LINUX_READY: TPromise | null = null; -export function getDefaultTerminalLinuxReady(): TPromise { +let _DEFAULT_TERMINAL_LINUX_READY: Promise | null = null; +export function getDefaultTerminalLinuxReady(): Promise { if (!_DEFAULT_TERMINAL_LINUX_READY) { _DEFAULT_TERMINAL_LINUX_READY = new Promise(c => { if (env.isLinux) { - TPromise.join([pfs.exists('/etc/debian_version'), process.lazyEnv]).then(([isDebian]) => { + Promise.all([pfs.exists('/etc/debian_version'), process.lazyEnv]).then(([isDebian]) => { if (isDebian) { c('x-terminal-emulator'); } else if (process.env.DESKTOP_SESSION === 'gnome' || process.env.DESKTOP_SESSION === 'gnome-classic') { @@ -68,10 +67,10 @@ export function getDefaultTerminalWindows(): string { } abstract class TerminalLauncher implements ITerminalLauncher { - public runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise { + public runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise { return this.runInTerminal0(args.title, args.cwd, args.args, args.env || {}, config); } - runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, config): TPromise { + runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, config): Promise { return void 0; } } @@ -80,11 +79,11 @@ class WinTerminalService extends TerminalLauncher { private static readonly CMD = 'cmd.exe'; - public runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): TPromise { + public runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise { const exec = configuration.external.windowsExec || getDefaultTerminalWindows(); - return new Promise((c, e) => { + return new Promise((c, e) => { const title = `"${dir} - ${TERMINAL_TITLE}"`; const command = `""${args.join('" "')}" & pause"`; // use '|' to only pause on non-zero exit code @@ -108,7 +107,7 @@ class WinTerminalService extends TerminalLauncher { const cmd = cp.spawn(WinTerminalService.CMD, cmdArgs, options); cmd.on('error', e); - c(null); + c(undefined); }); } } @@ -118,11 +117,11 @@ class MacTerminalService extends TerminalLauncher { private static readonly DEFAULT_TERMINAL_OSX = 'Terminal.app'; private static readonly OSASCRIPT = '/usr/bin/osascript'; // osascript is the AppleScript interpreter on OS X - public runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): TPromise { + public runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise { const terminalApp = configuration.external.osxExec || MacTerminalService.DEFAULT_TERMINAL_OSX; - return new Promise((c, e) => { + return new Promise((c, e) => { if (terminalApp === MacTerminalService.DEFAULT_TERMINAL_OSX || terminalApp === 'iTerm.app') { @@ -164,7 +163,7 @@ class MacTerminalService extends TerminalLauncher { }); osa.on('exit', (code: number) => { if (code === 0) { // OK - c(null); + c(undefined); } else { if (stderr) { const lines = stderr.split('\n', 1); @@ -185,12 +184,12 @@ class LinuxTerminalService extends TerminalLauncher { private static readonly WAIT_MESSAGE = nls.localize('press.any.key', "Press any key to continue..."); - public runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): TPromise { + public runInTerminal0(title: string, dir: string, args: string[], envVars: env.IProcessEnvironment, configuration: ITerminalSettings): Promise { const terminalConfig = configuration.external; const execThenable: Thenable = terminalConfig.linuxExec ? Promise.resolve(terminalConfig.linuxExec) : getDefaultTerminalLinuxReady(); - return new Promise((c, e) => { + return new Promise((c, e) => { let termArgs: string[] = []; //termArgs.push('--title'); @@ -226,7 +225,7 @@ class LinuxTerminalService extends TerminalLauncher { }); cmd.on('exit', (code: number) => { if (code === 0) { // OK - c(null); + c(undefined); } else { if (stderr) { const lines = stderr.split('\n', 1); diff --git a/src/vs/workbench/parts/debug/test/common/mockDebug.ts b/src/vs/workbench/parts/debug/test/common/mockDebug.ts index fdab674b3..8d7ede7ec 100644 --- a/src/vs/workbench/parts/debug/test/common/mockDebug.ts +++ b/src/vs/workbench/parts/debug/test/common/mockDebug.ts @@ -5,7 +5,6 @@ import { URI as uri } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { Position } from 'vs/editor/common/core/position'; import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IDebugModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent, IReplElement, IExpression, IReplElementSource } from 'vs/workbench/parts/debug/common/debug'; @@ -44,25 +43,25 @@ export class MockDebugService implements IDebugService { public focusStackFrame(focusedStackFrame: IStackFrame): void { } - sendAllBreakpoints(session?: IDebugSession): TPromise { + sendAllBreakpoints(session?: IDebugSession): Promise { return Promise.resolve(null); } - public addBreakpoints(uri: uri, rawBreakpoints: IBreakpointData[]): TPromise { + public addBreakpoints(uri: uri, rawBreakpoints: IBreakpointData[]): Promise { return Promise.resolve(null); } public updateBreakpoints(uri: uri, data: { [id: string]: IBreakpointUpdateData }, sendOnResourceSaved: boolean): void { } - public enableOrDisableBreakpoints(enabled: boolean): TPromise { + public enableOrDisableBreakpoints(enabled: boolean): Promise { return Promise.resolve(null); } - public setBreakpointsActivated(): TPromise { + public setBreakpointsActivated(): Promise { return Promise.resolve(null); } - public removeBreakpoints(): TPromise { + public removeBreakpoints(): Promise { return Promise.resolve(null); } @@ -70,39 +69,39 @@ export class MockDebugService implements IDebugService { public moveWatchExpression(id: string, position: number): void { } - public renameFunctionBreakpoint(id: string, newFunctionName: string): TPromise { + public renameFunctionBreakpoint(id: string, newFunctionName: string): Promise { return Promise.resolve(null); } - public removeFunctionBreakpoints(id?: string): TPromise { + public removeFunctionBreakpoints(id?: string): Promise { return Promise.resolve(null); } - public addReplExpression(name: string): TPromise { + public addReplExpression(name: string): Promise { return Promise.resolve(null); } public removeReplExpressions(): void { } - public addWatchExpression(name?: string): TPromise { + public addWatchExpression(name?: string): Promise { return Promise.resolve(null); } - public renameWatchExpression(id: string, newName: string): TPromise { + public renameWatchExpression(id: string, newName: string): Promise { return Promise.resolve(null); } public removeWatchExpressions(id?: string): void { } - public startDebugging(launch: ILaunch, configOrName?: IConfig | string, noDebug?: boolean): TPromise { + public startDebugging(launch: ILaunch, configOrName?: IConfig | string, noDebug?: boolean): Promise { return Promise.resolve(true); } - public restartSession(): TPromise { + public restartSession(): Promise { return Promise.resolve(null); } - public stopSession(): TPromise { + public stopSession(): Promise { return Promise.resolve(null); } @@ -118,7 +117,7 @@ export class MockDebugService implements IDebugService { public sourceIsNotAvailable(uri: uri): void { } - public tryToAutoFocusStackFrame(thread: IThread): TPromise { + public tryToAutoFocusStackFrame(thread: IThread): Promise { return Promise.resolve(null); } } @@ -133,15 +132,15 @@ export class MockSession implements IDebugSession { return null; } - addReplExpression(stackFrame: IStackFrame, name: string): TPromise { - return TPromise.as(void 0); + addReplExpression(stackFrame: IStackFrame, name: string): Promise { + return Promise.resolve(void 0); } appendToRepl(data: string | IExpression, severity: Severity, source?: IReplElementSource): void { } logToRepl(sev: Severity, args: any[], frame?: { uri: uri; line: number; column: number; }) { } - configuration: IConfig = { type: 'mock', request: 'launch' }; - unresolvedConfiguration: IConfig = { type: 'mock', request: 'launch' }; + configuration: IConfig = { type: 'mock', name: 'mock', request: 'launch' }; + unresolvedConfiguration: IConfig = { type: 'mock', name: 'mock', request: 'launch' }; state = State.Stopped; root: IWorkspaceFolder; capabilities: DebugProtocol.Capabilities = {}; @@ -188,11 +187,11 @@ export class MockSession implements IDebugSession { return undefined; } - getLoadedSources(): TPromise { + getLoadedSources(): Promise { return Promise.resolve([]); } - completions(frameId: number, text: string, position: Position, overwriteBefore: number): TPromise { + completions(frameId: number, text: string, position: Position, overwriteBefore: number): Promise { return Promise.resolve([]); } @@ -200,80 +199,80 @@ export class MockSession implements IDebugSession { rawUpdate(data: IRawModelUpdate): void { } - initialize(dbgr: IDebugger): TPromise { + initialize(dbgr: IDebugger): Thenable { throw new Error('Method not implemented.'); } - launchOrAttach(config: IConfig): TPromise { + launchOrAttach(config: IConfig): Promise { throw new Error('Method not implemented.'); } - restart(): TPromise { + restart(): Promise { throw new Error('Method not implemented.'); } - sendBreakpoints(modelUri: uri, bpts: IBreakpoint[], sourceModified: boolean): TPromise { + sendBreakpoints(modelUri: uri, bpts: IBreakpoint[], sourceModified: boolean): Promise { throw new Error('Method not implemented.'); } - sendFunctionBreakpoints(fbps: IFunctionBreakpoint[]): TPromise { + sendFunctionBreakpoints(fbps: IFunctionBreakpoint[]): Promise { throw new Error('Method not implemented.'); } - sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): TPromise { + sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise { throw new Error('Method not implemented.'); } - customRequest(request: string, args: any): TPromise { + customRequest(request: string, args: any): Promise { throw new Error('Method not implemented.'); } - stackTrace(threadId: number, startFrame: number, levels: number): TPromise { + stackTrace(threadId: number, startFrame: number, levels: number): Promise { throw new Error('Method not implemented.'); } - exceptionInfo(threadId: number): TPromise { + exceptionInfo(threadId: number): Promise { throw new Error('Method not implemented.'); } - scopes(frameId: number): TPromise { + scopes(frameId: number): Promise { throw new Error('Method not implemented.'); } - variables(variablesReference: number, filter: 'indexed' | 'named', start: number, count: number): TPromise { + variables(variablesReference: number, filter: 'indexed' | 'named', start: number, count: number): Promise { throw new Error('Method not implemented.'); } - evaluate(expression: string, frameId: number, context?: string): TPromise { + evaluate(expression: string, frameId: number, context?: string): Promise { throw new Error('Method not implemented.'); } - restartFrame(frameId: number, threadId: number): TPromise { + restartFrame(frameId: number, threadId: number): Promise { throw new Error('Method not implemented.'); } - next(threadId: number): TPromise { + next(threadId: number): Promise { throw new Error('Method not implemented.'); } - stepIn(threadId: number): TPromise { + stepIn(threadId: number): Promise { throw new Error('Method not implemented.'); } - stepOut(threadId: number): TPromise { + stepOut(threadId: number): Promise { throw new Error('Method not implemented.'); } - stepBack(threadId: number): TPromise { + stepBack(threadId: number): Promise { throw new Error('Method not implemented.'); } - continue(threadId: number): TPromise { + continue(threadId: number): Promise { throw new Error('Method not implemented.'); } - reverseContinue(threadId: number): TPromise { + reverseContinue(threadId: number): Promise { throw new Error('Method not implemented.'); } - pause(threadId: number): TPromise { + pause(threadId: number): Promise { throw new Error('Method not implemented.'); } - terminateThreads(threadIds: number[]): TPromise { + terminateThreads(threadIds: number[]): Promise { throw new Error('Method not implemented.'); } - setVariable(variablesReference: number, name: string, value: string): TPromise { + setVariable(variablesReference: number, name: string, value: string): Promise { throw new Error('Method not implemented.'); } - loadSource(resource: uri): TPromise { + loadSource(resource: uri): Promise { throw new Error('Method not implemented.'); } - terminate(restart = false): TPromise { + terminate(restart = false): Promise { throw new Error('Method not implemented.'); } - disconnect(restart = false): TPromise { + disconnect(restart = false): Promise { throw new Error('Method not implemented.'); } @@ -293,7 +292,7 @@ export class MockRawSession { return 100; } - public stackTrace(args: DebugProtocol.StackTraceArguments): TPromise { + public stackTrace(args: DebugProtocol.StackTraceArguments): Promise { return Promise.resolve({ seq: 1, type: 'response', @@ -311,103 +310,103 @@ export class MockRawSession { }); } - public exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): TPromise { + public exceptionInfo(args: DebugProtocol.ExceptionInfoArguments): Promise { return Promise.resolve(null); } - public launchOrAttach(args: IConfig): TPromise { + public launchOrAttach(args: IConfig): Promise { return Promise.resolve(null); } - public scopes(args: DebugProtocol.ScopesArguments): TPromise { + public scopes(args: DebugProtocol.ScopesArguments): Promise { return Promise.resolve(null); } - public variables(args: DebugProtocol.VariablesArguments): TPromise { + public variables(args: DebugProtocol.VariablesArguments): Promise { return Promise.resolve(null); } - evaluate(args: DebugProtocol.EvaluateArguments): TPromise { + evaluate(args: DebugProtocol.EvaluateArguments): Promise { return Promise.resolve(null); } - public custom(request: string, args: any): TPromise { + public custom(request: string, args: any): Promise { return Promise.resolve(null); } - public terminate(restart = false): TPromise { + public terminate(restart = false): Promise { return Promise.resolve(null); } - public disconnect(restart?: boolean): TPromise { + public disconnect(restart?: boolean): Promise { return Promise.resolve(null); } - public threads(): TPromise { + public threads(): Promise { return Promise.resolve(null); } - public stepIn(args: DebugProtocol.StepInArguments): TPromise { + public stepIn(args: DebugProtocol.StepInArguments): Promise { return Promise.resolve(null); } - public stepOut(args: DebugProtocol.StepOutArguments): TPromise { + public stepOut(args: DebugProtocol.StepOutArguments): Promise { return Promise.resolve(null); } - public stepBack(args: DebugProtocol.StepBackArguments): TPromise { + public stepBack(args: DebugProtocol.StepBackArguments): Promise { return Promise.resolve(null); } - public continue(args: DebugProtocol.ContinueArguments): TPromise { + public continue(args: DebugProtocol.ContinueArguments): Promise { return Promise.resolve(null); } - public reverseContinue(args: DebugProtocol.ReverseContinueArguments): TPromise { + public reverseContinue(args: DebugProtocol.ReverseContinueArguments): Promise { return Promise.resolve(null); } - public pause(args: DebugProtocol.PauseArguments): TPromise { + public pause(args: DebugProtocol.PauseArguments): Promise { return Promise.resolve(null); } - public terminateThreads(args: DebugProtocol.TerminateThreadsArguments): TPromise { + public terminateThreads(args: DebugProtocol.TerminateThreadsArguments): Promise { return Promise.resolve(null); } - public setVariable(args: DebugProtocol.SetVariableArguments): TPromise { + public setVariable(args: DebugProtocol.SetVariableArguments): Promise { return Promise.resolve(null); } - public restartFrame(args: DebugProtocol.RestartFrameArguments): TPromise { + public restartFrame(args: DebugProtocol.RestartFrameArguments): Promise { return Promise.resolve(null); } - public completions(args: DebugProtocol.CompletionsArguments): TPromise { + public completions(args: DebugProtocol.CompletionsArguments): Promise { return Promise.resolve(null); } - public next(args: DebugProtocol.NextArguments): TPromise { + public next(args: DebugProtocol.NextArguments): Promise { return Promise.resolve(null); } - public source(args: DebugProtocol.SourceArguments): TPromise { + public source(args: DebugProtocol.SourceArguments): Promise { return Promise.resolve(null); } - public loadedSources(args: DebugProtocol.LoadedSourcesArguments): TPromise { + public loadedSources(args: DebugProtocol.LoadedSourcesArguments): Promise { return Promise.resolve(null); } - public setBreakpoints(args: DebugProtocol.SetBreakpointsArguments): TPromise { + public setBreakpoints(args: DebugProtocol.SetBreakpointsArguments): Promise { return Promise.resolve(null); } - public setFunctionBreakpoints(args: DebugProtocol.SetFunctionBreakpointsArguments): TPromise { + public setFunctionBreakpoints(args: DebugProtocol.SetFunctionBreakpointsArguments): Promise { return Promise.resolve(null); } - public setExceptionBreakpoints(args: DebugProtocol.SetExceptionBreakpointsArguments): TPromise { + public setExceptionBreakpoints(args: DebugProtocol.SetExceptionBreakpointsArguments): Promise { return Promise.resolve(null); } diff --git a/src/vs/workbench/parts/debug/test/electron-browser/debugModel.test.ts b/src/vs/workbench/parts/debug/test/electron-browser/debugModel.test.ts index c16ffe8c9..51c92921e 100644 --- a/src/vs/workbench/parts/debug/test/electron-browser/debugModel.test.ts +++ b/src/vs/workbench/parts/debug/test/electron-browser/debugModel.test.ts @@ -110,7 +110,7 @@ suite('Debug - Model', () => { test('threads simple', () => { const threadId = 1; const threadName = 'firstThread'; - const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined); + const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined); model.addSession(session); assert.equal(model.getSessions(true).length, 1); @@ -138,7 +138,7 @@ suite('Debug - Model', () => { const stoppedReason = 'breakpoint'; // Add the threads - const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined); + const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined); model.addSession(session); session['raw'] = rawSession; @@ -225,7 +225,7 @@ suite('Debug - Model', () => { const runningThreadId = 2; const runningThreadName = 'runningThread'; const stoppedReason = 'breakpoint'; - const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined); + const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined); model.addSession(session); session['raw'] = rawSession; @@ -338,7 +338,7 @@ suite('Debug - Model', () => { }); test('repl expressions', () => { - const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined); + const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined); assert.equal(session.getReplElements().length, 0); model.addSession(session); @@ -362,7 +362,7 @@ suite('Debug - Model', () => { }); test('stack frame get specific source name', () => { - const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined); + const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined); model.addSession(session); let firstStackFrame: StackFrame; @@ -393,7 +393,7 @@ suite('Debug - Model', () => { // Repl output test('repl output', () => { - const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined); + const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined); const repl = new ReplModel(session); repl.appendToRepl('first line\n', severity.Error); repl.appendToRepl('second line', severity.Error); diff --git a/src/vs/workbench/parts/debug/test/node/debugger.test.ts b/src/vs/workbench/parts/debug/test/node/debugger.test.ts index a64cde0da..24e5fcc2c 100644 --- a/src/vs/workbench/parts/debug/test/node/debugger.test.ts +++ b/src/vs/workbench/parts/debug/test/node/debugger.test.ts @@ -10,7 +10,6 @@ import { IDebugAdapterExecutable, IConfigurationManager, IConfig, IDebugSession import { Debugger } from 'vs/workbench/parts/debug/node/debugger'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ExecutableDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter'; import { TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices'; @@ -118,7 +117,7 @@ suite('Debug - Debugger', () => { const configurationManager = { - provideDebugAdapter(session: IDebugSession, folderUri: URI | undefined, config: IConfig): TPromise { + provideDebugAdapter(session: IDebugSession, config: IConfig): Promise { return Promise.resolve(undefined); } }; diff --git a/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.ts b/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.ts index adaca0e37..8b9ea125d 100644 --- a/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.ts +++ b/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.ts @@ -5,7 +5,6 @@ import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { registerEditorAction, EditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; @@ -31,10 +30,10 @@ class ShowEmmetCommandsAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise { + public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { const quickOpenService = accessor.get(IQuickOpenService); quickOpenService.show(EMMET_COMMANDS_PREFIX); - return TPromise.as(void 0); + return Promise.resolve(void 0); } } diff --git a/src/vs/workbench/parts/execution/common/execution.ts b/src/vs/workbench/parts/execution/common/execution.ts index 99a38917a..4be8a7555 100644 --- a/src/vs/workbench/parts/execution/common/execution.ts +++ b/src/vs/workbench/parts/execution/common/execution.ts @@ -11,5 +11,5 @@ export const ITerminalService = createDecorator('nativeTermina export interface ITerminalService { _serviceBrand: any; openTerminal(path: string): void; - runInTerminal(title: string, cwd: string, args: string[], env: IProcessEnvironment): Promise; + runInTerminal(title: string, cwd: string, args: string[], env: IProcessEnvironment): Promise; } \ No newline at end of file diff --git a/src/vs/workbench/parts/execution/electron-browser/terminal.ts b/src/vs/workbench/parts/execution/electron-browser/terminal.ts index f08fd641f..525bd5003 100644 --- a/src/vs/workbench/parts/execution/electron-browser/terminal.ts +++ b/src/vs/workbench/parts/execution/electron-browser/terminal.ts @@ -11,7 +11,7 @@ export function getDefaultTerminalLinuxReady(): Promise { if (!_DEFAULT_TERMINAL_LINUX_READY) { _DEFAULT_TERMINAL_LINUX_READY = new Promise(c => { if (env.isLinux) { - Promise.all([pfs.exists('/etc/debian_version'), process.lazyEnv]).then(([isDebian]) => { + Promise.all([pfs.exists('/etc/debian_version'), process.lazyEnv || Promise.resolve(void 0)]).then(([isDebian]) => { if (isDebian) { c('x-terminal-emulator'); } else if (process.env.DESKTOP_SESSION === 'gnome' || process.env.DESKTOP_SESSION === 'gnome-classic') { diff --git a/src/vs/workbench/parts/execution/electron-browser/terminalService.ts b/src/vs/workbench/parts/execution/electron-browser/terminalService.ts index 60c991d4b..7fde5a1f5 100644 --- a/src/vs/workbench/parts/execution/electron-browser/terminalService.ts +++ b/src/vs/workbench/parts/execution/electron-browser/terminalService.ts @@ -37,13 +37,13 @@ export class WinTerminalService implements ITerminalService { this.spawnTerminal(cp, configuration, processes.getWindowsShell(), cwd); } - public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): Promise { + public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): Promise { const configuration = this._configurationService.getValue(); const terminalConfig = configuration.terminal.external; const exec = terminalConfig.windowsExec || getDefaultTerminalWindows(); - return new Promise((c, e) => { + return new Promise((c, e) => { const title = `"${dir} - ${TERMINAL_TITLE}"`; const command = `""${args.join('" "')}" & pause"`; // use '|' to only pause on non-zero exit code @@ -67,7 +67,7 @@ export class WinTerminalService implements ITerminalService { const cmd = cp.spawn(WinTerminalService.CMD, cmdArgs, options); cmd.on('error', e); - c(null); + c(undefined); }); } @@ -100,7 +100,7 @@ export class WinTerminalService implements ITerminalService { const env = cwd ? { cwd: cwd } : void 0; const child = spawner.spawn(command, cmdArgs, env); child.on('error', e); - child.on('exit', () => c(null)); + child.on('exit', () => c()); }); } @@ -128,13 +128,13 @@ export class MacTerminalService implements ITerminalService { this.spawnTerminal(cp, configuration, cwd); } - public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): Promise { + public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): Promise { const configuration = this._configurationService.getValue(); const terminalConfig = configuration.terminal.external; const terminalApp = terminalConfig.osxExec || DEFAULT_TERMINAL_OSX; - return new Promise((c, e) => { + return new Promise((c, e) => { if (terminalApp === DEFAULT_TERMINAL_OSX || terminalApp === 'iTerm.app') { @@ -176,7 +176,7 @@ export class MacTerminalService implements ITerminalService { }); osa.on('exit', (code: number) => { if (code === 0) { // OK - c(null); + c(undefined); } else { if (stderr) { const lines = stderr.split('\n', 1); @@ -199,7 +199,7 @@ export class MacTerminalService implements ITerminalService { return new Promise((c, e) => { const child = spawner.spawn('/usr/bin/open', ['-a', terminalApp, cwd]); child.on('error', e); - child.on('exit', () => c(null)); + child.on('exit', () => c()); }); } } @@ -220,13 +220,13 @@ export class LinuxTerminalService implements ITerminalService { this.spawnTerminal(cp, configuration, cwd); } - public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): Promise { + public runInTerminal(title: string, dir: string, args: string[], envVars: IProcessEnvironment): Promise { const configuration = this._configurationService.getValue(); const terminalConfig = configuration.terminal.external; const execPromise = terminalConfig.linuxExec ? Promise.resolve(terminalConfig.linuxExec) : getDefaultTerminalLinuxReady(); - return new Promise((c, e) => { + return new Promise((c, e) => { let termArgs: string[] = []; //termArgs.push('--title'); @@ -262,7 +262,7 @@ export class LinuxTerminalService implements ITerminalService { }); cmd.on('exit', (code: number) => { if (code === 0) { // OK - c(null); + c(undefined); } else { if (stderr) { const lines = stderr.split('\n', 1); @@ -285,7 +285,7 @@ export class LinuxTerminalService implements ITerminalService { execPromise.then(exec => { const child = spawner.spawn(exec, [], env); child.on('error', e); - child.on('exit', () => c(null)); + child.on('exit', () => c()); }); }); } diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts index 806a0d751..39905ac68 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts @@ -237,10 +237,10 @@ export class ExtensionEditor extends BaseEditor { animated: false, actionItemProvider: (action: Action) => { if (action.id === EnableAction.ID) { - return (action).actionItem; + return (action).createActionItem(); } if (action.id === DisableAction.ID) { - return (action).actionItem; + return (action).createActionItem(); } return null; } @@ -379,7 +379,7 @@ export class ExtensionEditor extends BaseEditor { reloadAction.extension = extension; this.extensionActionBar.clear(); - this.extensionActionBar.push([disabledStatusAction, reloadAction, updateAction, enableAction, disableAction, installAction, maliciousStatusAction], { icon: true, label: true }); + this.extensionActionBar.push([reloadAction, updateAction, enableAction, disableAction, installAction, maliciousStatusAction, disabledStatusAction], { icon: true, label: true }); this.transientDisposables.push(enableAction, updateAction, reloadAction, disableAction, installAction, maliciousStatusAction, disabledStatusAction); const ignoreAction = this.instantiationService.createInstance(IgnoreExtensionRecommendationAction); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts index e3bc078e9..9bdd6f3aa 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts @@ -43,6 +43,7 @@ import { URI } from 'vs/base/common/uri'; import { areSameExtensions, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IExperimentService, ExperimentActionType, ExperimentState } from 'vs/workbench/parts/experiments/node/experimentService'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { getKeywordsForExtension } from 'vs/workbench/parts/extensions/electron-browser/extensionsUtils'; const milliSecondsInADay = 1000 * 60 * 60 * 24; const choiceNever = localize('neverShowAgain', "Don't Show Again"); @@ -160,24 +161,6 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe })); } - private fetchProactiveRecommendations(calledDuringStartup?: boolean): Promise { - let fetchPromise = Promise.resolve(null); - if (!this.proactiveRecommendationsFetched) { - this.proactiveRecommendationsFetched = true; - - // Executable based recommendations carry out a lot of file stats, so run them after 10 secs - // So that the startup is not affected - - fetchPromise = new Promise((c, e) => { - setTimeout(() => { - Promise.all([this.fetchExecutableRecommendations(), this.fetchDynamicWorkspaceRecommendations()]).then(() => c(null)); - }, calledDuringStartup ? 10000 : 0); - }); - - } - return fetchPromise; - } - private isEnabled(): boolean { return this._galleryService.isEnabled() && !this.environmentService.extensionDevelopmentLocationURI; } @@ -232,12 +215,48 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe }; } + toggleIgnoredRecommendation(extensionId: string, shouldIgnore: boolean) { + const lowerId = extensionId.toLowerCase(); + if (shouldIgnore) { + const reason = this.getAllRecommendationsWithReason()[lowerId]; + if (reason && reason.reasonId) { + /* __GDPR__ + "extensionsRecommendations:ignoreRecommendation" : { + "recommendationReason": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog('extensionsRecommendations:ignoreRecommendation', { id: extensionId, recommendationReason: reason.reasonId }); + } + } + + this._globallyIgnoredRecommendations = shouldIgnore ? + distinct([...this._globallyIgnoredRecommendations, lowerId].map(id => id.toLowerCase())) : + this._globallyIgnoredRecommendations.filter(id => id !== lowerId); + + this.storageService.store('extensionsAssistant/ignored_recommendations', JSON.stringify(this._globallyIgnoredRecommendations), StorageScope.GLOBAL); + this._allIgnoredRecommendations = distinct([...this._globallyIgnoredRecommendations, ...this._workspaceIgnoredRecommendations]); + + this._onRecommendationChange.fire({ extensionId: extensionId, isRecommended: !shouldIgnore }); + } + + getKeymapRecommendations(): IExtensionRecommendation[] { + return (product.keymapExtensionTips || []) + .filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId)) + .map(extensionId => ({ extensionId, sources: ['application'] })); + } + + //#region workspaceRecommendations + getWorkspaceRecommendations(): Promise { if (!this.isEnabled()) { return Promise.resolve([]); } return this.fetchWorkspaceRecommendations() .then(() => this._allWorkspaceRecommendedExtensions.filter(rec => this.isExtensionAllowedToBeRecommended(rec.extensionId))); } + /** + * Parse all extensions.json files, fetch workspace recommendations, filter out invalid and unwanted ones + */ private fetchWorkspaceRecommendations(): Promise { if (!this.isEnabled) { return Promise.resolve(null); } @@ -286,6 +305,9 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe })); } + /** + * Parse all extensions.json files, fetch workspace recommendations + */ private fetchExtensionRecommendationContents(): Promise<{ contents: IExtensionsConfigContent, source: ExtensionRecommendationSource }[]> { const workspace = this.contextService.getWorkspace(); return Promise.all<{ contents: IExtensionsConfigContent, source: ExtensionRecommendationSource }>([ @@ -294,6 +316,10 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe ]).then(contents => coalesce(contents)); } + /** + * Parse the extensions.json file for given workspace and return the recommendations + * @param workspace + */ private resolveWorkspaceExtensionConfig(workspace: IWorkspace): Promise { if (!workspace.configuration) { return Promise.resolve(null); @@ -303,6 +329,10 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe .then(content => (json.parse(content.value)['extensions']), err => null)); } + /** + * Parse the extensions.json files for given workspace folder and return the recommendations + * @param workspaceFolder + */ private resolveWorkspaceFolderExtensionConfig(workspaceFolder: IWorkspaceFolder): Promise { const extensionsJsonUri = workspaceFolder.toResource(paths.join('.vscode', 'extensions.json')); @@ -311,6 +341,10 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe .then(content => json.parse(content.value), err => null)); } + /** + * Validate the extensions.json file contents using regex and querying the gallery + * @param contents + */ private async validateExtensions(contents: IExtensionsConfigContent[]): Promise<{ invalidExtensions: string[], message: string }> { const extensionsContent: IExtensionsConfigContent = { recommendations: distinct(flatten(contents.map(content => content.recommendations || []))), @@ -359,10 +393,6 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe return { invalidExtensions, message }; } - private isExtensionAllowedToBeRecommended(id: string): boolean { - return this._allIgnoredRecommendations.indexOf(id.toLowerCase()) === -1; - } - private onWorkspaceFoldersChanged(event: IWorkspaceFoldersChangeEvent): void { if (event.added.length) { const oldWorkspaceRecommended = this._allWorkspaceRecommendedExtensions; @@ -377,6 +407,101 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe this._dynamicWorkspaceRecommendations = []; } + /** + * Prompt the user to install workspace recommendations if there are any not already installed + */ + private promptWorkspaceRecommendations(): void { + const storageKey = 'extensionsAssistant/workspaceRecommendationsIgnore'; + const config = this.configurationService.getValue(ConfigurationKey); + const filteredRecs = this._allWorkspaceRecommendedExtensions.filter(rec => this.isExtensionAllowedToBeRecommended(rec.extensionId)); + + if (filteredRecs.length === 0 + || config.ignoreRecommendations + || config.showRecommendationsOnlyOnDemand + || this.storageService.getBoolean(storageKey, StorageScope.WORKSPACE, false)) { + return; + } + + this.extensionsService.getInstalled(LocalExtensionType.User).then(local => { + const recommendations = filteredRecs.filter(({ extensionId }) => local.every(local => !areSameExtensions({ id: extensionId }, { id: getGalleryExtensionIdFromLocal(local) }))); + + if (!recommendations.length) { + return Promise.resolve(void 0); + } + + return new Promise(c => { + this.notificationService.prompt( + Severity.Info, + localize('workspaceRecommended', "This workspace has extension recommendations."), + [{ + label: localize('installAll', "Install All"), + run: () => { + /* __GDPR__ + "extensionWorkspaceRecommendations:popup" : { + "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'install' }); + + const installAllAction = this.instantiationService.createInstance(InstallWorkspaceRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsAction.ID, localize('installAll', "Install All"), recommendations); + installAllAction.run(); + installAllAction.dispose(); + + c(void 0); + } + }, { + label: localize('showRecommendations', "Show Recommendations"), + run: () => { + /* __GDPR__ + "extensionWorkspaceRecommendations:popup" : { + "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'show' }); + + const showAction = this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, localize('showRecommendations', "Show Recommendations")); + showAction.run(); + showAction.dispose(); + + c(void 0); + } + }, { + label: choiceNever, + isSecondary: true, + run: () => { + /* __GDPR__ + "extensionWorkspaceRecommendations:popup" : { + "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'neverShowAgain' }); + this.storageService.store(storageKey, true, StorageScope.WORKSPACE); + + c(void 0); + } + }], + { + sticky: true, + onCancel: () => { + /* __GDPR__ + "extensionWorkspaceRecommendations:popup" : { + "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'cancelled' }); + + c(void 0); + } + } + ); + }); + }); + } + + //#endregion + + //#region fileBasedRecommendations + getFileBasedRecommendations(): IExtensionRecommendation[] { return Object.keys(this._fileBasedRecommendations) .sort((a, b) => { @@ -394,45 +519,10 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe .map(extensionId => ({ extensionId, sources: this._fileBasedRecommendations[extensionId].sources })); } - getOtherRecommendations(): Promise { - return this.fetchProactiveRecommendations().then(() => { - const others = distinct([ - ...Object.keys(this._exeBasedRecommendations), - ...this._dynamicWorkspaceRecommendations, - ...Object.keys(this._experimentalRecommendations), - ]).filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId)); - shuffle(others, this.sessionSeed); - return others.map(extensionId => { - const sources: ExtensionRecommendationSource[] = []; - if (this._exeBasedRecommendations[extensionId]) { - sources.push('executable'); - } - if (this._dynamicWorkspaceRecommendations.indexOf(extensionId) !== -1) { - sources.push('dynamic'); - } - return ({ extensionId, sources }); - }); - }); - } - - getKeymapRecommendations(): IExtensionRecommendation[] { - return (product.keymapExtensionTips || []) - .filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId)) - .map(extensionId => ({ extensionId, sources: ['application'] })); - } - - getAllRecommendations(): Promise { - if (!this.proactiveRecommendationsFetched) { - return Promise.resolve([]); - } - return Promise.all([ - this.getWorkspaceRecommendations(), - Promise.resolve(this.getFileBasedRecommendations()), - this.getOtherRecommendations(), - Promise.resolve(this.getKeymapRecommendations()) - ]).then(result => flatten(result).filter(e => this.isExtensionAllowedToBeRecommended(e.extensionId))); - } - + /** + * Parse all file based recommendations from product.extensionTips + * Retire existing recommendations if they are older than a week or are not part of product.extensionTips anymore + */ private fetchFileBasedRecommendations() { const extensionTips = product.extensionTips; if (!extensionTips) { @@ -486,12 +576,11 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } } - private getMimeTypes(path: string): Promise { - return Promise.resolve(this.extensionService.whenInstalledExtensionsRegistered().then(() => { - return guessMimeTypes(path); - })); - } - + /** + * Prompt the user to either install the recommended extension for the file type in the current editor model + * or prompt to search the marketplace if it has extensions that can support the file type + * @param model + */ private promptFiletypeBasedRecommendations(model: ITextModel): void { let hasSuggestion = false; @@ -563,6 +652,14 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe message = localize('reallyRecommendedExtensionPack', "The '{0}' extension pack is recommended for this file type.", name); } + const setIgnoreRecommendationsConfig = (configVal: boolean) => { + this.configurationService.updateValue('extensions.ignoreRecommendations', configVal, ConfigurationTarget.USER); + if (configVal) { + const ignoreWorkspaceRecommendationsStorageKey = 'extensionsAssistant/workspaceRecommendationsIgnore'; + this.storageService.store(ignoreWorkspaceRecommendationsStorageKey, true, StorageScope.WORKSPACE); + } + }; + this.notificationService.prompt(Severity.Info, message, [{ label: localize('install', 'Install'), @@ -608,7 +705,17 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } */ this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId: name }); - this.promptIgnoreExtensionRecommendations(); + this.notificationService.prompt( + Severity.Info, + localize('ignoreExtensionRecommendations', "Do you want to ignore all extension recommendations?"), + [{ + label: localize('ignoreAll', "Yes, Ignore All"), + run: () => setIgnoreRecommendationsConfig(true) + }, { + label: localize('no', "No"), + run: () => setIgnoreRecommendationsConfig(false) + }] + ); } }], { @@ -626,7 +733,11 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe ); }); - const mimeTypesPromise = this.getMimeTypes(uri.fsPath); + const mimeTypesPromise = this.extensionService.whenInstalledExtensionsRegistered() + .then(() => { + return guessMimeTypes(uri.fsPath); + }); + Promise.all([importantTipsPromise, mimeTypesPromise]).then(result => { const fileExtensionSuggestionIgnoreList = JSON.parse(this.storageService.get @@ -646,7 +757,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe return; } - const keywords = this.getKeywordsForExtension(fileExtension); + const keywords = getKeywordsForExtension(fileExtension); this._galleryService.query({ text: `tag:"__ext_${fileExtension}" ${keywords.map(tag => `tag:"${tag}"`)}` }).then(pager => { if (!pager || !pager.firstPage || !pager.firstPage.length) { return; @@ -708,108 +819,52 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe }); } - private promptWorkspaceRecommendations(): void { - const storageKey = 'extensionsAssistant/workspaceRecommendationsIgnore'; - const config = this.configurationService.getValue(ConfigurationKey); - const filteredRecs = this._allWorkspaceRecommendedExtensions.filter(rec => this.isExtensionAllowedToBeRecommended(rec.extensionId)); - - if (filteredRecs.length === 0 - || config.ignoreRecommendations - || config.showRecommendationsOnlyOnDemand - || this.storageService.getBoolean(storageKey, StorageScope.WORKSPACE, false)) { - return; - } + //#endregion - this.extensionsService.getInstalled(LocalExtensionType.User).then(local => { - const recommendations = filteredRecs.filter(({ extensionId }) => local.every(local => !areSameExtensions({ id: extensionId }, { id: getGalleryExtensionIdFromLocal(local) }))); - - if (!recommendations.length) { - return Promise.resolve(void 0); - } + //#region otherRecommendations - return new Promise(c => { - this.notificationService.prompt( - Severity.Info, - localize('workspaceRecommended', "This workspace has extension recommendations."), - [{ - label: localize('installAll', "Install All"), - run: () => { - /* __GDPR__ - "extensionWorkspaceRecommendations:popup" : { - "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'install' }); - - const installAllAction = this.instantiationService.createInstance(InstallWorkspaceRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsAction.ID, localize('installAll', "Install All"), recommendations); - installAllAction.run(); - installAllAction.dispose(); - - c(void 0); - } - }, { - label: localize('showRecommendations', "Show Recommendations"), - run: () => { - /* __GDPR__ - "extensionWorkspaceRecommendations:popup" : { - "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'show' }); - - const showAction = this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, localize('showRecommendations', "Show Recommendations")); - showAction.run(); - showAction.dispose(); + getOtherRecommendations(): Promise { + return this.fetchProactiveRecommendations().then(() => { + const others = distinct([ + ...Object.keys(this._exeBasedRecommendations), + ...this._dynamicWorkspaceRecommendations, + ...Object.keys(this._experimentalRecommendations), + ]).filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId)); + shuffle(others, this.sessionSeed); + return others.map(extensionId => { + const sources: ExtensionRecommendationSource[] = []; + if (this._exeBasedRecommendations[extensionId]) { + sources.push('executable'); + } + if (this._dynamicWorkspaceRecommendations.indexOf(extensionId) !== -1) { + sources.push('dynamic'); + } + return ({ extensionId, sources }); + }); + }); + } - c(void 0); - } - }, { - label: choiceNever, - isSecondary: true, - run: () => { - /* __GDPR__ - "extensionWorkspaceRecommendations:popup" : { - "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'neverShowAgain' }); - this.storageService.store(storageKey, true, StorageScope.WORKSPACE); + private fetchProactiveRecommendations(calledDuringStartup?: boolean): Promise { + let fetchPromise = Promise.resolve(null); + if (!this.proactiveRecommendationsFetched) { + this.proactiveRecommendationsFetched = true; - c(void 0); - } - }], - { - sticky: true, - onCancel: () => { - /* __GDPR__ - "extensionWorkspaceRecommendations:popup" : { - "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'cancelled' }); + // Executable based recommendations carry out a lot of file stats, so run them after 10 secs + // So that the startup is not affected - c(void 0); - } - } - ); + fetchPromise = new Promise((c, e) => { + setTimeout(() => { + Promise.all([this.fetchExecutableRecommendations(), this.fetchDynamicWorkspaceRecommendations()]).then(() => c(null)); + }, calledDuringStartup ? 10000 : 0); }); - }); - } - private promptIgnoreExtensionRecommendations() { - this.notificationService.prompt( - Severity.Info, - localize('ignoreExtensionRecommendations', "Do you want to ignore all extension recommendations?"), - [{ - label: localize('ignoreAll', "Yes, Ignore All"), - run: () => this.setIgnoreRecommendationsConfig(true) - }, { - label: localize('no', "No"), - run: () => this.setIgnoreRecommendationsConfig(false) - }] - ); + } + return fetchPromise; } + /** + * If user has any of the tools listed in product.exeBasedExtensionTips, fetch corresponding recommendations + */ private fetchExecutableRecommendations(): Promise { const homeDir = os.homedir(); let foundExecutables: Set = new Set(); @@ -855,14 +910,9 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe return Promise.all(promises); } - private setIgnoreRecommendationsConfig(configVal: boolean) { - this.configurationService.updateValue('extensions.ignoreRecommendations', configVal, ConfigurationTarget.USER); - if (configVal) { - const ignoreWorkspaceRecommendationsStorageKey = 'extensionsAssistant/workspaceRecommendationsIgnore'; - this.storageService.store(ignoreWorkspaceRecommendationsStorageKey, true, StorageScope.WORKSPACE); - } - } - + /** + * Fetch extensions used by others on the same workspace as recommendations from cache + */ private fetchCachedDynamicWorkspaceRecommendations() { if (this.contextService.getWorkbenchState() !== WorkbenchState.FOLDER) { return; @@ -891,6 +941,9 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } } + /** + * Fetch extensions used by others on the same workspace as recommendations from recommendation service + */ private fetchDynamicWorkspaceRecommendations(): Promise { if (this.contextService.getWorkbenchState() !== WorkbenchState.FOLDER || !this.fileService.canHandleResource(this.contextService.getWorkspace().folders[0].uri) @@ -942,6 +995,9 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe }); } + /** + * Fetch extension recommendations from currently running experiments + */ private fetchExperimentalRecommendations() { this.experimentService.getExperimentsByType(ExperimentActionType.AddToRecommendations).then(experiments => { (experiments || []).forEach(experiment => { @@ -954,34 +1010,10 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe }); } - getKeywordsForExtension(extension: string): string[] { - const keywords = product.extensionKeywords || {}; - return keywords[extension] || []; - } - - toggleIgnoredRecommendation(extensionId: string, shouldIgnore: boolean) { - const lowerId = extensionId.toLowerCase(); - if (shouldIgnore) { - const reason = this.getAllRecommendationsWithReason()[lowerId]; - if (reason && reason.reasonId) { - /* __GDPR__ - "extensionsRecommendations:ignoreRecommendation" : { - "recommendationReason": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('extensionsRecommendations:ignoreRecommendation', { id: extensionId, recommendationReason: reason.reasonId }); - } - } - - this._globallyIgnoredRecommendations = shouldIgnore ? - distinct([...this._globallyIgnoredRecommendations, lowerId].map(id => id.toLowerCase())) : - this._globallyIgnoredRecommendations.filter(id => id !== lowerId); - - this.storageService.store('extensionsAssistant/ignored_recommendations', JSON.stringify(this._globallyIgnoredRecommendations), StorageScope.GLOBAL); - this._allIgnoredRecommendations = distinct([...this._globallyIgnoredRecommendations, ...this._workspaceIgnoredRecommendations]); + //#endregion - this._onRecommendationChange.fire({ extensionId: extensionId, isRecommended: !shouldIgnore }); + private isExtensionAllowedToBeRecommended(id: string): boolean { + return this._allIgnoredRecommendations.indexOf(id.toLowerCase()) === -1; } dispose() { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts index 63c0b8a7a..d808473e5 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts @@ -45,6 +45,7 @@ import { RuntimeExtensionsInput } from 'vs/workbench/services/extensions/electro import { URI } from 'vs/base/common/uri'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ExtensionActivationProgress } from 'vs/workbench/parts/extensions/electron-browser/extensionsActivationProgress'; +import { ExtensionsAutoProfiler } from 'vs/workbench/parts/extensions/electron-browser/extensionsAutoProfiler'; // Singletons registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService); @@ -58,6 +59,7 @@ workbenchRegistry.registerWorkbenchContribution(ConfigureRecommendedExtensionsCo workbenchRegistry.registerWorkbenchContribution(KeymapExtensions, LifecyclePhase.Running); workbenchRegistry.registerWorkbenchContribution(ExtensionsViewletViewsContribution, LifecyclePhase.Starting); workbenchRegistry.registerWorkbenchContribution(ExtensionActivationProgress, LifecyclePhase.Eventually); +workbenchRegistry.registerWorkbenchContribution(ExtensionsAutoProfiler, LifecyclePhase.Eventually); Registry.as(OutputExtensions.OutputChannels) .registerChannel({ id: ExtensionsChannelId, label: ExtensionsLabel, log: false }); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index a67af30c6..95995b039 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -11,13 +11,14 @@ import * as DOM from 'vs/base/browser/dom'; import * as paths from 'vs/base/common/paths'; import { Event } from 'vs/base/common/event'; import * as json from 'vs/base/common/json'; -import { ActionItem, IActionItem, Separator, IActionItemOptions } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionItem, Separator, IActionItemOptions } from 'vs/base/browser/ui/actionbar/actionbar'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewlet, AutoUpdateConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions'; import { ExtensionsConfigurationInitialContent } from 'vs/workbench/parts/extensions/common/extensionsFileTemplate'; -import { LocalExtensionType, IExtensionEnablementService, IExtensionTipsService, EnablementState, ExtensionsLabel, IExtensionRecommendation, IGalleryExtension, IExtensionsConfigContent, IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { LocalExtensionType, IExtensionEnablementService, IExtensionTipsService, EnablementState, ExtensionsLabel, IExtensionRecommendation, IGalleryExtension, IExtensionsConfigContent, IExtensionManagementServerService, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { isUIExtension } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ShowViewletAction } from 'vs/workbench/browser/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -50,28 +51,38 @@ import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensions import product from 'vs/platform/node/product'; import { IQuickPickItem, IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { clipboard } from 'electron'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { alert } from 'vs/base/browser/ui/aria/aria'; - -const promptDownloadManually = (extension: IGalleryExtension, message: string, instantiationService: IInstantiationService, notificationService: INotificationService, openerService: IOpenerService) => { - const downloadUrl = `${product.extensionsGallery.serviceUrl}/publishers/${extension.publisher}/vsextensions/${extension.name}/${extension.version}/vspackage`; - notificationService.prompt(Severity.Error, message, [{ - label: localize('download', "Download Manually"), - run: () => openerService.open(URI.parse(downloadUrl)).then(() => { - notificationService.prompt( - Severity.Info, - localize('install vsix', 'Once downloaded, please manually install the downloaded VSIX of \'{0}\'.', extension.identifier.id), - [{ - label: InstallVSIXAction.LABEL, - run: () => { - const action = instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL); - action.run(); - action.dispose(); - } - }] - ); - }) - }]); +import { ILabelService } from 'vs/platform/label/common/label'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { isUndefinedOrNull } from 'vs/base/common/types'; + +const promptDownloadManually = (extension: IGalleryExtension, message: string, error: Error, instantiationService: IInstantiationService, notificationService: INotificationService, openerService: IOpenerService) => { + if (error.name === INSTALL_ERROR_INCOMPATIBLE || error.name === INSTALL_ERROR_MALICIOUS) { + return Promise.reject(error); + } else { + const downloadUrl = `${product.extensionsGallery.serviceUrl}/publishers/${extension.publisher}/vsextensions/${extension.name}/${extension.version}/vspackage`; + notificationService.prompt(Severity.Error, message, [{ + label: localize('download', "Download Manually"), + run: () => openerService.open(URI.parse(downloadUrl)).then(() => { + notificationService.prompt( + Severity.Info, + localize('install vsix', 'Once downloaded, please manually install the downloaded VSIX of \'{0}\'.', extension.identifier.id), + [{ + label: InstallVSIXAction.LABEL, + run: () => { + const action = instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL); + action.run(); + action.dispose(); + } + }] + ); + }) + }]); + return Promise.resolve(); + } }; export interface IExtensionAction extends IAction { @@ -147,7 +158,7 @@ export class InstallAction extends Action { console.error(err); - promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.id), this.instantiationService, this.notificationService, this.openerService); + return promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.id), err, this.instantiationService, this.notificationService, this.openerService); }); } @@ -374,7 +385,7 @@ export class UpdateAction extends Action { console.error(err); - promptDownloadManually(extension.gallery, localize('failedToUpdate', "Failed to update \'{0}\'.", extension.id), this.instantiationService, this.notificationService, this.openerService); + return promptDownloadManually(extension.gallery, localize('failedToUpdate', "Failed to update \'{0}\'.", extension.id), err, this.instantiationService, this.notificationService, this.openerService); }); } @@ -424,13 +435,77 @@ export class ExtensionActionItem extends ActionItem { } } +export abstract class DropDownAction extends Action { + + protected disposables: IDisposable[] = []; + + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { + this._extension = extension; + this.getActions().forEach(action => action.extension = extension); + this.update(extension); + } + + constructor( + id: string, + label: string, + cssClass: string, + enabled: boolean, + readonly actionsGroups: IExtensionAction[][], + private readonly tabOnlyOnFocus: boolean, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(id, label, cssClass, enabled); + + for (const group of actionsGroups) { + for (const action of group) { + if (action instanceof Action) { + action.onDidChange(({ enabled }) => { + if (!isUndefinedOrNull(enabled)) { + this.update(); + } + }, this, this.disposables); + } + } + } + } + + private _actionItem: DropDownMenuActionItem; + createActionItem(): DropDownMenuActionItem { + this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, this.actionsGroups, this.tabOnlyOnFocus); + return this._actionItem; + } + + getActions(): IExtensionAction[] { + const actions: IExtensionAction[] = []; + const groups = this.actionsGroups; + for (const menuActions of groups) { + actions.push(...menuActions); + } + return actions; + } + + public run(): Promise { + if (this._actionItem) { + this._actionItem.showMenu(); + } + return Promise.resolve(null); + } + + protected abstract update(extension?: IExtension); + + dispose(): void { + dispose(this.disposables); + super.dispose(); + } +} + export class DropDownMenuActionItem extends ExtensionActionItem { private disposables: IDisposable[] = []; private _menuActionGroups: IAction[][]; - get menuActionGroups(): IAction[][] { return this._menuActionGroups; } - set menuActionGroups(menuActionGroups: IAction[][]) { this._menuActionGroups = menuActionGroups; } constructor(action: IAction, menuActionGroups: IAction[][], @@ -438,7 +513,7 @@ export class DropDownMenuActionItem extends ExtensionActionItem { @IContextMenuService private contextMenuService: IContextMenuService ) { super(null, action, { icon: true, label: true, tabOnlyOnFocus }); - this.menuActionGroups = menuActionGroups; + this._menuActionGroups = menuActionGroups; } public showMenu(): void { @@ -447,15 +522,14 @@ export class DropDownMenuActionItem extends ExtensionActionItem { const anchor = { x: elementPosition.left, y: elementPosition.top + elementPosition.height + 10 }; this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => Promise.resolve(actions), + getActions: () => actions, actionRunner: this.actionRunner }); } getActions(): IAction[] { let actions: IAction[] = []; - const menuActionGroups = this.menuActionGroups; - for (const menuActions of menuActionGroups) { + for (const menuActions of this._menuActionGroups) { actions = [...actions, ...menuActions, new Separator()]; } return actions.length ? actions.slice(0, actions.length - 1) : actions; @@ -467,32 +541,32 @@ export class DropDownMenuActionItem extends ExtensionActionItem { } } -export class ManageExtensionAction extends Action { +export class ManageExtensionAction extends DropDownAction { static readonly ID = 'extensions.manage'; - private static readonly Class = 'extension-action manage'; private static readonly HideManageExtensionClass = `${ManageExtensionAction.Class} hide`; - private _actionItem: DropDownMenuActionItem; - get actionItem(): DropDownMenuActionItem { return this._actionItem; } - - private disposables: IDisposable[] = []; - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this.update(); } - constructor( - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - @IInstantiationService private instantiationService: IInstantiationService + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, + @IInstantiationService instantiationService: IInstantiationService ) { - super(ManageExtensionAction.ID); + const groups: IExtensionAction[][] = []; + groups.push([ + instantiationService.createInstance(EnableGloballyAction, EnableGloballyAction.LABEL), + instantiationService.createInstance(CombinedEnableForWorkspaceAction, CombinedEnableForWorkspaceAction.LABEL) + ]); + groups.push([ + instantiationService.createInstance(DisableGloballyAction, DisableGloballyAction.LABEL), + instantiationService.createInstance(DisableForWorkspaceAction, DisableForWorkspaceAction.LABEL) + ]); + groups.push([instantiationService.createInstance(UninstallAction)]); + groups.push([instantiationService.createInstance(ExtensionInfoAction)]); + super(ManageExtensionAction.ID, '', '', true, groups, true, instantiationService); this.tooltip = localize('manage', "Manage"); - this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, this.createMenuActionGroups(), true); - this.disposables.push(this._actionItem); - this.disposables.push(this.extensionsWorkbenchService.onChange(extension => { + this.disposables.push(extensionsWorkbenchService.onChange(extension => { if (extension && this.extension) { if (areSameExtensions(this.extension, extension)) { this.extension = extension; @@ -504,21 +578,7 @@ export class ManageExtensionAction extends Action { this.update(); } - private createMenuActionGroups(): IAction[][] { - const groups: IAction[][] = []; - groups.push([ - this.instantiationService.createInstance(EnableGloballyAction, EnableGloballyAction.LABEL), - this.instantiationService.createInstance(EnableForWorkspaceAction, EnableForWorkspaceAction.LABEL) - ]); - groups.push([ - this.instantiationService.createInstance(DisableGloballyAction, DisableGloballyAction.LABEL), - this.instantiationService.createInstance(DisableForWorkspaceAction, DisableForWorkspaceAction.LABEL) - ]); - groups.push([this.instantiationService.createInstance(UninstallAction)]); - return groups; - } - - private update(): void { + update(): void { this.class = ManageExtensionAction.HideManageExtensionClass; this.enabled = false; if (this.extension) { @@ -527,19 +587,94 @@ export class ManageExtensionAction extends Action { this.class = this.enabled || state === ExtensionState.Uninstalling ? ManageExtensionAction.Class : ManageExtensionAction.HideManageExtensionClass; this.tooltip = state === ExtensionState.Uninstalling ? localize('ManageExtensionAction.uninstallingTooltip', "Uninstalling") : ''; } - const menuActionGroups = this.createMenuActionGroups(); - for (const actions of menuActionGroups) { - for (const action of actions) { - (action).extension = this.extension; - } - } - this._actionItem.menuActionGroups = menuActionGroups; + } +} + +export class ExtensionInfoAction extends Action implements IExtensionAction { + static readonly ID = 'extensions.extensionInfo'; + static readonly LABEL = localize('extensionInfoAction', "Copy Extension information"); + + private _extension: IExtension; + + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; } + + constructor() { + super(ExtensionInfoAction.ID, ExtensionInfoAction.LABEL); } - public run(): Promise { - this._actionItem.showMenu(); + run(): Promise { + const { description, version, publisherDisplayName, id, displayName } = this.extension; + + const localizedExtension = localize('extensionInfoName', 'Name') + ': ' + displayName; + const localizedExtensionId = localize('extensionInfoId', 'Id') + ': ' + id; + const localizedDescription = localize('extensionInfoDescription', 'Description') + ': ' + description; + const localizedVersion = localize('extensionInfoVersion', 'Version') + ': ' + version; + const localizedPublisher = localize('extensionInfoPublisher', 'Publisher') + ': ' + publisherDisplayName; + const localizedVSMarketplaceLink = localize('extensionInfoVSMarketplaceLink', 'VS Marketplace Link') + ': https://marketplace.visualstudio.com/items?itemName=' + id; + + const clipboardStr = `${localizedExtension}\n${localizedExtensionId}\n${localizedDescription}\n${localizedVersion}\n${localizedPublisher}\n${localizedVSMarketplaceLink}`; + + clipboard.writeText(clipboardStr); return Promise.resolve(null); } +} + +export class CombinedEnableForWorkspaceAction extends Action implements IExtensionAction { + + static readonly ID = 'extensions.enableForWorkspace'; + static LABEL = localize('enableForWorkspaceAction', "Enable (Workspace)"); + + private disposables: IDisposable[] = []; + + private enableForWorkspaceAction: EnableForWorkspaceAction; + private installInRemoteServerAction: InstallInRemoteServerAction; + + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { + this._extension = extension; + this.enableForWorkspaceAction.extension = extension; + this.installInRemoteServerAction.extension = extension; + this.update(); + } + + constructor(label: string, + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(EnableForWorkspaceAction.ID, label); + + this.enableForWorkspaceAction = instantiationService.createInstance(EnableForWorkspaceAction); + this.installInRemoteServerAction = instantiationService.createInstance(InstallInRemoteServerAction); + const actions = [this.enableForWorkspaceAction, this.installInRemoteServerAction]; + + this.disposables.push(...actions); + for (const action of actions) { + action.onDidChange(e => { + if (!isUndefinedOrNull(e.enabled)) { + this.update(); + } + }, this, this.disposables); + } + + this.disposables.push(this.workspaceContextService.onDidChangeWorkbenchState(() => this.update())); + this.update(); + } + + private update(): void { + this.enabled = this.installInRemoteServerAction.enabled || this.enableForWorkspaceAction.enabled; + } + + run(): Promise { + if (this.installInRemoteServerAction.enabled) { + return this.installInRemoteServerAction.run(); + } + if (this.enableForWorkspaceAction.enabled) { + return this.enableForWorkspaceAction.run(); + } + return Promise.resolve(); + } dispose(): void { super.dispose(); @@ -558,12 +693,12 @@ export class EnableForWorkspaceAction extends Action implements IExtensionAction get extension(): IExtension { return this._extension; } set extension(extension: IExtension) { this._extension = extension; this.update(); } - constructor(label: string, + constructor( @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService ) { - super(EnableForWorkspaceAction.ID, label); + super(EnableForWorkspaceAction.ID, EnableForWorkspaceAction.LABEL); this.disposables.push(this.workspaceContextService.onDidChangeWorkbenchState(() => this.update())); this.update(); @@ -572,7 +707,7 @@ export class EnableForWorkspaceAction extends Action implements IExtensionAction private update(): void { this.enabled = false; if (this.extension) { - this.enabled = (this.extension.enablementState === EnablementState.Disabled || this.extension.enablementState === EnablementState.WorkspaceDisabled) && this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local); + this.enabled = this.extension.state === ExtensionState.Installed && (this.extension.enablementState === EnablementState.Disabled || this.extension.enablementState === EnablementState.WorkspaceDisabled) && this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local); } } @@ -586,6 +721,85 @@ export class EnableForWorkspaceAction extends Action implements IExtensionAction } } +export class InstallInRemoteServerAction extends Action implements IExtensionAction { + + static readonly ID = 'extensions.installInRemoteServerAction'; + static LABEL = localize('enableForWorkspaceAction', "Enable (Workspace)"); + + private disposables: IDisposable[] = []; + + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { this._extension = extension; this.update(); } + + private throttler: Throttler = new Throttler(); + + constructor( + @IExtensionsWorkbenchService extensionWorkbenchService: IExtensionsWorkbenchService, + @IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService, + @IConfigurationService private configurationService: IConfigurationService, + @IStorageService private storageService: IStorageService, + @ILabelService private labelService: ILabelService, + @IDialogService private dialogService: IDialogService, + @IExtensionGalleryService private extensionGalleryService: IExtensionGalleryService, + @IExtensionService private extensionService: IExtensionService + ) { + super(EnableForWorkspaceAction.ID, InstallInRemoteServerAction.LABEL); + this.disposables.push(extensionWorkbenchService.onChange(extension => this.update(extension))); + this.update(); + } + + private async update(extension?: IExtension) { + if (extension && this.extension && !areSameExtensions(this.extension, extension)) { + return; + } + this.enabled = false; + if (this.extension) { + if (this.extensionManagementServerService.remoteExtensionManagementServer + && this.extension && this.extension.locals && this.extension.locals.length > 0 + && !isUIExtension(this.extension.locals[0].manifest, this.configurationService) + && this.extension.state === ExtensionState.Installed) { + const installedInRemoteServer = this.extension.locals.some(local => { + const server = this.extensionManagementServerService.getExtensionManagementServer(local.location); + return server && server.authority === this.extensionManagementServerService.remoteExtensionManagementServer.authority; + }); + if (!installedInRemoteServer) { + const runningExtensions = await this.throttler.queue(() => this.extensionService.getExtensions()); + this.enabled = !runningExtensions.some(e => areSameExtensions({ id: e.id }, { id: this.extension.id })); + } + } + } + } + + async run(): Promise { + if (!this.enabled) { + return Promise.resolve(); + } + if (this.storageService.getBoolean('askToInstallRemoteServerExtension', StorageScope.GLOBAL, true)) { + const message = localize('install extension', "Enabling the '{0}' extension will also install it in {1}. Would you like to continue?", this.extension.displayName, this.labelService.getHostLabel() || this.extensionManagementServerService.remoteExtensionManagementServer.authority); + const response = await this.dialogService.confirm({ type: 'info', message, checkbox: { label: localize('do not ask me again', "Do not ask me again") } }); + if (!response || !response.confirmed) { + return Promise.resolve(); + } + if (response.checkboxChecked) { + this.storageService.store('askToInstallRemoteServerExtension', false, StorageScope.GLOBAL); + } + } + const galleryExtension = this.extension.gallery ? this.extension.gallery : await this.extensionGalleryService.getExtension(this.extension.local.galleryIdentifier); + if (galleryExtension) { + return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.installFromGallery(galleryExtension); + } else { + const zipLocation = await this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.zip(this.extension.local); + return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.unzip(zipLocation, this.extension.type); + } + } + + dispose(): void { + super.dispose(); + this.disposables = dispose(this.disposables); + } +} + export class EnableGloballyAction extends Action implements IExtensionAction { static readonly ID = 'extensions.enableGlobally'; @@ -599,7 +813,9 @@ export class EnableGloballyAction extends Action implements IExtensionAction { constructor(label: string, @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService, + @IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService, + @IConfigurationService private configurationService: IConfigurationService ) { super(EnableGloballyAction.ID, label); @@ -608,8 +824,16 @@ export class EnableGloballyAction extends Action implements IExtensionAction { private update(): void { this.enabled = false; - if (this.extension) { - this.enabled = (this.extension.enablementState === EnablementState.Disabled || this.extension.enablementState === EnablementState.WorkspaceDisabled) && this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local); + if (this.extension && this.extension.locals && this.extension.local) { + if (!isUIExtension(this.extension.local.manifest, this.configurationService) && this.extensionManagementServerService.remoteExtensionManagementServer) { + if (!this.extension.locals.some(local => { + const server = this.extensionManagementServerService.getExtensionManagementServer(local.location); + return server && server.authority === this.extensionManagementServerService.remoteExtensionManagementServer.authority; + })) { + return; + } + } + this.enabled = this.extension.state === ExtensionState.Installed && this.extension.enablementState === EnablementState.Disabled && this.extensionEnablementService.canChangeEnablement(this.extension.local); } } @@ -623,38 +847,23 @@ export class EnableGloballyAction extends Action implements IExtensionAction { } } -export class EnableAction extends Action { +export class EnableAction extends DropDownAction { static readonly ID = 'extensions.enable'; - private static readonly EnabledClass = 'extension-action prominent enable'; + private static readonly EnabledClass = 'extension-action enable'; + private static readonly EnabledDropDownClass = 'extension-action dropdown enable'; private static readonly DisabledClass = `${EnableAction.EnabledClass} disabled`; - private disposables: IDisposable[] = []; - - private _enableActions: IExtensionAction[]; - - private _actionItem: DropDownMenuActionItem; - get actionItem(): IActionItem { return this._actionItem; } - - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this.update(); } - - constructor( - @IInstantiationService private instantiationService: IInstantiationService, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService + @IInstantiationService instantiationService: IInstantiationService, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService ) { - super(EnableAction.ID, localize('enableAction', "Enable"), EnableAction.DisabledClass, false); - - this._enableActions = [ + super(EnableAction.ID, localize('enableAction', "Enable"), EnableAction.DisabledClass, false, [[ instantiationService.createInstance(EnableGloballyAction, EnableGloballyAction.LABEL), - instantiationService.createInstance(EnableForWorkspaceAction, EnableForWorkspaceAction.LABEL) - ]; - this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, [this._enableActions], false); - this.disposables.push(this._actionItem); + instantiationService.createInstance(CombinedEnableForWorkspaceAction, CombinedEnableForWorkspaceAction.LABEL) + ]], false, instantiationService); - this.disposables.push(this.extensionsWorkbenchService.onChange(extension => { + this.disposables.push(extensionsWorkbenchService.onChange(extension => { if (extension && this.extension) { if (areSameExtensions(this.extension, extension)) { this.extension = extension; @@ -666,33 +875,30 @@ export class EnableAction extends Action { this.update(); } - private update(): void { - for (const actions of this._actionItem.menuActionGroups) { - for (const action of actions) { - (action).extension = this.extension; + protected update(): void { + const enabledActions = this.getActions().filter(a => a.enabled); + this.enabled = enabledActions.length > 0; + if (this.enabled) { + if (enabledActions.length === 1) { + this.label = enabledActions[0].label; + this.class = EnableAction.EnabledClass; + } else { + this.class = EnableAction.EnabledDropDownClass; } - } - - if (!this.extension) { - this.enabled = false; + } else { this.class = EnableAction.DisabledClass; - return; } - - this.enabled = this.extension.state === ExtensionState.Installed && this._enableActions.some(e => e.enabled); - this.class = this.enabled ? EnableAction.EnabledClass : EnableAction.DisabledClass; } public run(): Promise { - this._actionItem.showMenu(); + const enabledActions = this.getActions().filter(a => a.enabled); + if (enabledActions.length === 1) { + enabledActions[0].run(); + } else { + return super.run(); + } return Promise.resolve(null); } - - dispose(): void { - super.dispose(); - this.disposables = dispose(this.disposables); - } - } export class DisableForWorkspaceAction extends Action implements IExtensionAction { @@ -706,10 +912,13 @@ export class DisableForWorkspaceAction extends Action implements IExtensionActio get extension(): IExtension { return this._extension; } set extension(extension: IExtension) { this._extension = extension; this.update(); } + private throttler: Throttler = new Throttler(); + constructor(label: string, @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService, + @IExtensionService private extensionService: IExtensionService ) { super(DisableForWorkspaceAction.ID, label); @@ -717,10 +926,11 @@ export class DisableForWorkspaceAction extends Action implements IExtensionActio this.workspaceContextService.onDidChangeWorkbenchState(() => this.update(), this, this.disposables); } - private update(): void { + private async update() { this.enabled = false; - if (this.extension && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY) { - this.enabled = (this.extension.enablementState === EnablementState.Enabled || this.extension.enablementState === EnablementState.WorkspaceEnabled) && this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local); + const runningExtensions = await this.throttler.queue(() => this.extensionService.getExtensions()); + if (this.extension && runningExtensions.some(e => areSameExtensions({ id: e.id }, { id: this.extension.id }) && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY)) { + this.enabled = this.extension.state === ExtensionState.Installed && (this.extension.enablementState === EnablementState.Enabled || this.extension.enablementState === EnablementState.WorkspaceEnabled) && this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local); } } @@ -745,19 +955,23 @@ export class DisableGloballyAction extends Action implements IExtensionAction { get extension(): IExtension { return this._extension; } set extension(extension: IExtension) { this._extension = extension; this.update(); } + private throttler: Throttler = new Throttler(); + constructor(label: string, @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService, + @IExtensionService private extensionService: IExtensionService ) { super(DisableGloballyAction.ID, label); this.update(); } - private update(): void { + private async update() { this.enabled = false; - if (this.extension) { - this.enabled = (this.extension.enablementState === EnablementState.Enabled || this.extension.enablementState === EnablementState.WorkspaceEnabled) && this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local); + const runningExtensions = await this.throttler.queue(() => this.extensionService.getExtensions()); + if (this.extension && runningExtensions.some(e => areSameExtensions({ id: e.id }, { id: this.extension.id }))) { + this.enabled = this.extension.state === ExtensionState.Installed && (this.extension.enablementState === EnablementState.Enabled || this.extension.enablementState === EnablementState.WorkspaceEnabled) && this.extension.local && this.extensionEnablementService.canChangeEnablement(this.extension.local); } } @@ -771,36 +985,24 @@ export class DisableGloballyAction extends Action implements IExtensionAction { } } -export class DisableAction extends Action { +export class DisableAction extends DropDownAction { static readonly ID = 'extensions.disable'; private static readonly EnabledClass = 'extension-action disable'; + private static readonly EnabledDropDownClass = 'extension-action dropdown enable'; private static readonly DisabledClass = `${DisableAction.EnabledClass} disabled`; - private disposables: IDisposable[] = []; - private _disableActions: IExtensionAction[]; - private _actionItem: DropDownMenuActionItem; - get actionItem(): IActionItem { return this._actionItem; } - - private _extension: IExtension; - get extension(): IExtension { return this._extension; } - set extension(extension: IExtension) { this._extension = extension; this.update(); } - - constructor( - @IInstantiationService private instantiationService: IInstantiationService, - @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, + @IInstantiationService instantiationService: IInstantiationService, + @IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService, ) { - super(DisableAction.ID, localize('disableAction', "Disable"), DisableAction.DisabledClass, false); - this._disableActions = [ + super(DisableAction.ID, localize('disableAction', "Disable"), DisableAction.DisabledClass, false, [[ instantiationService.createInstance(DisableGloballyAction, DisableGloballyAction.LABEL), instantiationService.createInstance(DisableForWorkspaceAction, DisableForWorkspaceAction.LABEL) - ]; - this._actionItem = this.instantiationService.createInstance(DropDownMenuActionItem, this, [this._disableActions], false); - this.disposables.push(this._actionItem); + ]], false, instantiationService); - this.disposables.push(this.extensionsWorkbenchService.onChange(extension => { + this.disposables.push(extensionsWorkbenchService.onChange(extension => { if (extension && this.extension) { if (areSameExtensions(this.extension, extension)) { this.extension = extension; @@ -812,25 +1014,28 @@ export class DisableAction extends Action { this.update(); } - private update(): void { - for (const actions of this._actionItem.menuActionGroups) { - for (const action of actions) { - (action).extension = this.extension; + protected update(): void { + const enabledActions = this.getActions().filter(a => a.enabled); + this.enabled = enabledActions.length > 0; + if (this.enabled) { + if (enabledActions.length === 1) { + this.label = enabledActions[0].label; + this.class = DisableAction.EnabledClass; + } else { + this.class = DisableAction.EnabledDropDownClass; } - } - - if (!this.extension) { - this.enabled = false; + } else { this.class = DisableAction.DisabledClass; - return; } - - this.enabled = this.extension.state === ExtensionState.Installed && this._disableActions.some(a => a.enabled); - this.class = this.enabled ? DisableAction.EnabledClass : DisableAction.DisabledClass; } public run(): Promise { - this._actionItem.showMenu(); + const enabledActions = this.getActions().filter(a => a.enabled); + if (enabledActions.length === 1) { + enabledActions[0].run(); + } else { + return super.run(); + } return Promise.resolve(null); } @@ -984,7 +1189,7 @@ export class UpdateAllAction extends Action { console.error(err); - promptDownloadManually(extension.gallery, localize('failedToUpdate', "Failed to update \'{0}\'.", extension.id), this.instantiationService, this.notificationService, this.openerService); + return promptDownloadManually(extension.gallery, localize('failedToUpdate', "Failed to update \'{0}\'.", extension.id), err, this.instantiationService, this.notificationService, this.openerService); }); } @@ -1012,7 +1217,8 @@ export class ReloadAction extends Action { @IWindowService private windowService: IWindowService, @IExtensionService private extensionService: IExtensionService, @IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService, - @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService + @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService, + @IConfigurationService private configurationService: IConfigurationService ) { super('extensions.reload', localize('reloadAction', "Reload"), ReloadAction.DisabledClass, false); this.throttler = new Throttler(); @@ -1074,14 +1280,30 @@ export class ReloadAction extends Action { return; } } else { - const extensionServer = this.extensionManagementServerService.getExtensionManagementServer(installed.local.location); - const localServer = this.extensionManagementServerService.getLocalExtensionManagementServer(); - // Only extension from local server requires reload if it is not running on the server - if (extensionServer && extensionServer.authority === localServer.authority && !isDisabled) { - // Requires reload to enable the extension - this.enabled = true; - this.tooltip = localize('postEnableTooltip', "Reload to Activate"); - return; + const uiExtension = isUIExtension(installed.local.manifest, this.configurationService); + if (!isDisabled) { + if (this.extensionManagementServerService.remoteExtensionManagementServer) { + if (uiExtension) { + // Only UI extension from local server requires reload if it is not running on the server + if (installed.locals.some(local => this.extensionManagementServerService.getExtensionManagementServer(local.location).authority === this.extensionManagementServerService.localExtensionManagementServer.authority)) { + // Requires reload to enable the extension + this.enabled = true; + this.tooltip = localize('postEnableTooltip', "Reload to Activate"); + return; + } + } else { + if (installed.locals.some(local => this.extensionManagementServerService.getExtensionManagementServer(local.location).authority === this.extensionManagementServerService.remoteExtensionManagementServer.authority)) { + // Requires reload to enable the extension + this.enabled = true; + this.tooltip = localize('postEnableTooltip', "Reload to Activate"); + return; + } + } + } else { + this.enabled = true; + this.tooltip = localize('postEnableTooltip', "Reload to Activate"); + return; + } } } return; @@ -1355,7 +1577,7 @@ export class InstallWorkspaceRecommendedExtensionsAction extends Action { installPromises.push(model.resolve(i, CancellationToken.None).then(e => { return this.extensionWorkbenchService.install(e).then(null, err => { console.error(err); - promptDownloadManually(e.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", e.id), this.instantiationService, this.notificationService, this.openerService); + return promptDownloadManually(e.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", e.id), err, this.instantiationService, this.notificationService, this.openerService); }); })); } @@ -1397,7 +1619,7 @@ export class InstallRecommendedExtensionAction extends Action { return this.extensionWorkbenchService.install(extension) .then(() => null, err => { console.error(err); - promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.id), this.instantiationService, this.notificationService, this.openerService); + return promptDownloadManually(extension.gallery, localize('failedToInstall', "Failed to install \'{0}\'.", extension.id), err, this.instantiationService, this.notificationService, this.openerService); }); } return null; @@ -2165,7 +2387,10 @@ export class DisabledStatusLabelAction extends Action { constructor( @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - @IExtensionService private extensionService: IExtensionService + @IExtensionService private extensionService: IExtensionService, + @IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService, + @IConfigurationService private configurationService: IConfigurationService, + @ILabelService private labelService: ILabelService ) { super('extensions.install', localize('disabled', "Disabled"), `${DisabledStatusLabelAction.Class} hide`, false); this.disposables.push(this.extensionsWorkbenchService.onChange((extension) => this.update(extension))); @@ -2180,10 +2405,24 @@ export class DisabledStatusLabelAction extends Action { .then(runningExtensions => { this.class = `${DisabledStatusLabelAction.Class} hide`; this.tooltip = ''; - if (this.extension && !this.extension.isMalicious && !runningExtensions.some(e => e.id === this.extension.id)) { - if (this.extension.enablementState === EnablementState.Disabled || this.extension.enablementState === EnablementState.WorkspaceDisabled) { + if (this.extension && this.extension.local && !this.extension.isMalicious && !runningExtensions.some(e => e.id === this.extension.id)) { + if (this.extensionManagementServerService.remoteExtensionManagementServer && !isUIExtension(this.extension.local.manifest, this.configurationService)) { + const installedInRemoteServer = this.extension.locals.some(local => { + const server = this.extensionManagementServerService.getExtensionManagementServer(local.location); + return server && server.authority === this.extensionManagementServerService.remoteExtensionManagementServer.authority; + }); + if (!installedInRemoteServer) { + this.class = `${DisabledStatusLabelAction.Class}`; + this.label = localize('disabled NonUI Extension', "Disabled for this Workspace because it is not installed in {0}.", this.labelService.getHostLabel() || this.extensionManagementServerService.remoteExtensionManagementServer.authority); + return; + } + } + if (this.extension.enablementState === EnablementState.Disabled) { + this.class = `${DisabledStatusLabelAction.Class}`; + this.label = localize('disabled globally', "Disabled for all Windows."); + } else if (this.extension.enablementState === EnablementState.WorkspaceDisabled) { this.class = `${DisabledStatusLabelAction.Class}`; - this.tooltip = this.extension.enablementState === EnablementState.Disabled ? localize('disabled globally', "Disabled") : localize('disabled workspace', "Disabled for this Workspace"); + this.label = localize('disabled workspace', "Disabled for this Workspace."); } } })); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActivationProgress.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActivationProgress.ts index 2e4226892..644b4523b 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActivationProgress.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActivationProgress.ts @@ -8,6 +8,8 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IProgressService2, ProgressLocation } from 'vs/platform/progress/common/progress'; import { localize } from 'vs/nls'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { timeout } from 'vs/base/common/async'; +import { ILogService } from 'vs/platform/log/common/log'; export class ExtensionActivationProgress implements IWorkbenchContribution { @@ -16,6 +18,7 @@ export class ExtensionActivationProgress implements IWorkbenchContribution { constructor( @IExtensionService extensionService: IExtensionService, @IProgressService2 progressService: IProgressService2, + @ILogService logService: ILogService, ) { const options = { @@ -24,7 +27,8 @@ export class ExtensionActivationProgress implements IWorkbenchContribution { }; this._listener = extensionService.onWillActivateByEvent(e => { - progressService.withProgress(options, _ => e.activation); + logService.trace('onWillActivateByEvent: ', e.event); + progressService.withProgress(options, _ => Promise.race([e.activation, timeout(5000)])); }); } diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsAutoProfiler.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsAutoProfiler.ts new file mode 100644 index 000000000..d400dfb34 --- /dev/null +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsAutoProfiler.ts @@ -0,0 +1,122 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { IExtensionService, IResponsiveStateChangeEvent, ICpuProfilerTarget, IExtensionHostProfile, ProfileSession } from 'vs/workbench/services/extensions/common/extensions'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ILogService } from 'vs/platform/log/common/log'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { onUnexpectedError } from 'vs/base/common/errors'; + +export class ExtensionsAutoProfiler extends Disposable implements IWorkbenchContribution { + + private readonly _session = new Map(); + + constructor( + @IExtensionService extensionService: IExtensionService, + @ITelemetryService private readonly _telemetryService: ITelemetryService, + @ILogService private readonly _logService: ILogService, + ) { + super(); + this._register(extensionService.onDidChangeResponsiveChange(this._onDidChangeResponsiveChange, this)); + } + + private async _onDidChangeResponsiveChange(event: IResponsiveStateChangeEvent): Promise { + const { target } = event; + + if (!target.canProfileExtensionHost()) { + return; + } + + if (event.isResponsive && this._session.has(target)) { + // stop profiling when responsive again + this._session.get(target).cancel(); + + } else if (!event.isResponsive && !this._session.has(target)) { + // start profiling if not yet profiling + const token = new CancellationTokenSource(); + this._session.set(target, token); + + let session: ProfileSession; + try { + session = await target.startExtensionHostProfile(); + } catch (err) { + this._session.delete(target); + // fail silent as this is often + // caused by another party being + // connected already + return; + } + + // wait 5 seconds or until responsive again + await new Promise(resolve => { + token.token.onCancellationRequested(resolve); + setTimeout(resolve, 5e3); + }); + + try { + // stop profiling and analyse results + this._processCpuProfile(await session.stop()); + } catch (err) { + onUnexpectedError(err); + } finally { + this._session.delete(target); + } + } + } + + private _processCpuProfile(profile: IExtensionHostProfile) { + + interface NamedSlice { + id: string; + total: number; + percentage: number; + } + + let data: NamedSlice[] = []; + for (let i = 0; i < profile.ids.length; i++) { + let id = profile.ids[i]; + let total = profile.deltas[i]; + data.push({ id, total, percentage: 0 }); + } + + // merge data by identifier + let anchor = 0; + data.sort((a, b) => a.id.localeCompare(b.id)); + for (let i = 1; i < data.length; i++) { + if (data[anchor].id === data[i].id) { + data[anchor].total += data[i].total; + } else { + anchor += 1; + data[anchor] = data[i]; + } + } + data = data.slice(0, anchor + 1); + + const duration = profile.endTime - profile.startTime; + const percentage = duration / 100; + let top: NamedSlice | undefined; + for (const slice of data) { + slice.percentage = Math.round(slice.total / percentage); + if (!top || top.percentage < slice.percentage) { + top = slice; + } + } + + this._logService.warn(`UNRESPONSIVE extension host, '${top ? top.id : 'unknown'}' took ${top ? top.percentage : 'unknown'}% of ${duration / 1e3}ms`, data); + + /* __GDPR__ + "exthostunresponsive" : { + "duration" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "data": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } + } + */ + this._telemetryService.publicLog('exthostunresponsive', { + duration, + data + }); + } +} diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts index 2f4900028..23d7c17bf 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts @@ -14,7 +14,7 @@ import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging'; import { once, Emitter, Event } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { IExtension, IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions'; -import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, extensionButtonProminentBackground, extensionButtonProminentForeground, MaliciousStatusLabelAction, DisabledStatusLabelAction, ExtensionActionItem } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; +import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, extensionButtonProminentBackground, extensionButtonProminentForeground, MaliciousStatusLabelAction, ExtensionActionItem } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { Label, RatingsWidget, InstallCountWidget } from 'vs/workbench/parts/extensions/browser/extensionsWidgets'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -91,7 +91,7 @@ export class Renderer implements IPagedRenderer { animated: false, actionItemProvider: (action: Action) => { if (action.id === ManageExtensionAction.ID) { - return (action).actionItem; + return (action).createActionItem(); } return new ExtensionActionItem(null, action, actionOptions); } @@ -103,14 +103,13 @@ export class Renderer implements IPagedRenderer { const ratingsWidget = this.instantiationService.createInstance(RatingsWidget, ratings, { small: true }); const maliciousStatusAction = this.instantiationService.createInstance(MaliciousStatusLabelAction, false); - const disabledStatusAction = this.instantiationService.createInstance(DisabledStatusLabelAction); const installAction = this.instantiationService.createInstance(InstallAction); const updateAction = this.instantiationService.createInstance(UpdateAction); const reloadAction = this.instantiationService.createInstance(ReloadAction, false); const manageAction = this.instantiationService.createInstance(ManageExtensionAction); - actionbar.push([updateAction, reloadAction, installAction, disabledStatusAction, maliciousStatusAction, manageAction], actionOptions); - const disposables = [versionWidget, installCountWidget, ratingsWidget, maliciousStatusAction, disabledStatusAction, updateAction, installAction, reloadAction, manageAction, actionbar, bookmarkStyler]; + actionbar.push([updateAction, reloadAction, installAction, maliciousStatusAction, manageAction], actionOptions); + const disposables = [versionWidget, installCountWidget, ratingsWidget, maliciousStatusAction, updateAction, installAction, reloadAction, manageAction, actionbar, bookmarkStyler]; return { root, element, icon, name, installCount, ratings, author, description, disposables, actionbar, @@ -120,7 +119,6 @@ export class Renderer implements IPagedRenderer { installCountWidget.extension = extension; ratingsWidget.extension = extension; maliciousStatusAction.extension = extension; - disabledStatusAction.extension = extension; installAction.extension = extension; updateAction.extension = extension; reloadAction.extension = extension; @@ -152,7 +150,16 @@ export class Renderer implements IPagedRenderer { this.extensionService.getExtensions().then(runningExtensions => { if (installed && installed.local) { const installedExtensionServer = this.extensionManagementServerService.getExtensionManagementServer(installed.local.location); - const isSameExtensionRunning = runningExtensions.some(e => areSameExtensions(e, extension) && installedExtensionServer.authority === this.extensionManagementServerService.getExtensionManagementServer(e.extensionLocation).authority); + const isSameExtensionRunning = runningExtensions.some(e => { + if (!areSameExtensions(e, extension)) { + return false; + } + const runningExtensionServer = this.extensionManagementServerService.getExtensionManagementServer(e.extensionLocation); + if (!installedExtensionServer || !runningExtensionServer) { + return false; + } + return installedExtensionServer.authority === runningExtensionServer.authority; + }); toggleClass(data.root, 'disabled', !isSameExtensionRunning); } else { removeClass(data.root, 'disabled'); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts index c89f7c835..9ea9c72fb 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts @@ -16,6 +16,7 @@ import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiati import { areSameExtensions, adoptToGalleryExtensionId, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { getIdAndVersionFromLocalExtensionId } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; import { Severity, INotificationService } from 'vs/platform/notification/common/notification'; +import product from 'vs/platform/node/product'; export interface IExtensionStatus { identifier: IExtensionIdentifier; @@ -142,3 +143,8 @@ export function isKeymapExtension(tipsService: IExtensionTipsService, extension: function stripVersion(id: string): string { return getIdAndVersionFromLocalExtensionId(id).id; } + +export function getKeywordsForExtension(extension: string): string[] { + const keywords = product.extensionKeywords || {}; + return keywords[extension] || []; +} \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index a54f1666b..0b93655ce 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -55,6 +55,7 @@ import { Query } from 'vs/workbench/parts/extensions/common/extensionQuery'; import { SuggestEnabledInput, attachSuggestEnabledInputBoxStyler } from 'vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; interface SearchInputEvent extends Event { target: HTMLInputElement; @@ -102,15 +103,15 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push(this.createOtherRecommendedExtensionsListViewDescriptor()); viewDescriptors.push(this.createWorkspaceRecommendedExtensionsListViewDescriptor()); - if (this.extensionManagementServerService.extensionManagementServers.length > 1) { - for (const extensionManagementServer of this.extensionManagementServerService.extensionManagementServers) { - viewDescriptors.push(...this.createExtensionsViewDescriptorsForServer(extensionManagementServer)); - } + if (this.extensionManagementServerService.remoteExtensionManagementServer) { + viewDescriptors.push(...this.createExtensionsViewDescriptorsForServer(this.extensionManagementServerService.localExtensionManagementServer)); + viewDescriptors.push(...this.createExtensionsViewDescriptorsForServer(this.extensionManagementServerService.remoteExtensionManagementServer)); } ViewsRegistry.registerViews(viewDescriptors); } + // View used for any kind of searching private createMarketPlaceExtensionsListViewDescriptor(): IViewDescriptor { const id = 'extensions.listView'; return { @@ -123,6 +124,8 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio }; } + // Separate view for enabled extensions required as we need to show enabled, disabled and recommended sections + // in the default view when there is no search text, but user has installed extensions. private createEnabledExtensionsListViewDescriptor(): IViewDescriptor { const id = 'extensions.enabledExtensionList'; return { @@ -137,6 +140,8 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio }; } + // Separate view for disabled extensions required as we need to show enabled, disabled and recommended sections + // in the default view when there is no search text, but user has installed extensions. private createDisabledExtensionsListViewDescriptor(): IViewDescriptor { const id = 'extensions.disabledExtensionList'; return { @@ -152,6 +157,8 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio }; } + // Separate view for popular extensions required as we need to show popular and recommended sections + // in the default view when there is no search text, and user has no installed extensions. private createPopularExtensionsListViewDescriptor(): IViewDescriptor { const id = 'extensions.popularExtensionsList'; return { @@ -176,6 +183,9 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio }]; } + // Separate view for recommended extensions required as we need to show it along with other views when there is no search text. + // When user has installed extensions, this is shown along with the views for enabled & disabled extensions + // When user has no installed extensions, this is shown along with the view for popular extensions private createDefaultRecommendedExtensionsListViewDescriptor(): IViewDescriptor { const id = 'extensions.recommendedList'; return { @@ -190,6 +200,8 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio }; } + // Separate view for recommedations that are not workspace recommendations. + // Shown along with view for workspace recommendations, when using the command that shows recommendations private createOtherRecommendedExtensionsListViewDescriptor(): IViewDescriptor { const id = 'extensions.otherrecommendedList'; return { @@ -204,6 +216,8 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio }; } + // Separate view for workspace recommendations. + // Shown along with view for other recommendations, when using the command that shows recommendations private createWorkspaceRecommendedExtensionsListViewDescriptor(): IViewDescriptor { const id = 'extensions.workspaceRecommendedList'; return { @@ -361,15 +375,14 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio super.create(this.extensionsBox); } - setVisible(visible: boolean): Promise { + setVisible(visible: boolean): void { const isVisibilityChanged = this.isVisible() !== visible; - return super.setVisible(visible).then(() => { - if (isVisibilityChanged) { - if (visible) { - this.searchBox.focus(); - } + super.setVisible(visible); + if (isVisibilityChanged) { + if (visible) { + this.searchBox.focus(); } - }); + } } focus(): void { @@ -416,7 +429,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.rating', localize('sort by rating', "Sort By: Rating"), this.onSearchChange, 'rating'), this.instantiationService.createInstance(ChangeSortAction, 'extensions.sort.name', localize('sort by name', "Sort By: Name"), this.onSearchChange, 'name'), new Separator(), - ...(this.extensionManagementServerService.extensionManagementServers.length > 1 ? [this.groupByServerAction, new Separator()] : []), + ...(this.extensionManagementServerService.remoteExtensionManagementServer ? [this.groupByServerAction, new Separator()] : []), this.instantiationService.createInstance(CheckForUpdatesAction, CheckForUpdatesAction.ID, CheckForUpdatesAction.LABEL), ...(this.configurationService.getValue(AutoUpdateConfigurationKey) ? [this.instantiationService.createInstance(DisableAutoUpdateAction, DisableAutoUpdateAction.ID, DisableAutoUpdateAction.LABEL)] : [this.instantiationService.createInstance(UpdateAllAction, UpdateAllAction.ID, UpdateAllAction.LABEL), this.instantiationService.createInstance(EnableAutoUpdateAction, EnableAutoUpdateAction.ID, EnableAutoUpdateAction.LABEL)]), this.instantiationService.createInstance(InstallVSIXAction, InstallVSIXAction.ID, InstallVSIXAction.LABEL), @@ -494,8 +507,10 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio } protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewletPanel { - for (const extensionManagementServer of this.extensionManagementServerService.extensionManagementServers) { - if (viewDescriptor.id === `server.extensionsList.${extensionManagementServer.authority}`) { + if (this.extensionManagementServerService.remoteExtensionManagementServer) { + const extensionManagementServer = viewDescriptor.id === `server.extensionsList.${this.extensionManagementServerService.localExtensionManagementServer.authority}` ? this.extensionManagementServerService.localExtensionManagementServer + : viewDescriptor.id === `server.extensionsList.${this.extensionManagementServerService.remoteExtensionManagementServer.authority}` ? this.extensionManagementServerService.remoteExtensionManagementServer : null; + if (extensionManagementServer) { const servicesCollection: ServiceCollection = new ServiceCollection(); servicesCollection.set(IExtensionManagementServerService, new SingleServerExtensionManagementServerService(extensionManagementServer)); servicesCollection.set(IExtensionManagementService, extensionManagementServer.extensionManagementService); @@ -608,9 +623,12 @@ export class MaliciousExtensionChecker implements IWorkbenchContribution { @IExtensionManagementService private extensionsManagementService: IExtensionManagementService, @IWindowService private windowService: IWindowService, @ILogService private logService: ILogService, - @INotificationService private notificationService: INotificationService + @INotificationService private notificationService: INotificationService, + @IEnvironmentService private environmentService: IEnvironmentService ) { - this.loopCheckForMaliciousExtensions(); + if (!this.environmentService.disableExtensions) { + this.loopCheckForMaliciousExtensions(); + } } private loopCheckForMaliciousExtensions(): void { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index 615297a92..d90d6bacc 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -9,7 +9,7 @@ import { assign } from 'vs/base/common/objects'; import { chain, Emitter } from 'vs/base/common/event'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { PagedModel, IPagedModel, IPager, DelayedPagedModel } from 'vs/base/common/paging'; -import { SortBy, SortOrder, IQueryOptions, LocalExtensionType, IExtensionTipsService, EnablementState, IExtensionRecommendation } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { SortBy, SortOrder, IQueryOptions, LocalExtensionType, IExtensionTipsService, IExtensionRecommendation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -40,6 +40,7 @@ import { alert } from 'vs/base/browser/ui/aria/aria'; import { IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { getKeywordsForExtension } from 'vs/workbench/parts/extensions/electron-browser/extensionsUtils'; export class ExtensionsListView extends ViewletPanel { @@ -48,7 +49,6 @@ export class ExtensionsListView extends ViewletPanel { private badge: CountBadge; protected badgeContainer: HTMLElement; private list: WorkbenchPagedList; - private searchExperiments: IExperiment[] = []; constructor( private options: IViewletViewOptions, @@ -68,7 +68,6 @@ export class ExtensionsListView extends ViewletPanel { @IExperimentService private experimentService: IExperimentService ) { super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService); - this.experimentService.getExperimentsByType(ExperimentActionType.ExtensionSearchResults).then(result => this.searchExperiments = result); } protected renderHeader(container: HTMLElement): void { @@ -125,9 +124,6 @@ export class ExtensionsListView extends ViewletPanel { case 'rating': options = assign(options, { sortBy: SortBy.WeightedRating }); break; case 'name': options = assign(options, { sortBy: SortBy.Title }); break; } - if (!query || !query.trim()) { - options.sortBy = SortBy.InstallCount; - } const successCallback = model => { this.setModel(model); @@ -171,7 +167,7 @@ export class ExtensionsListView extends ViewletPanel { if (manageExtensionAction.enabled) { this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, - getActions: () => Promise.resolve(manageExtensionAction.actionItem.getActions()) + getActions: () => manageExtensionAction.createActionItem().getActions() }); } } @@ -286,15 +282,14 @@ export class ExtensionsListView extends ViewletPanel { if (/@enabled/i.test(value)) { value = value ? value.replace(/@enabled/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase() : ''; - const local = await this.extensionsWorkbenchService.queryLocal(); + const local = (await this.extensionsWorkbenchService.queryLocal()).filter(e => e.type === LocalExtensionType.User); + const runningExtensions = await this.extensionService.getExtensions(); - let result = local + const result = local .sort((e1, e2) => e1.displayName.localeCompare(e2.displayName)) - .filter(e => e.type === LocalExtensionType.User && - (e.enablementState === EnablementState.Enabled || e.enablementState === EnablementState.WorkspaceEnabled) && - (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1) && - (!categories.length || categories.some(category => (e.local.manifest.categories || []).some(c => c.toLowerCase() === category))) - ); + .filter(e => runningExtensions.some(r => areSameExtensions(r, e)) + && (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1) + && (!categories.length || categories.some(category => (e.local.manifest.categories || []).some(c => c.toLowerCase() === category)))); return this.getPagedModel(this.sortExtensions(result, options)); } @@ -303,6 +298,11 @@ export class ExtensionsListView extends ViewletPanel { } private async queryGallery(query: Query, options: IQueryOptions): Promise> { + const hasUserDefinedSortOrder = options.sortBy !== undefined; + if (!hasUserDefinedSortOrder && !query.value.trim()) { + options.sortBy = SortBy.InstallCount; + } + let value = query.value; const idRegex = /@id:(([a-z0-9A-Z][a-z0-9\-A-Z]*)\.([a-z0-9A-Z][a-z0-9\-A-Z]*))/g; @@ -339,7 +339,7 @@ export class ExtensionsListView extends ViewletPanel { text = query.value.replace(extensionRegex, (m, ext) => { // Get curated keywords - const keywords = this.tipsService.getKeywordsForExtension(ext); + const keywords = getKeywordsForExtension(ext); // Get mode name const modeId = this.modeService.getModeIdByFilepathOrFirstLine(`.${ext}`); @@ -359,11 +359,14 @@ export class ExtensionsListView extends ViewletPanel { let preferredResults: string[] = []; if (text) { options = assign(options, { text: text.substr(0, 350), source: 'searchText' }); - for (let i = 0; i < this.searchExperiments.length; i++) { - if (text.toLowerCase() === this.searchExperiments[i].action.properties['searchText'] && Array.isArray(this.searchExperiments[i].action.properties['preferredResults'])) { - preferredResults = this.searchExperiments[i].action.properties['preferredResults']; - options.source += `-experiment-${this.searchExperiments[i].id}`; - break; + if (!hasUserDefinedSortOrder) { + const searchExperiments = await this.getSearchExperiments(); + for (let i = 0; i < searchExperiments.length; i++) { + if (text.toLowerCase() === searchExperiments[i].action.properties['searchText'] && Array.isArray(searchExperiments[i].action.properties['preferredResults'])) { + preferredResults = searchExperiments[i].action.properties['preferredResults']; + options.source += `-experiment-${searchExperiments[i].id}`; + break; + } } } } else { @@ -389,6 +392,14 @@ export class ExtensionsListView extends ViewletPanel { } + private _searchExperiments: Thenable; + private getSearchExperiments(): Thenable { + if (!this._searchExperiments) { + this._searchExperiments = this.experimentService.getExperimentsByType(ExperimentActionType.ExtensionSearchResults); + } + return this._searchExperiments; + } + private sortExtensions(extensions: IExtension[], options: IQueryOptions): IExtension[] { switch (options.sortBy) { case SortBy.InstallCount: @@ -408,6 +419,7 @@ export class ExtensionsListView extends ViewletPanel { return extensions; } + // Get All types of recommendations, trimmed to show a max of 8 at any given time private getAllRecommendationsModel(query: Query, options: IQueryOptions): Promise> { const value = query.value.replace(/@recommended:all/g, '').replace(/@recommended/g, '').trim().toLowerCase(); @@ -462,6 +474,7 @@ export class ExtensionsListView extends ViewletPanel { return new PagedModel([]); } + // Get All types of recommendations other than Workspace recommendations, trimmed to show a max of 8 at any given time private getRecommendationsModel(query: Query, options: IQueryOptions): Promise> { const value = query.value.replace(/@recommended/g, '').trim().toLowerCase(); @@ -576,7 +589,7 @@ export class ExtensionsListView extends ViewletPanel { .then(result => this.getPagedModel(result)); } - // Sorts the firsPage of the pager in the same order as given array of extension ids + // Sorts the firstPage of the pager in the same order as given array of extension ids private sortFirstPage(pager: IPager, ids: string[]) { ids = ids.map(x => x.toLowerCase()); pager.firstPage.sort((a, b) => { @@ -850,4 +863,4 @@ export class WorkspaceRecommendedExtensionsView extends ExtensionsListView { return this.tipsService.getWorkspaceRecommendations() .then(recommendations => recommendations.filter(({ extensionId }) => !this.extensionsWorkbenchService.local.some(i => areSameExtensions({ id: extensionId }, { id: i.id })))); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/defaultIcon.png b/src/vs/workbench/parts/extensions/electron-browser/media/defaultIcon.png index 2f36a9434..adad56268 100644 Binary files a/src/vs/workbench/parts/extensions/electron-browser/media/defaultIcon.png and b/src/vs/workbench/parts/extensions/electron-browser/media/defaultIcon.png differ diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css index 89c71289a..12f1b6f98 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css @@ -20,8 +20,8 @@ .monaco-action-bar .action-item .action-label.extension-action.multiserver.install:after, .monaco-action-bar .action-item .action-label.extension-action.multiserver.update:after, -.monaco-action-bar .action-item .action-label.extension-action.enable:after, -.monaco-action-bar .action-item .action-label.extension-action.disable:after { +.monaco-action-bar .action-item .action-label.extension-action.enable.dropdown:after, +.monaco-action-bar .action-item .action-label.extension-action.disable.dropdown:after { content: '▼'; padding-left: 2px; font-size: 80%; @@ -54,6 +54,11 @@ font-weight: normal; } +.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.disable-status:hover, +.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.malicious-status:hover { + opacity: 0.9; +} + .extensions-viewlet>.extensions .extension>.details>.footer>.monaco-action-bar .action-item .action-label.extension-action.manage.hide { display: none; } diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/theme-icon.png b/src/vs/workbench/parts/extensions/electron-browser/media/theme-icon.png index 8e903139c..1517cc520 100644 Binary files a/src/vs/workbench/parts/extensions/electron-browser/media/theme-icon.png and b/src/vs/workbench/parts/extensions/electron-browser/media/theme-icon.png differ diff --git a/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts index 7084c8925..9f9509d28 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -29,7 +29,7 @@ import { IWindowService, IWindowsService } from 'vs/platform/windows/common/wind import { writeFile } from 'vs/base/node/pfs'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { memoize } from 'vs/base/common/decorators'; -import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; import { Event } from 'vs/base/common/event'; import { DisableForWorkspaceAction, DisableGloballyAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -39,6 +39,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { randomPort } from 'vs/base/node/ports'; import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; export const IExtensionHostProfileService = createDecorator('extensionHostProfileService'); export const CONTEXT_PROFILE_SESSION_STATE = new RawContextKey('profileSessionState', 'none'); @@ -111,7 +112,8 @@ export class RuntimeExtensionsEditor extends BaseEditor { @IContextMenuService private readonly _contextMenuService: IContextMenuService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IExtensionHostProfileService private readonly _extensionHostProfileService: IExtensionHostProfileService, - @IStorageService storageService: IStorageService + @IStorageService storageService: IStorageService, + @IRemoteAuthorityResolverService private remoteAuthorityResolverService: IRemoteAuthorityResolverService ) { super(RuntimeExtensionsEditor.ID, telemetryService, themeService, storageService); @@ -254,6 +256,9 @@ export class RuntimeExtensionsEditor extends BaseEditor { msgIcon: HTMLElement; msgLabel: HTMLElement; + msgIcon2: HTMLElement; + msgLabel2: HTMLElement; + actionbar: ActionBar; disposables: IDisposable[]; elementDisposables: IDisposable[]; @@ -271,6 +276,9 @@ export class RuntimeExtensionsEditor extends BaseEditor { const msgIcon = append(msgContainer, $('.')); const msgLabel = append(msgContainer, $('span.msg-label')); + const msgIcon2 = append(msgContainer, $('.')); + const msgLabel2 = append(msgContainer, $('span.msg-label')); + const timeContainer = append(element, $('.time')); const activationTime = append(timeContainer, $('div.activation-time')); const profileTime = append(timeContainer, $('div.profile-time')); @@ -295,6 +303,8 @@ export class RuntimeExtensionsEditor extends BaseEditor { profileTimeline, msgIcon, msgLabel, + msgIcon2, + msgLabel2, disposables, elementDisposables: [] }; @@ -354,7 +364,7 @@ export class RuntimeExtensionsEditor extends BaseEditor { }, "Activated on {0}", activationTimes.activationEvent); } data.activationTime.title = title; - if (!isFalsyOrEmpty(element.status.runtimeErrors)) { + if (isNonEmptyArray(element.status.runtimeErrors)) { data.msgIcon.className = 'octicon octicon-bug'; data.msgLabel.textContent = nls.localize('errors', "{0} uncaught errors", element.status.runtimeErrors.length); } else if (element.status.messages && element.status.messages.length > 0) { @@ -365,6 +375,19 @@ export class RuntimeExtensionsEditor extends BaseEditor { data.msgLabel.textContent = ''; } + if (element.description.extensionLocation.scheme !== 'file') { + data.msgIcon2.className = 'octicon octicon-rss'; + data.msgLabel2.textContent = element.description.extensionLocation.authority; + this.remoteAuthorityResolverService.getRemoteAuthorityResolver(element.description.extensionLocation.authority).then(resolver => { + if (resolver && resolver.label.length) { + data.msgLabel2.textContent = resolver.label; + } + }); + } else { + data.msgIcon2.className = ''; + data.msgLabel2.textContent = ''; + } + if (this._profileInfo) { data.profileTime.textContent = `Profile: ${(element.profileInfo.totalTime / 1000).toFixed(2)}ms`; const elementSegments = element.profileInfo.segments; @@ -426,7 +449,7 @@ export class RuntimeExtensionsEditor extends BaseEditor { this._contextMenuService.showContextMenu({ getAnchor: () => e.anchor, - getActions: () => Promise.resolve(actions) + getActions: () => actions }); }); } @@ -528,6 +551,7 @@ export class DebugExtensionHostAction extends Action { return this._debugService.startDebugging(null, { type: 'node', + name: nls.localize('debugExtensionHost.launch.name', "Attach Extension Host"), request: 'attach', port: inspectPort }); @@ -614,7 +638,7 @@ export class SaveExtensionHostProfileAction extends Action { // about users. We also append the `.txt` suffix to make it // easier to attach these files to GH issues - let tmp = profiler.rewriteAbsolutePaths({ profile: dataToWrite }, 'piiRemoved'); + let tmp = profiler.rewriteAbsolutePaths({ profile: dataToWrite as any }, 'piiRemoved'); dataToWrite = tmp.profile; picked = picked + '.txt'; diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index a1412e047..7fb58618c 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -956,7 +956,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, private onEnablementChanged(identifier: IExtensionIdentifier) { const [extension] = this.local.filter(e => areSameExtensions(e, identifier)); - if (extension) { + if (extension && extension.local) { const enablementState = this.extensionEnablementService.getEnablementState(extension.local); if (enablementState !== extension.enablementState) { extension.enablementState = enablementState; diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts index 36baa5b1c..fb5515889 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts @@ -622,7 +622,7 @@ suite('ExtensionsActions Test', () => { return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { testObject.extension = extensions[0]; - assert.ok(testObject.enabled); + assert.ok(!testObject.enabled); }); }); }); @@ -653,7 +653,7 @@ suite('ExtensionsActions Test', () => { return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { testObject.extension = extensions[0]; - assert.ok(testObject.enabled); + assert.ok(!testObject.enabled); }); }); }); @@ -782,14 +782,22 @@ suite('ExtensionsActions Test', () => { }); test('Test DisableForWorkspaceAction when extension is enabled', () => { + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]); const testObject: ExtensionsActions.DisableForWorkspaceAction = instantiationService.createInstance(ExtensionsActions.DisableForWorkspaceAction, 'id'); const local = aLocalExtension('a'); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { - testObject.extension = extensions[0]; - assert.ok(testObject.enabled); + assert.ok(!testObject.enabled); + return new Promise(c => { + testObject.onDidChange(() => { + if (testObject.enabled) { + c(); + } + }); + testObject.extension = extensions[0]; + }); }); }); @@ -830,14 +838,22 @@ suite('ExtensionsActions Test', () => { }); test('Test DisableGloballyAction when the extension is enabled', () => { + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]); const testObject: ExtensionsActions.DisableGloballyAction = instantiationService.createInstance(ExtensionsActions.DisableGloballyAction, 'id'); const local = aLocalExtension('a'); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { - testObject.extension = extensions[0]; - assert.ok(testObject.enabled); + assert.ok(!testObject.enabled); + return new Promise(c => { + testObject.onDidChange(() => { + if (testObject.enabled) { + c(); + } + }); + testObject.extension = extensions[0]; + }); }); }); @@ -848,14 +864,22 @@ suite('ExtensionsActions Test', () => { }); test('Test DisableAction when extension is installed and enabled', () => { + instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ id: 'pub.a', extensionLocation: URI.file('pub.a') }]); const testObject: ExtensionsActions.DisableAction = instantiationService.createInstance(ExtensionsActions.DisableAction); const local = aLocalExtension('a'); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); return instantiationService.get(IExtensionsWorkbenchService).queryLocal() .then(extensions => { - testObject.extension = extensions[0]; - assert.ok(testObject.enabled); + assert.ok(!testObject.enabled); + return new Promise(c => { + testObject.onDidChange(() => { + if (testObject.enabled) { + c(); + } + }); + testObject.extension = extensions[0]; + }); }); }); diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts index 08b39415d..78d93df69 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts @@ -47,7 +47,6 @@ import { URLService } from 'vs/platform/url/common/urlService'; import { IExperimentService } from 'vs/workbench/parts/experiments/node/experimentService'; import { TestExperimentService } from 'vs/workbench/parts/experiments/test/electron-browser/experimentService.test'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { TPromise } from 'vs/base/common/winjs.base'; const mockExtensionGallery: IGalleryExtension[] = [ aGalleryExtension('MockExtension1', { @@ -259,27 +258,26 @@ suite('ExtensionsTipsService Test', () => { } }); - function setUpFolderWorkspace(folderName: string, recommendedExtensions: string[], ignoredRecommendations: string[] = []): TPromise { + function setUpFolderWorkspace(folderName: string, recommendedExtensions: string[], ignoredRecommendations: string[] = []): Promise { const id = uuid.generateUuid(); parentResource = path.join(os.tmpdir(), 'vsctests', id); return setUpFolder(folderName, parentResource, recommendedExtensions, ignoredRecommendations); } - function setUpFolder(folderName: string, parentDir: string, recommendedExtensions: string[], ignoredRecommendations: string[] = []): TPromise { + async function setUpFolder(folderName: string, parentDir: string, recommendedExtensions: string[], ignoredRecommendations: string[] = []): Promise { const folderDir = path.join(parentDir, folderName); const workspaceSettingsDir = path.join(folderDir, '.vscode'); - return mkdirp(workspaceSettingsDir, 493).then(() => { - const configPath = path.join(workspaceSettingsDir, 'extensions.json'); - fs.writeFileSync(configPath, JSON.stringify({ - 'recommendations': recommendedExtensions, - 'unwantedRecommendations': ignoredRecommendations, - }, null, '\t')); - - const myWorkspace = testWorkspace(URI.from({ scheme: 'file', path: folderDir })); - workspaceService = new TestContextService(myWorkspace); - instantiationService.stub(IWorkspaceContextService, workspaceService); - instantiationService.stub(IFileService, new FileService(workspaceService, TestEnvironmentService, new TestTextResourceConfigurationService(), new TestConfigurationService(), new TestLifecycleService(), new TestStorageService(), new TestNotificationService(), { disableWatcher: true })); - }); + await mkdirp(workspaceSettingsDir, 493); + const configPath = path.join(workspaceSettingsDir, 'extensions.json'); + fs.writeFileSync(configPath, JSON.stringify({ + 'recommendations': recommendedExtensions, + 'unwantedRecommendations': ignoredRecommendations, + }, null, '\t')); + + const myWorkspace = testWorkspace(URI.from({ scheme: 'file', path: folderDir })); + workspaceService = new TestContextService(myWorkspace); + instantiationService.stub(IWorkspaceContextService, workspaceService); + instantiationService.stub(IFileService, new FileService(workspaceService, TestEnvironmentService, new TestTextResourceConfigurationService(), new TestConfigurationService(), new TestLifecycleService(), new TestStorageService(), new TestNotificationService(), { disableWatcher: true })); } function testNoPromptForValidRecommendations(recommendations: string[]) { diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts index 452cd8865..6f3ea74be 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts @@ -12,7 +12,7 @@ import { IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/commo import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService'; import { IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, LocalExtensionType, IGalleryExtension, IQueryOptions, - DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, IExtensionManagementServerService, IExtensionManagementServer, EnablementState, ExtensionRecommendationReason + DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, IExtensionManagementServerService, IExtensionManagementServer, EnablementState, ExtensionRecommendationReason, SortBy } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getGalleryExtensionId, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionManagementService, getLocalExtensionIdFromManifest } from 'vs/platform/extensionManagement/node/extensionManagementService'; @@ -152,6 +152,33 @@ suite('ExtensionsListView Tests', () => { assert.equal(ExtensionsListView.isInstalledExtensionsQuery('@outdated searchText'), true); }); + test('Test empty query equates to sort by install count', () => { + const target = instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage()); + return testableView.show('').then(() => { + assert.ok(target.calledOnce); + const options: IQueryOptions = target.args[0][0]; + assert.equal(options.sortBy, SortBy.InstallCount); + }); + }); + + test('Test non empty query without sort doesnt use sortBy', () => { + const target = instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage()); + return testableView.show('some extension').then(() => { + assert.ok(target.calledOnce); + const options: IQueryOptions = target.args[0][0]; + assert.equal(options.sortBy, undefined); + }); + }); + + test('Test query with sort uses sortBy', () => { + const target = instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage()); + return testableView.show('some extension @sort:rating').then(() => { + assert.ok(target.calledOnce); + const options: IQueryOptions = target.args[0][0]; + assert.equal(options.sortBy, SortBy.WeightedRating); + }); + }); + test('Test installed query results', () => { const allInstalledCheck = testableView.show('@installed').then(result => { assert.equal(result.length, 5, 'Unexpected number of results for @installed query'); @@ -394,20 +421,20 @@ suite('ExtensionsListView Tests', () => { test('Test preferred search experiment', () => { const searchText = 'search-me'; - const realResults = [ + const actual = [ fileBasedRecommendationA, workspaceRecommendationA, otherRecommendationA, workspaceRecommendationB ]; - const preferredResults = [ + const expected = [ workspaceRecommendationA, workspaceRecommendationB, fileBasedRecommendationA, otherRecommendationA ]; - const queryTarget = instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(...realResults)); + const queryTarget = instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(...actual)); const experimentTarget = instantiationService.stubPromise(IExperimentService, 'getExperimentsByType', [{ id: 'someId', enabled: true, @@ -434,9 +461,35 @@ suite('ExtensionsListView Tests', () => { assert.ok(experimentTarget.calledOnce); assert.ok(queryTarget.calledOnce); assert.equal(options.text, searchText); - assert.equal(result.length, preferredResults.length); - for (let i = 0; i < preferredResults.length; i++) { - assert.equal(result.get(i).id, preferredResults[i].identifier.id); + assert.equal(result.length, expected.length); + for (let i = 0; i < expected.length; i++) { + assert.equal(result.get(i).id, expected[i].identifier.id); + } + }); + }); + + test('Skip preferred search experiment when user defines sort order', () => { + const searchText = 'search-me'; + const realResults = [ + fileBasedRecommendationA, + workspaceRecommendationA, + otherRecommendationA, + workspaceRecommendationB + ]; + + const queryTarget = instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(...realResults)); + + testableView.dispose(); + testableView = instantiationService.createInstance(ExtensionsListView, {}); + + return testableView.show('search-me @sort:installs').then(result => { + const options: IQueryOptions = queryTarget.args[0][0]; + + assert.ok(queryTarget.calledOnce); + assert.equal(options.text, searchText); + assert.equal(result.length, realResults.length); + for (let i = 0; i < realResults.length; i++) { + assert.equal(result.get(i).id, realResults[i].identifier.id); } }); }); diff --git a/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts b/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts index 28ae869f9..67e4b7438 100644 --- a/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts +++ b/src/vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts @@ -110,7 +110,7 @@ export class FeedbackStatusbarItem extends Themable implements IStatusbarItem { this.contextMenuService.showContextMenu({ getAnchor: () => this.container, - getActions: () => Promise.resolve([this.hideAction]) + getActions: () => [this.hideAction] }); })); diff --git a/src/vs/workbench/parts/files/browser/editors/fileEditorTracker.ts b/src/vs/workbench/parts/files/browser/editors/fileEditorTracker.ts index 537452c3c..2300e5405 100644 --- a/src/vs/workbench/parts/files/browser/editors/fileEditorTracker.ts +++ b/src/vs/workbench/parts/files/browser/editors/fileEditorTracker.ts @@ -314,7 +314,7 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut // to have a size of 2 (1 running load and 1 queued load). const queue = this.modelLoadQueue.queueFor(model.getResource()); if (queue.size <= 1) { - queue.queue(() => model.load().then(null, onUnexpectedError)); + queue.queue(() => model.load().then(null, onUnexpectedError)); } } diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts index 6838dbcc1..36660df42 100644 --- a/src/vs/workbench/parts/files/common/files.ts +++ b/src/vs/workbench/parts/files/common/files.ts @@ -13,9 +13,8 @@ import { ITextModelContentProvider } from 'vs/editor/common/services/resolverSer import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { TPromise } from 'vs/base/common/winjs.base'; import { ITextModel } from 'vs/editor/common/model'; -import { IMode } from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { IModeService } from 'vs/editor/common/services/modeService'; +import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { InputFocusedContextKey } from 'vs/platform/workbench/common/contextkeys'; @@ -181,7 +180,7 @@ export class FileOnDiskContentProvider implements ITextModelContentProvider { } private resolveEditorModel(resource: URI, createAsNeeded = true): TPromise { - const fileOnDiskResource = URI.file(resource.fsPath); + const fileOnDiskResource = resource.with({ scheme: Schemas.file }); return this.textFileService.resolveTextContent(fileOnDiskResource).then(content => { let codeEditorModel = this.modelService.getModel(resource); @@ -190,14 +189,14 @@ export class FileOnDiskContentProvider implements ITextModelContentProvider { } else if (createAsNeeded) { const fileOnDiskModel = this.modelService.getModel(fileOnDiskResource); - let mode: Promise; + let languageSelector: ILanguageSelection; if (fileOnDiskModel) { - mode = this.modeService.getOrCreateMode(fileOnDiskModel.getModeId()); + languageSelector = this.modeService.create(fileOnDiskModel.getModeId()); } else { - mode = this.modeService.getOrCreateModeByFilepathOrFirstLine(fileOnDiskResource.fsPath); + languageSelector = this.modeService.createByFilepathOrFirstLine(fileOnDiskResource.fsPath); } - codeEditorModel = this.modelService.createModel(content.value, mode, resource); + codeEditorModel = this.modelService.createModel(content.value, languageSelector, resource); } return codeEditorModel; diff --git a/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts b/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts index c986aa7ce..c7f73dc9e 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts @@ -217,7 +217,7 @@ export class ExplorerViewlet extends ViewContainerViewlet implements IExplorerVi }); const explorerInstantiator = this.instantiationService.createChild(new ServiceCollection([IEditorService, delegatingEditorService])); - return explorerInstantiator.createInstance(ExplorerView, { ...options, viewletState: this.fileViewletState }); + return explorerInstantiator.createInstance(ExplorerView, { ...options, fileViewletState: this.fileViewletState }); } return super.createView(viewDescriptor, options); } @@ -234,9 +234,9 @@ export class ExplorerViewlet extends ViewContainerViewlet implements IExplorerVi return this.getView(EmptyView.ID); } - public setVisible(visible: boolean): Promise { + public setVisible(visible: boolean): void { this.viewletVisibleContextKey.set(visible); - return super.setVisible(visible); + super.setVisible(visible); } public getActionRunner(): IActionRunner { diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 8cdac0809..a77c906d9 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -1546,7 +1546,7 @@ class ClipboardContentProvider implements ITextModelContentProvider { ) { } provideTextContent(resource: URI): TPromise { - const model = this.modelService.createModel(this.clipboardService.readText(), this.modeService.getOrCreateMode('text/plain'), resource); + const model = this.modelService.createModel(this.clipboardService.readText(), this.modeService.create('text/plain'), resource); return Promise.resolve(model); } diff --git a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts index b6a14d0f0..f857f474d 100644 --- a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts @@ -336,7 +336,7 @@ configurationRegistry.registerConfiguration({ 'editor.formatOnSave': { 'type': 'boolean', 'default': false, - 'description': nls.localize('formatOnSave', "Format a file on save. A formatter must be available, the file must not be auto-saved, and editor must not be shutting down."), + 'description': nls.localize('formatOnSave', "Format a file on save. A formatter must be available, the file must not be saved after delay, and the editor must not be shutting down."), 'overridable': true, 'scope': ConfigurationScope.RESOURCE }, diff --git a/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css b/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css index eb562776b..c3fc2504f 100644 --- a/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css +++ b/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css @@ -104,18 +104,15 @@ .explorer-viewlet .explorer-item .monaco-inputbox { width: 100%; line-height: normal; - margin-left: -3px; } .monaco-workbench.linux .explorer-viewlet .explorer-item .monaco-inputbox, .monaco-workbench.mac .explorer-viewlet .explorer-item .monaco-inputbox { height: 22px; - margin-left: -1px; } .explorer-viewlet .explorer-item .monaco-inputbox > .wrapper > .input { padding: 1px 2px; - height: 19px; } .monaco-workbench.linux .explorer-viewlet .explorer-item .monaco-inputbox > .wrapper > .input, diff --git a/src/vs/workbench/parts/files/electron-browser/saveErrorHandler.ts b/src/vs/workbench/parts/files/electron-browser/saveErrorHandler.ts index 3e9cfb1a1..c7da9ccf2 100644 --- a/src/vs/workbench/parts/files/electron-browser/saveErrorHandler.ts +++ b/src/vs/workbench/parts/files/electron-browser/saveErrorHandler.ts @@ -31,6 +31,7 @@ import { ExecuteCommandAction } from 'vs/platform/actions/common/actions'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { once } from 'vs/base/common/event'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { isWindows } from 'vs/base/common/platform'; export const CONFLICT_RESOLUTION_CONTEXT = 'saveConflictResolutionContext'; export const CONFLICT_RESOLUTION_SCHEME = 'conflictResolution'; @@ -158,12 +159,12 @@ export class SaveErrorHandler extends Disposable implements ISaveErrorHandler, I if (isReadonly) { if (triedToMakeWriteable) { - message = nls.localize('readonlySaveErrorAdmin', "Failed to save '{0}': File is write protected. Select 'Overwrite as Admin' to retry as administrator.", paths.basename(resource.fsPath)); + message = isWindows ? nls.localize('readonlySaveErrorAdmin', "Failed to save '{0}': File is write protected. Select 'Overwrite as Admin' to retry as administrator.", paths.basename(resource.fsPath)) : nls.localize('readonlySaveErrorSudo', "Failed to save '{0}': File is write protected. Select 'Overwrite as Sudo' to retry as superuser.", paths.basename(resource.fsPath)); } else { message = nls.localize('readonlySaveError', "Failed to save '{0}': File is write protected. Select 'Overwrite' to attempt to remove protection.", paths.basename(resource.fsPath)); } } else if (isPermissionDenied) { - message = nls.localize('permissionDeniedSaveError', "Failed to save '{0}': Insufficient permissions. Select 'Retry as Admin' to retry as administrator.", paths.basename(resource.fsPath)); + message = isWindows ? nls.localize('permissionDeniedSaveError', "Failed to save '{0}': Insufficient permissions. Select 'Retry as Admin' to retry as administrator.", paths.basename(resource.fsPath)) : nls.localize('permissionDeniedSaveErrorSudo', "Failed to save '{0}': Insufficient permissions. Select 'Retry as Sudo' to retry as superuser.", paths.basename(resource.fsPath)); } else { message = nls.localize('genericSaveError', "Failed to save '{0}': {1}", paths.basename(resource.fsPath), toErrorMessage(error, false)); } @@ -272,7 +273,7 @@ class SaveElevatedAction extends Action { private model: ITextFileEditorModel, private triedToMakeWriteable: boolean ) { - super('workbench.files.action.saveElevated', triedToMakeWriteable ? nls.localize('overwriteElevated', "Overwrite as Admin...") : nls.localize('saveElevated', "Retry as Admin...")); + super('workbench.files.action.saveElevated', triedToMakeWriteable ? isWindows ? nls.localize('overwriteElevated', "Overwrite as Admin...") : nls.localize('overwriteElevatedSudo', "Overwrite as Sudo...") : isWindows ? nls.localize('saveElevated', "Retry as Admin...") : nls.localize('saveElevatedSudo', "Retry as Sudo...")); } run(): Promise { diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts index 099168e9c..cad5c3d8f 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -6,6 +6,7 @@ import * as nls from 'vs/nls'; import { TPromise } from 'vs/base/common/winjs.base'; import { URI } from 'vs/base/common/uri'; +import * as perf from 'vs/base/common/performance'; import { ThrottledDelayer, Delayer } from 'vs/base/common/async'; import * as paths from 'vs/base/common/paths'; import * as resources from 'vs/base/common/resources'; @@ -44,7 +45,7 @@ import { IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelView import { ILabelService } from 'vs/platform/label/common/label'; export interface IExplorerViewOptions extends IViewletViewOptions { - viewletState: FileViewletState; + fileViewletState: FileViewletState; } export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView { @@ -60,7 +61,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView private explorerViewer: WorkbenchTree; private filter: FileFilter; - private viewletState: FileViewletState; + private fileViewletState: FileViewletState; private explorerRefreshDelayer: ThrottledDelayer; @@ -99,7 +100,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService); this.viewState = options.viewletState; - this.viewletState = options.viewletState; + this.fileViewletState = options.fileViewletState; this.autoReveal = true; this.explorerRefreshDelayer = new ThrottledDelayer(ExplorerView.EXPLORER_FILE_CHANGES_REFRESH_DELAY); @@ -306,7 +307,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView if (this.autoReveal) { const selection = this.explorerViewer.getSelection(); if (selection.length > 0) { - this.reveal(selection[0], 0.5); + this.reveal(selection[0]); } } @@ -322,57 +323,54 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView } } - public setVisible(visible: boolean): TPromise { - return super.setVisible(visible).then(() => { + public setVisible(visible: boolean): void { + super.setVisible(visible); - // Show - if (visible) { + // Show + if (visible) { - // If a refresh was requested and we are now visible, run it - let refreshPromise: Thenable = TPromise.as(null); - if (this.shouldRefresh) { - refreshPromise = this.doRefresh(); - this.shouldRefresh = false; // Reset flag - } - - if (!this.autoReveal) { - return refreshPromise; // do not react to setVisible call if autoReveal === false - } - - // Always select the current navigated file in explorer if input is file editor input - // unless autoReveal is set to false - const activeFile = this.getActiveFile(); - if (activeFile) { - return refreshPromise.then(() => { - return this.select(activeFile); - }); - } + // If a refresh was requested and we are now visible, run it + let refreshPromise: Thenable = TPromise.as(null); + if (this.shouldRefresh) { + refreshPromise = this.doRefresh(); + this.shouldRefresh = false; // Reset flag + } - // Return now if the workbench has not yet been created - in this case the workbench takes care of restoring last used editors - if (!this.partService.isCreated()) { - return Promise.resolve(null); - } + if (!this.autoReveal) { + return; // do not react to setVisible call if autoReveal === false + } - // Otherwise restore last used file: By lastActiveFileResource - let lastActiveFileResource: URI; - if (this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]) { - lastActiveFileResource = URI.parse(this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]); - } + // Always select the current navigated file in explorer if input is file editor input + // unless autoReveal is set to false + const activeFile = this.getActiveFile(); + if (activeFile) { + refreshPromise.then(() => { + this.select(activeFile); + }); + return; + } - if (lastActiveFileResource && this.isCreated && this.model.findClosest(lastActiveFileResource)) { - this.editorService.openEditor({ resource: lastActiveFileResource, options: { revealIfVisible: true } }); + // Return now if the workbench has not yet been created - in this case the workbench takes care of restoring last used editors + if (!this.partService.isCreated()) { + return; + } - return refreshPromise; - } + // Otherwise restore last used file: By lastActiveFileResource + let lastActiveFileResource: URI; + if (this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]) { + lastActiveFileResource = URI.parse(this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]); + } - // Otherwise restore last used file: By Explorer selection - return refreshPromise.then(() => { - this.openFocusedElement(); - }); + if (lastActiveFileResource && this.isCreated && this.model.findClosest(lastActiveFileResource)) { + this.editorService.openEditor({ resource: lastActiveFileResource, options: { revealIfVisible: true } }); + return; } - return void 0; - }); + // Otherwise restore last used file: By Explorer selection + refreshPromise.then(() => { + this.openFocusedElement(); + }); + } } private openFocusedElement(preserveFocus?: boolean): void { @@ -408,7 +406,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView private createViewer(container: HTMLElement): WorkbenchTree { const dataSource = this.instantiationService.createInstance(FileDataSource); - const renderer = this.instantiationService.createInstance(FileRenderer, this.viewletState); + const renderer = this.instantiationService.createInstance(FileRenderer, this.fileViewletState); const controller = this.instantiationService.createInstance(FileController); this.disposables.push(controller); const sorter = this.instantiationService.createInstance(FileSorter); @@ -506,7 +504,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView p.addChild(childElement); // Refresh the Parent (View) this.explorerViewer.refresh(p).then(() => { - return this.reveal(childElement, 0.5).then(() => { + return this.reveal(childElement).then(() => { // Focus new element this.explorerViewer.setFocus(childElement); @@ -717,7 +715,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView if (!this.explorerViewer.getHighlight()) { return this.doRefresh(newRoots.map(r => r.uri)).then(() => { if (newRoots.length === 1) { - return this.reveal(this.model.findClosest(newRoots[0].uri), 0.5); + return this.reveal(this.model.findClosest(newRoots[0].uri)); } return undefined; @@ -806,6 +804,9 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView // Display roots only when multi folder workspace let input = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER ? this.model.roots[0] : this.model; + if (input !== this.explorerViewer.getInput()) { + perf.mark('willResolveExplorer'); + } const errorRoot = (resource: URI, root: ExplorerItem) => { if (input === this.model.roots[0]) { @@ -828,7 +829,8 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView statsToExpand = this.model.roots.concat(statsToExpand); } - return this.explorerViewer.setInput(input).then(() => this.explorerViewer.expandAll(statsToExpand)); + return this.explorerViewer.setInput(input).then(() => this.explorerViewer.expandAll(statsToExpand)) + .then(() => perf.mark('didResolveExplorer')); }; if (targetsToResolve.every(t => t.root.resource.scheme === 'file')) { @@ -927,7 +929,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView // If path already selected, just reveal and return const selection = this.hasSingleSelection(resource); if (selection) { - return reveal ? this.reveal(selection, 0.5) : Promise.resolve(null); + return reveal ? this.reveal(selection) : Promise.resolve(null); } // First try to get the stat object from the input to avoid a roundtrip @@ -982,7 +984,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView // Reveal depending on flag let revealPromise: TPromise; if (reveal) { - revealPromise = this.reveal(fileStat, 0.5); + revealPromise = this.reveal(fileStat); } else { revealPromise = Promise.resolve(null); } diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts index 05f13e465..b13244965 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -543,7 +543,7 @@ export class FileController extends WorkbenchTreeController implements IDisposab getActions: () => { const actions: IAction[] = []; fillInContextMenuActions(this.contributedContextMenu, { arg: stat instanceof ExplorerItem ? stat.resource : {}, shouldForwardArgs: true }, actions, this.contextMenuService); - return Promise.resolve(actions); + return actions; }, onHide: (wasCancelled?: boolean) => { if (wasCancelled) { diff --git a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts index 20eb76e04..d57a8dd1f 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -297,13 +297,12 @@ export class OpenEditorsView extends ViewletPanel { } } - public setVisible(visible: boolean): TPromise { - return super.setVisible(visible).then(() => { - this.updateListVisibility(visible && this.isExpanded()); - if (visible && this.needsRefresh) { - this.listRefreshScheduler.schedule(0); - } - }); + public setVisible(visible: boolean): void { + super.setVisible(visible); + this.updateListVisibility(visible && this.isExpanded()); + if (visible && this.needsRefresh) { + this.listRefreshScheduler.schedule(0); + } } public focus(): void { @@ -393,7 +392,7 @@ export class OpenEditorsView extends ViewletPanel { getActions: () => { const actions: IAction[] = []; fillInContextMenuActions(this.contributedContextMenu, { shouldForwardArgs: true, arg: element instanceof OpenEditor ? element.editor.getResource() : {} }, actions, this.contextMenuService); - return Promise.resolve(actions); + return actions; }, getActionsContext: () => element instanceof OpenEditor ? { groupId: element.groupId, editorIndex: element.editorIndex } : { groupId: element.id } }); diff --git a/src/vs/workbench/parts/html/electron-browser/htmlPreviewPart.ts b/src/vs/workbench/parts/html/electron-browser/htmlPreviewPart.ts index 26825ffc7..96e890bad 100644 --- a/src/vs/workbench/parts/html/electron-browser/htmlPreviewPart.ts +++ b/src/vs/workbench/parts/html/electron-browser/htmlPreviewPart.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITextModel } from 'vs/editor/common/model'; import { Disposable, IDisposable, dispose, IReference } from 'vs/base/common/lifecycle'; import { EditorOptions, EditorInput, IEditorMemento } from 'vs/workbench/common/editor'; @@ -179,7 +178,7 @@ export class HtmlPreviewPart extends BaseWebviewEditor { public setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable { if (this.input && this.input.matches(input) && this._hasValidModel() && this.input instanceof HtmlInput && input instanceof HtmlInput && areHtmlInputOptionsEqual(this.input.options, input.options)) { - return TPromise.as(undefined); + return Promise.resolve(undefined); } let oldOptions: HtmlInputOptions | undefined = undefined; @@ -197,7 +196,7 @@ export class HtmlPreviewPart extends BaseWebviewEditor { this._modelChangeSubscription.dispose(); if (!(input instanceof HtmlInput)) { - return TPromise.wrapError(new Error('Invalid input')); + return Promise.reject(new Error('Invalid input')); } return super.setInput(input, options, token).then(() => { @@ -213,7 +212,7 @@ export class HtmlPreviewPart extends BaseWebviewEditor { } if (!this.model) { - return TPromise.wrapError(new Error(localize('html.voidInput', "Invalid editor input."))); + return Promise.reject(new Error(localize('html.voidInput', "Invalid editor input."))); } if (oldOptions && !areHtmlInputOptionsEqual(oldOptions, input.options)) { diff --git a/src/vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts b/src/vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts index 0ae117145..c5668ba17 100644 --- a/src/vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts +++ b/src/vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts @@ -24,7 +24,6 @@ import { URI } from 'vs/base/common/uri'; import { join } from 'vs/base/common/paths'; import { IWindowsService } from 'vs/platform/windows/common/windows'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { VIEWLET_ID as EXTENSIONS_VIEWLET_ID, IExtensionsViewlet } from 'vs/workbench/parts/extensions/common/extensions'; import { minimumTranslatedStrings } from 'vs/platform/node/minimalTranslations'; @@ -133,7 +132,7 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo return; } - TPromise.join([this.galleryService.getManifest(extensionToFetchTranslationsFrom, CancellationToken.None), this.galleryService.getCoreTranslation(extensionToFetchTranslationsFrom, locale)]) + Promise.all([this.galleryService.getManifest(extensionToFetchTranslationsFrom, CancellationToken.None), this.galleryService.getCoreTranslation(extensionToFetchTranslationsFrom, locale)]) .then(([manifest, translation]) => { const loc = manifest && manifest.contributes && manifest.contributes.localizations && manifest.contributes.localizations.filter(x => x.languageId.toLowerCase() === locale)[0]; const languageName = loc ? (loc.languageName || locale) : locale; @@ -214,12 +213,12 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo } - private isLanguageInstalled(language: string): TPromise { + private isLanguageInstalled(language: string): Promise { return this.extensionManagementService.getInstalled(LocalExtensionType.User) .then(installed => installed.some(i => i.manifest && i.manifest.contributes && i.manifest.contributes.localizations && i.manifest.contributes.localizations.length && i.manifest.contributes.localizations.some(l => l.languageId.toLowerCase() === language))); } - private installExtension(extension: IGalleryExtension): TPromise { + private installExtension(extension: IGalleryExtension): Thenable { return this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => viewlet.search(`@id:${extension.identifier.id}`)) diff --git a/src/vs/workbench/parts/localizations/electron-browser/localizationsActions.ts b/src/vs/workbench/parts/localizations/electron-browser/localizationsActions.ts index 6e0a1df13..9fe5a44ab 100644 --- a/src/vs/workbench/parts/localizations/electron-browser/localizationsActions.ts +++ b/src/vs/workbench/parts/localizations/electron-browser/localizationsActions.ts @@ -7,7 +7,6 @@ import { localize } from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { IFileService } from 'vs/platform/files/common/files'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IEditor } from 'vs/workbench/common/editor'; import { join } from 'vs/base/common/paths'; import { URI } from 'vs/base/common/uri'; @@ -37,7 +36,7 @@ export class ConfigureLocaleAction extends Action { super(id, label); } - public run(event?: any): TPromise { + public run(event?: any): Thenable { const file = URI.file(join(this.environmentService.appSettingsHome, 'locale.json')); return this.fileService.resolveFile(file).then(null, (error) => { return this.fileService.createFile(file, ConfigureLocaleAction.DEFAULT_CONTENT); diff --git a/src/vs/workbench/parts/logs/electron-browser/logsActions.ts b/src/vs/workbench/parts/logs/electron-browser/logsActions.ts index d0f67b0ac..df65f5410 100644 --- a/src/vs/workbench/parts/logs/electron-browser/logsActions.ts +++ b/src/vs/workbench/parts/logs/electron-browser/logsActions.ts @@ -8,7 +8,6 @@ import { Action } from 'vs/base/common/actions'; import * as paths from 'vs/base/common/paths'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWindowsService } from 'vs/platform/windows/common/windows'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ILogService, LogLevel, DEFAULT_LOG_LEVEL } from 'vs/platform/log/common/log'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; @@ -24,7 +23,7 @@ export class OpenLogsFolderAction extends Action { super(id, label); } - run(): TPromise { + run(): Thenable { return this.windowsService.showItemInFolder(paths.join(this.environmentService.logsPath, 'main.log')); } } @@ -41,7 +40,7 @@ export class SetLogLevelAction extends Action { super(id, label); } - run(): TPromise { + run(): Thenable { const current = this.logService.getLevel(); const entries = [ { label: nls.localize('trace', "Trace"), level: LogLevel.Trace, description: this.getDescription(LogLevel.Trace, current) }, diff --git a/src/vs/workbench/parts/markers/electron-browser/markers.contribution.ts b/src/vs/workbench/parts/markers/electron-browser/markers.contribution.ts index c5df5330e..82ee8baf8 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markers.contribution.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markers.contribution.ts @@ -20,10 +20,15 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { ToggleMarkersPanelAction, ShowProblemsPanelAction } from 'vs/workbench/parts/markers/electron-browser/markersPanelActions'; import Constants from 'vs/workbench/parts/markers/electron-browser/constants'; import Messages from 'vs/workbench/parts/markers/electron-browser/messages'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { IMarkersWorkbenchService, MarkersWorkbenchService, ActivityUpdater } from 'vs/workbench/parts/markers/electron-browser/markers'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import './markers'; import './markersFileDecorations'; +registerSingleton(IMarkersWorkbenchService, MarkersWorkbenchService, false); + KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Constants.MARKER_OPEN_SIDE_ACTION_ID, weight: KeybindingWeight.WorkbenchContrib, @@ -74,6 +79,10 @@ Registry.as(PanelExtensions.Panels).registerPanel(new PanelDescri ToggleMarkersPanelAction.ID )); +// workbench +const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); +workbenchRegistry.registerWorkbenchContribution(ActivityUpdater, LifecyclePhase.Running); + // actions const registry = Registry.as(ActionExtensions.WorkbenchActions); registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMarkersPanelAction, ToggleMarkersPanelAction.ID, ToggleMarkersPanelAction.LABEL, { diff --git a/src/vs/workbench/parts/markers/electron-browser/markers.ts b/src/vs/workbench/parts/markers/electron-browser/markers.ts index 0ff5025eb..b3231df8d 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markers.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markers.ts @@ -11,8 +11,8 @@ import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/co import { localize } from 'vs/nls'; import Constants from './constants'; import { URI } from 'vs/base/common/uri'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { groupBy } from 'vs/base/common/arrays'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; export const IMarkersWorkbenchService = createDecorator('markersWorkbenchService'); @@ -33,7 +33,6 @@ export class MarkersWorkbenchService extends Disposable implements IMarkersWorkb constructor( @IMarkerService private markerService: IMarkerService, - @IActivityService private activityService: IActivityService, @IInstantiationService instantiationService: IInstantiationService ) { super(); @@ -50,19 +49,28 @@ export class MarkersWorkbenchService extends Disposable implements IMarkersWorkb for (const resource of resources) { this.markersModel.setResourceMarkers(resource, this.readMarkers(resource)); } - - this.refreshBadge(); } private readMarkers(resource?: URI): IMarker[] { return this.markerService.read({ resource, severities: MarkerSeverity.Error | MarkerSeverity.Warning | MarkerSeverity.Info }); } - private refreshBadge(): void { - const total = this.markersModel.resourceMarkers.reduce((r, rm) => r + rm.markers.length, 0); +} + +export class ActivityUpdater extends Disposable implements IWorkbenchContribution { + + constructor( + @IActivityService private activityService: IActivityService, + @IMarkersWorkbenchService private markersWorkbenchService: IMarkersWorkbenchService + ) { + super(); + this._register(this.markersWorkbenchService.markersModel.onDidChange(() => this.updateBadge())); + this.updateBadge(); + } + + private updateBadge(): void { + const total = this.markersWorkbenchService.markersModel.resourceMarkers.reduce((r, rm) => r + rm.markers.length, 0); const message = localize('totalProblems', 'Total {0} Problems', total); this.activityService.showActivity(Constants.MARKERS_PANEL_ID, new NumberBadge(total, () => message)); } -} - -registerSingleton(IMarkersWorkbenchService, MarkersWorkbenchService); \ No newline at end of file +} \ No newline at end of file diff --git a/src/vs/workbench/parts/markers/electron-browser/markersModel.ts b/src/vs/workbench/parts/markers/electron-browser/markersModel.ts index 68eeff1f9..a3e554d21 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersModel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersModel.ts @@ -11,6 +11,7 @@ import { groupBy, flatten, isFalsyOrEmpty } from 'vs/base/common/arrays'; import { values } from 'vs/base/common/map'; import { memoize } from 'vs/base/common/decorators'; import { Emitter, Event } from 'vs/base/common/event'; +import { Hasher } from 'vs/base/common/hash'; function compareUris(a: URI, b: URI) { const astr = a.toString(); @@ -48,6 +49,13 @@ export class ResourceMarkers { @memoize get name(): string { return paths.basename(this.resource.fsPath); } + @memoize + get hash(): string { + const hasher = new Hasher(); + hasher.hash(this.resource.toString()); + return `${hasher.value}`; + } + constructor(readonly resource: URI, readonly markers: Marker[]) { } } @@ -56,6 +64,17 @@ export class Marker { get resource(): URI { return this.marker.resource; } get range(): IRange { return this.marker; } + @memoize + get hash(): string { + const hasher = new Hasher(); + hasher.hash(this.resource.toString()); + hasher.hash(this.marker.startLineNumber); + hasher.hash(this.marker.startColumn); + hasher.hash(this.marker.endLineNumber); + hasher.hash(this.marker.endColumn); + return `${hasher.value}`; + } + constructor( readonly marker: IMarker, readonly relatedInformation: RelatedInformation[] = [] @@ -72,7 +91,27 @@ export class Marker { export class RelatedInformation { - constructor(readonly raw: IRelatedInformation) { } + @memoize + get hash(): string { + const hasher = new Hasher(); + hasher.hash(this.resource.toString()); + hasher.hash(this.marker.startLineNumber); + hasher.hash(this.marker.startColumn); + hasher.hash(this.marker.endLineNumber); + hasher.hash(this.marker.endColumn); + hasher.hash(this.raw.resource.toString()); + hasher.hash(this.raw.startLineNumber); + hasher.hash(this.raw.startColumn); + hasher.hash(this.raw.endLineNumber); + hasher.hash(this.raw.endColumn); + return `${hasher.value}`; + } + + constructor( + private resource: URI, + private marker: IMarker, + readonly raw: IRelatedInformation + ) { } } export class MarkersModel { @@ -110,7 +149,7 @@ export class MarkersModel { if (rawMarker.relatedInformation) { const groupedByResource = groupBy(rawMarker.relatedInformation, compareMarkersByUri); groupedByResource.sort((a, b) => compareUris(a[0].resource, b[0].resource)); - relatedInformation = flatten(groupedByResource).map(r => new RelatedInformation(r)); + relatedInformation = flatten(groupedByResource).map(r => new RelatedInformation(resource, rawMarker, r)); } return new Marker(rawMarker, relatedInformation); diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts index 52b766d17..c19ab72a7 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts @@ -6,7 +6,6 @@ import 'vs/css!./media/markers'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as dom from 'vs/base/browser/dom'; import { IAction, IActionItem, Action } from 'vs/base/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -157,18 +156,16 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { } } - public setVisible(visible: boolean): Promise { + public setVisible(visible: boolean): void { const wasVisible = this.isVisible(); - return super.setVisible(visible) - .then(() => { - if (this.isVisible()) { - if (!wasVisible) { - this.refreshPanel(); - } - } else { - this.rangeHighlightDecorations.removeHighlightRange(); - } - }); + super.setVisible(visible); + if (this.isVisible()) { + if (!wasVisible) { + this.refreshPanel(); + } + } else { + this.rangeHighlightDecorations.removeHighlightRange(); + } } public getActions(): IAction[] { @@ -204,7 +201,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { return false; } - private refreshPanel(): TPromise { + private refreshPanel(): void { if (this.isVisible()) { this.cachedFilterStats = undefined; this.tree.setChildren(null, createModelIterator(this.markersWorkbenchService.markersModel)); @@ -214,7 +211,6 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { this.renderMessage(); this._onDidFilter.fire(); } - return TPromise.as(null); } private updateFilter() { @@ -223,6 +219,9 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { this.filter.options = new FilterOptions(this.filterAction.filterText, excludeExpression); this.tree.refilter(); this._onDidFilter.fire(); + + const { total, filtered } = this.getFilterStats(); + dom.toggleClass(this.treeContainer, 'hidden', total > 0 && filtered === 0); this.renderMessage(); } @@ -296,7 +295,8 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { renderers, { filter: this.filter, - accessibilityProvider + accessibilityProvider, + identityProvider: (element: TreeElement) => element.hash } ) as any as WorkbenchObjectTree; @@ -305,8 +305,8 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { const markerFocusContextKey = Constants.MarkerFocusContextKey.bindTo(this.tree.contextKeyService); const relatedInformationFocusContextKey = Constants.RelatedInformationFocusContextKey.bindTo(this.tree.contextKeyService); this._register(this.tree.onDidChangeFocus(focus => { - markerFocusContextKey.set(focus.elements.some(e => e.element instanceof Marker)); - relatedInformationFocusContextKey.set(focus.elements.some(e => e.element instanceof RelatedInformation)); + markerFocusContextKey.set(focus.elements.some(e => e instanceof Marker)); + relatedInformationFocusContextKey.set(focus.elements.some(e => e instanceof RelatedInformation)); })); const focusTracker = this._register(dom.trackFocus(this.tree.getHTMLElement())); this._register(focusTracker.onDidBlur(() => { @@ -542,7 +542,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { this.rangeHighlightDecorations.highlightRange(selection); } - private onContextMenu(e: ITreeContextMenuEvent): void { + private onContextMenu(e: ITreeContextMenuEvent): void { if (!e.element) { return; } @@ -550,21 +550,23 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { e.browserEvent.preventDefault(); e.browserEvent.stopPropagation(); - this.contextMenuService.showContextMenu({ - getAnchor: () => e.anchor, - getActions: () => TPromise.wrap(this._getMenuActions(e.element.element)), - getActionItem: (action) => { - const keybinding = this.keybindingService.lookupKeybinding(action.id); - if (keybinding) { - return new ActionItem(action, action, { label: true, keybinding: keybinding.getLabel() }); - } - return null; - }, - onHide: (wasCancelled?: boolean) => { - if (wasCancelled) { - this.tree.domFocus(); + this._getMenuActions(e.element).then(actions => { + this.contextMenuService.showContextMenu({ + getAnchor: () => e.anchor, + getActions: () => actions, + getActionItem: (action) => { + const keybinding = this.keybindingService.lookupKeybinding(action.id); + if (keybinding) { + return new ActionItem(action, action, { label: true, keybinding: keybinding.getLabel() }); + } + return null; + }, + onHide: (wasCancelled?: boolean) => { + if (wasCancelled) { + this.tree.domFocus(); + } } - } + }); }); } diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts index 6fd16ac8d..6c37bb109 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts @@ -5,7 +5,6 @@ import { Delayer } from 'vs/base/common/async'; import * as DOM from 'vs/base/browser/dom'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Action, IAction, IActionChangeEvent } from 'vs/base/common/actions'; import { HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -69,8 +68,9 @@ export class ShowProblemsPanelAction extends Action { super(id, label); } - public run(): TPromise { - return this.panelService.openPanel(Constants.MARKERS_PANEL_ID, true); + public run(): Thenable { + this.panelService.openPanel(Constants.MARKERS_PANEL_ID, true); + return Promise.resolve(null); } } @@ -147,7 +147,7 @@ export class MarkersFilterActionItem extends BaseActionItem { ) { super(null, action); this.focusContextKey = Constants.MarkerPanelFilterFocusContextKey.bindTo(contextKeyService); - this.delayedFilterUpdate = new Delayer(500); + this.delayedFilterUpdate = new Delayer(200); this._register(toDisposable(() => this.delayedFilterUpdate.cancel())); } @@ -346,7 +346,7 @@ export class QuickFixAction extends Action { })); } - public openFileAtMarker(element: Marker): TPromise { + public openFileAtMarker(element: Marker): Thenable { const { resource, selection } = { resource: element.resource, selection: element.range }; return this.editorService.openEditor({ resource, @@ -409,10 +409,11 @@ export class QuickFixActionItem extends ActionItem { public onClick(event: DOM.EventLike): void { DOM.EventHelper.stop(event, true); const elementPosition = DOM.getDomNodePagePosition(this.element); - this.contextMenuService.showContextMenu({ - getAnchor: () => ({ x: elementPosition.left + 10, y: elementPosition.top + elementPosition.height }), - getActions: () => TPromise.wrap((this.getAction()).getQuickFixActions()), + (this.getAction()).getQuickFixActions().then(actions => { + this.contextMenuService.showContextMenu({ + getAnchor: () => ({ x: elementPosition.left + 10, y: elementPosition.top + elementPosition.height }), + getActions: () => actions + }); }); } - -} \ No newline at end of file +} diff --git a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts index f45304ded..9b537d526 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts @@ -6,6 +6,7 @@ import * as dom from 'vs/base/browser/dom'; import * as network from 'vs/base/common/network'; import * as paths from 'vs/base/common/paths'; +import { escape } from 'vs/base/common/strings'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; import { FileLabel, ResourceLabel } from 'vs/workbench/browser/labels'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; @@ -221,9 +222,9 @@ export class MarkerRenderer implements ITreeRenderer { options = new FilterOptions(); - filter(element: TreeElement): TreeFilterResult { + filter(element: TreeElement, parentVisibility: TreeVisibility): TreeFilterResult { if (element instanceof ResourceMarkers) { return this.filterResourceMarkers(element); } else if (element instanceof Marker) { - return this.filterMarker(element); + return this.filterMarker(element, parentVisibility); } else { - return this.filterRelatedInformation(element); + return this.filterRelatedInformation(element, parentVisibility); } } @@ -347,20 +348,20 @@ export class Filter implements ITreeFilter { return false; } - if (this.options.includePattern && this.options.includePattern(resourceMarkers.resource.fsPath)) { - return true; - } - const uriMatches = FilterOptions._filter(this.options.textFilter, paths.basename(resourceMarkers.resource.fsPath)); if (this.options.textFilter && uriMatches) { return { visibility: true, data: { type: FilterDataType.ResourceMarkers, uriMatches } }; } + if (this.options.includePattern && this.options.includePattern(resourceMarkers.resource.fsPath)) { + return true; + } + return TreeVisibility.Recurse; } - private filterMarker(marker: Marker): TreeFilterResult { + private filterMarker(marker: Marker, parentVisibility: TreeVisibility): TreeFilterResult { if (this.options.filterErrors && MarkerSeverity.Error === marker.marker.severity) { return true; } @@ -385,10 +386,10 @@ export class Filter implements ITreeFilter { return { visibility: true, data: { type: FilterDataType.Marker, messageMatches: messageMatches || [], sourceMatches: sourceMatches || [], codeMatches: codeMatches || [] } }; } - return TreeVisibility.Recurse; + return parentVisibility; } - private filterRelatedInformation(relatedInformation: RelatedInformation): TreeFilterResult { + private filterRelatedInformation(relatedInformation: RelatedInformation, parentVisibility: TreeVisibility): TreeFilterResult { if (!this.options.textFilter) { return true; } @@ -400,6 +401,6 @@ export class Filter implements ITreeFilter { return { visibility: true, data: { type: FilterDataType.RelatedInformation, uriMatches: uriMatches || [], messageMatches: messageMatches || [] } }; } - return false; + return parentVisibility; } } \ No newline at end of file diff --git a/src/vs/workbench/parts/markers/test/electron-browser/markersModel.test.ts b/src/vs/workbench/parts/markers/test/electron-browser/markersModel.test.ts index 4f5cc884e..0b6b6ce60 100644 --- a/src/vs/workbench/parts/markers/test/electron-browser/markersModel.test.ts +++ b/src/vs/workbench/parts/markers/test/electron-browser/markersModel.test.ts @@ -120,7 +120,7 @@ suite('MarkersModel Test', () => { const testObject = new Marker(marker, null); // hack - (testObject as any).relatedInformation = marker.relatedInformation.map(r => new RelatedInformation(r)); + (testObject as any).relatedInformation = marker.relatedInformation.map(r => new RelatedInformation(marker.resource, marker, r)); assert.equal(JSON.stringify({ ...marker, resource: marker.resource.path, relatedInformation: marker.relatedInformation.map(r => ({ ...r, resource: r.resource.path })) }, null, '\t'), testObject.toString()); }); diff --git a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts index dbebd40fa..44f9459ed 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts +++ b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts @@ -21,7 +21,6 @@ import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { LRUCache } from 'vs/base/common/map'; import { escape } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITree } from 'vs/base/parts/tree/browser/tree'; import 'vs/css!./outlinePanel'; import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; @@ -383,12 +382,12 @@ export class OutlinePanel extends ViewletPanel { } } - setVisible(visible: boolean): TPromise { + setVisible(visible: boolean): void { if (visible && this.isExpanded() && !this._requestOracle) { // workaround for https://github.com/Microsoft/vscode/issues/60011 this.setExpanded(true); } - return super.setVisible(visible); + super.setVisible(visible); } setExpanded(expanded: boolean): void { @@ -708,9 +707,9 @@ export class OutlinePanel extends ViewletPanel { if (top < 0 || top > 1) { // only when outside view port await this._tree.reveal(item, .5); - this._tree.setFocus(item, this); - this._tree.setSelection([item], this); } + this._tree.setFocus(item, this); + this._tree.setSelection([item], this); } focusHighlightedElement(up: boolean): void { diff --git a/src/vs/workbench/parts/output/browser/outputActions.ts b/src/vs/workbench/parts/output/browser/outputActions.ts index 287425bc6..14da8c9db 100644 --- a/src/vs/workbench/parts/output/browser/outputActions.ts +++ b/src/vs/workbench/parts/output/browser/outputActions.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { IAction, Action } from 'vs/base/common/actions'; @@ -49,11 +48,11 @@ export class ClearOutputAction extends Action { super(id, label, 'output-action clear-output'); } - public run(): TPromise { + public run(): Promise { this.outputService.getActiveChannel().clear(); aria.status(nls.localize('outputCleared', "Output was cleared")); - return TPromise.as(true); + return Promise.resolve(true); } } @@ -70,14 +69,14 @@ export class ToggleOutputScrollLockAction extends Action { this.toDispose.push(this.outputService.onActiveOutputChannel(channel => this.setClass(this.outputService.getActiveChannel().scrollLock))); } - public run(): TPromise { + public run(): Promise { const activeChannel = this.outputService.getActiveChannel(); if (activeChannel) { activeChannel.scrollLock = !activeChannel.scrollLock; this.setClass(activeChannel.scrollLock); } - return TPromise.as(true); + return Promise.resolve(true); } private setClass(locked: boolean) { @@ -104,7 +103,7 @@ export class SwitchOutputAction extends Action { this.class = 'output-action switch-to-output'; } - public run(channelId?: string): TPromise { + public run(channelId?: string): Thenable { return this.outputService.showChannel(channelId); } } @@ -187,8 +186,8 @@ export class OpenLogOutputFile extends Action { this.enabled = outputChannelDescriptor && outputChannelDescriptor.file && outputChannelDescriptor.log; } - public run(): TPromise { - return this.enabled ? this.editorService.openEditor(this.instantiationService.createInstance(LogViewerInput, this.getOutputChannelDescriptor())).then(() => null) : TPromise.as(null); + public run(): Thenable { + return this.enabled ? this.editorService.openEditor(this.instantiationService.createInstance(LogViewerInput, this.getOutputChannelDescriptor())).then(() => null) : Promise.resolve(null); } private getOutputChannelDescriptor(): IOutputChannelDescriptor { @@ -209,7 +208,7 @@ export class ShowLogsOutputChannelAction extends Action { super(id, label); } - run(): TPromise { + run(): Thenable { const entries: IQuickPickItem[] = this.outputService.getChannelDescriptors().filter(c => c.file && c.log) .map(({ id, label }) => ({ id, label })); @@ -241,7 +240,7 @@ export class OpenOutputLogFileAction extends Action { super(id, label); } - run(): TPromise { + run(): Thenable { const entries: IOutputChannelQuickPickItem[] = this.outputService.getChannelDescriptors().filter(c => c.file && c.log) .map(channel => ({ id: channel.id, label: channel.label, channel })); diff --git a/src/vs/workbench/parts/output/browser/outputPanel.ts b/src/vs/workbench/parts/output/browser/outputPanel.ts index ac5f38f1d..d6eabdfeb 100644 --- a/src/vs/workbench/parts/output/browser/outputPanel.ts +++ b/src/vs/workbench/parts/output/browser/outputPanel.ts @@ -5,7 +5,6 @@ import 'vs/css!./media/output'; import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Action, IAction } from 'vs/base/common/actions'; import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; @@ -116,7 +115,7 @@ export class OutputPanel extends AbstractTextResourceEditor { public setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Thenable { this._focus = !options.preserveFocus; if (input.matches(this.input)) { - return TPromise.as(null); + return Promise.resolve(null); } if (this.input) { diff --git a/src/vs/workbench/parts/output/common/output.ts b/src/vs/workbench/parts/output/common/output.ts index 4aba023c5..61c29285b 100644 --- a/src/vs/workbench/parts/output/common/output.ts +++ b/src/vs/workbench/parts/output/common/output.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { Event, Emitter } from 'vs/base/common/event'; import { Registry } from 'vs/platform/registry/common/platform'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -85,7 +84,7 @@ export interface IOutputService { /** * Show the channel with the passed id. */ - showChannel(id: string, preserveFocus?: boolean): TPromise; + showChannel(id: string, preserveFocus?: boolean): Thenable; /** * Allows to register on active output channel change. diff --git a/src/vs/workbench/parts/output/common/outputLinkComputer.ts b/src/vs/workbench/parts/output/common/outputLinkComputer.ts index 012e4faf4..72f5ed65a 100644 --- a/src/vs/workbench/parts/output/common/outputLinkComputer.ts +++ b/src/vs/workbench/parts/output/common/outputLinkComputer.ts @@ -55,10 +55,10 @@ export class OutputLinkComputer { return null; } - public computeLinks(uri: string): Promise | undefined { + public computeLinks(uri: string): Promise { const model = this.getModel(uri); if (!model) { - return void 0; + return Promise.resolve([]); } const links: ILink[] = []; diff --git a/src/vs/workbench/parts/output/common/outputLinkProvider.ts b/src/vs/workbench/parts/output/common/outputLinkProvider.ts index de9ae22b1..63437034c 100644 --- a/src/vs/workbench/parts/output/common/outputLinkProvider.ts +++ b/src/vs/workbench/parts/output/common/outputLinkProvider.ts @@ -17,7 +17,7 @@ export class OutputLinkProvider { private static readonly DISPOSE_WORKER_TIME = 3 * 60 * 1000; // dispose worker after 3 minutes of inactivity - private worker: MonacoWebWorker; + private worker?: MonacoWebWorker; private disposeWorkerScheduler: RunOnceScheduler; private linkProviderRegistration: IDisposable; @@ -83,7 +83,7 @@ export class OutputLinkProvider { private disposeWorker(): void { if (this.worker) { this.worker.dispose(); - this.worker = null; + this.worker = undefined; } } } diff --git a/src/vs/workbench/parts/output/electron-browser/outputServices.ts b/src/vs/workbench/parts/output/electron-browser/outputServices.ts index c98327eee..e6ef18b8f 100644 --- a/src/vs/workbench/parts/output/electron-browser/outputServices.ts +++ b/src/vs/workbench/parts/output/electron-browser/outputServices.ts @@ -7,7 +7,6 @@ import * as nls from 'vs/nls'; import * as paths from 'vs/base/common/paths'; import * as strings from 'vs/base/common/strings'; import * as extfs from 'vs/base/node/extfs'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Event, Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -70,7 +69,7 @@ interface OutputChannel extends IOutputChannel { readonly file: URI; readonly onDidAppendedContent: Event; readonly onDispose: Event; - loadModel(): TPromise; + loadModel(): Thenable; } abstract class AbstractFileOutputChannel extends Disposable implements OutputChannel { @@ -131,7 +130,7 @@ abstract class AbstractFileOutputChannel extends Disposable implements OutputCha if (this.model) { this.model.setValue(content); } else { - this.model = this.modelService.createModel(content, this.modeService.getOrCreateMode(this.mimeType), this.modelUri); + this.model = this.modelService.createModel(content, this.modeService.create(this.mimeType), this.modelUri); this.onModelCreated(this.model); const disposables: IDisposable[] = []; disposables.push(this.model.onWillDispose(() => { @@ -152,7 +151,7 @@ abstract class AbstractFileOutputChannel extends Disposable implements OutputCha } } - abstract loadModel(): TPromise; + abstract loadModel(): Thenable; abstract append(message: string); protected onModelCreated(model: ITextModel) { } @@ -217,7 +216,7 @@ class OutputChannelBackedByFile extends AbstractFileOutputChannel implements Out this.appendedMessage = ''; } - loadModel(): TPromise { + loadModel(): Thenable { this.loadingFromFileInProgress = true; if (this.modelUpdater.isScheduled()) { this.modelUpdater.cancel(); @@ -243,16 +242,16 @@ class OutputChannelBackedByFile extends AbstractFileOutputChannel implements Out }); } - private resetModel(): TPromise { + private resetModel(): Thenable { this.startOffset = 0; this.endOffset = 0; if (this.model) { - return this.loadModel() as TPromise; + return this.loadModel().then(() => null); } - return TPromise.as(null); + return Promise.resolve(null); } - private loadFile(): TPromise { + private loadFile(): Thenable { return this.fileService.resolveContent(this.file, { position: this.startOffset, encoding: 'utf8' }) .then(content => this.appendedMessage ? content.value + this.appendedMessage : content.value); } @@ -310,7 +309,7 @@ class OutputFileListener extends Disposable { this.syncDelayer.trigger(loop); } - private doWatch(): TPromise { + private doWatch(): Thenable { return this.fileService.resolveFile(this.file) .then(stat => { if (stat.etag !== this.etag) { @@ -342,7 +341,7 @@ class FileOutputChannel extends AbstractFileOutputChannel implements OutputChann private updateInProgress: boolean = false; private etag: string = ''; - private loadModelPromise: TPromise = TPromise.as(null); + private loadModelPromise: Thenable = Promise.resolve(); constructor( outputChannelDescriptor: IOutputChannelDescriptor, @@ -358,7 +357,7 @@ class FileOutputChannel extends AbstractFileOutputChannel implements OutputChann this._register(toDisposable(() => this.fileHandler.unwatch())); } - loadModel(): TPromise { + loadModel(): Thenable { this.loadModelPromise = this.fileService.resolveContent(this.file, { position: this.startOffset, encoding: 'utf8' }) .then(content => { this.endOffset = this.startOffset + Buffer.from(content.value).byteLength; @@ -474,30 +473,30 @@ export class OutputService extends Disposable implements IOutputService, ITextMo this._register(this.storageService.onWillSaveState(() => this.saveState())); } - provideTextContent(resource: URI): TPromise { + provideTextContent(resource: URI): Thenable { const channel = this.getChannel(resource.path); if (channel) { return channel.loadModel(); } - return TPromise.as(null); + return null; } - showChannel(id: string, preserveFocus?: boolean): TPromise { + showChannel(id: string, preserveFocus?: boolean): Thenable { const channel = this.getChannel(id); if (!channel || this.isChannelShown(channel)) { if (this._outputPanel && !preserveFocus) { this._outputPanel.focus(); } - return TPromise.as(null); + return Promise.resolve(null); } this.activeChannel = channel; - let promise: TPromise = TPromise.as(null); + let promise: Thenable; if (this.isPanelShown()) { - this.doShowChannel(channel, preserveFocus); + promise = this.doShowChannel(channel, preserveFocus); } else { - promise = this.panelService.openPanel(OUTPUT_PANEL_ID) - .then(() => this.doShowChannel(this.activeChannel, preserveFocus)); + this.panelService.openPanel(OUTPUT_PANEL_ID); + promise = this.doShowChannel(this.activeChannel, preserveFocus); } return promise.then(() => this._onActiveOutputChannel.fire(id)); } @@ -531,7 +530,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo return this.doShowChannel(this.activeChannel, preserveFocus); } } - return TPromise.as(null); + return Promise.resolve(null); } private onDidPanelClose(panel: IPanel): void { @@ -617,7 +616,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo // Activate smart scroll when switching back to the output panel .then(() => this.setPrimaryCursorToLastLine()); } - return TPromise.as(null); + return Promise.resolve(null); } private isChannelShown(channel: IOutputChannel): boolean { @@ -651,14 +650,14 @@ export class LogContentProvider { ) { } - provideTextContent(resource: URI): TPromise { + provideTextContent(resource: URI): Thenable { if (resource.scheme === LOG_SCHEME) { let channel = this.getChannel(resource); if (channel) { return channel.loadModel(); } } - return TPromise.as(null); + return null; } private getChannel(resource: URI): OutputChannel { @@ -732,7 +731,7 @@ class BufferredOutputChannel extends Disposable implements OutputChannel { this.lastReadId = void 0; } - loadModel(): TPromise { + loadModel(): Thenable { const { value, id } = this.bufferredContent.getDelta(this.lastReadId); if (this.model) { this.model.setValue(value); @@ -740,11 +739,11 @@ class BufferredOutputChannel extends Disposable implements OutputChannel { this.model = this.createModel(value); } this.lastReadId = id; - return TPromise.as(this.model); + return Promise.resolve(this.model); } private createModel(content: string): ITextModel { - const model = this.modelService.createModel(content, this.modeService.getOrCreateMode(OUTPUT_MIME), URI.from({ scheme: OUTPUT_SCHEME, path: this.id })); + const model = this.modelService.createModel(content, this.modeService.create(OUTPUT_MIME), URI.from({ scheme: OUTPUT_SCHEME, path: this.id })); const disposables: IDisposable[] = []; disposables.push(model.onWillDispose(() => { this.model = null; diff --git a/src/vs/workbench/parts/performance/electron-browser/actions.ts b/src/vs/workbench/parts/performance/electron-browser/actions.ts index d54517c7f..ca7faa097 100644 --- a/src/vs/workbench/parts/performance/electron-browser/actions.ts +++ b/src/vs/workbench/parts/performance/electron-browser/actions.ts @@ -33,7 +33,8 @@ class Info { table['window.loadUrl() => begin to require(workbench.main.js)'] = new Info(metrics.timers.ellapsedWindowLoadToRequire, '[main->renderer]', StartupKindToString(metrics.windowKind)); table['require(workbench.main.js)'] = new Info(metrics.timers.ellapsedRequire, '[renderer]', `cached data: ${(metrics.didUseCachedData ? 'YES' : 'NO')}${nodeModuleLoadTime ? `, node_modules took ${nodeModuleLoadTime}ms` : ''}`); - table['init workspace storage'] = new Info(metrics.timers.ellapsedWorkspaceStorageInit, '[renderer]'); + table['require workspace storage'] = new Info(metrics.timers.ellapsedWorkspaceStorageRequire, '[renderer]'); + table['require & init workspace storage'] = new Info(metrics.timers.ellapsedWorkspaceStorageInit, '[renderer]'); table['register extensions & spawn extension host'] = new Info(metrics.timers.ellapsedExtensions, '[renderer]'); table['restore viewlet'] = new Info(metrics.timers.ellapsedViewletRestore, '[renderer]', metrics.viewletId); @@ -264,29 +265,27 @@ export class ReportPerformanceIssueAction extends Action { } - const osVersion = `${os.type()} ${os.arch()} ${os.release()} `; + const osVersion = `${os.type()} ${os.arch()} ${os.release()}`; const queryStringPrefix = baseUrl.indexOf('?') === -1 ? '?' : '&'; const body = encodeURIComponent( `- VSCode Version: ${name} ${version} ${isPure ? '' : ' **[Unsupported]**'} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'}) - - OS Version: ${ osVersion} - - CPUs: ${ metrics.cpus.model} (${metrics.cpus.count} x ${metrics.cpus.speed}) - - Memory(System): ${ (metrics.totalmem / (1024 * 1024 * 1024)).toFixed(2)} GB(${(metrics.freemem / (1024 * 1024 * 1024)).toFixed(2)}GB free) < /code> - - Memory(Process): ${ (metrics.meminfo.workingSetSize / 1024).toFixed(2)} MB working set(${(metrics.meminfo.peakWorkingSetSize / 1024).toFixed(2)}MB peak, ${(metrics.meminfo.privateBytes / 1024).toFixed(2)}MB private, ${(metrics.meminfo.sharedBytes / 1024).toFixed(2)}MB shared) < /code> - - Load(avg): ${ metrics.loadavg.map(l => Math.round(l)).join(', ')} - - VM: ${ metrics.isVMLikelyhood}% - - Initial Startup: ${ metrics.initialStartup ? 'yes' : 'no'} - - Screen Reader: ${ metrics.hasAccessibilitySupport ? 'yes' : 'no'} - - Empty Workspace: ${ metrics.emptyWorkbench ? 'yes' : 'no'} - - Timings: - -${ this.generatePerformanceTable(metrics, nodeModuleLoadTime)} - +- OS Version: ${ osVersion} +- CPUs: ${ metrics.cpus.model} (${metrics.cpus.count} x ${metrics.cpus.speed}) +- Memory(System): ${ (metrics.totalmem / (1024 * 1024 * 1024)).toFixed(2)} GB(${(metrics.freemem / (1024 * 1024 * 1024)).toFixed(2)}GB free) +- Memory(Process): ${ (metrics.meminfo.workingSetSize / 1024).toFixed(2)} MB working set(${(metrics.meminfo.peakWorkingSetSize / 1024).toFixed(2)}MB peak, ${(metrics.meminfo.privateBytes / 1024).toFixed(2)}MB private, ${(metrics.meminfo.sharedBytes / 1024).toFixed(2)}MB shared) +- Load(avg): ${ metrics.loadavg.map(l => Math.round(l)).join(', ')} +- VM: ${ metrics.isVMLikelyhood}% +- Initial Startup: ${ metrics.initialStartup ? 'yes' : 'no'} +- Screen Reader: ${ metrics.hasAccessibilitySupport ? 'yes' : 'no'} +- Empty Workspace: ${ metrics.emptyWorkbench ? 'yes' : 'no'} +- Timings: + +${this.generatePerformanceTable(metrics, nodeModuleLoadTime)} --- - ${ appendix} ` - ); +${appendix}`); - return `${baseUrl} ${queryStringPrefix} body = ${body} `; + return `${baseUrl}${queryStringPrefix}body=${body}`; } private generatePerformanceTable(metrics: IStartupMetrics, nodeModuleLoadTime?: number): string { diff --git a/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts b/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts index ee89b7889..9fbd72340 100644 --- a/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts +++ b/src/vs/workbench/parts/performance/electron-browser/performance.contribution.ts @@ -5,5 +5,4 @@ import './startupProfiler'; import './startupTimings'; -import './startupTimingsAppender'; import './stats'; diff --git a/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts b/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts index baccbedf7..2a1d9bd4a 100644 --- a/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts +++ b/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts @@ -17,6 +17,10 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { ITimerService, didUseCachedData } from 'vs/workbench/services/timer/electron-browser/timerService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import product from 'vs/platform/node/product'; +import { timeout, nfcall } from 'vs/base/common/async'; +import { appendFile } from 'fs'; class StartupTimings implements IWorkbenchContribution { @@ -30,13 +34,21 @@ class StartupTimings implements IWorkbenchContribution { @ITelemetryService private readonly _telemetryService: ITelemetryService, @ILifecycleService private readonly _lifecycleService: ILifecycleService, @IUpdateService private readonly _updateService: IUpdateService, + @IEnvironmentService private readonly _envService: IEnvironmentService, ) { + // + this._report().catch(onUnexpectedError); + } - this._reportVariedStartupTimes().then(undefined, onUnexpectedError); - this._reportStandardStartupTimes().then(undefined, onUnexpectedError); + private async _report() { + const isStandardStartup = await this._isStandardStartup(); + this._reportStartupTimes(isStandardStartup).catch(onUnexpectedError); + this._appendStartupTimes(isStandardStartup).catch(onUnexpectedError); } - private async _reportVariedStartupTimes(): Promise { + private async _reportStartupTimes(isStandardStartup: boolean): Promise { + const metrics = await this._timerService.startupMetrics; + /* __GDPR__ "startupTimeVaried" : { "${include}": [ @@ -44,10 +56,46 @@ class StartupTimings implements IWorkbenchContribution { ] } */ - this._telemetryService.publicLog('startupTimeVaried', await this._timerService.startupMetrics); + this._telemetryService.publicLog('startupTimeVaried', metrics); + + /* __GDPR__ + "startupTime" : { + "${include}": [ + "${IStartupMetrics}" + ] + } + */ + this._telemetryService.publicLog('startupTime', metrics); + } + + private async _appendStartupTimes(isStandardStartup: boolean) { + let appendTo = this._envService.args['prof-append-timers']; + if (!appendTo) { + // nothing to do + return; + } + + const waitWhenNoCachedData: () => Promise = () => { + // wait 15s for cached data to be produced + return !didUseCachedData() + ? timeout(15000) + : Promise.resolve(); + }; + + Promise.all([ + this._timerService.startupMetrics, + waitWhenNoCachedData(), + ]).then(([startupMetrics]) => { + return nfcall(appendFile, appendTo, `${startupMetrics.ellapsed}\t${product.nameLong}\t${product.commit || '0000000'}\t${isStandardStartup ? 'standard_start' : 'NOT_standard_start'}\n`); + }).then(() => { + this._windowsService.quit(); + }).catch(err => { + console.error(err); + this._windowsService.quit(); + }); } - private async _reportStandardStartupTimes(): Promise { + private async _isStandardStartup(): Promise { // check for standard startup: // * new window (no reload) // * just one window @@ -56,46 +104,37 @@ class StartupTimings implements IWorkbenchContribution { // * cached data present (not rejected, not created) if (this._lifecycleService.startupKind !== StartupKind.NewWindow) { this._logService.info('no standard startup: not a new window'); - return; + return false; } if (await this._windowsService.getWindowCount() !== 1) { this._logService.info('no standard startup: not just one window'); - return; + return false; } if (!this._viewletService.getActiveViewlet() || this._viewletService.getActiveViewlet().getId() !== files.VIEWLET_ID) { this._logService.info('no standard startup: not the explorer viewlet'); - return; + return false; } const visibleControls = this._editorService.visibleControls; if (visibleControls.length !== 1 || !isCodeEditor(visibleControls[0].getControl())) { this._logService.info('no standard startup: not just one text editor'); - return; + return false; } if (this._panelService.getActivePanel()) { this._logService.info('no standard startup: panel is active'); - return; + return false; } if (!didUseCachedData()) { this._logService.info('no standard startup: not using cached data'); - return; + return false; } if (!await this._updateService.isLatestVersion()) { this._logService.info('no standard startup: not running latest version'); - return; + return false; } - - /* __GDPR__ - "startupTime" : { - "${include}": [ - "${IStartupMetrics}" - ] - } - */ - const metrics = await this._timerService.startupMetrics; - this._telemetryService.publicLog('startupTime', metrics); - this._logService.info('standard startup', metrics); + this._logService.info('standard startup'); + return true; } } const registry = Registry.as(Extensions.Workbench); -registry.registerWorkbenchContribution(StartupTimings, LifecyclePhase.Running); +registry.registerWorkbenchContribution(StartupTimings, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/parts/performance/electron-browser/startupTimingsAppender.ts b/src/vs/workbench/parts/performance/electron-browser/startupTimingsAppender.ts deleted file mode 100644 index 4b70f8075..000000000 --- a/src/vs/workbench/parts/performance/electron-browser/startupTimingsAppender.ts +++ /dev/null @@ -1,53 +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 { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { IWindowsService } from 'vs/platform/windows/common/windows'; -import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions } from 'vs/workbench/common/contributions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { ITimerService, didUseCachedData } from 'vs/workbench/services/timer/electron-browser/timerService'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { nfcall, timeout } from 'vs/base/common/async'; -import { appendFile, } from 'fs'; -import product from 'vs/platform/node/product'; - -class StartupTimingsAppender implements IWorkbenchContribution { - - constructor( - @ITimerService timerService: ITimerService, - @IWindowsService windowsService: IWindowsService, - @ILifecycleService lifecycleService: ILifecycleService, - @IEnvironmentService environmentService: IEnvironmentService, - ) { - - let appendTo = environmentService.args['prof-append-timers']; - if (!appendTo) { - // nothing to do - return; - } - - Promise.all([ - timerService.startupMetrics, - this._waitWhenNoCachedData(), - ]).then(([startupMetrics]) => { - return nfcall(appendFile, appendTo, `${startupMetrics.ellapsed}\t${product.nameLong}\t${product.commit || '0000000'}\n`); - }).then(() => { - windowsService.quit(); - }).catch(err => { - console.error(err); - windowsService.quit(); - }); - } - - private _waitWhenNoCachedData(): Promise { - // wait 15s for cached data to be produced - return !didUseCachedData() - ? timeout(15000) - : Promise.resolve(); - } -} - -const registry = Registry.as(Extensions.Workbench); -registry.registerWorkbenchContribution(StartupTimingsAppender, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/parts/preferences/browser/keybindingWidgets.ts b/src/vs/workbench/parts/preferences/browser/keybindingWidgets.ts index 189020b8c..b975a47e9 100644 --- a/src/vs/workbench/parts/preferences/browser/keybindingWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/keybindingWidgets.ts @@ -6,7 +6,6 @@ import 'vs/css!./media/keybindings'; import * as nls from 'vs/nls'; import { OS } from 'vs/base/common/platform'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Disposable, dispose, toDisposable, IDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; @@ -180,9 +179,9 @@ export class DefineKeybindingWidget extends Widget { return this._domNode.domNode; } - define(): TPromise { + define(): Promise { this._keybindingInputWidget.clear(); - return new TPromise((c, e) => { + return new Promise((c) => { if (!this._isVisible) { this._isVisible = true; this._domNode.setDisplay('block'); @@ -331,7 +330,7 @@ export class DefineKeybindingOverlayWidget extends Disposable implements IOverla super.dispose(); } - public start(): TPromise { + public start(): Promise { this._editor.revealPositionInCenterIfOutsideViewport(this._editor.getPosition(), ScrollType.Smooth); const layoutInfo = this._editor.getLayoutInfo(); this._widget.layout(new dom.Dimension(layoutInfo.width, layoutInfo.height)); diff --git a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts index 4966fbe4f..c06fb76f7 100644 --- a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts @@ -5,7 +5,6 @@ import 'vs/css!./media/keybindingsEditor'; import { localize } from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Delayer } from 'vs/base/common/async'; import * as DOM from 'vs/base/browser/dom'; import { OS } from 'vs/base/common/platform'; @@ -154,7 +153,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor return focusedElement && focusedElement.templateId === KEYBINDING_ENTRY_TEMPLATE_ID ? focusedElement : null; } - defineKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise { + defineKeybinding(keybindingEntry: IKeybindingItemEntry): Thenable { this.selectEntry(keybindingEntry); this.showOverlayContainer(); return this.defineKeybindingWidget.define().then(key => { @@ -182,7 +181,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor }); } - removeKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise { + removeKeybinding(keybindingEntry: IKeybindingItemEntry): Thenable { this.selectEntry(keybindingEntry); if (keybindingEntry.keybindingItem.keybinding) { // This should be a pre-condition this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_REMOVE, keybindingEntry.keybindingItem.command, keybindingEntry.keybindingItem.keybinding); @@ -193,10 +192,10 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor this.selectEntry(keybindingEntry); }); } - return TPromise.as(null); + return Promise.resolve(null); } - resetKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise { + resetKeybinding(keybindingEntry: IKeybindingItemEntry): Thenable { this.selectEntry(keybindingEntry); this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_RESET, keybindingEntry.keybindingItem.command, keybindingEntry.keybindingItem.keybinding); return this.keybindingEditingService.resetKeybinding(keybindingEntry.keybindingItem.keybindingItem) @@ -212,7 +211,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor }); } - copyKeybinding(keybinding: IKeybindingItemEntry): TPromise { + copyKeybinding(keybinding: IKeybindingItemEntry): void { this.selectEntry(keybinding); this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_COPY, keybinding.keybindingItem.command, keybinding.keybindingItem.keybinding); const userFriendlyKeybinding: IUserFriendlyKeybinding = { @@ -223,14 +222,12 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor userFriendlyKeybinding.when = keybinding.keybindingItem.when; } this.clipboardService.writeText(JSON.stringify(userFriendlyKeybinding, null, ' ')); - return TPromise.as(null); } - copyKeybindingCommand(keybinding: IKeybindingItemEntry): TPromise { + copyKeybindingCommand(keybinding: IKeybindingItemEntry): void { this.selectEntry(keybinding); this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, keybinding.keybindingItem.command, keybinding.keybindingItem.keybinding); this.clipboardService.writeText(keybinding.keybindingItem.command); - return TPromise.as(null); } focusSearch(): void { @@ -246,12 +243,11 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor this.searchWidget.clear(); } - showSimilarKeybindings(keybindingEntry: IKeybindingItemEntry): TPromise { + showSimilarKeybindings(keybindingEntry: IKeybindingItemEntry): void { const value = `"${keybindingEntry.keybindingItem.keybinding.getAriaLabel()}"`; if (value !== this.searchWidget.getValue()) { this.searchWidget.setValue(value); } - return TPromise.as(null); } private createAriaLabelElement(parent: HTMLElement): void { @@ -425,7 +421,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor })); } - private render(preserveFocus: boolean, token: CancellationToken): TPromise { + private render(preserveFocus: boolean, token: CancellationToken): Thenable { if (this.input) { return this.input.resolve() .then((keybindingsModel: KeybindingsEditorModel) => { @@ -450,7 +446,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor this.renderKeybindingsEntries(false, preserveFocus); }); } - return TPromise.as(null); + return Promise.resolve(); } private filterKeybindings(): void { @@ -560,7 +556,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor this.selectEntry(e.element); this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, - getActions: () => TPromise.as([ + getActions: () => [ this.createCopyAction(e.element), this.createCopyCommandAction(e.element), new Separator(), @@ -568,7 +564,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor this.createRemoveAction(e.element), this.createResetAction(e.element), new Separator(), - this.createShowConflictsAction(e.element)]) + this.createShowConflictsAction(e.element)] }); } } @@ -863,15 +859,15 @@ class CommandColumn extends Column { this.commandColumn.setAttribute('aria-label', this.getAriaLabel(keybindingItemEntry)); let commandLabel: HighlightedLabel; if (keybindingItem.commandLabel) { - commandLabel = new HighlightedLabel(this.commandColumn); + commandLabel = new HighlightedLabel(this.commandColumn, false); commandLabel.set(keybindingItem.commandLabel, keybindingItemEntry.commandLabelMatches); } if (keybindingItemEntry.commandDefaultLabelMatches) { - commandLabel = new HighlightedLabel(DOM.append(this.commandColumn, $('.command-default-label'))); + commandLabel = new HighlightedLabel(DOM.append(this.commandColumn, $('.command-default-label')), false); commandLabel.set(keybindingItem.commandDefaultLabel, keybindingItemEntry.commandDefaultLabelMatches); } if (keybindingItemEntry.commandIdMatches || !keybindingItem.commandLabel) { - commandLabel = new HighlightedLabel(DOM.append(this.commandColumn, $('.code'))); + commandLabel = new HighlightedLabel(DOM.append(this.commandColumn, $('.code')), false); commandLabel.set(keybindingItem.command, keybindingItemEntry.commandIdMatches); } if (commandLabel) { @@ -918,7 +914,7 @@ class SourceColumn extends Column { render(keybindingItemEntry: IKeybindingItemEntry): void { DOM.clearNode(this.sourceColumn); this.sourceColumn.setAttribute('aria-label', this.getAriaLabel(keybindingItemEntry)); - new HighlightedLabel(this.sourceColumn).set(keybindingItemEntry.keybindingItem.source, keybindingItemEntry.sourceMatches); + new HighlightedLabel(this.sourceColumn, false).set(keybindingItemEntry.keybindingItem.source, keybindingItemEntry.sourceMatches); } private getAriaLabel(keybindingItemEntry: IKeybindingItemEntry): string { @@ -942,7 +938,7 @@ class WhenColumn extends Column { DOM.toggleClass(this.whenColumn, 'code', !!keybindingItemEntry.keybindingItem.when); DOM.toggleClass(this.whenColumn, 'empty', !keybindingItemEntry.keybindingItem.when); if (keybindingItemEntry.keybindingItem.when) { - const whenLabel = new HighlightedLabel(this.whenColumn); + const whenLabel = new HighlightedLabel(this.whenColumn, false); whenLabel.set(keybindingItemEntry.keybindingItem.when, keybindingItemEntry.whenMatches); this.whenColumn.title = keybindingItemEntry.keybindingItem.when; whenLabel.element.title = keybindingItemEntry.keybindingItem.when; diff --git a/src/vs/workbench/parts/preferences/browser/preferencesActions.ts b/src/vs/workbench/parts/preferences/browser/preferencesActions.ts index db35ad971..660f311f5 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesActions.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesActions.ts @@ -6,7 +6,7 @@ import { Action } from 'vs/base/common/actions'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import * as nls from 'vs/nls'; @@ -14,7 +14,6 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; -import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; export class OpenRawDefaultSettingsAction extends Action { @@ -30,7 +29,7 @@ export class OpenRawDefaultSettingsAction extends Action { super(id, label); } - public run(event?: any): TPromise { + public run(event?: any): Thenable { return this.preferencesService.openRawDefaultSettings(); } } @@ -48,7 +47,7 @@ export class OpenSettings2Action extends Action { super(id, label); } - public run(event?: any): TPromise { + public run(event?: any): Thenable { return this.preferencesService.openSettings(false); } } @@ -66,7 +65,7 @@ export class OpenSettingsAction extends Action { super(id, label); } - public run(event?: any): TPromise { + public run(event?: any): Thenable { return this.preferencesService.openSettings(); } } @@ -84,7 +83,7 @@ export class OpenSettingsJsonAction extends Action { super(id, label); } - public run(event?: any): TPromise { + public run(event?: any): Thenable { return this.preferencesService.openSettings(true); } } @@ -102,7 +101,7 @@ export class OpenGlobalSettingsAction extends Action { super(id, label); } - public run(event?: any): TPromise { + public run(event?: any): Thenable { return this.preferencesService.openGlobalSettings(); } } @@ -120,7 +119,7 @@ export class OpenGlobalKeybindingsAction extends Action { super(id, label); } - public run(event?: any): TPromise { + public run(event?: any): Thenable { return this.preferencesService.openGlobalKeybindingSettings(false); } } @@ -138,7 +137,7 @@ export class OpenGlobalKeybindingsFileAction extends Action { super(id, label); } - public run(event?: any): TPromise { + public run(event?: any): Thenable { return this.preferencesService.openGlobalKeybindingSettings(true); } } @@ -156,7 +155,7 @@ export class OpenDefaultKeybindingsFileAction extends Action { super(id, label); } - public run(event?: any): TPromise { + public run(event?: any): Thenable { return this.preferencesService.openDefaultKeybindingsFile(); } } @@ -183,7 +182,7 @@ export class OpenWorkspaceSettingsAction extends Action { this.enabled = this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY; } - public run(event?: any): TPromise { + public run(event?: any): Thenable { return this.preferencesService.openWorkspaceSettings(); } @@ -220,7 +219,7 @@ export class OpenFolderSettingsAction extends Action { this.enabled = this.workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE && this.workspaceContextService.getWorkspace().folders.length > 0; } - public run(): TPromise { + public run(): Thenable { return this.commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID) .then(workspaceFolder => { if (workspaceFolder) { @@ -253,7 +252,7 @@ export class ConfigureLanguageBasedSettingsAction extends Action { super(id, label); } - public run(): TPromise { + public run(): Thenable { const languages = this.modeService.getRegisteredLanguageNames(); const picks: IQuickPickItem[] = languages.sort().map((lang, index) => { let description: string = nls.localize('languageDescriptionConfigured', "({0})", this.modeService.getModeIdForLanguageName(lang.toLowerCase())); @@ -278,8 +277,8 @@ export class ConfigureLanguageBasedSettingsAction extends Action { return this.quickInputService.pick(picks, { placeHolder: nls.localize('pickLanguage', "Select Language") }) .then(pick => { if (pick) { - return this.modeService.getOrCreateModeByLanguageName(pick.label) - .then(mode => this.preferencesService.configureSettingsForLanguage(mode.getLanguageIdentifier().language)); + const modeId = this.modeService.getModeIdForLanguageName(pick.label.toLowerCase()); + return this.preferencesService.configureSettingsForLanguage(modeId); } return undefined; }); diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index 3a18ea81a..c5db416d9 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -16,7 +16,6 @@ import { ArrayNavigator } from 'vs/base/common/iterator'; import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorExtensionsRegistry, IEditorContributionCtor, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; @@ -48,7 +47,7 @@ import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorMo import { PREFERENCES_EDITOR_ID } from 'vs/workbench/parts/files/common/files'; import { DefaultSettingsRenderer, FolderSettingsRenderer, IPreferencesRenderer, UserSettingsRenderer, WorkspaceSettingsRenderer } from 'vs/workbench/parts/preferences/browser/preferencesRenderers'; import { SearchWidget, SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; -import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, IPreferencesSearchService, ISearchProvider } from 'vs/workbench/parts/preferences/common/preferences'; +import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, IPreferencesSearchService, ISearchProvider, CONTEXT_SETTINGS_JSON_EDITOR } from 'vs/workbench/parts/preferences/common/preferences'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import { IFilterResult, IPreferencesService, ISearchResult, ISetting, ISettingsEditorModel, ISettingsGroup, SettingsEditorOptions } from 'vs/workbench/services/preferences/common/preferences'; @@ -62,6 +61,7 @@ export class PreferencesEditor extends BaseEditor { public static readonly ID: string = PREFERENCES_EDITOR_ID; private defaultSettingsEditorContextKey: IContextKey; + private defaultSettingsJSONEditorContextKey: IContextKey; private searchFocusContextKey: IContextKey; private headerContainer: HTMLElement; private searchWidget: SearchWidget; @@ -99,6 +99,7 @@ export class PreferencesEditor extends BaseEditor { ) { super(PreferencesEditor.ID, telemetryService, themeService, storageService); this.defaultSettingsEditorContextKey = CONTEXT_SETTINGS_EDITOR.bindTo(this.contextKeyService); + this.defaultSettingsJSONEditorContextKey = CONTEXT_SETTINGS_JSON_EDITOR.bindTo(this.contextKeyService); this.searchFocusContextKey = CONTEXT_SETTINGS_SEARCH_FOCUS.bindTo(this.contextKeyService); this.delayedFilterLogging = new Delayer(1000); this.localSearchDelayer = new Delayer(100); @@ -155,6 +156,7 @@ export class PreferencesEditor extends BaseEditor { public setInput(newInput: PreferencesEditorInput, options: SettingsEditorOptions, token: CancellationToken): Thenable { this.defaultSettingsEditorContextKey.set(true); + this.defaultSettingsJSONEditorContextKey.set(true); if (options && options.query) { this.focusSearch(options.query); } @@ -194,6 +196,7 @@ export class PreferencesEditor extends BaseEditor { public clearInput(): void { this.defaultSettingsEditorContextKey.set(false); + this.defaultSettingsJSONEditorContextKey.set(false); this.sideBySidePreferencesWidget.clearInput(); this.preferencesRenderers.onHidden(); super.clearInput(); @@ -204,7 +207,7 @@ export class PreferencesEditor extends BaseEditor { super.setEditorVisible(visible, group); } - private updateInput(newInput: PreferencesEditorInput, options: EditorOptions, token: CancellationToken): TPromise { + private updateInput(newInput: PreferencesEditorInput, options: EditorOptions, token: CancellationToken): Promise { return this.sideBySidePreferencesWidget.setInput(newInput.details, newInput.master, options, token).then(({ defaultPreferencesRenderer, editablePreferencesRenderer }) => { if (token.isCancellationRequested) { return void 0; @@ -230,12 +233,12 @@ export class PreferencesEditor extends BaseEditor { }); } - private triggerSearch(query: string): TPromise { + private triggerSearch(query: string): Promise { if (query) { - return TPromise.join([ + return Promise.all([ this.localSearchDelayer.trigger(() => this.preferencesRenderers.localFilterPreferences(query).then(() => { })), - this.remoteSearchThrottle.trigger(() => TPromise.wrap(this.progressService.showWhile(this.preferencesRenderers.remoteSearchPreferences(query), 500))) - ]) as TPromise; + this.remoteSearchThrottle.trigger(() => Promise.resolve(this.progressService.showWhile(this.preferencesRenderers.remoteSearchPreferences(query), 500))) + ]).then(() => { }); } else { // When clearing the input, update immediately to clear it this.localSearchDelayer.cancel(); @@ -251,8 +254,8 @@ export class PreferencesEditor extends BaseEditor { if (this.editorService.activeControl !== this) { this.focus(); } - const promise = this.input && this.input.isDirty() ? this.input.save() : TPromise.as(true); - promise.then(value => { + const promise: Thenable = this.input && this.input.isDirty() ? this.input.save() : Promise.resolve(true); + promise.then(() => { if (target === ConfigurationTarget.USER) { this.preferencesService.switchSettings(ConfigurationTarget.USER, this.preferencesService.userSettingsResource, true); } else if (target === ConfigurationTarget.WORKSPACE) { @@ -269,14 +272,14 @@ export class PreferencesEditor extends BaseEditor { this.sideBySidePreferencesWidget.setResultCount(count.target, count.count); } else if (this.searchWidget.getValue()) { if (countValue === 0) { - this.searchWidget.showMessage(nls.localize('noSettingsFound', "No Results"), countValue); + this.searchWidget.showMessage(nls.localize('noSettingsFound', "No Settings Found")); } else if (countValue === 1) { - this.searchWidget.showMessage(nls.localize('oneSettingFound', "1 Setting Found"), countValue); + this.searchWidget.showMessage(nls.localize('oneSettingFound', "1 Setting Found")); } else { - this.searchWidget.showMessage(nls.localize('settingsFound', "{0} Settings Found", countValue), countValue); + this.searchWidget.showMessage(nls.localize('settingsFound', "{0} Settings Found", countValue)); } } else { - this.searchWidget.showMessage(nls.localize('totalSettingsMessage', "Total {0} Settings", countValue), countValue); + this.searchWidget.showMessage(nls.localize('totalSettingsMessage', "Total {0} Settings", countValue)); } } @@ -438,7 +441,7 @@ class PreferencesRenderersController extends Disposable { this._prefsModelsForSearch = new Map(); } - remoteSearchPreferences(query: string, updateCurrentResults?: boolean): TPromise { + remoteSearchPreferences(query: string, updateCurrentResults?: boolean): Promise { if (this.lastFilterResult && this.lastFilterResult.exactMatch) { // Skip and clear remote search query = ''; @@ -467,7 +470,7 @@ class PreferencesRenderersController extends Disposable { }); } - localFilterPreferences(query: string, updateCurrentResults?: boolean): TPromise { + localFilterPreferences(query: string, updateCurrentResults?: boolean): Promise { if (this._settingsNavigator) { this._settingsNavigator.reset(); } @@ -476,10 +479,10 @@ class PreferencesRenderersController extends Disposable { return this.filterOrSearchPreferences(query, this._currentLocalSearchProvider, 'filterResult', nls.localize('filterResult', "Filtered Results"), 0, undefined, updateCurrentResults); } - private filterOrSearchPreferences(query: string, searchProvider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken, editableContentOnly?: boolean): TPromise { + private filterOrSearchPreferences(query: string, searchProvider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken, editableContentOnly?: boolean): Promise { this._lastQuery = query; - const filterPs: TPromise[] = [this._filterOrSearchPreferences(query, this.editablePreferencesRenderer, searchProvider, groupId, groupLabel, groupOrder, token)]; + const filterPs: Promise[] = [this._filterOrSearchPreferences(query, this.editablePreferencesRenderer, searchProvider, groupId, groupLabel, groupOrder, token)]; if (!editableContentOnly) { filterPs.push( this._filterOrSearchPreferences(query, this.defaultPreferencesRenderer, searchProvider, groupId, groupLabel, groupOrder, token)); @@ -487,7 +490,7 @@ class PreferencesRenderersController extends Disposable { this.searchAllSettingsTargets(query, searchProvider, groupId, groupLabel, groupOrder, token).then(() => null)); } - return TPromise.join(filterPs).then(results => { + return Promise.all(filterPs).then(results => { let [editableFilterResult, defaultFilterResult] = results; if (!defaultFilterResult && editableContentOnly) { @@ -501,7 +504,7 @@ class PreferencesRenderersController extends Disposable { }); } - private searchAllSettingsTargets(query: string, searchProvider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): TPromise { + private searchAllSettingsTargets(query: string, searchProvider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): Promise { const searchPs = [ this.searchSettingsTarget(query, searchProvider, ConfigurationTarget.WORKSPACE, groupId, groupLabel, groupOrder, token), this.searchSettingsTarget(query, searchProvider, ConfigurationTarget.USER, groupId, groupLabel, groupOrder, token) @@ -513,7 +516,7 @@ class PreferencesRenderersController extends Disposable { } - return TPromise.join(searchPs).then(() => { }); + return Promise.all(searchPs).then(() => { }); } private searchSettingsTarget(query: string, provider: ISearchProvider, target: SettingsTarget, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): Promise { @@ -530,7 +533,7 @@ class PreferencesRenderersController extends Disposable { this._onDidFilterResultsCountChange.fire({ target, count }); }, err => { if (!isPromiseCanceledError(err)) { - return TPromise.wrapError(err); + return Promise.reject(err); } return null; @@ -582,9 +585,9 @@ class PreferencesRenderersController extends Disposable { } } - private _filterOrSearchPreferences(filter: string, preferencesRenderer: IPreferencesRenderer, provider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): TPromise { + private _filterOrSearchPreferences(filter: string, preferencesRenderer: IPreferencesRenderer, provider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): Promise { if (!preferencesRenderer) { - return TPromise.wrap(null); + return Promise.resolve(null); } const model = preferencesRenderer.preferencesModel; @@ -594,12 +597,12 @@ class PreferencesRenderersController extends Disposable { }); } - private _filterOrSearchPreferencesModel(filter: string, model: ISettingsEditorModel, provider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): TPromise { - const searchP = provider ? provider.searchModel(model, token) : TPromise.wrap(null); + private _filterOrSearchPreferencesModel(filter: string, model: ISettingsEditorModel, provider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, token?: CancellationToken): Promise { + const searchP = provider ? provider.searchModel(model, token) : Promise.resolve(null); return searchP .then(null, err => { if (isPromiseCanceledError(err)) { - return TPromise.wrapError(err); + return Promise.reject(err); } else { /* __GDPR__ "defaultSettings.searchError" : { @@ -845,10 +848,10 @@ class SideBySidePreferencesWidget extends Widget { this._register(focusTracker.onDidFocus(() => this._onFocus.fire())); } - public setInput(defaultPreferencesEditorInput: DefaultPreferencesEditorInput, editablePreferencesEditorInput: EditorInput, options: EditorOptions, token: CancellationToken): TPromise<{ defaultPreferencesRenderer?: IPreferencesRenderer, editablePreferencesRenderer?: IPreferencesRenderer }> { + public setInput(defaultPreferencesEditorInput: DefaultPreferencesEditorInput, editablePreferencesEditorInput: EditorInput, options: EditorOptions, token: CancellationToken): Promise<{ defaultPreferencesRenderer?: IPreferencesRenderer, editablePreferencesRenderer?: IPreferencesRenderer }> { this.getOrCreateEditablePreferencesEditor(editablePreferencesEditorInput); this.settingsTargetsWidget.settingsTarget = this.getSettingsTarget(editablePreferencesEditorInput.getResource()); - return TPromise.join([ + return Promise.all([ this.updateInput(this.defaultPreferencesEditor, defaultPreferencesEditorInput, DefaultSettingsEditorContribution.ID, editablePreferencesEditorInput.getResource(), options, token), this.updateInput(this.editablePreferencesEditor, editablePreferencesEditorInput, SettingsEditorContribution.ID, defaultPreferencesEditorInput.getResource(), options, token) ]) @@ -1076,13 +1079,13 @@ export class DefaultPreferencesEditor extends BaseTextEditor { interface ISettingsEditorContribution extends editorCommon.IEditorContribution { - updatePreferencesRenderer(associatedPreferencesModelUri: URI): TPromise>; + updatePreferencesRenderer(associatedPreferencesModelUri: URI): Thenable>; } abstract class AbstractSettingsEditorContribution extends Disposable implements ISettingsEditorContribution { - private preferencesRendererCreationPromise: TPromise>; + private preferencesRendererCreationPromise: Thenable>; constructor(protected editor: ICodeEditor, @IInstantiationService protected instantiationService: IInstantiationService, @@ -1093,7 +1096,7 @@ abstract class AbstractSettingsEditorContribution extends Disposable implements this._register(this.editor.onDidChangeModel(() => this._onModelChanged())); } - updatePreferencesRenderer(associatedPreferencesModelUri: URI): TPromise> { + updatePreferencesRenderer(associatedPreferencesModelUri: URI): Thenable> { if (!this.preferencesRendererCreationPromise) { this.preferencesRendererCreationPromise = this._createPreferencesRenderer(); } @@ -1103,7 +1106,7 @@ abstract class AbstractSettingsEditorContribution extends Disposable implements .then(changed => changed ? this._updatePreferencesRenderer(associatedPreferencesModelUri) : this.preferencesRendererCreationPromise); } - return TPromise.as(null); + return Promise.resolve(null); } protected _onModelChanged(): void { @@ -1114,13 +1117,13 @@ abstract class AbstractSettingsEditorContribution extends Disposable implements } } - private _hasAssociatedPreferencesModelChanged(associatedPreferencesModelUri: URI): TPromise { + private _hasAssociatedPreferencesModelChanged(associatedPreferencesModelUri: URI): Thenable { return this.preferencesRendererCreationPromise.then(preferencesRenderer => { return !(preferencesRenderer && preferencesRenderer.getAssociatedPreferencesModel() && preferencesRenderer.getAssociatedPreferencesModel().uri.toString() === associatedPreferencesModelUri.toString()); }); } - private _updatePreferencesRenderer(associatedPreferencesModelUri: URI): TPromise> { + private _updatePreferencesRenderer(associatedPreferencesModelUri: URI): Thenable> { return this.preferencesService.createPreferencesEditorModel(associatedPreferencesModelUri) .then(associatedPreferencesEditorModel => { return this.preferencesRendererCreationPromise.then(preferencesRenderer => { @@ -1148,7 +1151,7 @@ abstract class AbstractSettingsEditorContribution extends Disposable implements preferencesRenderer.dispose(); } }); - this.preferencesRendererCreationPromise = TPromise.as(null); + this.preferencesRendererCreationPromise = Promise.resolve(null); } } @@ -1157,7 +1160,7 @@ abstract class AbstractSettingsEditorContribution extends Disposable implements super.dispose(); } - protected abstract _createPreferencesRenderer(): TPromise>; + protected abstract _createPreferencesRenderer(): Thenable>; abstract getId(): string; } @@ -1169,7 +1172,7 @@ class DefaultSettingsEditorContribution extends AbstractSettingsEditorContributi return DefaultSettingsEditorContribution.ID; } - protected _createPreferencesRenderer(): TPromise> { + protected _createPreferencesRenderer(): Thenable> { return this.preferencesService.createPreferencesEditorModel(this.editor.getModel().uri) .then(editorModel => { if (editorModel instanceof DefaultSettingsEditorModel && this.editor.getModel()) { @@ -1199,7 +1202,7 @@ class SettingsEditorContribution extends AbstractSettingsEditorContribution impl return SettingsEditorContribution.ID; } - protected _createPreferencesRenderer(): TPromise> { + protected _createPreferencesRenderer(): Thenable> { if (this.isSettingsModel()) { return this.preferencesService.createPreferencesEditorModel(this.editor.getModel().uri) .then(settingsModel => { diff --git a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts index 3abe2ca6a..43578cf33 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts @@ -10,7 +10,6 @@ import { Delayer } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { Position } from 'vs/editor/common/core/position'; @@ -95,7 +94,7 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend } protected createHeader(): void { - this._register(new SettingsHeaderWidget(this.editor, '')).setMessage(nls.localize('emptyUserSettingsHeader', "Place your settings here to overwrite the Default Settings.")); + this._register(new SettingsHeaderWidget(this.editor, '')).setMessage(nls.localize('emptyUserSettingsHeader', "Place your settings here to override the Default Settings.")); } public render(): void { @@ -191,7 +190,7 @@ export class WorkspaceSettingsRenderer extends UserSettingsRenderer implements I } protected createHeader(): void { - this._register(new SettingsHeaderWidget(this.editor, '')).setMessage(nls.localize('emptyWorkspaceSettingsHeader', "Place your settings here to overwrite the User Settings.")); + this._register(new SettingsHeaderWidget(this.editor, '')).setMessage(nls.localize('emptyWorkspaceSettingsHeader', "Place your settings here to override the User Settings.")); } public setAssociatedPreferencesModel(associatedPreferencesModel: IPreferencesEditorModel): void { @@ -217,7 +216,7 @@ export class FolderSettingsRenderer extends UserSettingsRenderer implements IPre } protected createHeader(): void { - this._register(new SettingsHeaderWidget(this.editor, '')).setMessage(nls.localize('emptyFolderSettingsHeader', "Place your folder settings here to overwrite those from the Workspace Settings.")); + this._register(new SettingsHeaderWidget(this.editor, '')).setMessage(nls.localize('emptyFolderSettingsHeader', "Place your folder settings here to override those from the Workspace Settings.")); } } @@ -828,7 +827,7 @@ class EditSettingRenderer extends Disposable { : editPreferenceWidget.preferences.map(setting => new ContextSubMenu(setting.key, this.getActions(setting, this.getConfigurationsMap()[setting.key]))); this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => TPromise.wrap(actions) + getActions: () => actions }); } @@ -843,7 +842,7 @@ class EditSettingRenderer extends Disposable { const actions = this.getActions(this.editPreferenceWidgetForMouseMove.preferences[0], this.getConfigurationsMap()[this.editPreferenceWidgetForMouseMove.preferences[0].key]); this.contextMenuService.showContextMenu({ getAnchor: () => this.toAbsoluteCoords(new Position(startLine, 1)), - getActions: () => TPromise.wrap(actions) + getActions: () => actions }); return true; diff --git a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts index 0f5cd2fa6..17a1d0633 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts @@ -14,7 +14,6 @@ import { MarkdownString } from 'vs/base/common/htmlContent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IMarginData } from 'vs/editor/browser/controller/mouseTarget'; import { ICodeEditor, IEditorMouseEvent, IViewZone, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; @@ -25,7 +24,7 @@ import { ConfigurationTarget } from 'vs/platform/configuration/common/configurat import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { activeContrastBorder, badgeBackground, badgeForeground, contrastBorder, errorForeground, focusBorder } from 'vs/platform/theme/common/colorRegistry'; +import { activeContrastBorder, badgeBackground, badgeForeground, contrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry'; import { attachInputBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace'; @@ -416,7 +415,7 @@ export class FolderSettingsActionItem extends BaseActionItem { private showMenu(): void { this.contextMenuService.showContextMenu({ getAnchor: () => this.container, - getActions: () => TPromise.as(this.getDropdownMenuActions()), + getActions: () => this.getDropdownMenuActions(), getActionItem: () => null, onHide: () => { this.anchorElement.blur(); @@ -549,13 +548,13 @@ export class SettingsTargetsWidget extends Widget { } } - public updateTarget(settingsTarget: SettingsTarget): TPromise { + public updateTarget(settingsTarget: SettingsTarget): Promise { const isSameTarget = this.settingsTarget === settingsTarget || settingsTarget instanceof URI && this.settingsTarget instanceof URI && this.settingsTarget.toString() === settingsTarget.toString(); if (!isSameTarget) { this.settingsTarget = settingsTarget; this._onDidTargetChange.fire(this.settingsTarget); } - return TPromise.as(null); + return Promise.resolve(null); } private update(): void { @@ -614,7 +613,8 @@ export class SearchWidget extends Widget { this.countElement.style.borderStyle = border ? 'solid' : null; this.countElement.style.borderColor = border; - this.styleCountElementForeground(); + const color = this.themeService.getTheme().getColor(badgeForeground); + this.countElement.style.color = color ? color.toString() : null; })); } @@ -645,23 +645,15 @@ export class SearchWidget extends Widget { return box; } - public showMessage(message: string, count: number): void { + public showMessage(message: string): void { // Avoid setting the aria-label unnecessarily, the screenreader will read the count every time it's set, since it's aria-live:assertive. #50968 if (this.countElement && message !== this.countElement.textContent) { this.countElement.textContent = message; this.inputBox.inputElement.setAttribute('aria-label', message); - DOM.toggleClass(this.countElement, 'no-results', count === 0); this.inputBox.inputElement.style.paddingRight = this.getControlsWidth() + 'px'; - this.styleCountElementForeground(); } } - private styleCountElementForeground() { - const colorId = DOM.hasClass(this.countElement, 'no-results') ? errorForeground : badgeForeground; - const color = this.themeService.getTheme().getColor(colorId); - this.countElement.style.color = color ? color.toString() : null; - } - public layout(dimension: DOM.Dimension) { if (dimension.width < 400) { if (this.countElement) { @@ -840,7 +832,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { outline-style: solid; border-bottom: none; padding-bottom: 0; - outline-offset: 2px; + outline-offset: -1px; } .settings-tabs-widget > .monaco-action-bar .action-item .action-label:not(.checked):hover { diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index 5224b1cd0..3c136ead8 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -23,7 +23,6 @@ import { KeyCode } from 'vs/base/common/keyCodes'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { escapeRegExpCharacters, startsWith } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IAccessibilityProvider, IDataSource, IFilter, IRenderer as ITreeRenderer, ITree, ITreeConfiguration } from 'vs/base/parts/tree/browser/tree'; import { DefaultTreestyler } from 'vs/base/parts/tree/browser/treeDefaults'; import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; @@ -186,8 +185,8 @@ export class SettingsDataSource implements IDataSource { return false; } - getChildren(tree: ITree, element: SettingsTreeElement): TPromise { - return TPromise.as(this._getChildren(element)); + getChildren(tree: ITree, element: SettingsTreeElement): Promise { + return Promise.resolve(this._getChildren(element)); } private _getChildren(element: SettingsTreeElement): SettingsTreeElement[] { @@ -199,8 +198,8 @@ export class SettingsDataSource implements IDataSource { } } - getParent(tree: ITree, element: SettingsTreeElement): TPromise { - return TPromise.wrap(element && element.parent); + getParent(tree: ITree, element: SettingsTreeElement): Promise { + return Promise.resolve(element && element.parent); } shouldAutoexpand(): boolean { @@ -241,7 +240,7 @@ export class SimplePagedDataSource implements IDataSource { return this.realDataSource.hasChildren(tree, element); } - getChildren(tree: ITree, element: SettingsTreeGroupElement): TPromise { + getChildren(tree: ITree, element: SettingsTreeGroupElement): Thenable { return this.realDataSource.getChildren(tree, element).then(realChildren => { return this._getChildren(realChildren); }); @@ -258,7 +257,7 @@ export class SimplePagedDataSource implements IDataSource { } } - getParent(tree: ITree, element: any): TPromise { + getParent(tree: ITree, element: any): Thenable { return this.realDataSource.getParent(tree, element); } @@ -400,7 +399,7 @@ export class SettingsRenderer implements ITreeRenderer { this._onDidChangeSetting.fire({ key: context.setting.key, value: undefined, type: context.setting.type as SettingValueType }); } - return TPromise.wrap(null); + return Promise.resolve(null); }), new Separator(), this.instantiationService.createInstance(CopySettingIdAction), @@ -413,7 +412,7 @@ export class SettingsRenderer implements ITreeRenderer { const toolbarElement: HTMLElement = settingDOMElement.querySelector('.toolbar-toggle-more'); if (toolbarElement) { this.contextMenuService.showContextMenu({ - getActions: () => TPromise.wrap(this.settingActions), + getActions: () => this.settingActions, getAnchor: () => toolbarElement, getActionsContext: () => element }); @@ -1436,12 +1435,12 @@ export class SettingsAccessibilityProvider implements IAccessibilityProvider { } class NonExpandableOrSelectableTree extends Tree { - expand(): TPromise { - return TPromise.wrap(null); + expand(): Promise { + return Promise.resolve(null); } - collapse(): TPromise { - return TPromise.wrap(null); + collapse(): Promise { + return Promise.resolve(null); } public setFocus(element?: any, eventPayload?: any): void { @@ -1624,12 +1623,12 @@ class CopySettingIdAction extends Action { super(CopySettingIdAction.ID, CopySettingIdAction.LABEL); } - run(context: SettingsTreeSettingElement): TPromise { + run(context: SettingsTreeSettingElement): Promise { if (context) { this.clipboardService.writeText(context.setting.key); } - return TPromise.as(null); + return Promise.resolve(null); } } @@ -1643,12 +1642,12 @@ class CopySettingAsJSONAction extends Action { super(CopySettingAsJSONAction.ID, CopySettingAsJSONAction.LABEL); } - run(context: SettingsTreeSettingElement): TPromise { + run(context: SettingsTreeSettingElement): Promise { if (context) { const jsonResult = `"${context.setting.key}": ${JSON.stringify(context.value, undefined, ' ')}`; this.clipboardService.writeText(jsonResult); } - return TPromise.as(null); + return Promise.resolve(null); } } diff --git a/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts index c48c4e880..fe8eff8f9 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts @@ -12,8 +12,8 @@ import { ConfigurationScope } from 'vs/platform/configuration/common/configurati import { SettingsTarget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { ITOCEntry, knownAcronyms } from 'vs/workbench/parts/preferences/browser/settingsLayout'; import { IExtensionSetting, ISearchResult, ISetting, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; +import { MODIFIED_SETTING_TAG } from 'vs/workbench/parts/preferences/common/preferences'; -export const MODIFIED_SETTING_TAG = 'modified'; export const ONLINE_SERVICES_SETTING_TAG = 'usesOnlineServices'; export interface ISettingsEditorViewState { diff --git a/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts b/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts index 645523b75..eaf3cdf40 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts @@ -16,7 +16,7 @@ import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import 'vs/css!./media/settingsWidgets'; import { localize } from 'vs/nls'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { foreground, inputBackground, inputBorder, inputForeground, listActiveSelectionBackground, listActiveSelectionForeground, listHoverBackground, listHoverForeground, listInactiveSelectionBackground, listInactiveSelectionForeground, registerColor, selectBackground, selectBorder, selectForeground, textLinkForeground, textPreformatForeground, editorWidgetBorder } from 'vs/platform/theme/common/colorRegistry'; +import { foreground, inputBackground, inputBorder, inputForeground, listActiveSelectionBackground, listActiveSelectionForeground, listHoverBackground, listHoverForeground, listInactiveSelectionBackground, listInactiveSelectionForeground, registerColor, selectBackground, selectBorder, selectForeground, textLinkForeground, textPreformatForeground, editorWidgetBorder, textLinkActiveForeground } from 'vs/platform/theme/common/colorRegistry'; import { attachButtonStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler'; import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { disposableTimeout } from 'vs/base/common/async'; @@ -69,6 +69,14 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { collector.addRule(`.monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a > code { color: ${link}; }`); } + const activeLink = theme.getColor(textLinkActiveForeground); + if (activeLink) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown a:hover, .settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown a:active { color: ${activeLink}; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown a:hover > code, .settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown a:active > code { color: ${activeLink}; }`); + collector.addRule(`.monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a:hover, .monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a:active { color: ${activeLink}; }`); + collector.addRule(`.monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a:hover > code, .monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a:active > code { color: ${activeLink}; }`); + } + const headerForegroundColor = theme.getColor(settingsHeaderForeground); if (headerForegroundColor) { collector.addRule(`.settings-editor > .settings-header > .settings-header-controls .settings-tabs-widget .action-label.checked { color: ${headerForegroundColor}; border-bottom-color: ${headerForegroundColor}; }`); diff --git a/src/vs/workbench/parts/preferences/browser/tocTree.ts b/src/vs/workbench/parts/preferences/browser/tocTree.ts index f281c9d30..97fb24420 100644 --- a/src/vs/workbench/parts/preferences/browser/tocTree.ts +++ b/src/vs/workbench/parts/preferences/browser/tocTree.ts @@ -5,7 +5,6 @@ import * as DOM from 'vs/base/browser/dom'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IDataSource, IRenderer, ITree, ITreeConfiguration, ITreeOptions } from 'vs/base/parts/tree/browser/tree'; import { DefaultTreestyler } from 'vs/base/parts/tree/browser/treeDefaults'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -106,8 +105,8 @@ export class TOCDataSource implements IDataSource { return false; } - getChildren(tree: ITree, element: TOCTreeElement): TPromise { - return TPromise.as(this._getChildren(element)); + getChildren(tree: ITree, element: TOCTreeElement): Promise { + return Promise.resolve(this._getChildren(element)); } private _getChildren(element: TOCTreeElement): SettingsTreeElement[] { @@ -115,8 +114,8 @@ export class TOCDataSource implements IDataSource { .filter(child => child instanceof SettingsTreeGroupElement); } - getParent(tree: ITree, element: TOCTreeElement): TPromise { - return TPromise.wrap(element instanceof SettingsTreeGroupElement && element.parent); + getParent(tree: ITree, element: TOCTreeElement): Promise { + return Promise.resolve(element instanceof SettingsTreeGroupElement && element.parent); } } diff --git a/src/vs/workbench/parts/preferences/common/preferences.ts b/src/vs/workbench/parts/preferences/common/preferences.ts index 434b91a29..ac13278f9 100644 --- a/src/vs/workbench/parts/preferences/common/preferences.ts +++ b/src/vs/workbench/parts/preferences/common/preferences.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { join } from 'vs/base/common/paths'; @@ -41,7 +40,7 @@ export interface IPreferencesSearchService { } export interface ISearchProvider { - searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): TPromise; + searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): Promise; } export interface IKeybindingsEditor extends IEditor { @@ -54,15 +53,16 @@ export interface IKeybindingsEditor extends IEditor { focusKeybindings(): void; recordSearchKeys(): void; toggleSortByPrecedence(): void; - defineKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise; - removeKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise; - resetKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise; - copyKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise; - copyKeybindingCommand(keybindingEntry: IKeybindingItemEntry): TPromise; - showSimilarKeybindings(keybindingEntry: IKeybindingItemEntry): TPromise; + defineKeybinding(keybindingEntry: IKeybindingItemEntry): Thenable; + removeKeybinding(keybindingEntry: IKeybindingItemEntry): Thenable; + resetKeybinding(keybindingEntry: IKeybindingItemEntry): Thenable; + copyKeybinding(keybindingEntry: IKeybindingItemEntry): void; + copyKeybindingCommand(keybindingEntry: IKeybindingItemEntry): void; + showSimilarKeybindings(keybindingEntry: IKeybindingItemEntry): void; } export const CONTEXT_SETTINGS_EDITOR = new RawContextKey('inSettingsEditor', false); +export const CONTEXT_SETTINGS_JSON_EDITOR = new RawContextKey('inSettingsJSONEditor', false); export const CONTEXT_SETTINGS_SEARCH_FOCUS = new RawContextKey('inSettingsSearch', false); export const CONTEXT_TOC_ROW_FOCUS = new RawContextKey('settingsTocRowFocus', false); export const CONTEXT_KEYBINDINGS_EDITOR = new RawContextKey('inKeybindings', false); @@ -79,6 +79,10 @@ export const SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH = 'settings.acti export const SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_LIST = 'settings.action.focusSettingsList'; export const SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU = 'settings.action.showContextMenu'; +export const SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON = 'settings.switchToJSON'; +export const SETTINGS_EDITOR_COMMAND_FILTER_MODIFIED = 'settings.filterByModified'; +export const SETTINGS_EDITOR_COMMAND_FILTER_ONLINE = 'settings.filterByOnline'; + export const KEYBINDINGS_EDITOR_COMMAND_SEARCH = 'keybindings.editor.searchKeybindings'; export const KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS = 'keybindings.editor.clearSearchResults'; export const KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS = 'keybindings.editor.recordSearchKeys'; @@ -96,3 +100,5 @@ export const KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS = 'keybindings.editor.show export const FOLDER_SETTINGS_PATH = join('.vscode', 'settings.json'); export const DEFAULT_SETTINGS_EDITOR_SETTING = 'workbench.settings.openDefaultSettings'; + +export const MODIFIED_SETTING_TAG = 'modified'; \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/common/preferencesContribution.ts b/src/vs/workbench/parts/preferences/common/preferencesContribution.ts index c6c339df2..28d4324c3 100644 --- a/src/vs/workbench/parts/preferences/common/preferencesContribution.ts +++ b/src/vs/workbench/parts/preferences/common/preferencesContribution.ts @@ -6,7 +6,6 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITextModel } from 'vs/editor/common/model'; import * as JSONContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -109,14 +108,14 @@ export class PreferencesContribution implements IWorkbenchContribution { private start(): void { this.textModelResolverService.registerTextModelContentProvider('vscode', { - provideTextContent: (uri: URI): TPromise => { + provideTextContent: (uri: URI): Thenable => { if (uri.scheme !== 'vscode') { return null; } if (uri.authority === 'schemas') { const schemaModel = this.getSchemaModel(uri); if (schemaModel) { - return TPromise.as(schemaModel); + return Promise.resolve(schemaModel); } } return this.preferencesService.resolveModel(uri); @@ -128,8 +127,8 @@ export class PreferencesContribution implements IWorkbenchContribution { let schema = schemaRegistry.getSchemaContributions().schemas[uri.toString()]; if (schema) { const modelContent = JSON.stringify(schema); - const mode = this.modeService.getOrCreateMode('jsonc'); - const model = this.modelService.createModel(modelContent, mode, uri); + const languageSelection = this.modeService.create('jsonc'); + const model = this.modelService.createModel(modelContent, languageSelection, uri); let disposables: IDisposable[] = []; disposables.push(schemaRegistry.onDidChangeSchema(schemaUri => { diff --git a/src/vs/workbench/parts/preferences/electron-browser/media/edit-json-inverse.svg b/src/vs/workbench/parts/preferences/electron-browser/media/edit-json-inverse.svg new file mode 100644 index 000000000..6dc96a9e1 --- /dev/null +++ b/src/vs/workbench/parts/preferences/electron-browser/media/edit-json-inverse.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/workbench/parts/preferences/electron-browser/media/edit-json.svg b/src/vs/workbench/parts/preferences/electron-browser/media/edit-json.svg new file mode 100644 index 000000000..747e2706b --- /dev/null +++ b/src/vs/workbench/parts/preferences/electron-browser/media/edit-json.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css b/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css index 7f7bed3a2..11772ecd9 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css +++ b/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css @@ -51,12 +51,15 @@ opacity: 0.9; } +.settings-editor .settings-tabs-widget > .monaco-action-bar .action-item .action-details { + opacity: 0.9; +} + .settings-editor > .settings-header > .settings-header-controls .settings-tabs-widget .action-label:hover { opacity: 1; } .settings-editor > .settings-header > .settings-header-controls .settings-tabs-widget .action-label.checked { - font-weight: 500; opacity: 1; } diff --git a/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts b/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts index 399004504..3367d1969 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts @@ -3,40 +3,35 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { URI } from 'vs/base/common/uri'; import 'vs/css!../browser/media/preferences'; +import { Command } from 'vs/editor/browser/editorExtensions'; +import { Context as SuggestContext } from 'vs/editor/contrib/suggest/suggest'; import * as nls from 'vs/nls'; -import { URI } from 'vs/base/common/uri'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { EditorInput, IEditorInputFactory, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions } from 'vs/workbench/common/editor'; -import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; +import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { PreferencesEditor } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; -import { SettingsEditor2 } from 'vs/workbench/parts/preferences/electron-browser/settingsEditor2'; -import { DefaultPreferencesEditorInput, PreferencesEditorInput, KeybindingsEditorInput, SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; -import { KeybindingsEditor } from 'vs/workbench/parts/preferences/browser/keybindingsEditor'; -import { OpenDefaultKeybindingsFileAction, OpenRawDefaultSettingsAction, OpenSettingsAction, OpenGlobalSettingsAction, OpenGlobalKeybindingsFileAction, OpenWorkspaceSettingsAction, OpenFolderSettingsAction, ConfigureLanguageBasedSettingsAction, OPEN_FOLDER_SETTINGS_COMMAND, OpenGlobalKeybindingsAction, OpenSettings2Action, OpenSettingsJsonAction } from 'vs/workbench/parts/preferences/browser/preferencesActions'; -import { - IKeybindingsEditor, IPreferencesSearchService, CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, - KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SEARCH, CONTEXT_SETTINGS_EDITOR, SETTINGS_EDITOR_COMMAND_FOCUS_FILE, - CONTEXT_SETTINGS_SEARCH_FOCUS, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING, SETTINGS_EDITOR_COMMAND_EDIT_FOCUSED_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH, CONTEXT_TOC_ROW_FOCUS, SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_LIST, - SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS -} from 'vs/workbench/parts/preferences/common/preferences'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { PreferencesContribution } from 'vs/workbench/parts/preferences/common/preferencesContribution'; -import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor'; +import { Extensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; +import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; +import { EditorInput, Extensions as EditorInputExtensions, IEditorInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; +import { KeybindingsEditor } from 'vs/workbench/parts/preferences/browser/keybindingsEditor'; +import { ConfigureLanguageBasedSettingsAction, OpenDefaultKeybindingsFileAction, OpenFolderSettingsAction, OpenGlobalKeybindingsAction, OpenGlobalKeybindingsFileAction, OpenGlobalSettingsAction, OpenRawDefaultSettingsAction, OpenSettings2Action, OpenSettingsAction, OpenSettingsJsonAction, OpenWorkspaceSettingsAction, OPEN_FOLDER_SETTINGS_COMMAND } from 'vs/workbench/parts/preferences/browser/preferencesActions'; +import { PreferencesEditor } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; +import { CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, CONTEXT_KEYBINDING_FOCUS, CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, IKeybindingsEditor, IPreferencesSearchService, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_EDIT_FOCUSED_SETTING, SETTINGS_EDITOR_COMMAND_FILTER_MODIFIED, SETTINGS_EDITOR_COMMAND_FILTER_ONLINE, SETTINGS_EDITOR_COMMAND_FOCUS_FILE, SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH, SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_LIST, SETTINGS_EDITOR_COMMAND_SEARCH, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU, SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON } from 'vs/workbench/parts/preferences/common/preferences'; +import { PreferencesContribution } from 'vs/workbench/parts/preferences/common/preferencesContribution'; import { PreferencesSearchService } from 'vs/workbench/parts/preferences/electron-browser/preferencesSearch'; +import { SettingsEditor2 } from 'vs/workbench/parts/preferences/electron-browser/settingsEditor2'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; -import { Command } from 'vs/editor/browser/editorExtensions'; -import { Context as SuggestContext } from 'vs/editor/contrib/suggest/suggest'; +import { DefaultPreferencesEditorInput, KeybindingsEditorInput, PreferencesEditorInput, SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; registerSingleton(IPreferencesSearchService, PreferencesSearchService); @@ -386,6 +381,22 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } }); +CommandsRegistry.registerCommand(OpenGlobalKeybindingsFileAction.ID, serviceAccessor => { + serviceAccessor.get(IInstantiationService).createInstance(OpenGlobalKeybindingsFileAction, OpenGlobalKeybindingsFileAction.ID, OpenGlobalKeybindingsFileAction.LABEL).run(); +}); +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { + command: { + id: OpenGlobalKeybindingsFileAction.ID, + title: OpenGlobalKeybindingsFileAction.LABEL, + iconLocation: { + light: URI.parse(require.toUrl(`vs/workbench/parts/preferences/electron-browser/media/edit-json.svg`)), + dark: URI.parse(require.toUrl(`vs/workbench/parts/preferences/electron-browser/media/edit-json-inverse.svg`)) + } + }, + when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR), + group: 'navigation', +}); + CommandsRegistry.registerCommand(KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, serviceAccessor => { const control = serviceAccessor.get(IEditorService).activeControl as IKeybindingsEditor; if (control) { @@ -566,6 +577,29 @@ const showContextMenuCommand = new ShowContextMenuCommand({ }); showContextMenuCommand.register(); +CommandsRegistry.registerCommand(SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON, serviceAccessor => { + const control = serviceAccessor.get(IEditorService).activeControl as SettingsEditor2; + if (control instanceof SettingsEditor2) { + return control.switchToSettingsFile(); + } + + return Promise.resolve(null); +}); + +CommandsRegistry.registerCommand(SETTINGS_EDITOR_COMMAND_FILTER_MODIFIED, serviceAccessor => { + const control = serviceAccessor.get(IEditorService).activeControl as SettingsEditor2; + if (control instanceof SettingsEditor2) { + control.focusSearch(`@${MODIFIED_SETTING_TAG}`); + } +}); + +CommandsRegistry.registerCommand(SETTINGS_EDITOR_COMMAND_FILTER_ONLINE, serviceAccessor => { + const control = serviceAccessor.get(IEditorService).activeControl as SettingsEditor2; + if (control instanceof SettingsEditor2) { + control.focusSearch(`@tag:usesOnlineServices`); + } +}); + // Preferences menu MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { @@ -585,3 +619,48 @@ MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { }, order: 1 }); + +// Editor tool items + +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { + command: { + id: SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON, + title: nls.localize('openSettingsJson', "Open Settings (JSON)"), + iconLocation: { + dark: URI.parse(require.toUrl('vs/workbench/parts/preferences/electron-browser/media/edit-json-inverse.svg')), + light: URI.parse(require.toUrl('vs/workbench/parts/preferences/electron-browser/media/edit-json.svg')) + } + }, + group: 'navigation', + order: 1, + when: ContextKeyExpr.and( + CONTEXT_SETTINGS_EDITOR, + CONTEXT_SETTINGS_JSON_EDITOR.toNegated() + ) +}); + +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { + command: { + id: SETTINGS_EDITOR_COMMAND_FILTER_MODIFIED, + title: nls.localize('filterModifiedLabel', "Show modified settings") + }, + group: '1_filter', + order: 1, + when: ContextKeyExpr.and( + CONTEXT_SETTINGS_EDITOR, + CONTEXT_SETTINGS_JSON_EDITOR.toNegated() + ) +}); + +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { + command: { + id: SETTINGS_EDITOR_COMMAND_FILTER_ONLINE, + title: nls.localize('filterOnlineServicesLabel', "Show settings for online services"), + }, + group: '1_filter', + order: 2, + when: ContextKeyExpr.and( + CONTEXT_SETTINGS_EDITOR, + CONTEXT_SETTINGS_JSON_EDITOR.toNegated() + ) +}); diff --git a/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts b/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts index 9ff55ccdc..76c7f4a10 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { ISettingsEditorModel, ISetting, ISettingsGroup, IFilterMetadata, ISearchResult, IGroupFilter, ISettingMatcher, IScoredResults, ISettingMatch, IRemoteSetting, IExtensionSetting } from 'vs/workbench/services/preferences/common/preferences'; import { IRange } from 'vs/editor/common/core/range'; import { distinct, top } from 'vs/base/common/arrays'; @@ -32,7 +31,7 @@ export interface IEndpointDetails { export class PreferencesSearchService extends Disposable implements IPreferencesSearchService { _serviceBrand: any; - private _installedExtensions: TPromise; + private _installedExtensions: Promise; constructor( @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService, @@ -104,9 +103,9 @@ export class LocalSearchProvider implements ISearchProvider { .trim(); } - searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): TPromise { + searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): Promise { if (!this._filter) { - return TPromise.wrap(null); + return Promise.resolve(null); } let orderedScore = LocalSearchProvider.START_SCORE; // Sort is not stable @@ -126,12 +125,12 @@ export class LocalSearchProvider implements ISearchProvider { const filterMatches = preferencesModel.filterSettings(this._filter, this.getGroupFilter(this._filter), settingMatcher); if (filterMatches[0] && filterMatches[0].score === LocalSearchProvider.EXACT_MATCH_SCORE) { - return TPromise.wrap({ + return Promise.resolve({ filterMatches: filterMatches.slice(0, 1), exactMatch: true }); } else { - return TPromise.wrap({ + return Promise.resolve({ filterMatches }); } @@ -164,19 +163,19 @@ class RemoteSearchProvider implements ISearchProvider { private static readonly MAX_REQUESTS = 10; private static readonly NEW_EXTENSIONS_MIN_SCORE = 1; - private _remoteSearchP: TPromise; + private _remoteSearchP: Promise; - constructor(private options: IRemoteSearchProviderOptions, private installedExtensions: TPromise, + constructor(private options: IRemoteSearchProviderOptions, private installedExtensions: Promise, @IEnvironmentService private environmentService: IEnvironmentService, @IRequestService private requestService: IRequestService, @ILogService private logService: ILogService ) { this._remoteSearchP = this.options.filter ? - TPromise.wrap(this.getSettingsForFilter(this.options.filter)) : - TPromise.wrap(null); + Promise.resolve(this.getSettingsForFilter(this.options.filter)) : + Promise.resolve(null); } - searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): TPromise { + searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken): Promise { return this._remoteSearchP.then(remoteResult => { if (!remoteResult) { return null; @@ -239,7 +238,7 @@ class RemoteSearchProvider implements ISearchProvider { } } - return TPromise.join(allRequestDetails.map(details => this.getSettingsFromBing(details))).then(allResponses => { + return Promise.all(allRequestDetails.map(details => this.getSettingsFromBing(details))).then(allResponses => { // Merge all IFilterMetadata const metadata = allResponses[0]; metadata.requestCount = 1; @@ -253,7 +252,7 @@ class RemoteSearchProvider implements ISearchProvider { }); } - private getSettingsFromBing(details: IBingRequestDetails): TPromise { + private getSettingsFromBing(details: IBingRequestDetails): Promise { this.logService.debug(`Searching settings via ${details.url}`); if (details.body) { this.logService.debug(`Body: ${details.body}`); diff --git a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts index 504ebc980..a4c879530 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts @@ -4,25 +4,21 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; -import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; -import { Action } from 'vs/base/common/actions'; import * as arrays from 'vs/base/common/arrays'; -import { isArray } from 'vs/base/common/types'; import { Delayer, ThrottledDelayer } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import * as collections from 'vs/base/common/collections'; import { getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors'; +import { isArray } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; import { collapseAll, expandAll } from 'vs/base/parts/tree/browser/treeUtils'; import 'vs/css!./media/settingsEditor2'; import { localize } from 'vs/nls'; -import { ConfigurationTarget, IConfigurationOverrides, IConfigurationService, ConfigurationTargetToString } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationTarget, ConfigurationTargetToString, IConfigurationOverrides, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { WorkbenchTree } from 'vs/platform/list/browser/listService'; @@ -36,18 +32,17 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { IEditor, IEditorMemento } from 'vs/workbench/common/editor'; import { attachSuggestEnabledInputBoxStyler, SuggestEnabledInput } from 'vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput'; -import { PreferencesEditor } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; import { SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { commonlyUsedData, tocData } from 'vs/workbench/parts/preferences/browser/settingsLayout'; -import { ISettingLinkClickEvent, resolveExtensionsSettings, resolveSettingsTree, SettingsDataSource, SettingsRenderer, SettingsTree, SimplePagedDataSource, ISettingOverrideClickEvent } from 'vs/workbench/parts/preferences/browser/settingsTree'; -import { ISettingsEditorViewState, MODIFIED_SETTING_TAG, ONLINE_SERVICES_SETTING_TAG, parseQuery, SearchResultIdx, SearchResultModel, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/parts/preferences/browser/settingsTreeModels'; +import { ISettingLinkClickEvent, ISettingOverrideClickEvent, resolveExtensionsSettings, resolveSettingsTree, SettingsDataSource, SettingsRenderer, SettingsTree, SimplePagedDataSource } from 'vs/workbench/parts/preferences/browser/settingsTree'; +import { parseQuery, ISettingsEditorViewState, SearchResultIdx, SearchResultModel, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/parts/preferences/browser/settingsTreeModels'; import { settingsTextInputBorder } from 'vs/workbench/parts/preferences/browser/settingsWidgets'; import { TOCRenderer, TOCTree, TOCTreeModel } from 'vs/workbench/parts/preferences/browser/tocTree'; -import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, IPreferencesSearchService, ISearchProvider, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/parts/preferences/common/preferences'; +import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, IPreferencesSearchService, ISearchProvider, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/parts/preferences/common/preferences'; +import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import { IPreferencesService, ISearchResult, ISettingsEditorModel, ISettingsEditorOptions, SettingsEditorOptions, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { Settings2EditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; -import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; const $ = DOM.$; @@ -60,7 +55,7 @@ export class SettingsEditor2 extends BaseEditor { private static SETTING_UPDATE_SLOW_DEBOUNCE: number = 1000; private static readonly SUGGESTIONS: string[] = [ - '@modified', '@tag:usesOnlineServices' + `@${MODIFIED_SETTING_TAG}`, '@tag:usesOnlineServices' ]; private static shouldSettingUpdateFast(type: SettingValueType | SettingValueType[]): boolean { @@ -129,7 +124,6 @@ export class SettingsEditor2 extends BaseEditor { @IInstantiationService private instantiationService: IInstantiationService, @IPreferencesSearchService private preferencesSearchService: IPreferencesSearchService, @ILogService private logService: ILogService, - @IEnvironmentService private environmentService: IEnvironmentService, @IContextKeyService contextKeyService: IContextKeyService, @IContextMenuService private contextMenuService: IContextMenuService, @IStorageService private storageService: IStorageService, @@ -211,7 +205,11 @@ export class SettingsEditor2 extends BaseEditor { } this._setOptions(options); - this.render(token); + this._register(input.onDispose(() => { + this.searchWidget.setValue(''); + })); + + return this.render(token); }) .then(() => { // Init TOC selection @@ -416,31 +414,6 @@ export class SettingsEditor2 extends BaseEditor { actionRunner: this.actionRunner })); - const actions: Action[] = [ - this.instantiationService.createInstance(FilterByTagAction, - localize('filterModifiedLabel', "Show modified settings"), - MODIFIED_SETTING_TAG, - this) - ]; - if (this.environmentService.appQuality !== 'stable') { - actions.push( - this.instantiationService.createInstance( - FilterByTagAction, - localize('filterOnlineServicesLabel', "Show settings for online services"), - ONLINE_SERVICES_SETTING_TAG, - this)); - actions.push(new Separator()); - } - actions.push(new Action('settings.openSettingsJson', localize('openSettingsJsonLabel', "Open settings.json"), undefined, undefined, () => { - return this.openSettingsFile().then(editor => { - const currentQuery = parseQuery(this.searchWidget.getValue()); - if (editor instanceof PreferencesEditor && currentQuery) { - editor.focusSearch(currentQuery.query); - } - }); - })); - - this.toolbar.setActions([], actions)(); this.toolbar.context = { target: this.settingsTargetsWidget.settingsTarget }; } @@ -471,7 +444,12 @@ export class SettingsEditor2 extends BaseEditor { } } - private openSettingsFile(query?: string): TPromise { + public switchToSettingsFile(): Thenable { + const query = parseQuery(this.searchWidget.getValue()); + return this.openSettingsFile(query.query); + } + + private openSettingsFile(query?: string): Thenable { const currentSettingsTarget = this.settingsTargetsWidget.settingsTarget; const options: ISettingsEditorOptions = { query }; @@ -592,7 +570,7 @@ export class SettingsEditor2 extends BaseEditor { } if (element && (!e.payload || !e.payload.fromScroll)) { - let refreshP = TPromise.wrap(null); + let refreshP: Thenable = Promise.resolve(null); if (this.settingsTreeDataSource.pageTo(element.index, true)) { refreshP = this.renderTree(); } @@ -724,7 +702,7 @@ export class SettingsEditor2 extends BaseEditor { } } - private updateChangedSetting(key: string, value: any): TPromise { + private updateChangedSetting(key: string, value: any): Thenable { // ConfigurationService displays the error if this fails. // Force a render afterwards because onDidConfigurationUpdate doesn't fire if the update doesn't result in an effective setting value change const settingsTarget = this.settingsTargetsWidget.settingsTarget; @@ -815,7 +793,7 @@ export class SettingsEditor2 extends BaseEditor { this.telemetryService.publicLog('settingsEditor.settingModified', data); } - private render(token: CancellationToken): TPromise { + private render(token: CancellationToken): Thenable { if (this.input) { return this.input.resolve() .then((model: Settings2EditorModel) => { @@ -828,7 +806,7 @@ export class SettingsEditor2 extends BaseEditor { return this.onConfigUpdate(); }); } - return TPromise.as(null); + return Promise.resolve(null); } private onSearchModeToggled(): void { @@ -863,7 +841,7 @@ export class SettingsEditor2 extends BaseEditor { }); } - private onConfigUpdate(keys?: string[], forceRefresh = false): TPromise { + private onConfigUpdate(keys?: string[], forceRefresh = false): Thenable { if (keys && this.settingsTreeModel) { return this.updateElementsByKey(keys); } @@ -916,10 +894,10 @@ export class SettingsEditor2 extends BaseEditor { } } - return TPromise.wrap(null); + return Promise.resolve(null); } - private updateElementsByKey(keys: string[]): TPromise { + private updateElementsByKey(keys: string[]): Thenable { if (keys.length) { if (this.searchResultModel) { keys.forEach(key => this.searchResultModel.updateElementsByName(key)); @@ -929,7 +907,7 @@ export class SettingsEditor2 extends BaseEditor { keys.forEach(key => this.settingsTreeModel.updateElementsByName(key)); } - return TPromise.join( + return Promise.all( keys.map(key => this.renderTree(key))) .then(() => { }); } else { @@ -943,10 +921,10 @@ export class SettingsEditor2 extends BaseEditor { null; } - private renderTree(key?: string, force = false): TPromise { + private renderTree(key?: string, force = false): Thenable { if (!force && key && this.scheduledRefreshes.has(key)) { this.updateModifiedLabelForKey(key); - return TPromise.wrap(null); + return Promise.resolve(null); } // If a setting control is currently focused, schedule a refresh for later @@ -960,24 +938,24 @@ export class SettingsEditor2 extends BaseEditor { this.updateModifiedLabelForKey(key); this.scheduleRefresh(focusedSetting, key); - return TPromise.wrap(null); + return Promise.resolve(null); } } else { this.scheduleRefresh(focusedSetting); - return TPromise.wrap(null); + return Promise.resolve(null); } } - let refreshP: TPromise; + let refreshP: Thenable; if (key) { const elements = this.currentSettingsModel.getElementsByName(key); if (elements && elements.length) { // TODO https://github.com/Microsoft/vscode/issues/57360 - // refreshP = TPromise.join(elements.map(e => this.settingsTree.refresh(e))); + // refreshP = Promise.join(elements.map(e => this.settingsTree.refresh(e))); refreshP = this.settingsTree.refresh(); } else { // Refresh requested for a key that we don't know about - return TPromise.wrap(null); + return Promise.resolve(null); } } else { refreshP = this.settingsTree.refresh(); @@ -1019,7 +997,7 @@ export class SettingsEditor2 extends BaseEditor { return match && match[1]; } - private triggerSearch(query: string): TPromise { + private triggerSearch(query: string): Thenable { this.viewState.tagFilters = new Set(); if (query) { const parsedQuery = parseQuery(query); @@ -1126,7 +1104,7 @@ export class SettingsEditor2 extends BaseEditor { this.telemetryService.publicLog('settingsEditor.filter', data); } - private triggerFilterPreferences(query: string): TPromise { + private triggerFilterPreferences(query: string): Thenable { if (this.searchInProgress) { this.searchInProgress.cancel(); this.searchInProgress = null; @@ -1141,26 +1119,26 @@ export class SettingsEditor2 extends BaseEditor { this.remoteSearchThrottle.trigger(() => { return searchInProgress && !searchInProgress.token.isCancellationRequested ? this.remoteSearchPreferences(query, this.searchInProgress.token) : - TPromise.wrap(null); + Promise.resolve(null); }); } }); } else { - return TPromise.wrap(null); + return Promise.resolve(null); } }); } - private localFilterPreferences(query: string, token?: CancellationToken): TPromise { + private localFilterPreferences(query: string, token?: CancellationToken): Thenable { const localSearchProvider = this.preferencesSearchService.getLocalSearchProvider(query); return this.filterOrSearchPreferences(query, SearchResultIdx.Local, localSearchProvider, token); } - private remoteSearchPreferences(query: string, token?: CancellationToken): TPromise { + private remoteSearchPreferences(query: string, token?: CancellationToken): Thenable { const remoteSearchProvider = this.preferencesSearchService.getRemoteSearchProvider(query); const newExtSearchProvider = this.preferencesSearchService.getRemoteSearchProvider(query, true); - return TPromise.join([ + return Promise.all([ this.filterOrSearchPreferences(query, SearchResultIdx.Remote, remoteSearchProvider, token), this.filterOrSearchPreferences(query, SearchResultIdx.NewExtensions, newExtSearchProvider, token) ]).then(() => { @@ -1168,7 +1146,7 @@ export class SettingsEditor2 extends BaseEditor { }); } - private filterOrSearchPreferences(query: string, type: SearchResultIdx, searchProvider: ISearchProvider, token?: CancellationToken): TPromise { + private filterOrSearchPreferences(query: string, type: SearchResultIdx, searchProvider: ISearchProvider, token?: CancellationToken): Thenable { return this._filterOrSearchPreferencesModel(query, this.defaultSettingsEditorModel, searchProvider, token).then(result => { if (token && token.isCancellationRequested) { // Handle cancellation like this because cancellation is lost inside the search provider due to async/await @@ -1215,12 +1193,12 @@ export class SettingsEditor2 extends BaseEditor { } } - private _filterOrSearchPreferencesModel(filter: string, model: ISettingsEditorModel, provider: ISearchProvider, token?: CancellationToken): TPromise { - const searchP = provider ? provider.searchModel(model, token) : TPromise.wrap(null); + private _filterOrSearchPreferencesModel(filter: string, model: ISettingsEditorModel, provider: ISearchProvider, token?: CancellationToken): Thenable { + const searchP = provider ? provider.searchModel(model, token) : Promise.resolve(null); return searchP .then(null, err => { if (isPromiseCanceledError(err)) { - return TPromise.wrapError(err); + return Promise.reject(err); } else { /* __GDPR__ "settingsEditor.searchError" : { @@ -1272,20 +1250,3 @@ interface ISettingsEditor2State { interface ISettingsToolbarContext { target: SettingsTarget; } - -class FilterByTagAction extends Action { - static readonly ID = 'settings.filterByTag'; - - constructor( - label: string, - private tag: string, - private settingsEditor: SettingsEditor2 - ) { - super(FilterByTagAction.ID, label, 'toggle-filter-tag'); - } - - run(): TPromise { - this.settingsEditor.focusSearch(this.tag === MODIFIED_SETTING_TAG ? `@${this.tag} ` : `@tag:${this.tag} `, false); - return TPromise.as(null); - } -} diff --git a/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts b/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts index f7f920461..c5ceffb5b 100644 --- a/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts @@ -425,6 +425,7 @@ export class CommandsHandler extends QuickOpenHandler { const menu = this.editorService.invokeWithinEditorContext(accessor => this.menuService.createMenu(MenuId.CommandPalette, accessor.get(IContextKeyService))); const menuActions = menu.getActions().reduce((r, [, actions]) => [...r, ...actions], []).filter(action => action instanceof MenuItemAction) as MenuItemAction[]; const commandEntries = this.menuItemActionsToEntries(menuActions, searchValue); + menu.dispose(); // Concat let entries = [...editorEntries, ...commandEntries]; diff --git a/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts b/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts index a2e4eccb8..39f65feba 100644 --- a/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import * as types from 'vs/base/common/types'; import { IEntryRunContext, Mode, IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen'; @@ -38,7 +37,7 @@ export class GotoLineAction extends QuickOpenAction { super(actionId, actionLabel, GOTO_LINE_PREFIX, _quickOpenService); } - run(): TPromise { + run(): Promise { let activeTextEditorWidget = this.editorService.activeTextEditorWidget; if (isDiffEditor(activeTextEditorWidget)) { @@ -217,7 +216,7 @@ export class GotoLineHandler extends QuickOpenHandler { return nls.localize('gotoLineHandlerAriaLabel', "Type a line number to navigate to."); } - getResults(searchValue: string, token: CancellationToken): TPromise { + getResults(searchValue: string, token: CancellationToken): Promise { searchValue = searchValue.trim(); // Remember view state to be able to restore on cancel @@ -226,7 +225,7 @@ export class GotoLineHandler extends QuickOpenHandler { this.lastKnownEditorViewState = activeTextEditorWidget.saveViewState(); } - return TPromise.as(new QuickOpenModel([new GotoLineEntry(searchValue, this.editorService, this)])); + return Promise.resolve(new QuickOpenModel([new GotoLineEntry(searchValue, this.editorService, this)])); } canRun(): boolean | string { diff --git a/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts b/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts index eb66494ac..1f6017b09 100644 --- a/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!vs/editor/contrib/documentSymbols/media/symbol-icons'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import * as types from 'vs/base/common/types'; import * as strings from 'vs/base/common/strings'; @@ -369,7 +368,7 @@ export class GotoSymbolHandler extends QuickOpenHandler { private rangeHighlightDecorationId: IEditorLineDecoration; private lastKnownEditorViewState: IEditorViewState; - private cachedOutlineRequest: TPromise; + private cachedOutlineRequest: Promise; private pendingOutlineRequest: CancellationTokenSource; constructor( @@ -391,7 +390,7 @@ export class GotoSymbolHandler extends QuickOpenHandler { this.rangeHighlightDecorationId = void 0; } - getResults(searchValue: string, token: CancellationToken): TPromise { + getResults(searchValue: string, token: CancellationToken): Promise { searchValue = searchValue.trim(); // Support to cancel pending outline requests @@ -483,7 +482,7 @@ export class GotoSymbolHandler extends QuickOpenHandler { return results; } - private getOutline(): TPromise { + private getOutline(): Promise { if (!this.cachedOutlineRequest) { this.cachedOutlineRequest = this.doGetActiveOutline(); } @@ -491,7 +490,7 @@ export class GotoSymbolHandler extends QuickOpenHandler { return this.cachedOutlineRequest; } - private doGetActiveOutline(): TPromise { + private doGetActiveOutline(): Promise { const activeTextEditorWidget = this.editorService.activeTextEditorWidget; if (activeTextEditorWidget) { let model = activeTextEditorWidget.getModel(); @@ -500,13 +499,13 @@ export class GotoSymbolHandler extends QuickOpenHandler { } if (model && types.isFunction((model).getLanguageIdentifier)) { - return TPromise.wrap(asThenable(() => getDocumentSymbols(model, true, this.pendingOutlineRequest.token)).then(entries => { + return Promise.resolve(asThenable(() => getDocumentSymbols(model, true, this.pendingOutlineRequest.token)).then(entries => { return new OutlineModel(this.toQuickOpenEntries(entries)); })); } } - return TPromise.wrap(null); + return Promise.resolve(null); } decorateOutline(fullRange: IRange, startRange: IRange, editor: IEditor, group: IEditorGroup): void { diff --git a/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.ts b/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.ts index 24d959b54..2524fa81a 100644 --- a/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.ts @@ -102,6 +102,13 @@ export class ViewPickerHandler extends QuickOpenHandler { return true; }); + const entryToCategory = {}; + entries.forEach(e => { + if (!entryToCategory[e.getLabel()]) { + entryToCategory[e.getLabel()] = e.getCategory(); + } + }); + let lastCategory: string; entries.forEach((e, index) => { if (lastCategory !== e.getCategory()) { @@ -109,6 +116,11 @@ export class ViewPickerHandler extends QuickOpenHandler { e.setShowBorder(index > 0); e.setGroupLabel(lastCategory); + + // When the entry category has a parent category, set group label as Parent / Child. For example, Views / Explorer. + if (entryToCategory[lastCategory]) { + e.setGroupLabel(`${entryToCategory[lastCategory]} / ${lastCategory}`); + } } else { e.setShowBorder(false); e.setGroupLabel(void 0); @@ -136,11 +148,11 @@ export class ViewPickerHandler extends QuickOpenHandler { // Viewlets const viewlets = this.viewletService.getViewlets(); - viewlets.forEach((viewlet, index) => viewEntries.push(new ViewEntry(viewlet.name, nls.localize('views', "Views"), () => this.viewletService.openViewlet(viewlet.id, true)))); + viewlets.forEach((viewlet, index) => viewEntries.push(new ViewEntry(viewlet.name, nls.localize('views', "Side Bar"), () => this.viewletService.openViewlet(viewlet.id, true)))); // Panels const panels = this.panelService.getPanels(); - panels.forEach((panel, index) => viewEntries.push(new ViewEntry(panel.name, nls.localize('panels', "Panels"), () => this.panelService.openPanel(panel.id, true)))); + panels.forEach((panel, index) => viewEntries.push(new ViewEntry(panel.name, nls.localize('panels', "Panel"), () => this.panelService.openPanel(panel.id, true)))); // Viewlet Views viewlets.forEach((viewlet, index) => { diff --git a/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts index 76c20e076..071c50ff9 100644 --- a/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts +++ b/src/vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts @@ -15,7 +15,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { RunOnceScheduler } from 'vs/base/common/async'; import { URI } from 'vs/base/common/uri'; import { isEqual } from 'vs/base/common/resources'; -import { isLinux, isMacintosh } from 'vs/base/common/platform'; +import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { equals } from 'vs/base/common/objects'; @@ -24,7 +24,7 @@ interface IConfiguration extends IWindowsConfiguration { update: { channel: string; }; telemetry: { enableCrashReporter: boolean }; keyboard: { touchbar: { enabled: boolean } }; - workbench: { tree: { horizontalScrolling: boolean } }; + workbench: { tree: { horizontalScrolling: boolean }, enableLegacyStorage: boolean }; files: { useExperimentalFileWatcher: boolean, watcherExclude: object }; } @@ -32,15 +32,18 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo private titleBarStyle: 'native' | 'custom'; private nativeTabs: boolean; + private nativeFullScreen: boolean; private clickThroughInactive: boolean; private updateChannel: string; private enableCrashReporter: boolean; private touchbarEnabled: boolean; private treeHorizontalScrolling: boolean; + private windowsSmoothScrollingWorkaround: boolean; private experimentalFileWatcher: boolean; private fileWatcherExclude: object; + private legacyStorage: boolean; - private firstFolderResource: URI; + private firstFolderResource?: URI; private extensionHostRestarter: RunOnceScheduler; private onDidChangeWorkspaceFoldersUnbind: IDisposable; @@ -86,6 +89,12 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo changed = true; } + // macOS: Native fullscreen + if (isMacintosh && config.window && typeof config.window.nativeFullScreen === 'boolean' && config.window.nativeFullScreen !== this.nativeFullScreen) { + this.nativeFullScreen = config.window.nativeFullScreen; + changed = true; + } + // macOS: Click through (accept first mouse) if (isMacintosh && config.window && typeof config.window.clickThroughInactive === 'boolean' && config.window.clickThroughInactive !== this.clickThroughInactive) { this.clickThroughInactive = config.window.clickThroughInactive; @@ -130,6 +139,17 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo changed = true; } + // Legacy Workspace Storage + if (config.workbench && typeof config.workbench.enableLegacyStorage === 'boolean' && config.workbench.enableLegacyStorage !== this.legacyStorage) { + this.legacyStorage = config.workbench.enableLegacyStorage; + changed = true; + } + // Windows: smooth scrolling workaround + if (isWindows && config.window && typeof config.window.smoothScrollingWorkaround === 'boolean' && config.window.smoothScrollingWorkaround !== this.windowsSmoothScrollingWorkaround) { + this.windowsSmoothScrollingWorkaround = config.window.smoothScrollingWorkaround; + 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/parts/scm/electron-browser/media/scmViewlet.css b/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css index 0a15ee8d5..84ad51a4b 100644 --- a/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css +++ b/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css @@ -137,12 +137,12 @@ } .scm-viewlet .scm-editor { + box-sizing: border-box; padding: 5px 9px 5px 16px; } -.scm-viewlet .scm-editor { - box-sizing: border-box; - padding: 5px 9px 5px 16px; +.scm-viewlet .scm-editor.hidden { + display: none; } .scm-viewlet .scm-editor > .monaco-inputbox { diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index d2329cf19..b944b1532 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -11,7 +11,7 @@ import { domEvent, stop } from 'vs/base/browser/event'; import { basename } from 'vs/base/common/paths'; import { IDisposable, dispose, combinedDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { PanelViewlet, ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; -import { append, $, addClass, toggleClass, trackFocus, Dimension, addDisposableListener } from 'vs/base/browser/dom'; +import { append, $, addClass, toggleClass, trackFocus, Dimension, addDisposableListener, removeClass } from 'vs/base/browser/dom'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; @@ -316,7 +316,7 @@ class MainPanel extends ViewletPanel { this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, - getActions: () => TPromise.as(secondary), + getActions: () => secondary, getActionsContext: () => repository.provider }); } @@ -799,12 +799,12 @@ export class RepositoryPanel extends ViewletPanel { this.contextMenuService.showContextMenu({ getAnchor: () => ({ x: event.posx, y: event.posy }), - getActions: () => TPromise.as([{ + getActions: () => [{ id: `scm.hideRepository`, label: localize('hideRepository', "Hide"), enabled: true, run: () => this.viewModel.hide(this.repository) - }]), + }], }); } @@ -865,6 +865,10 @@ export class RepositoryPanel extends ViewletPanel { this.updateInputBox(); + // Input box visibility + this.repository.input.onDidChangeVisibility(this.updateInputBoxVisibility, this, this.disposables); + this.updateInputBoxVisibility(); + // List this.listContainer = append(container, $('.scm-status.show-file-icons')); @@ -918,21 +922,35 @@ export class RepositoryPanel extends ViewletPanel { } this.cachedHeight = height; - this.inputBox.layout(); - const editorHeight = this.inputBox.height; - const listHeight = height - (editorHeight + 12 /* margin */); - this.listContainer.style.height = `${listHeight}px`; - this.list.layout(listHeight); + if (this.repository.input.visible) { + removeClass(this.inputBoxContainer, 'hidden'); + this.inputBox.layout(); + + const editorHeight = this.inputBox.height; + const listHeight = height - (editorHeight + 12 /* margin */); + this.listContainer.style.height = `${listHeight}px`; + this.list.layout(listHeight); - toggleClass(this.inputBoxContainer, 'scroll', editorHeight >= 134); + toggleClass(this.inputBoxContainer, 'scroll', editorHeight >= 134); + } else { + addClass(this.inputBoxContainer, 'hidden'); + removeClass(this.inputBoxContainer, 'scroll'); + + this.listContainer.style.height = `${height}px`; + this.list.layout(height); + } } focus(): void { super.focus(); if (this.isExpanded()) { - this.inputBox.focus(); + if (this.repository.input.visible) { + this.inputBox.focus(); + } else { + this.list.domFocus(); + } } } @@ -979,7 +997,7 @@ export class RepositoryPanel extends ViewletPanel { this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, - getActions: () => TPromise.as(actions), + getActions: () => actions, getActionsContext: () => element, actionRunner: new MultipleSelectionActionRunner(() => this.getSelectedResources()) }); @@ -991,13 +1009,19 @@ export class RepositoryPanel extends ViewletPanel { } private updateInputBox(): void { - if (typeof this.repository.provider.commitTemplate === 'undefined' || this.inputBox.value) { + if (typeof this.repository.provider.commitTemplate === 'undefined' || !this.repository.input.visible || this.inputBox.value) { return; } this.inputBox.value = this.repository.provider.commitTemplate; } + private updateInputBoxVisibility(): void { + if (this.cachedHeight) { + this.layoutBody(this.cachedHeight); + } + } + dispose(): void { this.visibilityDisposables = dispose(this.visibilityDisposables); super.dispose(); @@ -1129,6 +1153,12 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle private onDidChangeRepositories(): void { toggleClass(this.el, 'empty', this.scmService.repositories.length === 0); + if (this.scmService.repositories.length === 0) { + this.el.tabIndex = 0; + } else { + this.el.removeAttribute('tabIndex'); + } + const shouldMainPanelAlwaysBeVisible = this.configurationService.getValue('scm.alwaysShowProviders'); const shouldMainPanelBeVisible = shouldMainPanelAlwaysBeVisible || this.scmService.repositories.length > 1; @@ -1160,9 +1190,16 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle return (this.mainPanel ? 1 : 0) + this.repositoryPanels.length; } - setVisible(visible: boolean): Promise { - const promises: TPromise[] = []; - promises.push(super.setVisible(visible)); + focus(): void { + if (this.scmService.repositories.length === 0) { + this.el.focus(); + } else { + super.focus(); + } + } + + setVisible(visible: boolean): void { + super.setVisible(visible); if (!visible) { this.cachedMainPanelHeight = this.getPanelSize(this.mainPanel); @@ -1174,10 +1211,8 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle for (let i = 0; i < this.contributedViews.visibleViewDescriptors.length; i++) { const panel = this.panels[start + i] as ViewletPanel; - promises.push(panel.setVisible(visible)); + panel.setVisible(visible); } - - return Promise.all(promises).then(() => null); } getOptimalWidth(): number { @@ -1371,7 +1406,7 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle let anchor: { x: number, y: number } = { x: event.posx, y: event.posy }; this.contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => TPromise.as(actions) + getActions: () => actions }); } @@ -1442,7 +1477,7 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle return super.isSingleView() && this.repositoryPanels.length + this.contributedViews.visibleViewDescriptors.length === 1; } - openView(id: string, focus?: boolean): TPromise { + openView(id: string, focus?: boolean): IView { if (focus) { this.focus(); } @@ -1452,8 +1487,10 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle } panel = this.panels.filter(panel => panel instanceof ViewletPanel && panel.id === id)[0]; panel.setExpanded(true); - panel.focus(); - return TPromise.as(panel); + if (focus) { + panel.focus(); + } + return panel; } hide(repository: ISCMRepository): void { diff --git a/src/vs/workbench/parts/search/browser/media/searchview.css b/src/vs/workbench/parts/search/browser/media/searchview.css index f7c854b53..6e8ab36c5 100644 --- a/src/vs/workbench/parts/search/browser/media/searchview.css +++ b/src/vs/workbench/parts/search/browser/media/searchview.css @@ -29,9 +29,21 @@ margin-left: 17px; } -.search-view .search-widget .input-box, -.search-view .search-widget .input-box .monaco-inputbox { - height: 25px; +.search-view .search-widget .monaco-inputbox > .wrapper { + height: 100%; +} + +.search-view .search-widget .monaco-inputbox > .wrapper > .mirror { + max-height: 134px; +} + +.search-view .monaco-inputbox > .wrapper > textarea.input { + overflow: initial; + height: 26px; /* set initial height before measure */ +} + +.search-view .monaco-inputbox > .wrapper > textarea.input::-webkit-scrollbar { + display: none; } .search-view .search-widget .monaco-findInput { @@ -39,6 +51,10 @@ vertical-align: middle; } +.search-view .search-widget .monaco-findInput > .controls { + top: 4px; /* Adjust controls, search view input box is slighty taller than find widget due to measurement */ +} + .search-view .search-widget .replace-container { margin-top: 6px; position: relative; diff --git a/src/vs/workbench/parts/search/browser/replaceContributions.ts b/src/vs/workbench/parts/search/browser/replaceContributions.ts index 2a0f463fa..85865f635 100644 --- a/src/vs/workbench/parts/search/browser/replaceContributions.ts +++ b/src/vs/workbench/parts/search/browser/replaceContributions.ts @@ -10,6 +10,6 @@ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } fr import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; export function registerContributions(): void { - registerSingleton(IReplaceService, ReplaceService); + registerSingleton(IReplaceService, ReplaceService, true); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ReplacePreviewContentProvider, LifecyclePhase.Starting); } diff --git a/src/vs/workbench/parts/search/browser/replaceService.ts b/src/vs/workbench/parts/search/browser/replaceService.ts index 54ff7977d..0287f28a5 100644 --- a/src/vs/workbench/parts/search/browser/replaceService.ts +++ b/src/vs/workbench/parts/search/browser/replaceService.ts @@ -5,7 +5,6 @@ import * as nls from 'vs/nls'; import * as errors from 'vs/base/common/errors'; -import { TPromise } from 'vs/base/common/winjs.base'; import { URI } from 'vs/base/common/uri'; import * as network from 'vs/base/common/network'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -47,7 +46,7 @@ export class ReplacePreviewContentProvider implements ITextModelContentProvider, this.textModelResolverService.registerTextModelContentProvider(network.Schemas.internal, this); } - public provideTextContent(uri: URI): TPromise { + public provideTextContent(uri: URI): Thenable { if (uri.fragment === REPLACE_PREVIEW) { return this.instantiationService.createInstance(ReplacePreviewModel).resolve(uri); } @@ -66,14 +65,14 @@ class ReplacePreviewModel extends Disposable { super(); } - resolve(replacePreviewUri: URI): TPromise { + resolve(replacePreviewUri: URI): Thenable { const fileResource = toFileResource(replacePreviewUri); const fileMatch = this.searchWorkbenchService.searchModel.searchResult.matches().filter(match => match.resource().toString() === fileResource.toString())[0]; return this.textModelResolverService.createModelReference(fileResource).then(ref => { ref = this._register(ref); const sourceModel = ref.object.textEditorModel; const sourceModelModeId = sourceModel.getLanguageIdentifier().language; - const replacePreviewModel = this.modelService.createModel(createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot()), this.modeService.getOrCreateMode(sourceModelModeId), replacePreviewUri); + const replacePreviewModel = this.modelService.createModel(createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot()), this.modeService.create(sourceModelModeId), replacePreviewUri); this._register(fileMatch.onChange(modelChange => this.update(sourceModel, replacePreviewModel, fileMatch, modelChange))); this._register(this.searchWorkbenchService.searchModel.onReplaceTermChanged(() => this.update(sourceModel, replacePreviewModel, fileMatch))); this._register(fileMatch.onDispose(() => replacePreviewModel.dispose())); // TODO@Sandeep we should not dispose a model directly but rather the reference (depends on https://github.com/Microsoft/vscode/issues/17073) @@ -101,17 +100,17 @@ export class ReplaceService implements IReplaceService { @IBulkEditService private bulkEditorService: IBulkEditService ) { } - public replace(match: Match): TPromise; - public replace(files: FileMatch[], progress?: IProgressRunner): TPromise; - public replace(match: FileMatchOrMatch, progress?: IProgressRunner, resource?: URI): TPromise; - public replace(arg: any, progress: IProgressRunner | null = null, resource: URI | null = null): TPromise { + public replace(match: Match): Promise; + public replace(files: FileMatch[], progress?: IProgressRunner): Promise; + public replace(match: FileMatchOrMatch, progress?: IProgressRunner, resource?: URI): Promise; + public replace(arg: any, progress: IProgressRunner | null = null, resource: URI | null = null): Promise { const edits: ResourceTextEdit[] = this.createEdits(arg, resource); return this.bulkEditorService.apply({ edits }, { progress }).then(() => this.textFileService.saveAll(edits.map(e => e.resource))); } - public openReplacePreview(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): TPromise { + public openReplacePreview(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Thenable { const fileMatch = element instanceof Match ? element.parent() : element; return this.editorService.openEditor({ @@ -139,13 +138,13 @@ export class ReplaceService implements IReplaceService { }, errors.onUnexpectedError); } - public updateReplacePreview(fileMatch: FileMatch, override: boolean = false): TPromise { + public updateReplacePreview(fileMatch: FileMatch, override: boolean = false): Promise { const replacePreviewUri = toReplaceResource(fileMatch.resource()); - return TPromise.join([this.textModelResolverService.createModelReference(fileMatch.resource()), this.textModelResolverService.createModelReference(replacePreviewUri)]) + return Promise.all([this.textModelResolverService.createModelReference(fileMatch.resource()), this.textModelResolverService.createModelReference(replacePreviewUri)]) .then(([sourceModelRef, replaceModelRef]) => { const sourceModel = sourceModelRef.object.textEditorModel; const replaceModel = replaceModelRef.object.textEditorModel; - let returnValue = TPromise.wrap(null); + let returnValue = Promise.resolve(null); // If model is disposed do not update if (sourceModel && replaceModel) { if (override) { diff --git a/src/vs/workbench/parts/search/browser/searchActions.ts b/src/vs/workbench/parts/search/browser/searchActions.ts index 6921c98e2..dc23bd390 100644 --- a/src/vs/workbench/parts/search/browser/searchActions.ts +++ b/src/vs/workbench/parts/search/browser/searchActions.ts @@ -11,7 +11,6 @@ import { normalizeDriveLetter } from 'vs/base/common/labels'; import { Schemas } from 'vs/base/common/network'; import { isWindows, OS } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITree } from 'vs/base/parts/tree/browser/tree'; import * as nls from 'vs/nls'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; @@ -45,12 +44,12 @@ export function appendKeyBindingLabel(label: string, keyBinding: number | Resolv } } -export function openSearchView(viewletService: IViewletService, panelService: IPanelService, focus?: boolean): TPromise { +export function openSearchView(viewletService: IViewletService, panelService: IPanelService, focus?: boolean): Thenable { if (viewletService.getViewlets().filter(v => v.id === VIEW_ID).length) { return viewletService.openViewlet(VIEW_ID, focus).then(viewlet => viewlet); } - return panelService.openPanel(VIEW_ID, focus).then(panel => panel); + return Promise.resolve(panelService.openPanel(VIEW_ID, focus) as SearchView); } export function getSearchView(viewletService: IViewletService, panelService: IPanelService): SearchView { @@ -97,10 +96,10 @@ export class FocusNextInputAction extends Action { super(id, label); } - public run(): TPromise { + public run(): Thenable { const searchView = getSearchView(this.viewletService, this.panelService); searchView.focusNextInputBox(); - return TPromise.as(null); + return Promise.resolve(null); } } @@ -115,10 +114,10 @@ export class FocusPreviousInputAction extends Action { super(id, label); } - public run(): TPromise { + public run(): Thenable { const searchView = getSearchView(this.viewletService, this.panelService); searchView.focusPreviousInputBox(); - return TPromise.as(null); + return Promise.resolve(null); } } @@ -130,7 +129,7 @@ export abstract class FindOrReplaceInFilesAction extends Action { super(id, label); } - public run(): TPromise { + public run(): Thenable { return openSearchView(this.viewletService, this.panelService, false).then(openedView => { const searchAndReplaceWidget = openedView.searchAndReplaceWidget; searchAndReplaceWidget.toggleReplace(this.expandSearchReplaceWidget); @@ -165,7 +164,7 @@ export class OpenSearchViewletAction extends FindOrReplaceInFilesAction { super(id, label, viewletService, panelService, /*expandSearchReplaceWidget=*/false); } - public run(): TPromise { + public run(): Thenable { // Pass focus to viewlet if not open or focused if (this.otherViewletShowing() || !isSearchViewFocused(this.viewletService, this.panelService)) { @@ -175,7 +174,7 @@ export class OpenSearchViewletAction extends FindOrReplaceInFilesAction { // Otherwise pass focus to editor group this.editorGroupService.activeGroup.focus(); - return TPromise.as(true); + return Promise.resolve(true); } private otherViewletShowing(): boolean { @@ -205,11 +204,11 @@ export class CloseReplaceAction extends Action { super(id, label); } - public run(): TPromise { + public run(): Thenable { const searchView = getSearchView(this.viewletService, this.panelService); searchView.searchAndReplaceWidget.toggleReplace(false); searchView.searchAndReplaceWidget.focus(); - return TPromise.as(null); + return Promise.resolve(null); } } @@ -231,12 +230,12 @@ export class RefreshAction extends Action { this.enabled = searchView && searchView.isSearchSubmitted(); } - public run(): TPromise { + public run(): Thenable { const searchView = getSearchView(this.viewletService, this.panelService); if (searchView) { searchView.onQueryChanged(); } - return TPromise.as(null); + return Promise.resolve(null); } } @@ -258,12 +257,12 @@ export class CollapseDeepestExpandedLevelAction extends Action { this.enabled = searchView && searchView.hasSearchResults(); } - public run(): TPromise { + public run(): Thenable { const searchView = getSearchView(this.viewletService, this.panelService); if (searchView) { const viewer = searchView.getControl(); if (viewer.getHighlight()) { - return TPromise.as(null); // Global action disabled if user is in edit mode from another action + return Promise.resolve(null); // Global action disabled if user is in edit mode from another action } /** @@ -299,7 +298,7 @@ export class CollapseDeepestExpandedLevelAction extends Action { viewer.domFocus(); viewer.focusFirst(); } - return TPromise.as(null); + return Promise.resolve(null); } } @@ -321,12 +320,12 @@ export class ClearSearchResultsAction extends Action { this.enabled = searchView && searchView.isSearchSubmitted(); } - public run(): TPromise { + public run(): Thenable { const searchView = getSearchView(this.viewletService, this.panelService); if (searchView) { searchView.clearSearchResults(); } - return TPromise.as(null); + return Promise.resolve(null); } } @@ -348,13 +347,13 @@ export class CancelSearchAction extends Action { this.enabled = searchView && searchView.isSearching(); } - public run(): TPromise { + public run(): Thenable { const searchView = getSearchView(this.viewletService, this.panelService); if (searchView) { searchView.cancelSearch(); } - return TPromise.as(null); + return Promise.resolve(null); } } @@ -369,7 +368,7 @@ export class FocusNextSearchResultAction extends Action { super(id, label); } - public run(): TPromise { + public run(): Thenable { return openSearchView(this.viewletService, this.panelService).then(searchView => { searchView.selectNextMatch(); }); @@ -387,7 +386,7 @@ export class FocusPreviousSearchResultAction extends Action { super(id, label); } - public run(): TPromise { + public run(): Thenable { return openSearchView(this.viewletService, this.panelService).then(searchView => { searchView.selectPreviousMatch(); }); @@ -470,7 +469,7 @@ export class RemoveAction extends AbstractSearchAndReplaceAction { super('remove', RemoveAction.LABEL, 'action-remove'); } - public run(): TPromise { + public run(): Thenable { const currentFocusElement = this.viewer.getFocus(); const nextFocusElement = !currentFocusElement || currentFocusElement instanceof SearchResult || elementIsEqualOrParent(currentFocusElement, this.element) ? this.getElementToFocusAfterRemoved(this.viewer, this.element) : @@ -521,7 +520,7 @@ export class ReplaceAllAction extends AbstractSearchAndReplaceAction { super(Constants.ReplaceAllInFileActionId, appendKeyBindingLabel(ReplaceAllAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceAllInFileActionId), keyBindingService), 'action-replace-all'); } - public run(): TPromise { + public run(): Thenable { let nextFocusElement = this.getElementToFocusAfterRemoved(this.viewer, this.fileMatch); return this.fileMatch.parent().replace(this.fileMatch).then(() => { if (nextFocusElement) { @@ -543,7 +542,7 @@ export class ReplaceAllInFolderAction extends AbstractSearchAndReplaceAction { super(Constants.ReplaceAllInFolderActionId, appendKeyBindingLabel(ReplaceAllInFolderAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceAllInFolderActionId), keyBindingService), 'action-replace-all'); } - public run(): TPromise { + public run(): Thenable { let nextFocusElement = this.getElementToFocusAfterRemoved(this.viewer, this.folderMatch); return this.folderMatch.replaceAll() .then(() => { @@ -567,7 +566,7 @@ export class ReplaceAction extends AbstractSearchAndReplaceAction { super(Constants.ReplaceActionId, appendKeyBindingLabel(ReplaceAction.LABEL, keyBindingService.lookupKeybinding(Constants.ReplaceActionId), keyBindingService), 'action-replace'); } - public run(): TPromise { + public run(): Thenable { this.enabled = false; return this.element.parent().replace(this.element).then(() => { diff --git a/src/vs/workbench/parts/search/browser/searchResultsView.ts b/src/vs/workbench/parts/search/browser/searchResultsView.ts index 28982417c..e70eaf83b 100644 --- a/src/vs/workbench/parts/search/browser/searchResultsView.ts +++ b/src/vs/workbench/parts/search/browser/searchResultsView.ts @@ -10,7 +10,6 @@ import { IAction } from 'vs/base/common/actions'; import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import * as paths from 'vs/base/common/paths'; import * as resources from 'vs/base/common/resources'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ContextMenuEvent, IAccessibilityProvider, IDataSource, IFilter, IRenderer, ISorter, ITree } from 'vs/base/parts/tree/browser/tree'; import * as nls from 'vs/nls'; import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; @@ -80,15 +79,15 @@ export class SearchDataSource implements IDataSource { return []; } - public getChildren(tree: ITree, element: any): TPromise { - return TPromise.as(this._getChildren(element)); + public getChildren(tree: ITree, element: any): Thenable { + return Promise.resolve(this._getChildren(element)); } public hasChildren(tree: ITree, element: any): boolean { return element instanceof FileMatch || element instanceof FolderMatch || element instanceof SearchResult; } - public getParent(tree: ITree, element: any): TPromise { + public getParent(tree: ITree, element: any): Thenable { let value: any = null; if (element instanceof Match) { @@ -99,7 +98,7 @@ export class SearchDataSource implements IDataSource { value = element.parent(); } - return TPromise.as(value); + return Promise.resolve(value); } public shouldAutoexpand(tree: ITree, element: any): boolean { @@ -414,6 +413,7 @@ export class SearchTreeController extends WorkbenchTreeController { public onContextMenu(tree: WorkbenchTree, element: any, event: ContextMenuEvent): boolean { if (!this.contextMenu) { this.contextMenu = this.menuService.createMenu(MenuId.SearchContext, tree.contextKeyService); + this.disposables.push(this.contextMenu); } tree.setFocus(element, { preventOpenOnFocus: true }); @@ -425,7 +425,7 @@ export class SearchTreeController extends WorkbenchTreeController { getActions: () => { const actions: IAction[] = []; fillInContextMenuActions(this.contextMenu, { shouldForwardArgs: true }, actions, this.contextMenuService); - return TPromise.as(actions); + return actions; }, getActionsContext: () => element diff --git a/src/vs/workbench/parts/search/browser/searchView.ts b/src/vs/workbench/parts/search/browser/searchView.ts index 02c5c1522..9966cb4e4 100644 --- a/src/vs/workbench/parts/search/browser/searchView.ts +++ b/src/vs/workbench/parts/search/browser/searchView.ts @@ -18,7 +18,6 @@ import * as paths from 'vs/base/common/paths'; import * as env from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITree } from 'vs/base/parts/tree/browser/tree'; import 'vs/css!./media/searchview'; import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; @@ -31,9 +30,9 @@ import { IConfirmation, IDialogService } from 'vs/platform/dialogs/common/dialog import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TreeResourceNavigator, WorkbenchTree } from 'vs/platform/list/browser/listService'; -import { INotificationService } from 'vs/platform/notification/common/notification'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IProgressService } from 'vs/platform/progress/common/progress'; -import { IPatternInfo, ISearchComplete, ISearchConfiguration, ISearchHistoryService, ISearchProgressItem, VIEW_ID, ISearchHistoryValues, ITextQuery } from 'vs/platform/search/common/search'; +import { IPatternInfo, ISearchComplete, ISearchConfiguration, ISearchHistoryService, ISearchHistoryValues, ISearchProgressItem, ITextQuery, VIEW_ID, SearchErrorCode } from 'vs/platform/search/common/search'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, editorFindMatchHighlight, editorFindMatchHighlightBorder, listActiveSelectionForeground } from 'vs/platform/theme/common/colorRegistry'; @@ -42,6 +41,7 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { OpenFileFolderAction, OpenFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; import { SimpleFileResourceDragAndDrop } from 'vs/workbench/browser/dnd'; import { Viewlet } from 'vs/workbench/browser/viewlet'; +import { IEditor } from 'vs/workbench/common/editor'; import { IPanel } from 'vs/workbench/common/panel'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { ExcludePatternInputWidget, PatternInputWidget } from 'vs/workbench/parts/search/browser/patternInputWidget'; @@ -49,7 +49,7 @@ import { CancelSearchAction, ClearSearchResultsAction, CollapseDeepestExpandedLe import { SearchAccessibilityProvider, SearchDataSource, SearchFilter, SearchRenderer, SearchSorter, SearchTreeController } from 'vs/workbench/parts/search/browser/searchResultsView'; import { ISearchWidgetOptions, SearchWidget } from 'vs/workbench/parts/search/browser/searchWidget'; import * as Constants from 'vs/workbench/parts/search/common/constants'; -import { QueryBuilder, ITextQueryBuilderOptions } from 'vs/workbench/parts/search/common/queryBuilder'; +import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder'; import { IReplaceService } from 'vs/workbench/parts/search/common/replace'; import { getOutOfWorkspaceEditorResources } from 'vs/workbench/parts/search/common/search'; import { FileMatch, FileMatchOrMatch, FolderMatch, IChangeEvent, ISearchWorkbenchService, Match, SearchModel } from 'vs/workbench/parts/search/common/searchModel'; @@ -66,7 +66,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { private static readonly MAX_TEXT_RESULTS = 10000; private static readonly WIDE_CLASS_NAME = 'wide'; - private static readonly WIDE_VIEW_SIZE = 600; + private static readonly WIDE_VIEW_SIZE = 1000; private isDisposed: boolean; @@ -367,23 +367,23 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this.layout(this.size); } - private onSearchResultsChanged(event?: IChangeEvent): TPromise { + private onSearchResultsChanged(event?: IChangeEvent): Thenable { if (this.isVisible()) { return this.refreshAndUpdateCount(event); } else { this.changedWhileHidden = true; - return TPromise.wrap(null); + return Promise.resolve(null); } } - private refreshAndUpdateCount(event?: IChangeEvent): TPromise { + private refreshAndUpdateCount(event?: IChangeEvent): Thenable { return this.refreshTree(event).then(() => { this.searchWidget.setReplaceAllActionState(!this.viewModel.searchResult.isEmpty()); - this.updateSearchResultCount(); + this.updateSearchResultCount(this.viewModel.searchResult.query.userDisabledExcludesAndIgnoreFiles); }); } - private refreshTree(event?: IChangeEvent): TPromise { + private refreshTree(event?: IChangeEvent): Thenable { if (!event || event.added || event.removed) { return this.tree.refresh(this.viewModel.searchResult); } else { @@ -651,8 +651,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { } } - public setVisible(visible: boolean): Promise { - let promise: Promise; + public setVisible(visible: boolean): void { this.viewletVisible.set(visible); if (visible) { if (this.changedWhileHidden) { @@ -661,11 +660,11 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this.changedWhileHidden = false; } - promise = super.setVisible(visible); + super.setVisible(visible); this.tree.onVisible(); } else { this.tree.onHidden(); - promise = super.setVisible(visible); + super.setVisible(visible); } // Enable highlights if there are searchresults @@ -680,8 +679,6 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this.onFocus(focus, true); } } - - return promise; } public moveFocusToResults(): void { @@ -1056,8 +1053,8 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { _reason: 'searchView', extraFileResources: getOutOfWorkspaceEditorResources(this.editorService, this.contextService), maxResults: SearchView.MAX_TEXT_RESULTS, - disregardIgnoreFiles: !useExcludesAndIgnoreFiles, - disregardExcludeSettings: !useExcludesAndIgnoreFiles, + disregardIgnoreFiles: !useExcludesAndIgnoreFiles || undefined, + disregardExcludeSettings: !useExcludesAndIgnoreFiles || undefined, excludePattern, includePattern, previewOptions: { @@ -1081,7 +1078,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { } this.validateQuery(query).then(() => { - this.onQueryTriggered(query, excludePatternText, includePatternText); + this.onQueryTriggered(query, options, excludePatternText, includePatternText); if (!preserveFocus) { this.searchWidget.focus(false); // focus back to input field @@ -1089,14 +1086,14 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { }, onQueryValidationError); } - private validateQuery(query: ITextQuery): TPromise { + private validateQuery(query: ITextQuery): Thenable { // Validate folderQueries const folderQueriesExistP = query.folderQueries.map(fq => { return this.fileService.existsFile(fq.folder); }); - return TPromise.join(folderQueriesExistP).then(existResults => { + return Promise.resolve(folderQueriesExistP).then(existResults => { // If no folders exist, show an error message about the first one const existingFolderQueries = query.folderQueries.filter((folderQuery, i) => existResults[i]); if (!query.folderQueries.length || existingFolderQueries.length) { @@ -1104,14 +1101,15 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { } else { const nonExistantPath = query.folderQueries[0].folder.fsPath; const searchPathNotFoundError = nls.localize('searchPathNotFoundError', "Search path not found: {0}", nonExistantPath); - return TPromise.wrapError(new Error(searchPathNotFoundError)); + return Promise.reject(new Error(searchPathNotFoundError)); } return undefined; }); } - private onQueryTriggered(query: ITextQuery, excludePatternText: string, includePatternText: string): void { + private onQueryTriggered(query: ITextQuery, options: ITextQueryBuilderOptions, excludePatternText: string, includePatternText: string): void { + this.searchWidget.searchInput.onSearchSubmit(); this.inputPatternExcludes.onSearchSubmit(); this.inputPatternIncludes.onSearchSubmit(); @@ -1246,6 +1244,21 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { progressRunner.done(); this.searchWidget.searchInput.showMessage({ content: e.message, type: MessageType.ERROR }); this.viewModel.searchResult.clear(); + + if (e.code === SearchErrorCode.unknownEncoding && !this.configurationService.getValue('search.useLegacySearch')) { + this.notificationService.prompt(Severity.Info, nls.localize('otherEncodingWarning', "You can enable \"search.useLegacySearch\" to search non-standard file encodings."), + [{ + label: nls.localize('otherEncodingWarning.openSettingsLabel', "Open Settings"), + run: () => this.openSettings('search.useLegacySearch') + }]); + } else if (e.code === SearchErrorCode.regexParseError && !this.configurationService.getValue('search.usePCRE2')) { + // If the regex parsed in JS but not rg, it likely uses features that are supported in JS and PCRE2 but not Rust + this.notificationService.prompt(Severity.Info, nls.localize('rgRegexError', "You can enable \"search.usePCRE2\" to enable some extra regex features like lookbehind and backreferences."), + [{ + label: nls.localize('otherEncodingWarning.openSettingsLabel', "Open Settings"), + run: () => this.openSettings('search.usePCRE2') + }]); + } } }; @@ -1299,7 +1312,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { visibleMatches = fileCount; this.tree.refresh(); - this.updateSearchResultCount(); + this.updateSearchResultCount(options.disregardExcludeSettings); } if (fileCount > 0) { this.updateActions(); @@ -1333,8 +1346,12 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { private onOpenSettings = (e: dom.EventLike): void => { dom.EventHelper.stop(e, false); - const options: ISettingsEditorOptions = { query: '.exclude' }; - this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? + this.openSettings('.exclude'); + } + + private openSettings(query: string): Thenable { + const options: ISettingsEditorOptions = { query }; + return this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? this.preferencesService.openWorkspaceSettings(undefined, options) : this.preferencesService.openGlobalSettings(undefined, options); } @@ -1345,14 +1362,19 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { window.open('https://go.microsoft.com/fwlink/?linkid=853977'); } - private updateSearchResultCount(): void { + private updateSearchResultCount(disregardExcludesAndIgnores?: boolean): void { const fileCount = this.viewModel.searchResult.fileCount(); this.hasSearchResultsKey.set(fileCount > 0); const msgWasHidden = this.messagesElement.style.display === 'none'; if (fileCount > 0) { const messageEl = this.clearMessage(); - dom.append(messageEl, $('p', undefined, this.buildResultCountMessage(this.viewModel.searchResult.count(), fileCount))); + let resultMsg = this.buildResultCountMessage(this.viewModel.searchResult.count(), fileCount); + if (disregardExcludesAndIgnores) { + resultMsg += nls.localize('useIgnoresAndExcludesDisabled', " - exclude settings and ignore files are disabled"); + } + + dom.append(messageEl, $('p', undefined, resultMsg)); if (msgWasHidden) { this.reLayout(); } @@ -1410,10 +1432,10 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this.currentSelectedFileMatch = null; } - private onFocus(lineMatch: any, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): TPromise { + private onFocus(lineMatch: any, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Thenable { if (!(lineMatch instanceof Match)) { this.viewModel.searchResult.rangeHighlightDecorations.removeHighlightRange(); - return TPromise.as(true); + return Promise.resolve(true); } const useReplacePreview = this.configurationService.getValue().search.useReplacePreview; @@ -1422,7 +1444,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this.open(lineMatch, preserveFocus, sideBySide, pinned); } - public open(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): TPromise { + public open(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Thenable { const selection = this.getSelectionFrom(element); const resource = element instanceof Match ? element.parent().resource() : (element).resource(); return this.editorService.openEditor({ @@ -1446,7 +1468,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { if (editor) { return this.editorGroupsService.activateGroup(editor.group); } else { - return TPromise.wrap(null); + return Promise.resolve(null); } }, errors.onUnexpectedError); } diff --git a/src/vs/workbench/parts/search/browser/searchWidget.ts b/src/vs/workbench/parts/search/browser/searchWidget.ts index e038970fa..689933f92 100644 --- a/src/vs/workbench/parts/search/browser/searchWidget.ts +++ b/src/vs/workbench/parts/search/browser/searchWidget.ts @@ -17,7 +17,6 @@ import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import * as env from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; -import { TPromise } from 'vs/base/common/winjs.base'; import { CONTEXT_FIND_WIDGET_NOT_VISIBLE } from 'vs/editor/contrib/find/findModel'; import * as nls from 'vs/nls'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; @@ -67,11 +66,11 @@ class ReplaceAllAction extends Action { this._searchWidget = searchWidget; } - run(): TPromise { + run(): Promise { if (this._searchWidget) { return this._searchWidget.triggerReplaceAll(); } - return TPromise.as(null); + return Promise.resolve(null); } } @@ -282,7 +281,8 @@ export class SearchWidget extends Widget { appendCaseSensitiveLabel: appendKeyBindingLabel('', this.keyBindingService.lookupKeybinding(Constants.ToggleCaseSensitiveCommandId), this.keyBindingService), appendWholeWordsLabel: appendKeyBindingLabel('', this.keyBindingService.lookupKeybinding(Constants.ToggleWholeWordCommandId), this.keyBindingService), appendRegexLabel: appendKeyBindingLabel('', this.keyBindingService.lookupKeybinding(Constants.ToggleRegexCommandId), this.keyBindingService), - history: options.searchHistory + history: options.searchHistory, + flexibleHeight: true }; let searchInputContainer = dom.append(parent, dom.$('.search-container.input-box')); @@ -330,7 +330,8 @@ export class SearchWidget extends Widget { this.replaceInput = this._register(new ContextScopedHistoryInputBox(replaceBox, this.contextViewService, { ariaLabel: nls.localize('label.Replace', 'Replace: Type replace term and press Enter to preview or Escape to cancel'), placeholder: nls.localize('search.replace.placeHolder', "Replace"), - history: options.replaceHistory || [] + history: options.replaceHistory || [], + flexibleHeight: true }, this.contextKeyService)); this._register(attachInputBoxStyler(this.replaceInput, this.themeService)); this.onkeydown(this.replaceInput.inputElement, (keyboardEvent) => this.onReplaceInputKeyDown(keyboardEvent)); @@ -349,9 +350,9 @@ export class SearchWidget extends Widget { this._register(this.replaceInputFocusTracker.onDidBlur(() => this.replaceInputBoxFocused.set(false))); } - triggerReplaceAll(): TPromise { + triggerReplaceAll(): Promise { this._onReplaceAll.fire(); - return TPromise.as(null); + return Promise.resolve(null); } private onToggleReplaceButton(): void { @@ -377,6 +378,7 @@ export class SearchWidget extends Widget { if (currentState !== newState) { this.replaceActive.set(newState); this._onReplaceStateChange.fire(newState); + this.replaceInput.layout(); } } @@ -430,6 +432,20 @@ export class SearchWidget extends Widget { } keyboardEvent.preventDefault(); } + + else if (keyboardEvent.equals(KeyCode.UpArrow)) { + const ta = this.searchInput.domNode.querySelector('textarea'); + if (ta && ta.selectionStart > 0) { + keyboardEvent.stopPropagation(); + } + } + + else if (keyboardEvent.equals(KeyCode.DownArrow)) { + const ta = this.searchInput.domNode.querySelector('textarea'); + if (ta && ta.selectionEnd < ta.value.length) { + keyboardEvent.stopPropagation(); + } + } } private onCaseSensitiveKeyDown(keyboardEvent: IKeyboardEvent) { @@ -467,6 +483,20 @@ export class SearchWidget extends Widget { this.searchInput.focus(); keyboardEvent.preventDefault(); } + + else if (keyboardEvent.equals(KeyCode.UpArrow)) { + const ta = this.searchInput.domNode.querySelector('textarea'); + if (ta && ta.selectionStart > 0) { + keyboardEvent.stopPropagation(); + } + } + + else if (keyboardEvent.equals(KeyCode.DownArrow)) { + const ta = this.searchInput.domNode.querySelector('textarea'); + if (ta && ta.selectionEnd < ta.value.length) { + keyboardEvent.stopPropagation(); + } + } } private onReplaceActionbarKeyDown(keyboardEvent: IKeyboardEvent) { diff --git a/src/vs/workbench/parts/search/common/queryBuilder.ts b/src/vs/workbench/parts/search/common/queryBuilder.ts index 202b704d4..b1e24134c 100644 --- a/src/vs/workbench/parts/search/common/queryBuilder.ts +++ b/src/vs/workbench/parts/search/common/queryBuilder.ts @@ -54,6 +54,8 @@ export interface IFileQueryBuilderOptions extends ICommonQueryBuilderOptions { export interface ITextQueryBuilderOptions extends ICommonQueryBuilderOptions { previewOptions?: ITextSearchPreviewOptions; fileEncoding?: string; + beforeContext?: number; + afterContext?: number; } export class QueryBuilder { @@ -64,11 +66,9 @@ export class QueryBuilder { @IEnvironmentService private environmentService: IEnvironmentService ) { } - text(contentPattern: IPatternInfo, folderResources?: uri[], options?: ITextQueryBuilderOptions): ITextQuery { - contentPattern.isCaseSensitive = this.isCaseSensitive(contentPattern); - contentPattern.isMultiline = this.isMultiline(contentPattern); + text(contentPattern: IPatternInfo, folderResources?: uri[], options: ITextQueryBuilderOptions = {}): ITextQuery { + contentPattern = this.getContentPattern(contentPattern); const searchConfig = this.configurationService.getValue(); - contentPattern.wordSeparators = searchConfig.editor.wordSeparators; const fallbackToPCRE = folderResources && folderResources.some(folder => { const folderConfig = this.configurationService.getValue({ resource: folder }); @@ -80,13 +80,38 @@ export class QueryBuilder { ...commonQuery, type: QueryType.Text, contentPattern, - previewOptions: options && options.previewOptions, - maxFileSize: options && options.maxFileSize, - usePCRE2: searchConfig.search.usePCRE2 || fallbackToPCRE || false + previewOptions: options.previewOptions, + maxFileSize: options.maxFileSize, + usePCRE2: searchConfig.search.usePCRE2 || fallbackToPCRE || false, + beforeContext: options.beforeContext, + afterContext: options.afterContext, + userDisabledExcludesAndIgnoreFiles: options.disregardExcludeSettings && options.disregardIgnoreFiles }; } - file(folderResources: uri[] | undefined, options: IFileQueryBuilderOptions): IFileQuery { + /** + * Adjusts input pattern for config + */ + private getContentPattern(inputPattern: IPatternInfo): IPatternInfo { + const searchConfig = this.configurationService.getValue(); + + const newPattern = { + ...inputPattern, + wordSeparators: searchConfig.editor.wordSeparators + }; + + if (this.isCaseSensitive(inputPattern)) { + newPattern.isCaseSensitive = true; + } + + if (this.isMultiline(inputPattern)) { + newPattern.isMultiline = true; + } + + return newPattern; + } + + file(folderResources: uri[] | undefined, options: IFileQueryBuilderOptions = {}): IFileQuery { const commonQuery = this.commonQuery(folderResources, options); return { ...commonQuery, @@ -105,18 +130,13 @@ export class QueryBuilder { let excludePattern = this.parseExcludePattern(options.excludePattern || ''); // Build folderQueries from searchPaths, if given, otherwise folderResources - let folderQueries = folderResources && folderResources.map(uri => this.getFolderQueryForRoot(uri, options)); - if (searchPaths && searchPaths.length) { - const allRootExcludes = folderQueries && this.mergeExcludesFromFolderQueries(folderQueries); - folderQueries = searchPaths.map(searchPath => this.getFolderQueryForSearchPath(searchPath)); // TODO Rob - if (allRootExcludes) { - excludePattern = objects.mixin(excludePattern || Object.create(null), allRootExcludes); - } - } + const folderQueries = searchPaths && searchPaths.length ? + searchPaths.map(searchPath => this.getFolderQueryForSearchPath(searchPath, options)) : + folderResources && folderResources.map(uri => this.getFolderQueryForRoot(uri, options)); const useRipgrep = !folderResources || folderResources.every(folder => { const folderConfig = this.configurationService.getValue({ resource: folder }); - return !folderConfig.search.disableRipgrep; + return !folderConfig.search.useLegacySearch; }); const queryProps: ICommonQueryProps = { @@ -165,6 +185,10 @@ export class QueryBuilder { return true; } + if (contentPattern.pattern.indexOf('\n') >= 0) { + return true; + } + return false; } @@ -174,7 +198,7 @@ export class QueryBuilder { * * Public for test. */ - public parseSearchPaths(pattern: string): ISearchPathsResult { + public parseSearchPaths(pattern: string, expandSearchPaths = true): ISearchPathsResult { const isSearchPath = (segment: string) => { // A segment is a search path if it is an absolute path or starts with ./, ../, .\, or ..\ return paths.isAbsolute(segment) || /^\.\.?[\/\\]/.test(segment); @@ -196,9 +220,15 @@ export class QueryBuilder { const exprSegments = arrays.flatten(expandedExprSegments); const result: ISearchPathsResult = {}; - const searchPaths = this.expandSearchPathPatterns(groups.searchPaths); - if (searchPaths && searchPaths.length) { - result.searchPaths = searchPaths; + if (expandSearchPaths) { + const searchPaths = this.expandSearchPathPatterns(groups.searchPaths); + if (searchPaths && searchPaths.length) { + result.searchPaths = searchPaths; + } + } else if (groups.searchPaths) { + exprSegments.push(...groups.searchPaths + .map(p => strings.ltrim(p, './')) + .map(p => strings.ltrim(p, '.\\'))); } const includePattern = patternListToIExpression(exprSegments); @@ -214,7 +244,7 @@ export class QueryBuilder { * but the result is a single IExpression that encapsulates all the exclude patterns. */ public parseExcludePattern(pattern: string): glob.IExpression | undefined { - const result = this.parseSearchPaths(pattern); + const result = this.parseSearchPaths(pattern, false); let excludeExpression = glob.getEmptyExpression(); if (result.pattern) { excludeExpression = objects.mixin(excludeExpression, result.pattern); @@ -234,31 +264,6 @@ export class QueryBuilder { return Object.keys(excludeExpression).length ? excludeExpression : undefined; } - private mergeExcludesFromFolderQueries(folderQueries: IFolderQuery[]): glob.IExpression | undefined { - const mergedExcludes = folderQueries.reduce((merged: glob.IExpression, fq: IFolderQuery) => { - if (fq.excludePattern) { - objects.mixin(merged, this.getAbsoluteIExpression(fq.excludePattern, fq.folder.fsPath)); - } - - return merged; - }, Object.create(null)); - - // Don't return an empty IExpression - return Object.keys(mergedExcludes).length ? mergedExcludes : undefined; - } - - private getAbsoluteIExpression(expr: glob.IExpression, root: string): glob.IExpression { - return Object.keys(expr) - .reduce((absExpr: glob.IExpression, key: string) => { - if (expr[key] && !paths.isAbsolute(key)) { - const absPattern = paths.join(root, key); - absExpr[absPattern] = expr[key]; - } - - return absExpr; - }, Object.create(null)); - } - private getExcludesForFolder(folderConfig: ISearchConfiguration, options: ICommonQueryBuilderOptions): glob.IExpression | undefined { return options.disregardExcludeSettings ? undefined : @@ -327,13 +332,38 @@ export class QueryBuilder { return []; } - private getFolderQueryForSearchPath(searchPath: ISearchPathPattern): IFolderQuery { - const folder = searchPath.searchPath; - const folderConfig = this.configurationService.getValue({ resource: folder }); - return { - folder, - includePattern: searchPath.pattern && patternListToIExpression([searchPath.pattern]), - fileEncoding: folderConfig.files && folderConfig.files.encoding + private getFolderQueryForSearchPath(searchPath: ISearchPathPattern, options: ICommonQueryBuilderOptions): IFolderQuery { + const searchPathWorkspaceFolder = this.workspaceContextService.getWorkspaceFolder(searchPath.searchPath); + const searchPathRelativePath = searchPathWorkspaceFolder && searchPath.searchPath.path.substr(searchPathWorkspaceFolder.uri.path.length + 1); + + const rootConfig = this.getFolderQueryForRoot(searchPath.searchPath, options); + let resolvedExcludes: glob.IExpression = {}; + if (searchPathWorkspaceFolder && rootConfig.excludePattern) { + // Resolve excludes relative to the search path + for (let excludePattern in rootConfig.excludePattern) { + const { pathPortion, globPortion } = splitSimpleGlob(excludePattern); + if (!pathPortion) { // **/foo + if (globPortion) { + resolvedExcludes[globPortion] = rootConfig.excludePattern[excludePattern]; + } + } else if (strings.startsWith(pathPortion, searchPathRelativePath)) { // searchPathRelativePath/something/**/foo + // Strip `searchPathRelativePath/` + const resolvedPathPortion = pathPortion.substr(searchPathRelativePath.length + 1); + const resolvedPattern = globPortion ? + resolvedPathPortion + globPortion : + resolvedPathPortion; + + resolvedExcludes[resolvedPattern] = rootConfig.excludePattern[excludePattern]; + } + } + } + + return { + ...rootConfig, + ...{ + includePattern: searchPath.pattern ? patternListToIExpression([searchPath.pattern]) : undefined, + excludePattern: Object.keys(resolvedExcludes).length ? resolvedExcludes : undefined + } }; } @@ -358,7 +388,7 @@ function splitGlobFromPath(searchPath: string): { pathPortion: string, globPorti if (lastSlashMatch) { let pathPortion = searchPath.substr(0, lastSlashMatch.index); if (!pathPortion.match(/[/\\]/)) { - // If the last slash was the only slash, then we now have '' or 'C:'. Append a slash. + // If the last slash was the only slash, then we now have '' or 'C:' or '.'. Append a slash. pathPortion += '/'; } @@ -375,6 +405,22 @@ function splitGlobFromPath(searchPath: string): { pathPortion: string, globPorti }; } +function splitSimpleGlob(searchPath: string): { pathPortion: string, globPortion?: string } { + const globCharMatch = searchPath.match(/[\*\{\}\(\)\[\]\?]/); + if (globCharMatch) { + const globCharIdx = globCharMatch.index || 0; + return { + pathPortion: searchPath.substr(0, globCharIdx), + globPortion: searchPath.substr(globCharIdx) + }; + } + + // No glob char + return { + pathPortion: searchPath + }; +} + function patternListToIExpression(patterns: string[]): glob.IExpression { return patterns.length ? patterns.reduce((glob, cur) => { glob[cur] = true; return glob; }, Object.create(null)) : diff --git a/src/vs/workbench/parts/search/common/replace.ts b/src/vs/workbench/parts/search/common/replace.ts index 953a4704e..8f2d2e902 100644 --- a/src/vs/workbench/parts/search/common/replace.ts +++ b/src/vs/workbench/parts/search/common/replace.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { Match, FileMatch, FileMatchOrMatch } from 'vs/workbench/parts/search/common/searchModel'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProgressRunner } from 'vs/platform/progress/common/progress'; @@ -17,22 +16,22 @@ export interface IReplaceService { /** * Replaces the given match in the file that match belongs to */ - replace(match: Match): TPromise; + replace(match: Match): Thenable; /** * Replace all the matches from the given file matches in the files * You can also pass the progress runner to update the progress of replacing. */ - replace(files: FileMatch[], progress?: IProgressRunner): TPromise; + replace(files: FileMatch[], progress?: IProgressRunner): Thenable; /** * Opens the replace preview for given file match or match */ - openReplacePreview(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): TPromise; + openReplacePreview(element: FileMatchOrMatch, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Thenable; /** * Update the replace preview for the given file. * If `override` is `true`, then replace preview is constructed from source model */ - updateReplacePreview(file: FileMatch, override?: boolean): TPromise; + updateReplacePreview(file: FileMatch, override?: boolean): Thenable; } diff --git a/src/vs/workbench/parts/search/common/searchModel.ts b/src/vs/workbench/parts/search/common/searchModel.ts index 2aa6b6536..eb7ff0306 100644 --- a/src/vs/workbench/parts/search/common/searchModel.ts +++ b/src/vs/workbench/parts/search/common/searchModel.ts @@ -13,7 +13,6 @@ import { ResourceMap, TernarySearchTree, values } from 'vs/base/common/map'; import * as objects from 'vs/base/common/objects'; import { lcut } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Range } from 'vs/editor/common/core/range'; import { FindMatch, IModelDeltaDecoration, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; @@ -21,11 +20,12 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IProgressRunner } from 'vs/platform/progress/common/progress'; import { ReplacePattern } from 'vs/platform/search/common/replace'; -import { IFileMatch, IPatternInfo, ISearchComplete, ISearchProgressItem, ISearchService, ITextSearchPreviewOptions, ITextSearchResult, ITextSearchStats, TextSearchResult, ITextQuery } from 'vs/platform/search/common/search'; +import { IFileMatch, IPatternInfo, ISearchComplete, ISearchProgressItem, ISearchService, ITextQuery, ITextSearchPreviewOptions, ITextSearchMatch, ITextSearchStats, resultIsMatch, ISearchRange, OneLineRange } from 'vs/platform/search/common/search'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { overviewRulerFindMatchForeground } from 'vs/platform/theme/common/colorRegistry'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { IReplaceService } from 'vs/workbench/parts/search/common/replace'; +import { editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; export class Match { @@ -33,22 +33,26 @@ export class Match { private _id: string; private _range: Range; - private _previewText: string; - private _rangeInPreviewText: Range; + private _oneLinePreviewText: string; + private _rangeInPreviewText: ISearchRange; + + // For replace + private _fullPreviewRange: ISearchRange; + + constructor(private _parent: FileMatch, private _fullPreviewLines: string[], _fullPreviewRange: ISearchRange, _documentRange: ISearchRange) { + this._oneLinePreviewText = _fullPreviewLines[_fullPreviewRange.startLineNumber]; + const adjustedEndCol = _fullPreviewRange.startLineNumber === _fullPreviewRange.endLineNumber ? + _fullPreviewRange.endColumn : + this._oneLinePreviewText.length; + this._rangeInPreviewText = new OneLineRange(1, _fullPreviewRange.startColumn + 1, adjustedEndCol + 1); - constructor(private _parent: FileMatch, _result: ITextSearchResult) { this._range = new Range( - _result.range.startLineNumber + 1, - _result.range.startColumn + 1, - _result.range.endLineNumber + 1, - _result.range.endColumn + 1); - - this._rangeInPreviewText = new Range( - _result.preview.match.startLineNumber + 1, - _result.preview.match.startColumn + 1, - _result.preview.match.endLineNumber + 1, - _result.preview.match.endColumn + 1); - this._previewText = _result.preview.text; + _documentRange.startLineNumber + 1, + _documentRange.startColumn + 1, + _documentRange.endLineNumber + 1, + _documentRange.endColumn + 1); + + this._fullPreviewRange = _fullPreviewRange; this._id = this._parent.id() + '>' + this._range + this.getMatchString(); } @@ -62,7 +66,7 @@ export class Match { } public text(): string { - return this._previewText; + return this._oneLinePreviewText; } public range(): Range { @@ -70,9 +74,9 @@ export class Match { } public preview(): { before: string; inside: string; after: string; } { - let before = this._previewText.substring(0, this._rangeInPreviewText.startColumn - 1), + let before = this._oneLinePreviewText.substring(0, this._rangeInPreviewText.startColumn - 1), inside = this.getMatchString(), - after = this._previewText.substring(this._rangeInPreviewText.endColumn - 1); + after = this._oneLinePreviewText.substring(this._rangeInPreviewText.endColumn - 1); before = lcut(before, 26); @@ -89,13 +93,15 @@ export class Match { } public get replaceString(): string { - let searchModel = this.parent().parent().searchModel; - let matchString = this.getMatchString(); - let replaceString = searchModel.replacePattern.getReplaceString(matchString); + const searchModel = this.parent().parent().searchModel; + + const fullMatchText = this.getFullMatchText(); + let replaceString = searchModel.replacePattern.getReplaceString(fullMatchText); // If match string is not matching then regex pattern has a lookahead expression if (replaceString === null) { - replaceString = searchModel.replacePattern.getReplaceString(matchString + this._previewText.substring(this._rangeInPreviewText.endColumn - 1)); + const fullMatchTextWithTrailingContent = this.getFullMatchText(true); + replaceString = searchModel.replacePattern.getReplaceString(fullMatchTextWithTrailingContent); } // Match string is still not matching. Could be unsupported matches (multi-line). @@ -106,8 +112,22 @@ export class Match { return replaceString; } + private getFullMatchText(includeTrailing = false): string { + let thisMatchPreviewLines: string[]; + if (includeTrailing) { + thisMatchPreviewLines = this._fullPreviewLines.slice(this._fullPreviewRange.startLineNumber); + } else { + thisMatchPreviewLines = this._fullPreviewLines.slice(this._fullPreviewRange.startLineNumber, this._fullPreviewRange.endLineNumber + 1); + thisMatchPreviewLines[thisMatchPreviewLines.length - 1] = thisMatchPreviewLines[thisMatchPreviewLines.length - 1].slice(0, this._fullPreviewRange.endColumn); + + } + + thisMatchPreviewLines[0] = thisMatchPreviewLines[0].slice(this._fullPreviewRange.startColumn); + return thisMatchPreviewLines.join('\n'); + } + public getMatchString(): string { - return this._previewText.substring(this._rangeInPreviewText.startColumn - 1, this._rangeInPreviewText.endColumn - 1); + return this._oneLinePreviewText.substring(this._rangeInPreviewText.startColumn - 1, this._rangeInPreviewText.endColumn - 1); } } @@ -170,10 +190,12 @@ export class FileMatch extends Disposable { this.bindModel(model); this.updateMatchesForModel(); } else { - this.rawMatch.matches.forEach(rawMatch => { - let match = new Match(this, rawMatch); - this.add(match); - }); + this.rawMatch.results + .filter(resultIsMatch) + .forEach(rawMatch => { + textSearchResultToMatches(rawMatch, this) + .forEach(m => this.add(m)); + }); } } @@ -237,16 +259,16 @@ export class FileMatch extends Disposable { } private updateMatches(matches: FindMatch[], modelChange: boolean) { - matches.forEach(m => { - const textSearchResult = editorMatchToTextSearchResult(m, this._model, this._previewOptions); - const match = new Match(this, textSearchResult); - - if (!this._removedMatches.has(match.id())) { - this.add(match); - if (this.isMatchSelected(match)) { - this._selectedMatch = match; + const textSearchResults = editorMatchesToTextSearchResults(matches, this._model, this._previewOptions); + textSearchResults.forEach(textSearchResult => { + textSearchResultToMatches(textSearchResult, this).forEach(match => { + if (!this._removedMatches.has(match.id())) { + this.add(match); + if (this.isMatchSelected(match)) { + this._selectedMatch = match; + } } - } + }); }); this._onChange.fire(modelChange); @@ -286,7 +308,7 @@ export class FileMatch extends Disposable { this._onChange.fire(false); } - public replace(toReplace: Match): TPromise { + public replace(toReplace: Match): Thenable { return this.replaceService.replace(toReplace) .then(() => this.updatesMatchesForLineAfterReplace(toReplace.range().startLineNumber, false)); } @@ -415,10 +437,13 @@ export class FolderMatch extends Disposable { raw.forEach((rawFileMatch) => { if (this._fileMatches.has(rawFileMatch.resource)) { const existingFileMatch = this._fileMatches.get(rawFileMatch.resource); - rawFileMatch.matches.forEach(m => { - let match = new Match(existingFileMatch, m); - existingFileMatch.add(match); - }); + rawFileMatch + .results + .filter(resultIsMatch) + .forEach(m => { + textSearchResultToMatches(m, existingFileMatch) + .forEach(m => existingFileMatch.add(m)); + }); updated.push(existingFileMatch); } else { const fileMatch = this.instantiationService.createInstance(FileMatch, this._query.contentPattern, this._query.previewOptions, this._query.maxResults, this, rawFileMatch); @@ -445,13 +470,13 @@ export class FolderMatch extends Disposable { this.doRemove(match); } - public replace(match: FileMatch): TPromise { + public replace(match: FileMatch): Thenable { return this.replaceService.replace([match]).then(() => { this.doRemove(match, false, true); }); } - public replaceAll(): TPromise { + public replaceAll(): Thenable { const matches = this.matches(); return this.replaceService.replace(matches).then(() => { matches.forEach(match => this.doRemove(match, false, true)); @@ -554,6 +579,7 @@ export class SearchResult extends Disposable { private _otherFilesMatch: FolderMatch; private _folderMatchesMap: TernarySearchTree = TernarySearchTree.forPaths(); private _showHighlights: boolean; + private _query: ITextQuery; private _rangeHighlightDecorations: RangeHighlightDecorations; @@ -563,6 +589,10 @@ export class SearchResult extends Disposable { this._rangeHighlightDecorations = this.instantiationService.createInstance(RangeHighlightDecorations); } + public get query(): ITextQuery { + return this._query; + } + public set query(query: ITextQuery) { // When updating the query we could change the roots, so ensure we clean up the old roots first. this.clear(); @@ -572,6 +602,7 @@ export class SearchResult extends Disposable { this._folderMatches.forEach(fm => this._folderMatchesMap.set(fm.resource().toString(), fm)); this._otherFilesMatch = this.createFolderMatch(null, 'otherFiles', this._folderMatches.length + 1, query); + this._query = query; } private createFolderMatch(resource: URI | null, id: string, index: number, query: ITextQuery): FolderMatch { @@ -631,11 +662,11 @@ export class SearchResult extends Disposable { } } - public replace(match: FileMatch): TPromise { + public replace(match: FileMatch): Thenable { return this.getFolderMatch(match.resource()).replace(match); } - public replaceAll(progressRunner: IProgressRunner): TPromise { + public replaceAll(progressRunner: IProgressRunner): Thenable { this.replacingAll = true; const promise = this.replaceService.replace(this.matches(), progressRunner); @@ -787,7 +818,7 @@ export class SearchModel extends Disposable { return this._searchResult; } - public search(query: ITextQuery, onProgress?: (result: ISearchProgressItem) => void): TPromise { + public search(query: ITextQuery, onProgress?: (result: ISearchProgressItem) => void): Thenable { this.cancelSearch(); this._searchQuery = query; @@ -1005,9 +1036,16 @@ export class RangeHighlightDecorations implements IDisposable { }); } -export function editorMatchToTextSearchResult(match: FindMatch, model: ITextModel, previewOptions: ITextSearchPreviewOptions): TextSearchResult { - return new TextSearchResult( - model.getLineContent(match.range.startLineNumber), - new Range(match.range.startLineNumber - 1, match.range.startColumn - 1, match.range.endLineNumber - 1, match.range.endColumn - 1), - previewOptions); +function textSearchResultToMatches(rawMatch: ITextSearchMatch, fileMatch: FileMatch): Match[] { + const previewLines = rawMatch.preview.text.split('\n'); + if (Array.isArray(rawMatch.ranges)) { + return rawMatch.ranges.map((r, i) => { + const previewRange: ISearchRange = rawMatch.preview.matches[i]; + return new Match(fileMatch, previewLines, previewRange, r); + }); + } else { + const previewRange = rawMatch.preview.matches; + let match = new Match(fileMatch, previewLines, previewRange, rawMatch.ranges); + return [match]; + } } diff --git a/src/vs/workbench/parts/search/electron-browser/search.contribution.ts b/src/vs/workbench/parts/search/electron-browser/search.contribution.ts index a89f1ecfc..b38067dd9 100644 --- a/src/vs/workbench/parts/search/electron-browser/search.contribution.ts +++ b/src/vs/workbench/parts/search/electron-browser/search.contribution.ts @@ -9,7 +9,6 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor } from 'vs/workbench/browser/viewlet'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import * as nls from 'vs/nls'; -import { TPromise } from 'vs/base/common/winjs.base'; import { Action } from 'vs/base/common/actions'; import * as objects from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; @@ -58,7 +57,7 @@ import { SearchViewLocationUpdater } from 'vs/workbench/parts/search/browser/sea import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -registerSingleton(ISearchWorkbenchService, SearchWorkbenchService); +registerSingleton(ISearchWorkbenchService, SearchWorkbenchService, true); replaceContributions(); searchWidgetContributions(); @@ -430,7 +429,7 @@ class ShowAllSymbolsAction extends Action { this.enabled = !!this.quickOpenService; } - public run(context?: any): TPromise { + public run(context?: any): Promise { let prefix = ShowAllSymbolsAction.ALL_SYMBOLS_PREFIX; let inputSelection: { start: number; end: number; } = void 0; @@ -443,7 +442,7 @@ class ShowAllSymbolsAction extends Action { this.quickOpenService.show(prefix, { inputSelection }); - return TPromise.as(null); + return Promise.resolve(null); } } @@ -586,14 +585,13 @@ configurationRegistry.registerConfiguration({ }, 'search.useRipgrep': { type: 'boolean', - description: nls.localize('useRipgrep', "Deprecated. This setting now falls back on \"search.usePCRE2\"."), + description: nls.localize('useRipgrep', "This setting is deprecated and now falls back on \"search.usePCRE2\"."), deprecationMessage: nls.localize('useRipgrepDeprecated', "Deprecated. Consider \"search.usePCRE2\" for advanced regex feature support."), default: true }, - 'search.disableRipgrep': { + 'search.useLegacySearch': { type: 'boolean', - description: nls.localize('disableRipgrep', "Deprecated. Controls whether to use ripgrep in text and file search."), - deprecationMessage: nls.localize('disableRipgrepDeprecated', "Deprecated. Consider \"search.usePCRE2\" for advanced regex feature support."), + description: nls.localize('useLegacySearch', "Controls whether to use the deprecated legacy mode for text and file search. It supports some text encodings that are not supported by the standard ripgrep-based search."), default: false }, 'search.useIgnoreFiles': { @@ -675,7 +673,7 @@ configurationRegistry.registerConfiguration({ 'search.usePCRE2': { type: 'boolean', default: false, - description: nls.localize('search.usePCRE2', "Whether to use the PCRE2 regex engine in text search. This enables using some advaned regex features like lookbehind and backreferences.") + description: nls.localize('search.usePCRE2', "Whether to use the PCRE2 regex engine in text search. This enables using some advanced regex features like lookbehind and backreferences. However, not all PCRE2 features are supported - only features that are also supported by JavaScript.") } } }); @@ -708,4 +706,4 @@ MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { title: nls.localize({ key: 'miGotoSymbolInWorkspace', comment: ['&& denotes a mnemonic'] }, "Go to Symbol in &&Workspace...") }, order: 3 -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/parts/search/test/browser/openFileHandler.test.ts b/src/vs/workbench/parts/search/test/browser/openFileHandler.test.ts index 9186a2bce..ced5dcf21 100644 --- a/src/vs/workbench/parts/search/test/browser/openFileHandler.test.ts +++ b/src/vs/workbench/parts/search/test/browser/openFileHandler.test.ts @@ -6,14 +6,13 @@ import * as assert from 'assert'; import * as errors from 'vs/base/common/errors'; import * as objects from 'vs/base/common/objects'; -import { TPromise } from 'vs/base/common/winjs.base'; import { CacheState } from 'vs/workbench/parts/search/browser/openFileHandler'; -import { DeferredTPromise } from 'vs/base/test/common/utils'; +import { DeferredPromise } from 'vs/base/test/common/utils'; import { QueryType, IFileQuery } from 'vs/platform/search/common/search'; suite('CacheState', () => { - test('reuse old cacheKey until new cache is loaded', function () { + test('reuse old cacheKey until new cache is loaded', async function () { const cache = new MockCache(); @@ -26,7 +25,7 @@ suite('CacheState', () => { assert.strictEqual(first.isLoaded, false); assert.strictEqual(first.isUpdating, true); - cache.loading[firstKey].complete(null); + await cache.loading[firstKey].complete(null); assert.strictEqual(first.isLoaded, true); assert.strictEqual(first.isUpdating, false); @@ -34,18 +33,18 @@ suite('CacheState', () => { second.load(); assert.strictEqual(second.isLoaded, true); assert.strictEqual(second.isUpdating, true); - assert.strictEqual(Object.keys(cache.disposing).length, 0); + await cache.awaitDisposal(0); assert.strictEqual(second.cacheKey, firstKey); // still using old cacheKey const secondKey = cache.cacheKeys[1]; - cache.loading[secondKey].complete(null); + await cache.loading[secondKey].complete(null); assert.strictEqual(second.isLoaded, true); assert.strictEqual(second.isUpdating, false); - assert.strictEqual(Object.keys(cache.disposing).length, 1); + await cache.awaitDisposal(1); assert.strictEqual(second.cacheKey, secondKey); }); - test('do not spawn additional load if previous is still loading', function () { + test('do not spawn additional load if previous is still loading', async function () { const cache = new MockCache(); @@ -64,29 +63,29 @@ suite('CacheState', () => { assert.strictEqual(Object.keys(cache.loading).length, 1); // still only one loading assert.strictEqual(second.cacheKey, firstKey); - cache.loading[firstKey].complete(null); + await cache.loading[firstKey].complete(null); assert.strictEqual(second.isLoaded, true); assert.strictEqual(second.isUpdating, false); - assert.strictEqual(Object.keys(cache.disposing).length, 0); + await cache.awaitDisposal(0); }); - test('do not use previous cacheKey if query changed', function () { + test('do not use previous cacheKey if query changed', async function () { const cache = new MockCache(); const first = createCacheState(cache); const firstKey = first.cacheKey; first.load(); - cache.loading[firstKey].complete(null); + await cache.loading[firstKey].complete(null); assert.strictEqual(first.isLoaded, true); assert.strictEqual(first.isUpdating, false); - assert.strictEqual(Object.keys(cache.disposing).length, 0); + await cache.awaitDisposal(0); cache.baseQuery.excludePattern = { '**/node_modules': true }; const second = createCacheState(cache, first); assert.strictEqual(second.isLoaded, false); assert.strictEqual(second.isUpdating, false); - assert.strictEqual(Object.keys(cache.disposing).length, 1); + await cache.awaitDisposal(1); second.load(); assert.strictEqual(second.isLoaded, false); @@ -95,40 +94,40 @@ suite('CacheState', () => { const secondKey = cache.cacheKeys[1]; assert.strictEqual(second.cacheKey, secondKey); - cache.loading[secondKey].complete(null); + await cache.loading[secondKey].complete(null); assert.strictEqual(second.isLoaded, true); assert.strictEqual(second.isUpdating, false); - assert.strictEqual(Object.keys(cache.disposing).length, 1); + await cache.awaitDisposal(1); }); - test('dispose propagates', function () { + test('dispose propagates', async function () { const cache = new MockCache(); const first = createCacheState(cache); const firstKey = first.cacheKey; first.load(); - cache.loading[firstKey].complete(null); + await cache.loading[firstKey].complete(null); const second = createCacheState(cache, first); assert.strictEqual(second.isLoaded, true); assert.strictEqual(second.isUpdating, false); - assert.strictEqual(Object.keys(cache.disposing).length, 0); + await cache.awaitDisposal(0); second.dispose(); assert.strictEqual(second.isLoaded, false); assert.strictEqual(second.isUpdating, false); - assert.strictEqual(Object.keys(cache.disposing).length, 1); + await cache.awaitDisposal(1); assert.ok(cache.disposing[firstKey]); }); - test('keep using old cacheKey when loading fails', function () { + test('keep using old cacheKey when loading fails', async function () { const cache = new MockCache(); const first = createCacheState(cache); const firstKey = first.cacheKey; first.load(); - cache.loading[firstKey].complete(null); + await cache.loading[firstKey].complete(null); const second = createCacheState(cache, first); second.load(); @@ -136,14 +135,14 @@ suite('CacheState', () => { const origErrorHandler = errors.errorHandler.getUnexpectedErrorHandler(); try { errors.setUnexpectedErrorHandler(() => null); - cache.loading[secondKey].error('loading failed'); + await cache.loading[secondKey].error('loading failed'); } finally { errors.setUnexpectedErrorHandler(origErrorHandler); } assert.strictEqual(second.isLoaded, true); assert.strictEqual(second.isUpdating, false); assert.strictEqual(Object.keys(cache.loading).length, 2); - assert.strictEqual(Object.keys(cache.disposing).length, 0); + await cache.awaitDisposal(0); assert.strictEqual(second.cacheKey, firstKey); // keep using old cacheKey const third = createCacheState(cache, second); @@ -151,15 +150,15 @@ suite('CacheState', () => { assert.strictEqual(third.isLoaded, true); assert.strictEqual(third.isUpdating, true); assert.strictEqual(Object.keys(cache.loading).length, 3); - assert.strictEqual(Object.keys(cache.disposing).length, 0); + await cache.awaitDisposal(0); assert.strictEqual(third.cacheKey, firstKey); const thirdKey = cache.cacheKeys[2]; - cache.loading[thirdKey].complete(null); + await cache.loading[thirdKey].complete(null); assert.strictEqual(third.isLoaded, true); assert.strictEqual(third.isUpdating, false); assert.strictEqual(Object.keys(cache.loading).length, 3); - assert.strictEqual(Object.keys(cache.disposing).length, 2); + await cache.awaitDisposal(2); assert.strictEqual(third.cacheKey, thirdKey); // recover with next successful load }); @@ -175,8 +174,10 @@ suite('CacheState', () => { class MockCache { public cacheKeys: string[] = []; - public loading: { [cacheKey: string]: DeferredTPromise } = {}; - public disposing: { [cacheKey: string]: DeferredTPromise } = {}; + public loading: { [cacheKey: string]: DeferredPromise } = {}; + public disposing: { [cacheKey: string]: DeferredPromise } = {}; + + private _awaitDisposal: (() => void)[][] = []; public baseQuery: IFileQuery = { type: QueryType.File @@ -187,16 +188,31 @@ suite('CacheState', () => { return objects.assign({ cacheKey: cacheKey }, this.baseQuery); } - public load(query: IFileQuery): TPromise { - const promise = new DeferredTPromise(); + public load(query: IFileQuery): Promise { + const promise = new DeferredPromise(); this.loading[query.cacheKey] = promise; - return promise; + return promise.p; } - public dispose(cacheKey: string): TPromise { - const promise = new DeferredTPromise(); + public dispose(cacheKey: string): Promise { + const promise = new DeferredPromise(); this.disposing[cacheKey] = promise; - return promise; + const n = Object.keys(this.disposing).length; + for (const done of this._awaitDisposal[n] || []) { + done(); + } + delete this._awaitDisposal[n]; + return promise.p; + } + + public awaitDisposal(n: number) { + return new Promise(resolve => { + if (n === Object.keys(this.disposing).length) { + resolve(); + } else { + (this._awaitDisposal[n] || (this._awaitDisposal[n] = [])).push(resolve); + } + }); } } }); diff --git a/src/vs/workbench/parts/search/test/browser/searchActions.test.ts b/src/vs/workbench/parts/search/test/browser/searchActions.test.ts index e16249785..44e0af7b7 100644 --- a/src/vs/workbench/parts/search/test/browser/searchActions.test.ts +++ b/src/vs/workbench/parts/search/test/browser/searchActions.test.ts @@ -127,26 +127,29 @@ suite('Search Actions', () => { function aFileMatch(): FileMatch { let rawMatch: IFileMatch = { resource: URI.file('somepath' + ++counter), - matches: [] + results: [] }; return instantiationService.createInstance(FileMatch, null, null, null, null, rawMatch); } function aMatch(fileMatch: FileMatch): Match { const line = ++counter; - const range = { - startLineNumber: line, - startColumn: 0, - endLineNumber: line, - endColumn: 2 - }; - let match = new Match(fileMatch, { - preview: { - text: 'some match', - match: range + let match = new Match( + fileMatch, + ['some match'], + { + startLineNumber: 0, + startColumn: 0, + endLineNumber: 0, + endColumn: 2 }, - range - }); + { + startLineNumber: line, + startColumn: 0, + endLineNumber: line, + endColumn: 2 + } + ); fileMatch.add(match); return match; } diff --git a/src/vs/workbench/parts/search/test/browser/searchViewlet.test.ts b/src/vs/workbench/parts/search/test/browser/searchViewlet.test.ts index fa0456019..cb709cf27 100644 --- a/src/vs/workbench/parts/search/test/browser/searchViewlet.test.ts +++ b/src/vs/workbench/parts/search/test/browser/searchViewlet.test.ts @@ -4,17 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { URI as uri } from 'vs/base/common/uri'; -import { Match, FileMatch, SearchResult } from 'vs/workbench/parts/search/common/searchModel'; -import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { SearchDataSource, SearchSorter } from 'vs/workbench/parts/search/browser/searchResultsView'; -import { IFileMatch, TextSearchResult, OneLineRange, ITextSearchResult, QueryType } from 'vs/platform/search/common/search'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; -import { IModelService } from 'vs/editor/common/services/modelService'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { IFileMatch, ITextSearchMatch, OneLineRange, QueryType } from 'vs/platform/search/common/search'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { TestContextService } from 'vs/workbench/test/workbenchTestServices'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; +import { SearchDataSource, SearchSorter } from 'vs/workbench/parts/search/browser/searchResultsView'; +import { FileMatch, Match, SearchResult } from 'vs/workbench/parts/search/common/searchModel'; +import { TestContextService } from 'vs/workbench/test/workbenchTestServices'; suite('Search - Viewlet', () => { let instantiation: TestInstantiationService; @@ -36,20 +36,24 @@ suite('Search - Viewlet', () => { }] }; - const range = { - startLineNumber: 1, - startColumn: 0, - endLineNumber: 1, - endColumn: 1 - }; result.add([{ resource: uri.parse('file:///c:/foo'), - matches: [{ + results: [{ preview: { text: 'bar', - match: range + matches: { + startLineNumber: 0, + startColumn: 0, + endLineNumber: 0, + endColumn: 1 + } }, - range + ranges: { + startLineNumber: 1, + startColumn: 0, + endLineNumber: 1, + endColumn: 1 + } }] }]); @@ -70,9 +74,9 @@ suite('Search - Viewlet', () => { let fileMatch1 = aFileMatch('C:\\foo'); let fileMatch2 = aFileMatch('C:\\with\\path'); let fileMatch3 = aFileMatch('C:\\with\\path\\foo'); - let lineMatch1 = new Match(fileMatch1, new TextSearchResult('bar', new OneLineRange(0, 1, 1))); - let lineMatch2 = new Match(fileMatch1, new TextSearchResult('bar', new OneLineRange(2, 1, 1))); - let lineMatch3 = new Match(fileMatch1, new TextSearchResult('bar', new OneLineRange(2, 1, 1))); + let lineMatch1 = new Match(fileMatch1, ['bar'], new OneLineRange(0, 1, 1), new OneLineRange(0, 1, 1)); + let lineMatch2 = new Match(fileMatch1, ['bar'], new OneLineRange(0, 1, 1), new OneLineRange(2, 1, 1)); + let lineMatch3 = new Match(fileMatch1, ['bar'], new OneLineRange(0, 1, 1), new OneLineRange(2, 1, 1)); let s = new SearchSorter(); @@ -86,10 +90,10 @@ suite('Search - Viewlet', () => { assert(s.compare(null, lineMatch2, lineMatch3) === 0); }); - function aFileMatch(path: string, searchResult?: SearchResult, ...lineMatches: ITextSearchResult[]): FileMatch { + function aFileMatch(path: string, searchResult?: SearchResult, ...lineMatches: ITextSearchMatch[]): FileMatch { let rawMatch: IFileMatch = { resource: uri.file('C:\\' + path), - matches: lineMatches + results: lineMatches }; return instantiation.createInstance(FileMatch, null, null, null, searchResult, rawMatch); } diff --git a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts index c7d5ae3fe..9c5d86b98 100644 --- a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts +++ b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts @@ -153,14 +153,11 @@ suite('QueryBuilder', () => { { contentPattern: PATTERN_INFO, folderQueries: [{ - folder: getUri(paths.join(ROOT_1, 'foo')) - }], - excludePattern: { - [paths.join(ROOT_1, 'foo/**/*.js')]: true, - [paths.join(ROOT_1, 'bar/**')]: { - 'when': '$(basename).ts' + folder: getUri(paths.join(ROOT_1, 'foo')), + excludePattern: { + ['**/*.js']: true } - }, + }], type: QueryType.Text }); }); @@ -212,7 +209,6 @@ suite('QueryBuilder', () => { folderQueries: [ { folder: getUri(paths.join(ROOT_2, 'src')) } ], - excludePattern: patternsToIExpression(paths.join(ROOT_1, 'foo/**/*.js'), paths.join(ROOT_2, 'bar')), type: QueryType.Text } ); @@ -261,7 +257,7 @@ suite('QueryBuilder', () => { folderQueries: [{ folder: ROOT_1_URI }], - excludePattern: patternsToIExpression(fixPath(paths.join(ROOT_1, 'bar'))), + excludePattern: patternsToIExpression('bar'), type: QueryType.Text }); @@ -276,7 +272,7 @@ suite('QueryBuilder', () => { folderQueries: [{ folder: ROOT_1_URI }], - excludePattern: patternsToIExpression(fixPath(paths.join(ROOT_1, 'bar/**/*.ts'))), + excludePattern: patternsToIExpression('bar/**/*.ts'), type: QueryType.Text }); @@ -291,7 +287,7 @@ suite('QueryBuilder', () => { folderQueries: [{ folder: ROOT_1_URI }], - excludePattern: patternsToIExpression(fixPath(paths.join(ROOT_1, 'bar/**/*.ts'))), + excludePattern: patternsToIExpression('bar/**/*.ts'), type: QueryType.Text }); }); diff --git a/src/vs/workbench/parts/search/test/common/searchModel.test.ts b/src/vs/workbench/parts/search/test/common/searchModel.test.ts index 920fb34e5..43d9115a4 100644 --- a/src/vs/workbench/parts/search/test/common/searchModel.test.ts +++ b/src/vs/workbench/parts/search/test/common/searchModel.test.ts @@ -5,20 +5,19 @@ import * as assert from 'assert'; import * as sinon from 'sinon'; import { timeout } from 'vs/base/common/async'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { DeferredTPromise } from 'vs/base/test/common/utils'; +import { DeferredPromise } from 'vs/base/test/common/utils'; import { Range } from 'vs/editor/common/core/range'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { IFileMatch, IFileSearchStats, IFolderQuery, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchService, ITextSearchResult, TextSearchResult, OneLineRange } from 'vs/platform/search/common/search'; +import { IFileMatch, IFileSearchStats, IFolderQuery, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchService, ITextSearchMatch, OneLineRange, TextSearchMatch } from 'vs/platform/search/common/search'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { SearchModel } from 'vs/workbench/parts/search/common/searchModel'; -import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; const nullEvent = new class { @@ -71,7 +70,7 @@ suite('SearchModel', () => { instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(IModelService, stubModelService(instantiationService)); instantiationService.stub(ISearchService, {}); - instantiationService.stub(ISearchService, 'textSearch', TPromise.as({ results: [] })); + instantiationService.stub(ISearchService, 'textSearch', Promise.resolve({ results: [] })); }); teardown(() => { @@ -82,8 +81,8 @@ suite('SearchModel', () => { function searchServiceWithResults(results: IFileMatch[], complete: ISearchComplete | null = null): ISearchService { return { - textSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): TPromise { - return new TPromise(resolve => { + textSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): Thenable { + return new Promise(resolve => { process.nextTick(() => { results.forEach(onProgress); resolve(complete); @@ -95,8 +94,8 @@ suite('SearchModel', () => { function searchServiceWithError(error: Error): ISearchService { return { - textSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): TPromise { - return new TPromise((resolve, reject) => { + textSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): Thenable { + return new Promise((resolve, reject) => { reject(error); }); } @@ -105,12 +104,12 @@ suite('SearchModel', () => { function canceleableSearchService(tokenSource: CancellationTokenSource): ISearchService { return { - textSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): TPromise { + textSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): Thenable { if (token) { token.onCancellationRequested(() => tokenSource.cancel()); } - return new TPromise(resolve => { + return new Promise(resolve => { process.nextTick(() => { resolve({}); }); @@ -122,9 +121,9 @@ suite('SearchModel', () => { test('Search Model: Search adds to results', async () => { let results = [ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)), - new TextSearchResult('preview 1', new OneLineRange(1, 4, 11))), - aRawMatch('file://c:/2', new TextSearchResult('preview 2', lineOneRange))]; + new TextSearchMatch('preview 1', new OneLineRange(1, 1, 4)), + new TextSearchMatch('preview 1', new OneLineRange(1, 4, 11))), + aRawMatch('file://c:/2', new TextSearchMatch('preview 2', lineOneRange))]; instantiationService.stub(ISearchService, searchServiceWithResults(results)); let testObject: SearchModel = instantiationService.createInstance(SearchModel); @@ -152,10 +151,10 @@ suite('SearchModel', () => { let target = instantiationService.spy(ITelemetryService, 'publicLog'); let results = [ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)), - new TextSearchResult('preview 1', new OneLineRange(1, 4, 11))), + new TextSearchMatch('preview 1', new OneLineRange(1, 1, 4)), + new TextSearchMatch('preview 1', new OneLineRange(1, 4, 11))), aRawMatch('file://c:/2', - new TextSearchResult('preview 2', lineOneRange))]; + new TextSearchMatch('preview 2', lineOneRange))]; instantiationService.stub(ISearchService, searchServiceWithResults(results)); let testObject: SearchModel = instantiationService.createInstance(SearchModel); @@ -193,7 +192,7 @@ suite('SearchModel', () => { instantiationService.stub(ITelemetryService, 'publicLog', target1); instantiationService.stub(ISearchService, searchServiceWithResults( - [aRawMatch('file://c:/1', new TextSearchResult('some preview', lineOneRange))], + [aRawMatch('file://c:/1', new TextSearchMatch('some preview', lineOneRange))], { results: [], stats: testSearchStats })); let testObject = instantiationService.createInstance(SearchModel); @@ -221,8 +220,8 @@ suite('SearchModel', () => { let testObject = instantiationService.createInstance(SearchModel); let result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); - return timeout(1).then(() => { - return result.then(() => { }, () => { + return result.then(() => { }, () => { + return timeout(1).then(() => { assert.ok(target1.calledWith('searchResultsFirstRender')); assert.ok(target1.calledWith('searchResultsFinished')); // assert.ok(target2.calledOnce); @@ -236,16 +235,16 @@ suite('SearchModel', () => { let target1 = sinon.stub().returns(nullEvent); instantiationService.stub(ITelemetryService, 'publicLog', target1); - let promise = new DeferredTPromise(); - instantiationService.stub(ISearchService, 'textSearch', promise); + let deferredPromise = new DeferredPromise(); + instantiationService.stub(ISearchService, 'textSearch', deferredPromise.p); let testObject = instantiationService.createInstance(SearchModel); let result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); - promise.cancel(); + deferredPromise.cancel(); - return timeout(1).then(() => { - return result.then(() => { }, () => { + return result.then(() => { }, () => { + return timeout(1).then(() => { assert.ok(target1.calledWith('searchResultsFirstRender')); assert.ok(target1.calledWith('searchResultsFinished')); // assert.ok(target2.calledOnce); @@ -256,10 +255,10 @@ suite('SearchModel', () => { test('Search Model: Search results are cleared during search', async () => { let results = [ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)), - new TextSearchResult('preview 1', new OneLineRange(1, 4, 11))), + new TextSearchMatch('preview 1', new OneLineRange(1, 1, 4)), + new TextSearchMatch('preview 1', new OneLineRange(1, 4, 11))), aRawMatch('file://c:/2', - new TextSearchResult('preview 2', lineOneRange))]; + new TextSearchMatch('preview 2', lineOneRange))]; instantiationService.stub(ISearchService, searchServiceWithResults(results)); let testObject: SearchModel = instantiationService.createInstance(SearchModel); await testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); @@ -286,8 +285,8 @@ suite('SearchModel', () => { test('getReplaceString returns proper replace string for regExpressions', async () => { let results = [ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)), - new TextSearchResult('preview 1', new OneLineRange(1, 4, 11)))]; + new TextSearchMatch('preview 1', new OneLineRange(1, 1, 4)), + new TextSearchMatch('preview 1', new OneLineRange(1, 4, 11)))]; instantiationService.stub(ISearchService, searchServiceWithResults(results)); let testObject: SearchModel = instantiationService.createInstance(SearchModel); @@ -314,8 +313,8 @@ suite('SearchModel', () => { assert.equal('helloe', match.replaceString); }); - function aRawMatch(resource: string, ...matches: ITextSearchResult[]): IFileMatch { - return { resource: URI.parse(resource), matches }; + function aRawMatch(resource: string, ...results: ITextSearchMatch[]): IFileMatch { + return { resource: URI.parse(resource), results }; } function stub(arg1: any, arg2: any, arg3: any): sinon.SinonStub { diff --git a/src/vs/workbench/parts/search/test/common/searchResult.test.ts b/src/vs/workbench/parts/search/test/common/searchResult.test.ts index 882b52066..669d31be0 100644 --- a/src/vs/workbench/parts/search/test/common/searchResult.test.ts +++ b/src/vs/workbench/parts/search/test/common/searchResult.test.ts @@ -7,7 +7,7 @@ import * as sinon from 'sinon'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { Match, FileMatch, SearchResult, SearchModel } from 'vs/workbench/parts/search/common/searchModel'; import { URI } from 'vs/base/common/uri'; -import { IFileMatch, TextSearchResult, OneLineRange, ITextSearchResult } from 'vs/platform/search/common/search'; +import { IFileMatch, TextSearchMatch, OneLineRange, ITextSearchMatch } from 'vs/platform/search/common/search'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { Range } from 'vs/editor/common/core/range'; @@ -33,7 +33,7 @@ suite('SearchResult', () => { test('Line Match', function () { let fileMatch = aFileMatch('folder/file.txt', null); - let lineMatch = new Match(fileMatch, new TextSearchResult('foo bar', new OneLineRange(1, 0, 3))); + let lineMatch = new Match(fileMatch, ['foo bar'], new OneLineRange(0, 0, 3), new OneLineRange(1, 0, 3)); assert.equal(lineMatch.text(), 'foo bar'); assert.equal(lineMatch.range().startLineNumber, 2); assert.equal(lineMatch.range().endLineNumber, 2); @@ -43,7 +43,7 @@ suite('SearchResult', () => { }); test('Line Match - Remove', function () { - let fileMatch = aFileMatch('folder/file.txt', aSearchResult(), new TextSearchResult('foo bar', new OneLineRange(1, 0, 3))); + let fileMatch = aFileMatch('folder/file.txt', aSearchResult(), new TextSearchMatch('foo bar', new OneLineRange(1, 0, 3))); let lineMatch = fileMatch.matches()[0]; fileMatch.remove(lineMatch); assert.equal(fileMatch.matches().length, 0); @@ -65,8 +65,8 @@ suite('SearchResult', () => { let testObject = aFileMatch( 'folder/file.txt', aSearchResult(), - new TextSearchResult('foo', new OneLineRange(1, 0, 3)), - new TextSearchResult('bar', new OneLineRange(1, 5, 3))); + new TextSearchMatch('foo', new OneLineRange(1, 0, 3)), + new TextSearchMatch('bar', new OneLineRange(1, 5, 3))); testObject.setSelectedMatch(testObject.matches()[0]); @@ -77,8 +77,8 @@ suite('SearchResult', () => { let testObject = aFileMatch( 'folder/file.txt', aSearchResult(), - new TextSearchResult('foo', new OneLineRange(1, 0, 3)), - new TextSearchResult('bar', new OneLineRange(1, 5, 3))); + new TextSearchMatch('foo', new OneLineRange(1, 0, 3)), + new TextSearchMatch('bar', new OneLineRange(1, 5, 3))); let target = testObject.matches()[0]; testObject.remove(target); @@ -91,8 +91,8 @@ suite('SearchResult', () => { let testObject = aFileMatch( 'folder/file.txt', aSearchResult(), - new TextSearchResult('foo', new OneLineRange(1, 0, 3)), - new TextSearchResult('bar', new OneLineRange(1, 5, 3))); + new TextSearchMatch('foo', new OneLineRange(1, 0, 3)), + new TextSearchMatch('bar', new OneLineRange(1, 5, 3))); let target = testObject.matches()[0]; testObject.setSelectedMatch(target); @@ -102,8 +102,8 @@ suite('SearchResult', () => { test('File Match: isSelected return false for un-selected match', function () { let testObject = aFileMatch('folder/file.txt', aSearchResult(), - new TextSearchResult('foo', new OneLineRange(1, 0, 3)), - new TextSearchResult('bar', new OneLineRange(1, 5, 3))); + new TextSearchMatch('foo', new OneLineRange(1, 0, 3)), + new TextSearchMatch('bar', new OneLineRange(1, 5, 3))); testObject.setSelectedMatch(testObject.matches()[0]); assert.ok(!testObject.isMatchSelected(testObject.matches()[1])); }); @@ -112,8 +112,8 @@ suite('SearchResult', () => { let testObject = aFileMatch( 'folder/file.txt', aSearchResult(), - new TextSearchResult('foo', new OneLineRange(1, 0, 3)), - new TextSearchResult('bar', new OneLineRange(1, 5, 3))); + new TextSearchMatch('foo', new OneLineRange(1, 0, 3)), + new TextSearchMatch('bar', new OneLineRange(1, 5, 3))); testObject.setSelectedMatch(testObject.matches()[0]); testObject.setSelectedMatch(null); @@ -124,8 +124,8 @@ suite('SearchResult', () => { let testObject = aFileMatch( 'folder/file.txt', aSearchResult(), - new TextSearchResult('foo', new OneLineRange(1, 0, 3)), - new TextSearchResult('bar', new OneLineRange(1, 5, 3))); + new TextSearchMatch('foo', new OneLineRange(1, 0, 3)), + new TextSearchMatch('bar', new OneLineRange(1, 5, 3))); testObject.setSelectedMatch(null); assert.equal(null, testObject.getSelectedMatch()); @@ -134,7 +134,7 @@ suite('SearchResult', () => { test('Alle Drei Zusammen', function () { let searchResult = instantiationService.createInstance(SearchResult, null); let fileMatch = aFileMatch('far/boo', searchResult); - let lineMatch = new Match(fileMatch, new TextSearchResult('foo bar', new OneLineRange(1, 0, 3))); + let lineMatch = new Match(fileMatch, ['foo bar'], new OneLineRange(0, 0, 3), new OneLineRange(1, 0, 3)); assert(lineMatch.parent() === fileMatch); assert(fileMatch.parent() === searchResult); @@ -143,9 +143,9 @@ suite('SearchResult', () => { test('Adding a raw match will add a file match with line matches', function () { let testObject = aSearchResult(); let target = [aRawMatch('file://c:/', - new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)), - new TextSearchResult('preview 1', new OneLineRange(1, 4, 11)), - new TextSearchResult('preview 2', lineOneRange))]; + new TextSearchMatch('preview 1', new OneLineRange(1, 1, 4)), + new TextSearchMatch('preview 1', new OneLineRange(1, 4, 11)), + new TextSearchMatch('preview 2', lineOneRange))]; testObject.add(target); @@ -172,10 +172,10 @@ suite('SearchResult', () => { let testObject = aSearchResult(); let target = [ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', new OneLineRange(1, 1, 4)), - new TextSearchResult('preview 1', new OneLineRange(1, 4, 11))), + new TextSearchMatch('preview 1', new OneLineRange(1, 1, 4)), + new TextSearchMatch('preview 1', new OneLineRange(1, 4, 11))), aRawMatch('file://c:/2', - new TextSearchResult('preview 2', lineOneRange))]; + new TextSearchMatch('preview 2', lineOneRange))]; testObject.add(target); @@ -205,9 +205,9 @@ suite('SearchResult', () => { let testObject = aSearchResult(); testObject.add([ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', lineOneRange)), + new TextSearchMatch('preview 1', lineOneRange)), aRawMatch('file://c:/2', - new TextSearchResult('preview 2', lineOneRange))]); + new TextSearchMatch('preview 2', lineOneRange))]); testObject.matches()[0].onDispose(target1); testObject.matches()[1].onDispose(target2); @@ -224,7 +224,7 @@ suite('SearchResult', () => { let testObject = aSearchResult(); testObject.add([ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', lineOneRange))]); + new TextSearchMatch('preview 1', lineOneRange))]); let objectRoRemove = testObject.matches()[0]; testObject.onChange(target); @@ -239,7 +239,7 @@ suite('SearchResult', () => { let testObject = aSearchResult(); testObject.add([ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', lineOneRange))]); + new TextSearchMatch('preview 1', lineOneRange))]); let objectRoRemove = testObject.matches()[0]; testObject.onChange(target); @@ -253,7 +253,7 @@ suite('SearchResult', () => { let testObject = aSearchResult(); testObject.add([ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', lineOneRange))]); + new TextSearchMatch('preview 1', lineOneRange))]); let target = testObject.matches()[0]; let matchToRemove = target.matches()[0]; target.remove(matchToRemove); @@ -270,7 +270,7 @@ suite('SearchResult', () => { let testObject = aSearchResult(); testObject.add([ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', lineOneRange))]); + new TextSearchMatch('preview 1', lineOneRange))]); testObject.replace(testObject.matches()[0]); @@ -283,7 +283,7 @@ suite('SearchResult', () => { let testObject = aSearchResult(); testObject.add([ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', lineOneRange))]); + new TextSearchMatch('preview 1', lineOneRange))]); testObject.onChange(target); let objectRoRemove = testObject.matches()[0]; @@ -298,9 +298,9 @@ suite('SearchResult', () => { let testObject = aSearchResult(); testObject.add([ aRawMatch('file://c:/1', - new TextSearchResult('preview 1', lineOneRange)), + new TextSearchMatch('preview 1', lineOneRange)), aRawMatch('file://c:/2', - new TextSearchResult('preview 2', lineOneRange))]); + new TextSearchMatch('preview 2', lineOneRange))]); testObject.replaceAll(null); @@ -351,10 +351,10 @@ suite('SearchResult', () => { // lineHasNoDecoration(oneModel, 2); //}); - function aFileMatch(path: string, searchResult?: SearchResult, ...lineMatches: ITextSearchResult[]): FileMatch { + function aFileMatch(path: string, searchResult?: SearchResult, ...lineMatches: ITextSearchMatch[]): FileMatch { let rawMatch: IFileMatch = { resource: URI.file('/' + path), - matches: lineMatches + results: lineMatches }; return instantiationService.createInstance(FileMatch, null, null, null, searchResult, rawMatch); } @@ -365,8 +365,8 @@ suite('SearchResult', () => { return searchModel.searchResult; } - function aRawMatch(resource: string, ...matches: ITextSearchResult[]): IFileMatch { - return { resource: URI.parse(resource), matches }; + function aRawMatch(resource: string, ...results: ITextSearchMatch[]): IFileMatch { + return { resource: URI.parse(resource), results }; } function stubModelService(instantiationService: TestInstantiationService): IModelService { diff --git a/src/vs/workbench/parts/snippets/electron-browser/configureSnippets.ts b/src/vs/workbench/parts/snippets/electron-browser/configureSnippets.ts index 74a545675..5a6d39efa 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/configureSnippets.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/configureSnippets.ts @@ -57,11 +57,13 @@ async function computePicks(snippetService: ISnippetsService, envService: IEnvir outer: for (const snippet of file.data) { for (const scope of snippet.scopes) { const name = modeService.getLanguageName(scope); - if (names.size >= 4) { - names.add(`${name}...`); - break outer; - } else { - names.add(name); + if (name) { + if (names.size >= 4) { + names.add(`${name}...`); + break outer; + } else { + names.add(name); + } } } } @@ -158,6 +160,7 @@ async function createGlobalSnippetFile(defaultPath: URI, windowService: IWindowS ].join('\n')); await opener.open(resource); + return undefined; } async function createLanguageSnippetFile(pick: ISnippetPick, fileService: IFileService) { @@ -218,7 +221,7 @@ CommandsRegistry.registerCommand(id, async accessor => { existing.push({ type: 'separator', label: nls.localize('new.global.sep', "New Snippets") }); } - const pick = await quickInputService.pick(<(IQuickPickItem | ISnippetPick | GlobalSnippetPick)[]>[].concat(existing, globalSnippetPicks, picks.future), { + const pick = await quickInputService.pick(([] as QuickPickInput[]).concat(existing, globalSnippetPicks, picks.future), { placeHolder: nls.localize('openSnippet.pickLanguage', "Select Snippets File or Create Snippets"), matchOnDescription: true }); diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippetCompletionProvider.ts b/src/vs/workbench/parts/snippets/electron-browser/snippetCompletionProvider.ts index fafe8a563..f2abc6ac9 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippetCompletionProvider.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetCompletionProvider.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import { MarkdownString } from 'vs/base/common/htmlContent'; import { compare } from 'vs/base/common/strings'; import { Position } from 'vs/editor/common/core/position'; @@ -75,7 +73,7 @@ export class SnippetCompletionProvider implements CompletionItemProvider { // } - provideCompletionItems(model: ITextModel, position: Position): Promise { + provideCompletionItems(model: ITextModel, position: Position): Promise | undefined { if (position.column >= SnippetCompletionProvider._maxPrefix) { return undefined; @@ -88,6 +86,8 @@ export class SnippetCompletionProvider implements CompletionItemProvider { let pos = { lineNumber: position.lineNumber, column: 1 }; let lineOffsets: number[] = []; let linePrefixLow = model.getLineContent(position.lineNumber).substr(0, position.column - 1).toLowerCase(); + let endsInWhitespace = linePrefixLow.match(/\s$/); + while (pos.column < position.column) { let word = model.getWordAtPosition(pos); if (word) { @@ -108,23 +108,27 @@ export class SnippetCompletionProvider implements CompletionItemProvider { pos.column += 1; } } - if (lineOffsets.length === 0) { - // no interesting spans found -> pick all snippets - suggestions = snippets.map(snippet => new SnippetCompletion(snippet, Range.fromPositions(position))); - } - else { - let consumed = new Set(); - suggestions = []; - for (let start of lineOffsets) { - for (const snippet of snippets) { - if (!consumed.has(snippet) && matches(linePrefixLow, start, snippet.prefixLow, 0)) { - suggestions.push(new SnippetCompletion(snippet, Range.fromPositions(position.delta(0, -(linePrefixLow.length - start)), position))); - consumed.add(snippet); - } + + let availableSnippets = new Set(); + snippets.forEach(availableSnippets.add, availableSnippets); + suggestions = []; + for (let start of lineOffsets) { + availableSnippets.forEach(snippet => { + if (matches(linePrefixLow, start, snippet.prefixLow, 0)) { + suggestions.push(new SnippetCompletion(snippet, Range.fromPositions(position.delta(0, -(linePrefixLow.length - start)), position))); + availableSnippets.delete(snippet); } - } + }); + } + if (endsInWhitespace || lineOffsets.length === 0) { + // add remaing snippets when the current prefix ends in whitespace or when no + // interesting positions have been found + availableSnippets.forEach(snippet => { + suggestions.push(new SnippetCompletion(snippet, Range.fromPositions(position))); + }); } + // dismbiguate suggestions with same labels suggestions.sort(SnippetCompletion.compareByLabel); for (let i = 0; i < suggestions.length; i++) { @@ -152,8 +156,8 @@ export class SnippetCompletionProvider implements CompletionItemProvider { // snippets, else fall back to the outer language model.tokenizeIfCheap(position.lineNumber); let languageId = model.getLanguageIdAtPosition(position.lineNumber, position.column); - let { language } = this._modeService.getLanguageIdentifier(languageId); - if (!this._modeService.getLanguageName(language)) { + const languageIdentifier = this._modeService.getLanguageIdentifier(languageId); + if (languageIdentifier && !this._modeService.getLanguageName(languageIdentifier.language)) { languageId = model.getLanguageIdentifier().id; } return languageId; diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippetsFile.ts b/src/vs/workbench/parts/snippets/electron-browser/snippetsFile.ts index cd7ad31f6..bf2157469 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippetsFile.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetsFile.ts @@ -84,7 +84,7 @@ export class Snippet { let stack = [...textmateSnippet.children]; while (stack.length > 0) { - let marker = stack.shift(); + const marker = stack.shift()!; if ( marker instanceof Variable @@ -143,7 +143,7 @@ export class SnippetFile { readonly isGlobalSnippets: boolean; readonly isUserSnippets: boolean; - private _loadPromise: Promise; + private _loadPromise?: Promise; constructor( readonly source: SnippetSource, diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts index c1a957ebc..ab33b9093 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { basename, extname, join } from 'path'; -import { isFalsyOrEmpty } from 'vs/base/common/arrays'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { combinedDisposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { values } from 'vs/base/common/map'; @@ -281,10 +280,8 @@ class SnippetsService implements ISnippetsService { let addFolderSnippets = () => { disposables = dispose(disposables); return this._fileService.resolveFile(folder).then(stat => { - if (!isFalsyOrEmpty(stat.children)) { - for (const entry of stat.children) { - disposables.push(this._addSnippetFile(entry.resource, source)); - } + for (const entry of stat.children || []) { + disposables.push(this._addSnippetFile(entry.resource, source)); } }, err => { this._logService.error(`Failed snippets from folder '${folder.toString()}'`, err); diff --git a/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts b/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts index 226ed0380..9af0d8744 100644 --- a/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts +++ b/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts @@ -352,4 +352,59 @@ suite('SnippetsService', function () { toDispose.dispose(); }); + + test('No snippets shown when triggering completions at whitespace on line that already has text #62335', async function () { + snippetService = new SimpleSnippetService([new Snippet( + ['fooLang'], + 'bug', + 'bug', + '', + 'second', + '', + SnippetSource.User + )]); + + const provider = new SnippetCompletionProvider(modeService, snippetService); + + let model = TextModel.createFromString('a ', undefined, modeService.getLanguageIdentifier('fooLang')); + let result = await provider.provideCompletionItems(model, new Position(1, 3)); + + assert.equal(result.suggestions.length, 1); + }); + + test('Snippet prefix with special chars and numbers does not work #62906', async function () { + snippetService = new SimpleSnippetService([new Snippet( + ['fooLang'], + 'noblockwdelay', + '<<', + '', + '<= #dly"', + '', + SnippetSource.User + ), new Snippet( + ['fooLang'], + 'noblockwdelay', + '11', + '', + 'eleven', + '', + SnippetSource.User + )]); + + const provider = new SnippetCompletionProvider(modeService, snippetService); + + let model = TextModel.createFromString(' <', undefined, modeService.getLanguageIdentifier('fooLang')); + let result = await provider.provideCompletionItems(model, new Position(1, 3)); + + assert.equal(result.suggestions.length, 1); + let [first] = result.suggestions; + assert.equal(first.range.startColumn, 2); + + model = TextModel.createFromString('1', undefined, modeService.getLanguageIdentifier('fooLang')); + result = await provider.provideCompletionItems(model, new Position(1, 2)); + + assert.equal(result.suggestions.length, 1); + [first] = result.suggestions; + assert.equal(first.range.startColumn, 1); + }); }); diff --git a/src/vs/workbench/parts/stats/node/workspaceStats.ts b/src/vs/workbench/parts/stats/node/workspaceStats.ts index 9d27b1cee..16dabafd2 100644 --- a/src/vs/workbench/parts/stats/node/workspaceStats.ts +++ b/src/vs/workbench/parts/stats/node/workspaceStats.ts @@ -228,6 +228,8 @@ export class WorkspaceStats implements IWorkbenchContribution { // Cloud Stats this.reportCloudStats(); + + this.reportProxyStats(); } private static searchArray(arr: string[], regEx: RegExp): boolean { @@ -695,4 +697,20 @@ export class WorkspaceStats implements IWorkbenchContribution { this.reportAzure(uris); } } + + private reportProxyStats() { + this.windowService.resolveProxy('https://www.example.com/') + .then(proxy => { + let type = proxy ? String(proxy).trim().split(/\s+/, 1)[0] : 'EMPTY'; + if (['DIRECT', 'PROXY', 'HTTPS', 'SOCKS', 'EMPTY'].indexOf(type) === -1) { + type = 'UNKNOWN'; + } + /* __GDPR__ + "resolveProxy.stats" : { + "type": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } + } + */ + this.telemetryService.publicLog('resolveProxy.stats', { type }); + }).then(null, onUnexpectedError); + } } diff --git a/src/vs/workbench/parts/tasks/common/problemMatcher.ts b/src/vs/workbench/parts/tasks/common/problemMatcher.ts index eb502913d..188cc5327 100644 --- a/src/vs/workbench/parts/tasks/common/problemMatcher.ts +++ b/src/vs/workbench/parts/tasks/common/problemMatcher.ts @@ -14,7 +14,6 @@ import * as UUID from 'vs/base/common/uuid'; import * as Platform from 'vs/base/common/platform'; import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { ValidationStatus, ValidationState, IProblemReporter, Parser } from 'vs/base/common/parsers'; import { IStringDictionary } from 'vs/base/common/collections'; @@ -1099,7 +1098,7 @@ let problemPatternExtPoint = ExtensionsRegistry.registerExtensionPoint; + onReady(): Promise; get(key: string): ProblemPattern | MultiLineProblemPattern; } @@ -1107,12 +1106,12 @@ export interface IProblemPatternRegistry { class ProblemPatternRegistryImpl implements IProblemPatternRegistry { private patterns: IStringDictionary; - private readyPromise: TPromise; + private readyPromise: Promise; constructor() { this.patterns = Object.create(null); this.fillDefaults(); - this.readyPromise = new TPromise((resolve, reject) => { + this.readyPromise = new Promise((resolve, reject) => { problemPatternExtPoint.setHandler((extensions) => { // We get all statically know extension during startup in one batch try { @@ -1149,7 +1148,7 @@ class ProblemPatternRegistryImpl implements IProblemPatternRegistry { }); } - public onReady(): TPromise { + public onReady(): Promise { return this.readyPromise; } @@ -1317,7 +1316,7 @@ export class ProblemMatcherParser extends Parser { private createProblemMatcher(description: Config.ProblemMatcher): ProblemMatcher | null { let result: ProblemMatcher | null = null; - let owner = description.owner ? description.owner : UUID.generateUuid(); + let owner = Types.isString(description.owner) ? description.owner : UUID.generateUuid(); let source = Types.isString(description.source) ? description.source : undefined; let applyTo = Types.isString(description.applyTo) ? ApplyToKind.fromString(description.applyTo) : ApplyToKind.allDocuments; if (!applyTo) { @@ -1365,25 +1364,23 @@ export class ProblemMatcherParser extends Parser { let base = ProblemMatcherRegistry.get(variableName.substring(1)); if (base) { result = Objects.deepClone(base); - if (description.owner) { + if (description.owner !== void 0 && owner !== void 0) { result.owner = owner; } - if (source) { + if (description.source !== void 0 && source !== void 0) { result.source = source; } - if (fileLocation) { + if (description.fileLocation !== void 0 && fileLocation !== void 0) { result.fileLocation = fileLocation; - } - if (filePrefix) { result.filePrefix = filePrefix; } - if (pattern) { + if (description.pattern !== void 0 && pattern !== void 0 && pattern !== null) { result.pattern = pattern; } - if (description.severity) { + if (description.severity !== void 0 && severity !== void 0) { result.severity = severity; } - if (description.applyTo) { + if (description.applyTo !== void 0 && applyTo !== void 0) { result.applyTo = applyTo; } } @@ -1669,7 +1666,7 @@ let problemMatchersExtPoint = ExtensionsRegistry.registerExtensionPoint; + onReady(): Promise; get(name: string): NamedProblemMatcher; keys(): string[]; } @@ -1677,12 +1674,12 @@ export interface IProblemMatcherRegistry { class ProblemMatcherRegistryImpl implements IProblemMatcherRegistry { private matchers: IStringDictionary; - private readyPromise: TPromise; + private readyPromise: Promise; constructor() { this.matchers = Object.create(null); this.fillDefaults(); - this.readyPromise = new TPromise((resolve, reject) => { + this.readyPromise = new Promise((resolve, reject) => { problemMatchersExtPoint.setHandler((extensions) => { try { extensions.forEach(extension => { @@ -1706,7 +1703,7 @@ class ProblemMatcherRegistryImpl implements IProblemMatcherRegistry { }); } - public onReady(): TPromise { + public onReady(): Promise { ProblemPatternRegistry.onReady(); return this.readyPromise; } diff --git a/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts b/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts index 1da2c7456..57fa3aba3 100644 --- a/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts +++ b/src/vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; import { IStringDictionary } from 'vs/base/common/collections'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as Types from 'vs/base/common/types'; import * as Objects from 'vs/base/common/objects'; @@ -46,7 +45,7 @@ namespace Configuration { properties?: IJSONSchemaMap; } - export function from(value: TaskDefinition, extensionId: string, messageCollector: ExtensionMessageCollector): Tasks.TaskDefinition { + export function from(value: TaskDefinition, extensionId: string, messageCollector: ExtensionMessageCollector): Tasks.TaskDefinition | undefined { if (!value) { return undefined; } @@ -75,7 +74,7 @@ const taskDefinitionsExtPoint = ExtensionsRegistry.registerExtensionPoint; + onReady(): Promise; get(key: string): Tasks.TaskDefinition; all(): Tasks.TaskDefinition[]; @@ -85,12 +84,12 @@ export interface ITaskDefinitionRegistry { class TaskDefinitionRegistryImpl implements ITaskDefinitionRegistry { private taskTypes: IStringDictionary; - private readyPromise: TPromise; + private readyPromise: Promise; private _schema: IJSONSchema; constructor() { this.taskTypes = Object.create(null); - this.readyPromise = new TPromise((resolve, reject) => { + this.readyPromise = new Promise((resolve, reject) => { taskDefinitionsExtPoint.setHandler((extensions) => { try { for (let extension of extensions) { @@ -109,7 +108,7 @@ class TaskDefinitionRegistryImpl implements ITaskDefinitionRegistry { }); } - public onReady(): TPromise { + public onReady(): Promise { return this.readyPromise; } @@ -137,7 +136,7 @@ class TaskDefinitionRegistryImpl implements ITaskDefinitionRegistry { } else { schema.properties = Object.create(null); } - schema.properties.type = { + schema.properties!.type = { type: 'string', enum: [definition.taskType] }; diff --git a/src/vs/workbench/parts/tasks/common/taskService.ts b/src/vs/workbench/parts/tasks/common/taskService.ts index 316d78782..6eed335a6 100644 --- a/src/vs/workbench/parts/tasks/common/taskService.ts +++ b/src/vs/workbench/parts/tasks/common/taskService.ts @@ -8,6 +8,7 @@ import { Action } from 'vs/base/common/actions'; import { Event } from 'vs/base/common/event'; import { LinkedMap } from 'vs/base/common/map'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { Task, ContributedTask, CustomTask, TaskSet, TaskSorter, TaskEvent, TaskIdentifier } from 'vs/workbench/parts/tasks/common/tasks'; @@ -66,8 +67,7 @@ export interface ITaskService { customize(task: ContributedTask | CustomTask, properties?: {}, openConfig?: boolean): TPromise; openConfig(task: CustomTask): TPromise; - registerTaskProvider(handle: number, taskProvider: ITaskProvider): void; - unregisterTaskProvider(handle: number): boolean; + registerTaskProvider(taskProvider: ITaskProvider): IDisposable; registerTaskSystem(scheme: string, taskSystemInfo: TaskSystemInfo): void; } diff --git a/src/vs/workbench/parts/tasks/common/taskSystem.ts b/src/vs/workbench/parts/tasks/common/taskSystem.ts index b565fea81..5287e7b99 100644 --- a/src/vs/workbench/parts/tasks/common/taskSystem.ts +++ b/src/vs/workbench/parts/tasks/common/taskSystem.ts @@ -9,9 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { TerminateResponse } from 'vs/base/common/processes'; import { Event } from 'vs/base/common/event'; import { Platform } from 'vs/base/common/platform'; - import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; - import { Task, TaskEvent, KeyedTaskIdentifier } from './tasks'; export const enum TaskErrors { @@ -85,6 +83,7 @@ export const enum TaskExecuteKind { export interface ITaskExecuteResult { kind: TaskExecuteKind; promise: TPromise; + task: Task; started?: { restartOnFileChanges?: string; }; @@ -102,11 +101,25 @@ export interface TaskTerminateResponse extends TerminateResponse { task: Task | undefined; } +export interface ResolveSet { + process?: { + name: string; + cwd?: string; + path?: string; + }; + variables: Set; +} + +export interface ResolvedVariables { + process?: string; + variables: Map; +} + export interface TaskSystemInfo { platform: Platform; context: any; uriProvider: (this: void, path: string) => URI; - resolveVariables(workspaceFolder: IWorkspaceFolder, variables: Set): TPromise>; + resolveVariables(workspaceFolder: IWorkspaceFolder, toResolve: ResolveSet): Promise; } export interface TaskSystemInfoResovler { @@ -116,6 +129,7 @@ export interface TaskSystemInfoResovler { export interface ITaskSystem { onDidStateChange: Event; run(task: Task, resolver: ITaskResolver): ITaskExecuteResult; + rerun(): ITaskExecuteResult | undefined; isActive(): TPromise; isActiveSync(): boolean; getActiveTasks(): Task[]; diff --git a/src/vs/workbench/parts/tasks/common/tasks.ts b/src/vs/workbench/parts/tasks/common/tasks.ts index 1a7dad590..000ba0e11 100644 --- a/src/vs/workbench/parts/tasks/common/tasks.ts +++ b/src/vs/workbench/parts/tasks/common/tasks.ts @@ -32,6 +32,8 @@ export enum ShellQuoting { Weak = 3, } +export const CUSTOMIZED_TASK_TYPE = '$customized'; + export namespace ShellQuoting { export function from(this: void, value: string): ShellQuoting { if (!value) { @@ -174,6 +176,21 @@ export namespace PanelKind { } } +export namespace RerunBehavior { + export function fromString(value: string | undefined): RerunBehavior { + if (!value) { + return RerunBehavior.reevaluate; + } + switch (value.toLowerCase()) { + case 'useevaluated': + return RerunBehavior.useEvaluated; + case 'reevaulate': + default: + return RerunBehavior.reevaluate; + } + } +} + export interface PresentationOptions { /** * Controls whether the task output is reveal in the user interface. @@ -419,6 +436,14 @@ export interface ConfigurationProperties { problemMatchers?: (string | ProblemMatcher)[]; } +export enum RerunBehavior { + reevaluate = 1, + useEvaluated = 2, +} + +export interface RunOptions { + rerunBehavior?: RerunBehavior; +} export interface CommonTask { /** @@ -432,11 +457,13 @@ export interface CommonTask { _label: string; type: string; + + runOptions: RunOptions; } export interface CustomTask extends CommonTask, ConfigurationProperties { - type: 'custom'; + type: '$customized'; // CUSTOMIZED_TASK_TYPE /** * Indicated the source of the task (e.g tasks.json or extension) @@ -458,7 +485,7 @@ export interface CustomTask extends CommonTask, ConfigurationProperties { export namespace CustomTask { export function is(value: any): value is CustomTask { let candidate: CustomTask = value; - return candidate && candidate.type === 'custom'; + return candidate && candidate.type === CUSTOMIZED_TASK_TYPE; } export function getDefinition(task: CustomTask): KeyedTaskIdentifier { let type: string; @@ -564,7 +591,7 @@ export namespace Task { if (!workspaceFolder) { return undefined; } - let key: CustomKey = { type: 'custom', folder: workspaceFolder.uri.toString(), id: task.identifier }; + let key: CustomKey = { type: CUSTOMIZED_TASK_TYPE, folder: workspaceFolder.uri.toString(), id: task.identifier }; return JSON.stringify(key); } if (ContributedTask.is(task)) { diff --git a/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts b/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts index bef22d20a..d891dc33b 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/jsonSchema_v2.ts @@ -5,12 +5,13 @@ import * as nls from 'vs/nls'; import * as Objects from 'vs/base/common/objects'; -import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; import commonSchema from './jsonSchemaCommon'; import { ProblemMatcherRegistry } from 'vs/workbench/parts/tasks/common/problemMatcher'; import { TaskDefinitionRegistry } from '../common/taskDefinitionRegistry'; +import * as ConfigurationResolverUtils from 'vs/workbench/services/configurationResolver/common/configurationResolverUtils'; function fixReferences(literal: any) { if (Array.isArray(literal)) { @@ -280,6 +281,20 @@ const identifier: IJSONSchema = { deprecationMessage: nls.localize('JsonSchema.tasks.identifier.deprecated', 'User defined identifiers are deprecated. For custom task use the name as a reference and for tasks provided by extensions use their defined task identifier.') }; +const rerunBehavior: IJSONSchema = { + type: 'string', + enum: ['reevauate', 'useEvaluated'], + description: nls.localize('JsonSchema.tasks.rerunBehavior', 'The task\'s behavior on rerun') +}; + +const runOptions: IJSONSchema = { + type: 'object', + properties: { + rerunBehavior: Objects.deepClone(rerunBehavior), + }, + description: nls.localize('JsonSchema.tasks.runOptions', 'The task\'s run related options') +}; + const options: IJSONSchema = Objects.deepClone(commonSchema.definitions.options); options.properties.shell = Objects.deepClone(commonSchema.definitions.shellConfiguration); @@ -313,7 +328,8 @@ let taskConfiguration: IJSONSchema = { 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.') - } + }, + runOptions: Objects.deepClone(runOptions), } }; @@ -361,6 +377,7 @@ taskDescription.properties.type = Objects.deepClone(taskType); taskDescription.properties.presentation = Objects.deepClone(presentation); taskDescription.properties.terminal = terminal; taskDescription.properties.group = Objects.deepClone(group); +taskDescription.properties.runOptions = Objects.deepClone(runOptions); taskDescription.properties.taskName.deprecationMessage = nls.localize( 'JsonSchema.tasks.taskName.deprecated', 'The task\'s name property is deprecated. Use the label property instead.' @@ -458,10 +475,21 @@ const schema: IJSONSchema = { schema.definitions = definitions; +function deprecatedVariableMessage(schemaMap: IJSONSchemaMap, property: string) { + if (schemaMap[property].properties) { + Object.keys(schemaMap[property].properties).forEach(name => { + deprecatedVariableMessage(schemaMap[property].properties, name); + }); + } else { + ConfigurationResolverUtils.applyDeprecatedVariableMessage(schemaMap[property]); + } +} + Object.getOwnPropertyNames(definitions).forEach(key => { let newKey = key + '2'; definitions[newKey] = definitions[key]; delete definitions[key]; + deprecatedVariableMessage(definitions, newKey); }); fixReferences(schema); diff --git a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts index 302a6e091..156acce0e 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts @@ -70,11 +70,11 @@ import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs import { ITerminalService } from 'vs/workbench/parts/terminal/common/terminal'; -import { ITaskSystem, ITaskResolver, ITaskSummary, TaskExecuteKind, TaskError, TaskErrors, TaskTerminateResponse, TaskSystemInfo } from 'vs/workbench/parts/tasks/common/taskSystem'; +import { ITaskSystem, ITaskResolver, ITaskSummary, TaskExecuteKind, TaskError, TaskErrors, TaskTerminateResponse, TaskSystemInfo, ITaskExecuteResult } from 'vs/workbench/parts/tasks/common/taskSystem'; import { Task, CustomTask, ConfiguringTask, ContributedTask, InMemoryTask, TaskEvent, TaskEventKind, TaskSet, TaskGroup, GroupType, ExecutionEngine, JsonSchemaVersion, TaskSourceKind, - TaskSorter, TaskIdentifier, KeyedTaskIdentifier, TASK_RUNNING_STATE + TaskSorter, TaskIdentifier, KeyedTaskIdentifier, TASK_RUNNING_STATE, RerunBehavior } from 'vs/workbench/parts/tasks/common/tasks'; import { ITaskService, ITaskProvider, RunOptions, CustomizationProperties, TaskFilter } from 'vs/workbench/parts/tasks/common/taskService'; import { getTemplates as getTaskTemplates } from 'vs/workbench/parts/tasks/common/taskTemplates'; @@ -444,6 +444,8 @@ class TaskService extends Disposable implements ITaskService { public static OutputChannelId: string = 'tasks'; public static OutputChannelLabel: string = nls.localize('tasks', "Tasks"); + private static nextHandle: number = 0; + private _configHasErrors: boolean; private _schemaVersion: JsonSchemaVersion; private _executionEngine: ExecutionEngine; @@ -553,6 +555,10 @@ class TaskService extends Disposable implements ITaskService { this.runTaskCommand(arg); }); + CommandsRegistry.registerCommand('workbench.action.tasks.reRunTask', (accessor, arg) => { + this.reRunTaskCommand(arg); + }); + CommandsRegistry.registerCommand('workbench.action.tasks.restartTask', (accessor, arg) => { this.runRestartTaskCommand(arg); }); @@ -675,15 +681,19 @@ class TaskService extends Disposable implements ITaskService { } } - public registerTaskProvider(handle: number, provider: ITaskProvider): void { + public registerTaskProvider(provider: ITaskProvider): IDisposable { if (!provider) { - return; + return { + dispose: () => { } + }; } + let handle = TaskService.nextHandle++; this._providers.set(handle, provider); - } - - public unregisterTaskProvider(handle: number): boolean { - return this._providers.delete(handle); + return { + dispose: () => { + this._providers.delete(handle); + } + }; } public registerTaskSystem(key: string, info: TaskSystemInfo): void { @@ -1168,7 +1178,8 @@ class TaskService extends Disposable implements ITaskService { type: 'inMemory', name: id, identifier: id, - dependsOn: extensionTasks.map((task) => { return { workspaceFolder: Task.getWorkspaceFolder(task), task: task._id }; }) + dependsOn: extensionTasks.map((task) => { return { workspaceFolder: Task.getWorkspaceFolder(task), task: task._id }; }), + runOptions: { rerunBehavior: RerunBehavior.reevaluate }, }; return { task, resolver }; } @@ -1217,39 +1228,43 @@ class TaskService extends Disposable implements ITaskService { return ProblemMatcherRegistry.onReady().then(() => { return this.textFileService.saveAll().then((value) => { // make sure all dirty files are saved let executeResult = this.getTaskSystem().run(task, resolver); - let key = Task.getRecentlyUsedKey(task); - if (key) { - this.getRecentlyUsedTasks().set(key, key, Touch.AsOld); - } - if (executeResult.kind === TaskExecuteKind.Active) { - let active = executeResult.active; - if (active.same) { - let message; - if (active.background) { - message = nls.localize('TaskSystem.activeSame.background', 'The task \'{0}\' is already active and in background mode.', Task.getQualifiedLabel(task)); - } else { - message = nls.localize('TaskSystem.activeSame.noBackground', 'The task \'{0}\' is already active.', Task.getQualifiedLabel(task)); - } - this.notificationService.prompt(Severity.Info, message, - [{ - label: nls.localize('terminateTask', "Terminate Task"), - run: () => this.terminate(task) - }, - { - label: nls.localize('restartTask', "Restart Task"), - run: () => this.restart(task) - }], - { sticky: true } - ); - } else { - throw new TaskError(Severity.Warning, nls.localize('TaskSystem.active', 'There is already a task running. Terminate it first before executing another task.'), TaskErrors.RunningTask); - } - } - return executeResult.promise; + return this.handleExecuteResult(executeResult); }); }); } + private handleExecuteResult(executeResult: ITaskExecuteResult): TPromise { + let key = Task.getRecentlyUsedKey(executeResult.task); + if (key) { + this.getRecentlyUsedTasks().set(key, key, Touch.AsOld); + } + if (executeResult.kind === TaskExecuteKind.Active) { + let active = executeResult.active; + if (active.same) { + let message; + if (active.background) { + message = nls.localize('TaskSystem.activeSame.background', 'The task \'{0}\' is already active and in background mode.', Task.getQualifiedLabel(executeResult.task)); + } else { + message = nls.localize('TaskSystem.activeSame.noBackground', 'The task \'{0}\' is already active.', Task.getQualifiedLabel(executeResult.task)); + } + this.notificationService.prompt(Severity.Info, message, + [{ + label: nls.localize('terminateTask', "Terminate Task"), + run: () => this.terminate(executeResult.task) + }, + { + label: nls.localize('restartTask', "Restart Task"), + run: () => this.restart(executeResult.task) + }], + { sticky: true } + ); + } else { + throw new TaskError(Severity.Warning, nls.localize('TaskSystem.active', 'There is already a task running. Terminate it first before executing another task.'), TaskErrors.RunningTask); + } + } + return executeResult.promise; + } + public restart(task: Task): void { if (!this._taskSystem) { return; @@ -1801,7 +1816,7 @@ class TaskService extends Disposable implements ITaskService { return true; } - private createTaskQuickPickEntries(tasks: Task[], group: boolean = false, sort: boolean = false): TaskQuickPickEntry[] { + private createTaskQuickPickEntries(tasks: Task[], group: boolean = false, sort: boolean = false, selectedEntry?: TaskQuickPickEntry): TaskQuickPickEntry[] { if (tasks === void 0 || tasks === null || tasks.length === 0) { return []; } @@ -1822,7 +1837,11 @@ class TaskService extends Disposable implements ITaskService { for (let task of tasks) { let entry: TaskQuickPickEntry = TaskQuickPickEntry(task); entry.buttons = [{ iconClass: 'quick-open-task-configure', tooltip: nls.localize('configureTask', "Configure Task") }]; - entries.push(entry); + if (selectedEntry && (task === selectedEntry.task)) { + entries.unshift(selectedEntry); + } else { + entries.push(entry); + } } } let entries: TaskQuickPickEntry[]; @@ -1875,16 +1894,16 @@ class TaskService extends Disposable implements ITaskService { return entries; } - private showQuickPick(tasks: TPromise | Task[], placeHolder: string, defaultEntry?: TaskQuickPickEntry, group: boolean = false, sort: boolean = false): TPromise { + private showQuickPick(tasks: TPromise | Task[], placeHolder: string, defaultEntry?: TaskQuickPickEntry, group: boolean = false, sort: boolean = false, selectedEntry?: TaskQuickPickEntry): TPromise { let _createEntries = (): TPromise => { if (Array.isArray(tasks)) { - return TPromise.as(this.createTaskQuickPickEntries(tasks, group, sort)); + return TPromise.as(this.createTaskQuickPickEntries(tasks, group, sort, selectedEntry)); } else { - return tasks.then((tasks) => this.createTaskQuickPickEntries(tasks, group, sort)); + return tasks.then((tasks) => this.createTaskQuickPickEntries(tasks, group, sort, selectedEntry)); } }; return this.quickInputService.pick(_createEntries().then((entries) => { - if (entries.length === 0 && defaultEntry) { + if ((entries.length === 0) && defaultEntry) { entries.push(defaultEntry); } return entries; @@ -1971,6 +1990,24 @@ class TaskService extends Disposable implements ITaskService { }); } + private reRunTaskCommand(arg?: any): void { + if (!this.canRunCommand()) { + return; + } + + ProblemMatcherRegistry.onReady().then(() => { + return this.textFileService.saveAll().then((value) => { // make sure all dirty files are saved + let executeResult = this.getTaskSystem().rerun(); + if (executeResult) { + return this.handleExecuteResult(executeResult); + } else { + this.doRunTaskCommand(); + return undefined; + } + }); + }); + } + private splitPerGroupType(tasks: Task[]): { none: Task[], defaults: Task[], users: Task[] } { let none: Task[] = []; let defaults: Task[] = []; @@ -2338,33 +2375,36 @@ class TaskService extends Disposable implements ITaskService { this.runConfigureTasks(); return; } - let defaultTask: Task; - let defaultEntry: TaskQuickPickEntry; + let selectedTask: Task; + let selectedEntry: TaskQuickPickEntry; for (let task of tasks) { if (task.group === TaskGroup.Build && task.groupType === GroupType.default) { - defaultTask = task; + selectedTask = task; break; } } - if (defaultTask) { - tasks = []; - defaultEntry = { - label: nls.localize('TaskService.defaultBuildTaskExists', '{0} is already marked as the default build task', Task.getQualifiedLabel(defaultTask)), - task: defaultTask + if (selectedTask) { + selectedEntry = { + label: nls.localize('TaskService.defaultBuildTaskExists', '{0} is already marked as the default build task', Task.getQualifiedLabel(selectedTask)), + task: selectedTask }; } this.showIgnoredFoldersMessage().then(() => { this.showQuickPick(tasks, - nls.localize('TaskService.pickDefaultBuildTask', 'Select the task to be used as the default build task'), defaultEntry, true). + nls.localize('TaskService.pickDefaultBuildTask', 'Select the task to be used as the default build task'), undefined, true, false, selectedEntry). then((task) => { if (task === void 0) { return; } - if (task === defaultTask && CustomTask.is(task)) { + if (task === selectedTask && CustomTask.is(task)) { this.openConfig(task); } if (!InMemoryTask.is(task)) { - this.customize(task, { group: { kind: 'build', isDefault: true } }, true); + this.customize(task, { group: { kind: 'build', isDefault: true } }, true).then(() => { + if (selectedTask && (task !== selectedTask) && !InMemoryTask.is(selectedTask)) { + this.customize(selectedTask, { group: 'build' }, true); + } + }); } }); }); @@ -2382,27 +2422,41 @@ class TaskService extends Disposable implements ITaskService { this.tasks().then((tasks => { if (tasks.length === 0) { this.runConfigureTasks(); + return; } - let defaultTask: Task; + let selectedTask: Task; + let selectedEntry: TaskQuickPickEntry; + for (let task of tasks) { if (task.group === TaskGroup.Test && task.groupType === GroupType.default) { - defaultTask = task; + selectedTask = task; break; } } - if (defaultTask) { - this.notificationService.info(nls.localize('TaskService.defaultTestTaskExists', '{0} is already marked as the default test task.', Task.getQualifiedLabel(defaultTask))); - return; + if (selectedTask) { + selectedEntry = { + label: nls.localize('TaskService.defaultTestTaskExists', '{0} is already marked as the default test task.', Task.getQualifiedLabel(selectedTask)), + task: selectedTask + }; } + this.showIgnoredFoldersMessage().then(() => { - this.showQuickPick(tasks, nls.localize('TaskService.pickDefaultTestTask', 'Select the task to be used as the default test task'), undefined, true).then((task) => { - if (!task) { - return; - } - if (!InMemoryTask.is(task)) { - this.customize(task, { group: { kind: 'test', isDefault: true } }, true); - } - }); + this.showQuickPick(tasks, + nls.localize('TaskService.pickDefaultTestTask', 'Select the task to be used as the default test task'), undefined, true, false, selectedEntry).then((task) => { + if (!task) { + return; + } + if (task === selectedTask && CustomTask.is(task)) { + this.openConfig(task); + } + if (!InMemoryTask.is(task)) { + this.customize(task, { group: { kind: 'test', isDefault: true } }, true).then(() => { + if (selectedTask && (task !== selectedTask) && !InMemoryTask.is(selectedTask)) { + this.customize(selectedTask, { group: 'test' }, true); + } + }); + } + }); }); })); } else { @@ -2502,6 +2556,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { MenuRegistry.addCommand({ id: ConfigureTaskAction.ID, title: { value: ConfigureTaskAction.TEXT, original: 'Configure Task' }, category: { value: tasksCategory, original: 'Tasks' } }); MenuRegistry.addCommand({ id: 'workbench.action.tasks.showLog', title: { value: nls.localize('ShowLogAction.label', "Show Task Log"), original: 'Show Task Log' }, category: { value: tasksCategory, original: 'Tasks' } }); MenuRegistry.addCommand({ id: 'workbench.action.tasks.runTask', title: { value: nls.localize('RunTaskAction.label', "Run Task"), original: 'Run Task' }, category: { value: tasksCategory, original: 'Tasks' } }); +MenuRegistry.addCommand({ id: 'workbench.action.tasks.reRunTask', title: { value: nls.localize('ReRunTaskAction.label', "Rerun Last Task"), original: 'Rerun Last Task' }, category: { value: tasksCategory, original: 'Tasks' } }); MenuRegistry.addCommand({ id: 'workbench.action.tasks.restartTask', title: { value: nls.localize('RestartTaskAction.label', "Restart Running Task"), original: 'Restart Running Task' }, category: { value: tasksCategory, original: 'Tasks' } }); MenuRegistry.addCommand({ id: 'workbench.action.tasks.showTasks', title: { value: nls.localize('ShowTasksAction.label', "Show Running Tasks"), original: 'Show Running Tasks' }, category: { value: tasksCategory, original: 'Tasks' } }); MenuRegistry.addCommand({ id: 'workbench.action.tasks.terminate', title: { value: nls.localize('TerminateAction.label', "Terminate Task"), original: 'Terminate Task' }, category: { value: tasksCategory, original: 'Tasks' } }); @@ -2517,7 +2572,7 @@ let outputChannelRegistry = Registry.as(OutputExt.Output outputChannelRegistry.registerChannel({ id: TaskService.OutputChannelId, label: TaskService.OutputChannelLabel, log: false }); // Task Service -registerSingleton(ITaskService, TaskService); +registerSingleton(ITaskService, TaskService, true); // Register Quick Open const quickOpenRegistry = (Registry.as(QuickOpenExtensions.Quickopen)); diff --git a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts index 88e457e83..647915a82 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; import * as path from 'path'; import * as nls from 'vs/nls'; @@ -11,7 +10,6 @@ import * as Objects from 'vs/base/common/objects'; import * as Types from 'vs/base/common/types'; import * as Platform from 'vs/base/common/platform'; import * as Async from 'vs/base/common/async'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IStringDictionary } from 'vs/base/common/collections'; import { LinkedMap, Touch } from 'vs/base/common/map'; import Severity from 'vs/base/common/severity'; @@ -19,6 +17,8 @@ import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import * as TPath from 'vs/base/common/paths'; +import { win32 } from 'vs/base/node/processes'; + import { IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -32,11 +32,11 @@ import { IOutputService, IOutputChannel } from 'vs/workbench/parts/output/common import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEventKind } from 'vs/workbench/parts/tasks/common/problemCollectors'; import { Task, CustomTask, ContributedTask, RevealKind, CommandOptions, ShellConfiguration, RuntimeType, PanelKind, - TaskEvent, TaskEventKind, ShellQuotingOptions, ShellQuoting, CommandString, CommandConfiguration + TaskEvent, TaskEventKind, ShellQuotingOptions, ShellQuoting, CommandString, CommandConfiguration, RerunBehavior } from 'vs/workbench/parts/tasks/common/tasks'; import { ITaskSystem, ITaskSummary, ITaskExecuteResult, TaskExecuteKind, TaskError, TaskErrors, ITaskResolver, - TelemetryEvent, Triggers, TaskTerminateResponse, TaskSystemInfoResovler, TaskSystemInfo + TelemetryEvent, Triggers, TaskTerminateResponse, TaskSystemInfoResovler, TaskSystemInfo, ResolveSet, ResolvedVariables } from 'vs/workbench/parts/tasks/common/taskSystem'; interface TerminalData { @@ -47,7 +47,7 @@ interface TerminalData { interface ActiveTerminalData { terminal: ITerminalInstance; task: Task; - promise: TPromise; + promise: Promise; } class VariableResolver { @@ -68,10 +68,41 @@ class VariableResolver { } } + +export class VerifiedTask { + readonly task: Task; + readonly resolver: ITaskResolver; + readonly trigger: string; + resolvedVariables?: ResolvedVariables; + systemInfo?: TaskSystemInfo; + workspaceFolder?: IWorkspaceFolder; + shellLaunchConfig?: IShellLaunchConfig; + + constructor(task: Task, resolver: ITaskResolver, trigger: string) { + this.task = task; + this.resolver = resolver; + this.trigger = trigger; + } + + public verify(): boolean { + return this.trigger && this.resolvedVariables && this.workspaceFolder && (this.shellLaunchConfig !== undefined); + } + + public getVerifiedTask(): { task: Task, resolver: ITaskResolver, trigger: string, resolvedVariables: ResolvedVariables, systemInfo: TaskSystemInfo, workspaceFolder: IWorkspaceFolder, shellLaunchConfig: IShellLaunchConfig } { + if (this.verify()) { + return { task: this.task, resolver: this.resolver, trigger: this.trigger, resolvedVariables: this.resolvedVariables, systemInfo: this.systemInfo, workspaceFolder: this.workspaceFolder, shellLaunchConfig: this.shellLaunchConfig }; + } else { + throw new Error('VerifiedTask was not checked. verify must be checked before getVerifiedTask.'); + } + } +} + export class TerminalTaskSystem implements ITaskSystem { public static TelemetryEventName: string = 'taskService'; + private static ProcessVarName = '${__process__}'; + private static shellQuotes: IStringDictionary = { 'cmd': { strong: '"' @@ -114,6 +145,9 @@ export class TerminalTaskSystem implements ITaskSystem { private idleTaskTerminals: LinkedMap; private sameTaskTerminals: IStringDictionary; private taskSystemInfoResolver: TaskSystemInfoResovler; + private lastTask: VerifiedTask; + private currentTask: VerifiedTask; + private isRerun: boolean; private readonly _onDidStateChange: Emitter; @@ -148,6 +182,7 @@ export class TerminalTaskSystem implements ITaskSystem { } public run(task: Task, resolver: ITaskResolver, trigger: string = Triggers.command): ITaskExecuteResult { + this.currentTask = new VerifiedTask(task, resolver, trigger); let terminalData = this.activeTasks[Task.getMapKey(task)]; if (terminalData && terminalData.promise) { let reveal = RevealKind.Always; @@ -160,11 +195,14 @@ export class TerminalTaskSystem implements ITaskSystem { this.terminalService.setActiveInstance(terminalData.terminal); this.terminalService.showPanel(focus); } - return { kind: TaskExecuteKind.Active, active: { same: true, background: task.isBackground }, promise: terminalData.promise }; + this.lastTask = this.currentTask; + return { kind: TaskExecuteKind.Active, task, active: { same: true, background: task.isBackground }, promise: terminalData.promise }; } try { - return { kind: TaskExecuteKind.Started, started: {}, promise: this.executeTask(Object.create(null), task, resolver, trigger) }; + const executeResult = { kind: TaskExecuteKind.Started, task, started: {}, promise: this.executeTask(task, resolver, trigger) }; + this.lastTask = this.currentTask; + return executeResult; } catch (error) { if (error instanceof TaskError) { throw error; @@ -178,6 +216,18 @@ export class TerminalTaskSystem implements ITaskSystem { } } + public rerun(): ITaskExecuteResult | undefined { + if (this.lastTask && this.lastTask.verify()) { + if (this.lastTask.task.runOptions.rerunBehavior === RerunBehavior.useEvaluated) { + this.isRerun = true; + } + const result = this.run(this.lastTask.task, this.lastTask.resolver); + this.isRerun = false; + return result; + } else { + return undefined; + } + } public revealTask(task: Task): boolean { let terminalData = this.activeTasks[Task.getMapKey(task)]; @@ -191,8 +241,8 @@ export class TerminalTaskSystem implements ITaskSystem { return true; } - public isActive(): TPromise { - return TPromise.as(this.isActiveSync()); + public isActive(): Promise { + return Promise.resolve(this.isActiveSync()); } public isActiveSync(): boolean { @@ -207,12 +257,12 @@ export class TerminalTaskSystem implements ITaskSystem { return Object.keys(this.activeTasks).map(key => this.activeTasks[key].task); } - public terminate(task: Task): TPromise { + public terminate(task: Task): Promise { let activeTerminal = this.activeTasks[Task.getMapKey(task)]; if (!activeTerminal) { - return TPromise.as({ success: false, task: undefined }); + return Promise.resolve({ success: false, task: undefined }); } - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { let terminal = activeTerminal.terminal; const onExit = terminal.onExit(() => { let task = activeTerminal.task; @@ -228,12 +278,12 @@ export class TerminalTaskSystem implements ITaskSystem { }); } - public terminateAll(): TPromise { - let promises: TPromise[] = []; + public terminateAll(): Promise { + let promises: Promise[] = []; Object.keys(this.activeTasks).forEach((key) => { let terminalData = this.activeTasks[key]; let terminal = terminalData.terminal; - promises.push(new TPromise((resolve, reject) => { + promises.push(new Promise((resolve, reject) => { const onExit = terminal.onExit(() => { let task = terminalData.task; try { @@ -248,20 +298,19 @@ export class TerminalTaskSystem implements ITaskSystem { terminal.dispose(); }); this.activeTasks = Object.create(null); - return TPromise.join(promises); + return Promise.all(promises); } - private executeTask(startedTasks: IStringDictionary>, task: Task, resolver: ITaskResolver, trigger: string): TPromise { - let promises: TPromise[] = []; + private executeTask(task: Task, resolver: ITaskResolver, trigger: string): Promise { + let promises: Promise[] = []; if (task.dependsOn) { task.dependsOn.forEach((dependency) => { let task = resolver.resolve(dependency.workspaceFolder, dependency.task); if (task) { let key = Task.getMapKey(task); - let promise = startedTasks[key]; + let promise = this.activeTasks[key] ? this.activeTasks[key].promise : undefined; if (!promise) { - promise = this.executeTask(startedTasks, task, resolver, trigger); - startedTasks[key] = promise; + promise = this.executeTask(task, resolver, trigger); } promises.push(promise); } else { @@ -276,16 +325,20 @@ export class TerminalTaskSystem implements ITaskSystem { } if ((ContributedTask.is(task) || CustomTask.is(task)) && (task.command)) { - return TPromise.join(promises).then((summaries): TPromise | ITaskSummary => { + return Promise.all(promises).then((summaries): Promise | ITaskSummary => { for (let summary of summaries) { if (summary.exitCode !== 0) { return { exitCode: summary.exitCode }; } } - return this.executeCommand(task, trigger); + if (this.isRerun) { + return this.reexecuteCommand(task, trigger); + } else { + return this.executeCommand(task, trigger); + } }); } else { - return TPromise.join(promises).then((summaries): ITaskSummary => { + return Promise.all(promises).then((summaries): ITaskSummary => { for (let summary of summaries) { if (summary.exitCode !== 0) { return { exitCode: summary.exitCode }; @@ -296,36 +349,111 @@ export class TerminalTaskSystem implements ITaskSystem { } } - private executeCommand(task: CustomTask | ContributedTask, trigger: string): TPromise { - let variables = new Set(); - this.collectTaskVariables(variables, task); - let workspaceFolder = Task.getWorkspaceFolder(task); - let taskSystemInfo: TaskSystemInfo; - if (workspaceFolder) { - taskSystemInfo = this.taskSystemInfoResolver(workspaceFolder); + private resolveVariablesFromSet(taskSystemInfo: TaskSystemInfo, workspaceFolder: IWorkspaceFolder, task: CustomTask | ContributedTask, variables: Set): Promise { + let isProcess = task.command && task.command.runtime === RuntimeType.Process; + let options = task.command && task.command.options ? task.command.options : undefined; + let cwd = options ? options.cwd : undefined; + let envPath: string | undefined = undefined; + if (options && options.env) { + for (let key of Object.keys(options.env)) { + if (key.toLowerCase() === 'path') { + if (Types.isString(options.env[key])) { + envPath = options.env[key]; + } + break; + } + } } - let resolvedVariables: TPromise>; + + let resolvedVariables: Promise; if (taskSystemInfo) { - resolvedVariables = taskSystemInfo.resolveVariables(workspaceFolder, variables); + let resolveSet: ResolveSet = { + variables + }; + + if (taskSystemInfo.platform === Platform.Platform.Windows && isProcess) { + resolveSet.process = { name: CommandString.value(task.command.name) }; + if (cwd) { + resolveSet.process.cwd = cwd; + } + if (envPath) { + resolveSet.process.path = envPath; + } + } + resolvedVariables = taskSystemInfo.resolveVariables(workspaceFolder, resolveSet); } else { let result = new Map(); variables.forEach(variable => { result.set(variable, this.configurationResolverService.resolve(workspaceFolder, variable)); }); - resolvedVariables = TPromise.as(result); + if (isProcess) { + let processVarValue: string; + if (Platform.isWindows) { + processVarValue = win32.findExecutable( + this.configurationResolverService.resolve(workspaceFolder, CommandString.value(task.command.name)), + cwd ? this.configurationResolverService.resolve(workspaceFolder, cwd) : undefined, + envPath ? envPath.split(path.delimiter).map(p => this.configurationResolverService.resolve(workspaceFolder, p)) : undefined + ); + } else { + processVarValue = this.configurationResolverService.resolve(workspaceFolder, CommandString.value(task.command.name)); + } + result.set(TerminalTaskSystem.ProcessVarName, processVarValue); + } + let resolvedVariablesResult: ResolvedVariables = { + variables: result, + }; + resolvedVariables = Promise.resolve(resolvedVariablesResult); } - return resolvedVariables.then((variables) => { - return this.executeInTerminal(task, trigger, new VariableResolver(workspaceFolder, taskSystemInfo, variables, this.configurationResolverService)); + return resolvedVariables; + } + + private executeCommand(task: CustomTask | ContributedTask, trigger: string): Promise { + this.currentTask.workspaceFolder = Task.getWorkspaceFolder(task); + if (this.currentTask.workspaceFolder) { + this.currentTask.systemInfo = this.taskSystemInfoResolver(this.currentTask.workspaceFolder); + } + + let variables = new Set(); + this.collectTaskVariables(variables, task); + const resolvedVariables = this.resolveVariablesFromSet(this.currentTask.systemInfo, this.currentTask.workspaceFolder, task, variables); + + return resolvedVariables.then((resolvedVariables) => { + this.currentTask.resolvedVariables = resolvedVariables; + return this.executeInTerminal(task, trigger, new VariableResolver(this.currentTask.workspaceFolder, this.currentTask.systemInfo, resolvedVariables.variables, this.configurationResolverService)); }); } - private executeInTerminal(task: CustomTask | ContributedTask, trigger: string, resolver: VariableResolver): TPromise { + private reexecuteCommand(task: CustomTask | ContributedTask, trigger: string): Promise { + this.currentTask.workspaceFolder = this.lastTask.workspaceFolder; + let variables = new Set(); + this.collectTaskVariables(variables, task); + + // Check that the task hasn't changed to include new variables + let hasAllVariables = true; + variables.forEach(value => { + if (value in this.lastTask.getVerifiedTask().resolvedVariables) { + hasAllVariables = false; + } + }); + + if (!hasAllVariables) { + return this.resolveVariablesFromSet(this.lastTask.getVerifiedTask().systemInfo, this.lastTask.getVerifiedTask().workspaceFolder, task, variables).then((resolvedVariables) => { + this.currentTask.resolvedVariables = resolvedVariables; + return this.executeInTerminal(task, trigger, new VariableResolver(this.lastTask.getVerifiedTask().workspaceFolder, this.lastTask.getVerifiedTask().systemInfo, resolvedVariables.variables, this.configurationResolverService)); + }); + } else { + this.currentTask.resolvedVariables = this.lastTask.getVerifiedTask().resolvedVariables; + return this.executeInTerminal(task, trigger, new VariableResolver(this.lastTask.getVerifiedTask().workspaceFolder, this.lastTask.getVerifiedTask().systemInfo, this.lastTask.getVerifiedTask().resolvedVariables.variables, this.configurationResolverService)); + } + } + + private executeInTerminal(task: CustomTask | ContributedTask, trigger: string, resolver: VariableResolver): Promise { let terminal: ITerminalInstance | undefined = undefined; let executedCommand: string | undefined = undefined; let error: TaskError | undefined = undefined; - let promise: TPromise | undefined = undefined; + let promise: Promise | undefined = undefined; if (task.isBackground) { - promise = new TPromise((resolve, reject) => { + promise = new Promise((resolve, reject) => { const problemMatchers = this.resolveMatchers(resolver, task.problemMatchers); let watchingProblemMatcher = new WatchingProblemCollector(problemMatchers, this.markerService, this.modelService); let toDispose: IDisposable[] = []; @@ -339,7 +467,7 @@ export class TerminalTaskSystem implements ITaskSystem { this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Inactive, task)); if (eventCounter === 0) { let reveal = task.command.presentation.reveal; - if (reveal === RevealKind.Silent && watchingProblemMatcher.numberOfMatches > 0 && watchingProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error) { + if ((reveal === RevealKind.Silent) && (watchingProblemMatcher.numberOfMatches > 0) && (watchingProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error)) { this.terminalService.setActiveInstance(terminal); this.terminalService.showPanel(false); } @@ -352,10 +480,12 @@ export class TerminalTaskSystem implements ITaskSystem { if (error || !terminal) { return; } - let processStartedSignaled: boolean = false; + let processStartedSignaled = false; terminal.processReady.then(() => { - processStartedSignaled = true; - this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId)); + if (!processStartedSignaled) { + this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId)); + processStartedSignaled = true; + } }, (_error) => { // The process never got ready. Need to think how to handle this. }); @@ -386,37 +516,42 @@ export class TerminalTaskSystem implements ITaskSystem { break; } let reveal = task.command.presentation.reveal; - if (reveal === RevealKind.Silent && (exitCode !== 0 || watchingProblemMatcher.numberOfMatches > 0 && watchingProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error)) { + if ((reveal === RevealKind.Silent) && ((exitCode !== 0) || (watchingProblemMatcher.numberOfMatches > 0) && (watchingProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error))) { this.terminalService.setActiveInstance(terminal); this.terminalService.showPanel(false); } watchingProblemMatcher.done(); watchingProblemMatcher.dispose(); registeredLinkMatchers.forEach(handle => terminal.deregisterLinkMatcher(handle)); - if (processStartedSignaled) { - this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessEnded, task, exitCode)); + if (!processStartedSignaled) { + this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId)); + processStartedSignaled = true; } - toDispose = dispose(toDispose); - toDispose = null; + this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessEnded, task, exitCode)); for (let i = 0; i < eventCounter; i++) { let event = TaskEvent.create(TaskEventKind.Inactive, task); this._onDidStateChange.fire(event); } eventCounter = 0; this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.End, task)); + toDispose = dispose(toDispose); + toDispose = null; resolve({ exitCode }); }); }); } else { - promise = new TPromise((resolve, reject) => { + promise = new Promise((resolve, reject) => { [terminal, executedCommand, error] = this.createTerminal(task, resolver); if (error || !terminal) { return; } - let processStartedSignaled: boolean = false; + + let processStartedSignaled = false; terminal.processReady.then(() => { - processStartedSignaled = true; - this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId)); + if (!processStartedSignaled) { + this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId)); + processStartedSignaled = true; + } }, (_error) => { // The process never got ready. Need to think how to handle this. }); @@ -443,16 +578,18 @@ export class TerminalTaskSystem implements ITaskSystem { break; } let reveal = task.command.presentation.reveal; - if (reveal === RevealKind.Silent && (exitCode !== 0 || startStopProblemMatcher.numberOfMatches > 0 && startStopProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error)) { + if ((reveal === RevealKind.Silent) && ((exitCode !== 0) || (startStopProblemMatcher.numberOfMatches > 0) && (startStopProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error))) { this.terminalService.setActiveInstance(terminal); this.terminalService.showPanel(false); } startStopProblemMatcher.done(); startStopProblemMatcher.dispose(); registeredLinkMatchers.forEach(handle => terminal.deregisterLinkMatcher(handle)); - if (processStartedSignaled) { - this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessEnded, task, exitCode)); + if (!processStartedSignaled) { + this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.processId)); + processStartedSignaled = true; } + this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.ProcessEnded, task, exitCode)); this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Inactive, task)); this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.End, task)); resolve({ exitCode }); @@ -460,12 +597,12 @@ export class TerminalTaskSystem implements ITaskSystem { }); } if (error) { - return TPromise.wrapError(new Error(error.message)); + return Promise.reject(new Error(error.message)); } if (!terminal) { - return TPromise.wrapError(new Error(`Failed to create terminal for task ${task._label}`)); + return Promise.reject(new Error(`Failed to create terminal for task ${task._label}`)); } - if (task.command.presentation.reveal === RevealKind.Always || (task.command.presentation.reveal === RevealKind.Silent && task.problemMatchers.length === 0)) { + if (task.command.presentation.reveal === RevealKind.Always) { this.terminalService.setActiveInstance(terminal); this.terminalService.showPanel(task.command.presentation.focus); } @@ -511,31 +648,16 @@ export class TerminalTaskSystem implements ITaskSystem { this.telemetryService.publicLog(TerminalTaskSystem.TelemetryEventName, telemetryEvent); } catch (error) { } - return TPromise.wrapError(error); + return Promise.reject(error); }); } - private createTerminal(task: CustomTask | ContributedTask, resolver: VariableResolver): [ITerminalInstance, string, TaskError | undefined] { - let platform = resolver.taskSystemInfo ? resolver.taskSystemInfo.platform : Platform.platform; - let options = this.resolveOptions(resolver, task.command.options); - let originalCommand = task.command.name; - let { command, args } = this.resolveCommandAndArgs(resolver, task.command); - let commandExecutable = CommandString.value(command); - let workspaceFolder = Task.getWorkspaceFolder(task); - let needsFolderQualification = workspaceFolder && this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE; - let terminalName = nls.localize('TerminalTaskSystem.terminalName', 'Task - {0}', needsFolderQualification ? Task.getQualifiedLabel(task) : task.name); - let waitOnExit: boolean | string = false; - if (task.command.presentation.reveal !== RevealKind.Never || !task.isBackground) { - if (task.command.presentation.panel === PanelKind.New) { - waitOnExit = nls.localize('closeTerminal', 'Press any key to close the terminal.'); - } else if (task.command.presentation.showReuseMessage) { - waitOnExit = nls.localize('reuseTerminal', 'Terminal will be reused by tasks, press any key to close it.'); - } else { - waitOnExit = true; - } - } + private createShellLaunchConfig(task: CustomTask | ContributedTask, variableResolver: VariableResolver, platform: Platform.Platform, options: CommandOptions, command: CommandString, args: CommandString[], waitOnExit: boolean | string): IShellLaunchConfig | undefined { let shellLaunchConfig: IShellLaunchConfig | undefined = undefined; let isShellCommand = task.command.runtime === RuntimeType.Shell; + let needsFolderQualification = this.currentTask.workspaceFolder && this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE; + let terminalName = nls.localize('TerminalTaskSystem.terminalName', 'Task - {0}', needsFolderQualification ? Task.getQualifiedLabel(task) : task.name); + let originalCommand = task.command.name; if (isShellCommand) { shellLaunchConfig = { name: terminalName, executable: null, args: null, waitOnExit }; this.terminalService.configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig, platform); @@ -543,18 +665,18 @@ export class TerminalTaskSystem implements ITaskSystem { let shellOptions: ShellConfiguration = task.command.options && task.command.options.shell; if (shellOptions) { if (shellOptions.executable) { - shellLaunchConfig.executable = this.resolveVariable(resolver, shellOptions.executable); + shellLaunchConfig.executable = this.resolveVariable(variableResolver, shellOptions.executable); shellSpecified = true; } if (shellOptions.args) { - shellLaunchConfig.args = this.resolveVariables(resolver, shellOptions.args.slice()); + shellLaunchConfig.args = this.resolveVariables(variableResolver, shellOptions.args.slice()); } else { shellLaunchConfig.args = []; } } let shellArgs = shellLaunchConfig.args.slice(0); let toAdd: string[] = []; - let commandLine = this.buildShellCommandLine(shellLaunchConfig.executable, shellOptions, command, originalCommand, args); + let commandLine = this.buildShellCommandLine(platform, shellLaunchConfig.executable, shellOptions, command, originalCommand, args); let windowsShellArgs: boolean = false; if (platform === Platform.Platform.Windows) { // Change Sysnative to System32 if the OS is Windows but NOT WoW64. It's @@ -569,7 +691,7 @@ export class TerminalTaskSystem implements ITaskSystem { windowsShellArgs = true; let basename = path.basename(shellLaunchConfig.executable).toLowerCase(); if (basename === 'cmd.exe' && ((options.cwd && TPath.isUNC(options.cwd)) || (!options.cwd && TPath.isUNC(process.cwd())))) { - return [undefined, undefined, new TaskError(Severity.Error, nls.localize('TerminalTaskSystem', 'Can\'t execute a shell command on an UNC drive using cmd.exe.'), TaskErrors.UnknownError)]; + return undefined; } if (basename === 'powershell.exe' || basename === 'pwsh.exe') { if (!shellSpecified) { @@ -588,7 +710,7 @@ export class TerminalTaskSystem implements ITaskSystem { } else { if (!shellSpecified) { // Under Mac remove -l to not start it as a login shell. - if (Platform.isMacintosh) { + if (platform === Platform.Platform.Mac) { let index = shellArgs.indexOf('-l'); if (index !== -1) { shellArgs.splice(index, 1); @@ -606,16 +728,16 @@ export class TerminalTaskSystem implements ITaskSystem { shellLaunchConfig.args = windowsShellArgs ? shellArgs.join(' ') : shellArgs; if (task.command.presentation.echo) { if (needsFolderQualification) { - shellLaunchConfig.initialText = `\x1b[1m> Executing task in folder ${workspaceFolder.name}: ${commandLine} <\x1b[0m\n`; + shellLaunchConfig.initialText = `\x1b[1m> Executing task in folder ${this.currentTask.workspaceFolder.name}: ${commandLine} <\x1b[0m\n`; } else { shellLaunchConfig.initialText = `\x1b[1m> Executing task: ${commandLine} <\x1b[0m\n`; } } } else { - let cwd = options && options.cwd ? options.cwd : process.cwd(); - // On Windows executed process must be described absolute. Since we allowed command without an - // absolute path (e.g. "command": "node") we need to find the executable in the CWD or PATH. - let executable = Platform.isWindows && !isShellCommand ? this.findExecutable(commandExecutable, cwd, options) : commandExecutable; + let commandExecutable = CommandString.value(command); + let executable = !isShellCommand + ? this.resolveVariable(variableResolver, TerminalTaskSystem.ProcessVarName) + : commandExecutable; // When we have a process task there is no need to quote arguments. So we go ahead and take the string value. shellLaunchConfig = { @@ -635,12 +757,13 @@ export class TerminalTaskSystem implements ITaskSystem { return args.join(' '); }; if (needsFolderQualification) { - shellLaunchConfig.initialText = `\x1b[1m> Executing task in folder ${workspaceFolder.name}: ${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)} <\x1b[0m\n`; + shellLaunchConfig.initialText = `\x1b[1m> Executing task in folder ${this.currentTask.workspaceFolder.name}: ${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)} <\x1b[0m\n`; } else { shellLaunchConfig.initialText = `\x1b[1m> Executing task: ${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)} <\x1b[0m\n`; } } } + if (options.cwd) { let cwd = options.cwd; let p: typeof path; @@ -664,6 +787,29 @@ export class TerminalTaskSystem implements ITaskSystem { if (options.env) { shellLaunchConfig.env = options.env; } + return shellLaunchConfig; + } + + private createTerminal(task: CustomTask | ContributedTask, resolver: VariableResolver): [ITerminalInstance, string, TaskError | undefined] { + let platform = resolver.taskSystemInfo ? resolver.taskSystemInfo.platform : Platform.platform; + let options = this.resolveOptions(resolver, task.command.options); + let waitOnExit: boolean | string = false; + let { command, args } = this.resolveCommandAndArgs(resolver, task.command); + let commandExecutable = CommandString.value(command); + + if (task.command.presentation.reveal !== RevealKind.Never || !task.isBackground) { + if (task.command.presentation.panel === PanelKind.New) { + waitOnExit = nls.localize('closeTerminal', 'Press any key to close the terminal.'); + } else if (task.command.presentation.showReuseMessage) { + waitOnExit = nls.localize('reuseTerminal', 'Terminal will be reused by tasks, press any key to close it.'); + } else { + waitOnExit = true; + } + } + this.currentTask.shellLaunchConfig = this.isRerun ? this.lastTask.getVerifiedTask().shellLaunchConfig : this.createShellLaunchConfig(task, resolver, platform, options, command, args, waitOnExit); + if (this.currentTask.shellLaunchConfig === undefined) { + return [undefined, undefined, new TaskError(Severity.Error, nls.localize('TerminalTaskSystem', 'Can\'t execute a shell command on an UNC drive using cmd.exe.'), TaskErrors.UnknownError)]; + } let prefersSameTerminal = task.command.presentation.panel === PanelKind.Dedicated; let allowsSharedTerminal = task.command.presentation.panel === PanelKind.Shared; @@ -682,14 +828,14 @@ export class TerminalTaskSystem implements ITaskSystem { } } if (terminalToReuse) { - terminalToReuse.terminal.reuseTerminal(shellLaunchConfig); + terminalToReuse.terminal.reuseTerminal(this.currentTask.shellLaunchConfig); if (task.command.presentation.clear) { terminalToReuse.terminal.clear(); } return [terminalToReuse.terminal, commandExecutable, undefined]; } - const result = this.terminalService.createTerminal(shellLaunchConfig); + const result = this.terminalService.createTerminal(this.currentTask.shellLaunchConfig); const terminalKey = result.id.toString(); result.onDisposed((terminal) => { let terminalData = this.terminals[terminalKey]; @@ -708,7 +854,7 @@ export class TerminalTaskSystem implements ITaskSystem { return [result, commandExecutable, undefined]; } - private buildShellCommandLine(shellExecutable: string, shellOptions: ShellConfiguration, command: CommandString, originalCommand: CommandString, args: CommandString[]): string { + private buildShellCommandLine(platform: Platform.Platform, shellExecutable: string, shellOptions: ShellConfiguration, command: CommandString, originalCommand: CommandString, args: CommandString[]): string { let basename = path.parse(shellExecutable).name.toLowerCase(); let shellQuoteOptions = this.getQuotingOptions(basename, shellOptions); @@ -796,7 +942,7 @@ export class TerminalTaskSystem implements ITaskSystem { let commandLine = result.join(' '); // There are special rules quoted command line in cmd.exe - if (Platform.isWindows) { + if (platform === Platform.Platform.Windows) { if (basename === 'cmd' && commandQuoted && argQuoted) { commandLine = '"' + commandLine + '"'; } else if (basename === 'powershell' && commandQuoted) { @@ -804,7 +950,7 @@ export class TerminalTaskSystem implements ITaskSystem { } } - if (basename === 'cmd' && Platform.isWindows && commandQuoted && argQuoted) { + if (basename === 'cmd' && platform === Platform.Platform.Windows && commandQuoted && argQuoted) { commandLine = '"' + commandLine + '"'; } return commandLine; @@ -895,62 +1041,6 @@ export class TerminalTaskSystem implements ITaskSystem { return { command, args }; } - private findExecutable(command: string, cwd: string, options: CommandOptions): string { - // If we have an absolute path then we take it. - if (path.isAbsolute(command)) { - return command; - } - let dir = path.dirname(command); - if (dir !== '.') { - // We have a directory and the directory is relative (see above). Make the path absolute - // to the current working directory. - return path.join(cwd, command); - } - let paths: string[] | undefined = undefined; - // The options can override the PATH. So consider that PATH if present. - if (options && options.env) { - // Path can be named in many different ways and for the execution it doesn't matter - for (let key of Object.keys(options.env)) { - if (key.toLowerCase() === 'path') { - if (Types.isString(options.env[key])) { - paths = options.env[key].split(path.delimiter); - } - break; - } - } - } - if (paths === void 0 && Types.isString(process.env.PATH)) { - paths = process.env.PATH.split(path.delimiter); - } - // No PATH environment. Make path absolute to the cwd. - if (paths === void 0 || paths.length === 0) { - return path.join(cwd, command); - } - // We have a simple file name. We get the path variable from the env - // and try to find the executable on the path. - for (let pathEntry of paths) { - // The path entry is absolute. - let fullPath: string; - if (path.isAbsolute(pathEntry)) { - fullPath = path.join(pathEntry, command); - } else { - fullPath = path.join(cwd, pathEntry, command); - } - if (fs.existsSync(fullPath)) { - return fullPath; - } - let withExtension = fullPath + '.com'; - if (fs.existsSync(withExtension)) { - return withExtension; - } - withExtension = fullPath + '.exe'; - if (fs.existsSync(withExtension)) { - return withExtension; - } - } - return path.join(cwd, command); - } - private resolveVariables(resolver: VariableResolver, value: string[]): string[]; private resolveVariables(resolver: VariableResolver, value: CommandString[]): CommandString[]; private resolveVariables(resolver: VariableResolver, value: CommandString[]): CommandString[] { diff --git a/src/vs/workbench/parts/tasks/node/processTaskSystem.ts b/src/vs/workbench/parts/tasks/node/processTaskSystem.ts index 7ef52c614..bc7e662af 100644 --- a/src/vs/workbench/parts/tasks/node/processTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/node/processTaskSystem.ts @@ -7,7 +7,6 @@ import * as nls from 'vs/nls'; import * as Objects from 'vs/base/common/objects'; import * as Types from 'vs/base/common/types'; import * as Platform from 'vs/base/common/platform'; -import { TPromise, Promise } from 'vs/base/common/winjs.base'; import * as Async from 'vs/base/common/async'; import Severity from 'vs/base/common/severity'; import * as Strings from 'vs/base/common/strings'; @@ -51,7 +50,7 @@ export class ProcessTaskSystem implements ITaskSystem { private errorsShown: boolean; private childProcess: LineProcess; private activeTask: CustomTask; - private activeTaskPromise: TPromise; + private activeTaskPromise: Promise; private readonly _onDidStateChange: Emitter; @@ -75,8 +74,8 @@ export class ProcessTaskSystem implements ITaskSystem { return this._onDidStateChange.event; } - public isActive(): TPromise { - return TPromise.as(!!this.childProcess); + public isActive(): Promise { + return Promise.resolve(!!this.childProcess); } public isActiveSync(): boolean { @@ -93,7 +92,7 @@ export class ProcessTaskSystem implements ITaskSystem { public run(task: Task): ITaskExecuteResult { if (this.activeTask) { - return { kind: TaskExecuteKind.Active, active: { same: this.activeTask._id === task._id, background: this.activeTask.isBackground }, promise: this.activeTaskPromise }; + return { kind: TaskExecuteKind.Active, task, active: { same: this.activeTask._id === task._id, background: this.activeTask.isBackground }, promise: this.activeTaskPromise }; } return this.executeTask(task); } @@ -117,14 +116,14 @@ export class ProcessTaskSystem implements ITaskSystem { return true; } - public terminate(task: Task): TPromise { + public terminate(task: Task): Promise { if (!this.activeTask || Task.getMapKey(this.activeTask) !== Task.getMapKey(task)) { - return TPromise.as({ success: false, task: undefined }); + return Promise.resolve({ success: false, task: undefined }); } return this.terminateAll().then(values => values[0]); } - public terminateAll(): TPromise { + public terminateAll(): Promise { if (this.childProcess) { let task = this.activeTask; return this.childProcess.terminate().then((response) => { @@ -133,7 +132,7 @@ export class ProcessTaskSystem implements ITaskSystem { return [result]; }); } - return TPromise.as([{ success: true, task: undefined }]); + return Promise.resolve([{ success: true, task: undefined }]); } private executeTask(task: Task, trigger: string = Triggers.command): ITaskExecuteResult { @@ -169,7 +168,7 @@ export class ProcessTaskSystem implements ITaskSystem { } */ this.telemetryService.publicLog(ProcessTaskSystem.TelemetryEventName, telemetryEvent); - return TPromise.wrapError(err); + return Promise.reject(err); }); return result; } catch (err) { @@ -195,6 +194,10 @@ export class ProcessTaskSystem implements ITaskSystem { } } + public rerun(): ITaskExecuteResult | undefined { + return undefined; + } + private doExecuteTask(task: CustomTask, telemetryEvent: TelemetryEvent): ITaskExecuteResult { let taskSummary: ITaskSummary = {}; let commandConfig: CommandConfiguration = task.command; @@ -302,8 +305,8 @@ export class ProcessTaskSystem implements ITaskSystem { return this.handleError(task, error); }); let result: ITaskExecuteResult = (task).tscWatch - ? { kind: TaskExecuteKind.Started, started: { restartOnFileChanges: '**/*.ts' }, promise: this.activeTaskPromise } - : { kind: TaskExecuteKind.Started, started: {}, promise: this.activeTaskPromise }; + ? { kind: TaskExecuteKind.Started, task, started: { restartOnFileChanges: '**/*.ts' }, promise: this.activeTaskPromise } + : { kind: TaskExecuteKind.Started, task, started: {}, promise: this.activeTaskPromise }; return result; } else { this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Start, task)); @@ -346,7 +349,7 @@ export class ProcessTaskSystem implements ITaskSystem { this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.End, task)); return this.handleError(task, error); }); - return { kind: TaskExecuteKind.Started, started: {}, promise: this.activeTaskPromise }; + return { kind: TaskExecuteKind.Started, task, started: {}, promise: this.activeTaskPromise }; } } @@ -356,7 +359,7 @@ export class ProcessTaskSystem implements ITaskSystem { this.activeTaskPromise = null; } - private handleError(task: CustomTask, errorData: ErrorData): Promise { + private handleError(task: CustomTask, errorData: ErrorData): Promise { let makeVisible = false; if (errorData.error && !errorData.terminated) { let args: string = task.command.args ? task.command.args.join(' ') : ''; @@ -382,7 +385,7 @@ export class ProcessTaskSystem implements ITaskSystem { error.stderr = errorData.stderr; error.stdout = errorData.stdout; error.terminated = errorData.terminated; - return TPromise.wrapError(error); + return Promise.reject(error); } private checkTerminated(task: Task, data: SuccessData | ErrorData): boolean { diff --git a/src/vs/workbench/parts/tasks/node/taskConfiguration.ts b/src/vs/workbench/parts/tasks/node/taskConfiguration.ts index 4c964af70..87ff76845 100644 --- a/src/vs/workbench/parts/tasks/node/taskConfiguration.ts +++ b/src/vs/workbench/parts/tasks/node/taskConfiguration.ts @@ -119,6 +119,10 @@ export interface PresentationOptions { clear?: boolean; } +export interface RunOptions { + rerunBehavior?: string; +} + export interface TaskIdentifier { type?: string; [name: string]: any; @@ -313,11 +317,16 @@ export interface ConfigurationProperties { * output. */ problemMatcher?: ProblemMatcherConfig.ProblemMatcherType; + + /** + * Task run options. Control run related properties. + */ + runOptions?: RunOptions; } export interface CustomTask extends CommandProperties, ConfigurationProperties { /** - * Custom tasks have the type 'custom' + * Custom tasks have the type CUSTOMIZED_TASK_TYPE */ type?: string; @@ -747,7 +756,7 @@ namespace CommandOptions { namespace CommandConfiguration { export namespace PresentationOptions { - const properties: MetaData[] = [{ property: 'echo' }, { property: 'reveal' }, { property: 'focus' }, { property: 'panel' }, { property: 'showReuseMessage' }]; + const properties: MetaData[] = [{ property: 'echo' }, { property: 'reveal' }, { property: 'focus' }, { property: 'panel' }, { property: 'showReuseMessage' }, { property: 'clear' }]; interface PresentationOptionsShape extends LegacyCommandProperties { presentation?: PresentationOptions; @@ -1285,7 +1294,8 @@ namespace ConfiguringTask { configures: taskIdentifier, _id: `${typeDeclaration.extensionId}.${taskIdentifier._key}`, _source: Objects.assign({}, source, { config: configElement }), - _label: undefined + _label: undefined, + runOptions: { rerunBehavior: external.runOptions ? Tasks.RerunBehavior.fromString(external.runOptions.rerunBehavior) : Tasks.RerunBehavior.reevaluate }, }; let configuration = ConfigurationProperties.from(external, context, true); if (configuration) { @@ -1321,9 +1331,9 @@ namespace CustomTask { } let type = external.type; if (type === void 0 || type === null) { - type = 'custom'; + type = Tasks.CUSTOMIZED_TASK_TYPE; } - if (type !== 'custom' && type !== 'shell' && type !== 'process') { + if (type !== Tasks.CUSTOMIZED_TASK_TYPE && type !== 'shell' && type !== 'process') { context.problemReporter.error(nls.localize('ConfigurationParser.notCustom', 'Error: tasks is not declared as a custom task. The configuration will be ignored.\n{0}\n', JSON.stringify(external, null, 4))); return undefined; } @@ -1337,14 +1347,15 @@ namespace CustomTask { } let result: Tasks.CustomTask = { - type: 'custom', + type: Tasks.CUSTOMIZED_TASK_TYPE, _id: context.uuidMap.getUUID(taskName), _source: Objects.assign({}, source, { config: { index, element: external, file: '.vscode\\tasks.json', workspaceFolder: context.workspaceFolder } }), _label: taskName, name: taskName, identifier: taskName, hasDefinedMatchers: false, - command: undefined + command: undefined, + runOptions: { rerunBehavior: external.runOptions ? Tasks.RerunBehavior.fromString(external.runOptions.rerunBehavior) : Tasks.RerunBehavior.reevaluate } }; let configuration = ConfigurationProperties.from(external, context, false); if (configuration) { @@ -1413,11 +1424,12 @@ namespace CustomTask { _id: configuredProps._id, _source: Objects.assign({}, configuredProps._source, { customizes: contributedTask.defines }), _label: configuredProps.name || contributedTask._label, - type: 'custom', + type: Tasks.CUSTOMIZED_TASK_TYPE, command: contributedTask.command, name: configuredProps.name || contributedTask.name, identifier: configuredProps.identifier || contributedTask.identifier, - hasDefinedMatchers: false + hasDefinedMatchers: false, + runOptions: contributedTask.runOptions, }; let resultConfigProps: Tasks.ConfigurationProperties = result; @@ -1460,7 +1472,7 @@ namespace TaskParser { function isCustomTask(value: CustomTask | ConfiguringTask): value is CustomTask { let type = value.type; let customize = (value as any).customize; - return customize === void 0 && (type === void 0 || type === null || type === 'custom' || type === 'shell' || type === 'process'); + return customize === void 0 && (type === void 0 || type === null || type === Tasks.CUSTOMIZED_TASK_TYPE || type === 'shell' || type === 'process'); } export function from(this: void, externals: (CustomTask | ConfiguringTask)[], globals: Globals, context: ParseContext): TaskParseResult { @@ -1839,7 +1851,7 @@ class ConfigurationParser { _id: context.uuidMap.getUUID(name), _source: Objects.assign({}, source, { config: { index: -1, element: fileConfig, workspaceFolder: context.workspaceFolder } }), _label: name, - type: 'custom', + type: Tasks.CUSTOMIZED_TASK_TYPE, name: name, identifier: name, group: Tasks.TaskGroup.Build, @@ -1852,6 +1864,7 @@ class ConfigurationParser { isBackground: isBackground, problemMatchers: matchers, hasDefinedMatchers: false, + runOptions: { rerunBehavior: Tasks.RerunBehavior.reevaluate }, }; let value = GroupKind.from(fileConfig.group); if (value) { diff --git a/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts b/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts index 88ad98e7c..dbfb58f11 100644 --- a/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts +++ b/src/vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts @@ -185,14 +185,15 @@ class CustomTaskBuilder { _id: name, _source: { kind: Tasks.TaskSourceKind.Workspace, label: 'workspace', config: { workspaceFolder: workspaceFolder, element: undefined, index: -1, file: '.vscode/tasks.json' } }, _label: name, - type: 'custom', + type: Tasks.CUSTOMIZED_TASK_TYPE, identifier: name, name: name, command: this.commandBuilder.result, isBackground: false, promptOnClose: true, problemMatchers: [], - hasDefinedMatchers: false + hasDefinedMatchers: false, + runOptions: { rerunBehavior: Tasks.RerunBehavior.reevaluate }, }; } diff --git a/src/vs/workbench/parts/terminal/common/terminal.ts b/src/vs/workbench/parts/terminal/common/terminal.ts index 477fd3204..3b5e23acb 100644 --- a/src/vs/workbench/parts/terminal/common/terminal.ts +++ b/src/vs/workbench/parts/terminal/common/terminal.ts @@ -100,7 +100,7 @@ export interface ITerminalConfiguration { }; showExitAlert: boolean; experimentalBufferImpl: 'JsArray' | 'TypedArray'; - splitCwd: 'workspaceRoot' | 'sourceInitialCwd' | 'sourceCwd'; + splitCwd: 'workspaceRoot' | 'initial' | 'inherited'; } export interface ITerminalConfigHelper { diff --git a/src/vs/workbench/parts/terminal/common/terminalService.ts b/src/vs/workbench/parts/terminal/common/terminalService.ts index 91d353aa7..c4230b231 100644 --- a/src/vs/workbench/parts/terminal/common/terminalService.ts +++ b/src/vs/workbench/parts/terminal/common/terminalService.ts @@ -298,22 +298,21 @@ export abstract class TerminalService implements ITerminalService { return new Promise((complete) => { const panel = this._panelService.getActivePanel(); if (!panel || panel.getId() !== TERMINAL_PANEL_ID) { - return this._panelService.openPanel(TERMINAL_PANEL_ID, focus).then(() => { - if (focus) { - // Do the focus call asynchronously as going through the - // command palette will force editor focus - setTimeout(() => { - const instance = this.getActiveInstance(); - if (instance) { - instance.focusWhenReady(true).then(() => complete(void 0)); - } else { - complete(void 0); - } - }, 0); - } else { - complete(void 0); - } - }); + this._panelService.openPanel(TERMINAL_PANEL_ID, focus); + if (focus) { + // Do the focus call asynchronously as going through the + // command palette will force editor focus + setTimeout(() => { + const instance = this.getActiveInstance(); + if (instance) { + instance.focusWhenReady(true).then(() => complete(void 0)); + } else { + complete(void 0); + } + }, 0); + } else { + complete(void 0); + } } else { if (focus) { // Do the focus call asynchronously as going through the diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts index 45e57d048..db8cc15c3 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts @@ -300,6 +300,7 @@ configurationRegistry.registerConfiguration({ 'workbench.action.tasks.build', 'workbench.action.tasks.restartTask', 'workbench.action.tasks.runTask', + 'workbench.action.tasks.reRunTask', 'workbench.action.tasks.showLog', 'workbench.action.tasks.showTasks', 'workbench.action.tasks.terminate', @@ -382,15 +383,15 @@ configurationRegistry.registerConfiguration({ default: 'JsArray' }, 'terminal.integrated.splitCwd': { - description: nls.localize('terminal.integrated.splitCwd', "Controls the source of the starting cwd for terminals created by splitting."), + description: nls.localize('terminal.integrated.splitCwd', "Controls the working directory a split terminal starts with."), type: 'string', - enum: ['workspaceRoot', 'sourceInitialCwd', 'sourceCwd'], + enum: ['workspaceRoot', 'initial', 'inherited'], enumDescriptions: [ - nls.localize('terminal.integrated.splitCwd.workspaceRoot', "A new split terminal will use the workspace root as the cwd."), - nls.localize('terminal.integrated.splitCwd.sourceInitialCwd', "A new split terminal will use the cwd that the parent terminal started with."), - nls.localize('terminal.integrated.splitCwd.sourceCwd', "On macOS and Linux, a new split terminal will use the cwd of the parent terminal. On Windows, this behaves the same as sourceInitialCwd."), + nls.localize('terminal.integrated.splitCwd.workspaceRoot', "A new split terminal will use the workspace root as the working directory. In a multi-root workspace a choice for which root folder to use is offered."), + nls.localize('terminal.integrated.splitCwd.initial', "A new split terminal will use the working directory that the parent terminal started with."), + nls.localize('terminal.integrated.splitCwd.inherited', "On macOS and Linux, a new split terminal will use the working directory of the parent terminal. On Windows, this behaves the same as initial."), ], - default: 'sourceCwd' + default: 'inherited' }, } }); diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts index 4c3c65875..367833d0d 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts @@ -8,7 +8,7 @@ import * as os from 'os'; import { Action, IAction } from 'vs/base/common/actions'; import { EndOfLinePreference } from 'vs/editor/common/model'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance, Direction, IShellLaunchConfig, ITerminalConfigHelper } from 'vs/workbench/parts/terminal/common/terminal'; +import { ITerminalService, TERMINAL_PANEL_ID, ITerminalInstance, Direction, ITerminalConfigHelper } from 'vs/workbench/parts/terminal/common/terminal'; import { SelectActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { TogglePanelAction } from 'vs/workbench/browser/panel'; import { IPartService } from 'vs/workbench/services/part/common/partService'; @@ -22,7 +22,7 @@ import { TerminalEntry } from 'vs/workbench/parts/terminal/browser/terminalQuick import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { TERMINAL_COMMAND_ID } from 'vs/workbench/parts/terminal/common/terminalCommands'; @@ -32,20 +32,33 @@ import { FindReplaceState } from 'vs/editor/contrib/find/findState'; export const TERMINAL_PICKER_PREFIX = 'term '; -function getCwdForSplit(configHelper: ITerminalConfigHelper, instance: ITerminalInstance): Promise { +function getCwdForSplit(configHelper: ITerminalConfigHelper, instance: ITerminalInstance, folders?: IWorkspaceFolder[], commandService?: ICommandService): Promise { switch (configHelper.config.splitCwd) { case 'workspaceRoot': { - // allow default behavior - return new Promise(resolve => { - resolve(''); - }); + // allow original behavior + let pathPromise: Promise = Promise.resolve(''); + if (folders.length > 1) { + // Only choose a path when there's more than 1 folder + const options: IPickOptions = { + placeHolder: nls.localize('workbench.action.terminal.newWorkspacePlaceholder', "Select current working directory for new terminal") + }; + pathPromise = commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, [options]).then(workspace => { + if (!workspace) { + // Don't split the instance if the workspace picker was canceled + return undefined; + } + return Promise.resolve(workspace.uri.fsPath); + }); + } + + return pathPromise; } - case 'sourceInitialCwd': { + case 'initial': { return new Promise(resolve => { resolve(instance.initialCwd); }); } - case 'sourceCwd': { + case 'inherited': { return instance.getCwd(); } } @@ -372,32 +385,13 @@ export class SplitTerminalAction extends Action { return Promise.resolve(void 0); } - const folders = this.workspaceContextService.getWorkspace().folders; - - let pathPromise: Promise = Promise.resolve({}); - if (folders.length > 1) { - // Only choose a path when there's more than 1 folder - const options: IPickOptions = { - placeHolder: nls.localize('workbench.action.terminal.newWorkspacePlaceholder', "Select current working directory for new terminal") - }; - pathPromise = this.commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, [options]).then(workspace => { - if (!workspace) { - // Don't split the instance if the workspace picker was canceled - return null; - } - return Promise.resolve({ cwd: workspace.uri.fsPath }); - }); - } - - return pathPromise.then(path => { - if (!path) { - return Promise.resolve(void 0); - } - return getCwdForSplit(this._terminalService.configHelper, instance).then(cwd => { - path.cwd = cwd; - this._terminalService.splitInstance(instance, path); + return getCwdForSplit(this._terminalService.configHelper, instance, this.workspaceContextService.getWorkspace().folders, this.commandService).then(cwd => { + if (cwd || (cwd === '')) { + this._terminalService.splitInstance(instance, { cwd }); return this._terminalService.showPanel(true); - }); + } else { + return undefined; + } }); } } diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 296f10cde..fedf68dba 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -487,8 +487,8 @@ export class TerminalInstance implements ITerminalInstance { // Discard first frame time as it's normal to take longer frameTimes.shift(); - const averageTime = frameTimes.reduce((p, c) => p + c) / frameTimes.length; - if (averageTime > SLOW_CANVAS_RENDER_THRESHOLD) { + const medianTime = frameTimes.sort()[Math.floor(frameTimes.length / 2)]; + if (medianTime > SLOW_CANVAS_RENDER_THRESHOLD) { const promptChoices: IPromptChoice[] = [ { label: nls.localize('yes', "Yes"), @@ -513,7 +513,6 @@ export class TerminalInstance implements ITerminalInstance { nls.localize('terminal.slowRendering', 'The standard renderer for the integrated terminal appears to be slow on your computer. Would you like to switch to the alternative DOM-based renderer which may improve performance? [Read more about terminal settings](https://code.visualstudio.com/docs/editor/integrated-terminal#_changing-how-the-terminal-is-rendered).'), promptChoices ); - console.warn('The standard renderer for the integrated terminal appears to be slow, frame times follow:', frameTimes); } }; diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts index dedd5fb4e..89eca9e2f 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts @@ -107,26 +107,26 @@ export class TerminalPanel extends Panel { this._terminalService.terminalTabs.forEach(t => t.layout(dimension.width, dimension.height)); } - public setVisible(visible: boolean): Promise { + public setVisible(visible: boolean): void { if (visible) { if (this._terminalService.terminalInstances.length > 0) { this._updateFont(); this._updateTheme(); } else { - return super.setVisible(visible).then(() => { - // Check if instances were already restored as part of workbench restore - if (this._terminalService.terminalInstances.length === 0) { - this._terminalService.createTerminal(); - } - if (this._terminalService.terminalInstances.length > 0) { - this._updateFont(); - this._updateTheme(); - } - return Promise.resolve(void 0); - }); + super.setVisible(visible); + // Check if instances were already restored as part of workbench restore + if (this._terminalService.terminalInstances.length === 0) { + this._terminalService.createTerminal(); + } + if (this._terminalService.terminalInstances.length > 0) { + this._updateFont(); + this._updateTheme(); + } + return; } } - return super.setVisible(visible); + super.setVisible(visible); + } public getActions(): IAction[] { @@ -259,7 +259,7 @@ export class TerminalPanel extends Panel { const anchor: { x: number, y: number } = { x: standardEvent.posx, y: standardEvent.posy }; this._contextMenuService.showContextMenu({ getAnchor: () => anchor, - getActions: () => Promise.resolve(this._getContextMenuActions()), + getActions: () => this._getContextMenuActions(), getActionsContext: () => this._parentDomElement }); } diff --git a/src/vs/workbench/parts/terminal/node/terminalCommandTracker.ts b/src/vs/workbench/parts/terminal/node/terminalCommandTracker.ts index 3ecc8d5cb..7074ca39d 100644 --- a/src/vs/workbench/parts/terminal/node/terminalCommandTracker.ts +++ b/src/vs/workbench/parts/terminal/node/terminalCommandTracker.ts @@ -8,7 +8,7 @@ import { ITerminalCommandTracker } from 'vs/workbench/parts/terminal/common/term import { IDisposable } from 'vs/base/common/lifecycle'; /** - * The minimize size of the prompt in which to assume the line is a command. + * The minimum size of the prompt in which to assume the line is a command. */ const MINIMUM_PROMPT_LENGTH = 2; diff --git a/src/vs/workbench/parts/terminal/node/terminalProcess.ts b/src/vs/workbench/parts/terminal/node/terminalProcess.ts index cb23452fd..ea9573246 100644 --- a/src/vs/workbench/parts/terminal/node/terminalProcess.ts +++ b/src/vs/workbench/parts/terminal/node/terminalProcess.ts @@ -17,6 +17,7 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { private _closeTimeout: any; private _ptyProcess: pty.IPty; private _currentTitle: string = ''; + private _processStartupComplete: Promise; private readonly _onProcessData: Emitter = new Emitter(); public get onProcessData(): Event { return this._onProcessData.event; } @@ -36,7 +37,7 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { ) { let shellName: string; if (os.platform() === 'win32') { - shellName = path.basename(shellLaunchConfig.executable); + shellName = path.basename(shellLaunchConfig.executable || ''); } else { // Using 'xterm-256color' here helps ensure that the majority of Linux distributions will use a // color prompt as defined in the default ~/.bashrc file. @@ -52,11 +53,17 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { }; try { - this._ptyProcess = pty.spawn(shellLaunchConfig.executable, shellLaunchConfig.args, options); + this._ptyProcess = pty.spawn(shellLaunchConfig.executable!, shellLaunchConfig.args || [], options); + this._processStartupComplete = new Promise(c => { + this.onProcessIdReady((pid) => { + c(); + }); + }); } catch (error) { // The only time this is expected to happen is when the file specified to launch with does not exist. this._exitCode = 2; this._queueProcessExit(); + this._processStartupComplete = Promise.resolve(void 0); return; } this._ptyProcess.on('data', (data) => { @@ -108,15 +115,19 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { } private _kill(): void { - // Attempt to kill the pty, it may have already been killed at this - // point but we want to make sure - try { - this._ptyProcess.kill(); - } catch (ex) { - // Swallow, the pty has already been killed - } - this._onProcessExit.fire(this._exitCode); - this.dispose(); + // Wait to kill to process until the start up code has run. This prevents us from firing a process exit before a + // process start. + this._processStartupComplete.then(() => { + // Attempt to kill the pty, it may have already been killed at this + // point but we want to make sure + try { + this._ptyProcess.kill(); + } catch (ex) { + // Swallow, the pty has already been killed + } + this._onProcessExit.fire(this._exitCode); + this.dispose(); + }); } private _sendProcessId() { diff --git a/src/vs/workbench/parts/themes/test/electron-browser/themes.test.contribution.ts b/src/vs/workbench/parts/themes/test/electron-browser/themes.test.contribution.ts index 8ac211ef7..f2696c752 100644 --- a/src/vs/workbench/parts/themes/test/electron-browser/themes.test.contribution.ts +++ b/src/vs/workbench/parts/themes/test/electron-browser/themes.test.contribution.ts @@ -215,15 +215,14 @@ class Snapper { } public captureSyntaxTokens(fileName: string, content: string): Thenable { - return this.modeService.getOrCreateModeByFilepathOrFirstLine(fileName).then(mode => { - return this.textMateService.createGrammar(mode.getId()).then((grammar) => { - let lines = content.split(/\r\n|\r|\n/); - - let result = this._tokenize(grammar, lines); - return this._getThemesResult(grammar, lines).then((themesResult) => { - this._enrichResult(result, themesResult); - return result.filter(t => t.c.length > 0); - }); + const modeId = this.modeService.getModeIdByFilepathOrFirstLine(fileName); + return this.textMateService.createGrammar(modeId).then((grammar) => { + let lines = content.split(/\r\n|\r|\n/); + + let result = this._tokenize(grammar, lines); + return this._getThemesResult(grammar, lines).then((themesResult) => { + this._enrichResult(result, themesResult); + return result.filter(t => t.c.length > 0); }); }); } diff --git a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts index f12c4ed45..cbabfd24a 100644 --- a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts +++ b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts @@ -26,6 +26,7 @@ import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/commo import { WebviewEditorInput } from 'vs/workbench/parts/webview/electron-browser/webviewEditorInput'; import { KeybindingParser } from 'vs/base/common/keybindingParser'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; function renderBody( body: string, @@ -60,7 +61,8 @@ export class ReleaseNotesManager { @IRequestService private readonly _requestService: IRequestService, @ITelemetryService private readonly _telemetryService: ITelemetryService, @IEditorService private readonly _editorService: IEditorService, - @IWebviewEditorService private readonly _webviewEditorService: IWebviewEditorService + @IWebviewEditorService private readonly _webviewEditorService: IWebviewEditorService, + @IExtensionService private readonly _extensionService: IExtensionService ) { TokenizationRegistry.onDidChange(async () => { if (!this._currentReleaseNotes || !this._lastText) { @@ -194,7 +196,10 @@ export class ReleaseNotesManager { const renderer = new marked.Renderer(); renderer.code = (code, lang) => { const modeId = this._modeService.getModeIdForLanguageName(lang); - result.push(this._modeService.getOrCreateMode(modeId).then(_ => TokenizationRegistry.getPromise(modeId))); + result.push(this._extensionService.whenInstalledExtensionsRegistered().then(_ => { + this._modeService.triggerMode(modeId); + return TokenizationRegistry.getPromise(modeId); + })); return ''; }; diff --git a/src/vs/workbench/parts/update/electron-browser/update.ts b/src/vs/workbench/parts/update/electron-browser/update.ts index 68e4617fc..530ddf989 100644 --- a/src/vs/workbench/parts/update/electron-browser/update.ts +++ b/src/vs/workbench/parts/update/electron-browser/update.ts @@ -424,24 +424,31 @@ export class UpdateContribution implements IGlobalActivity { return; } - // windows user fast updates and mac - this.notificationService.prompt( - severity.Info, - nls.localize('updateAvailableAfterRestart', "Restart {0} to apply the latest update.", product.nameLong), - [{ - label: nls.localize('updateNow', "Update Now"), - run: () => this.updateService.quitAndInstall() - }, { - label: nls.localize('later', "Later"), - run: () => { } - }, { + const actions = [{ + label: nls.localize('updateNow', "Update Now"), + run: () => this.updateService.quitAndInstall() + }, { + label: nls.localize('later', "Later"), + run: () => { } + }]; + + // TODO@joao check why snap updates send `update` as falsy + if (update.productVersion) { + actions.push({ label: nls.localize('releaseNotes', "Release Notes"), run: () => { const action = this.instantiationService.createInstance(ShowReleaseNotesAction, update.productVersion); action.run(); action.dispose(); } - }], + }); + } + + // windows user fast updates and mac + this.notificationService.prompt( + severity.Info, + nls.localize('updateAvailableAfterRestart', "Restart {0} to apply the latest update.", product.nameLong), + actions, { sticky: true } ); } diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewEditor.ts b/src/vs/workbench/parts/webview/electron-browser/webviewEditor.ts index 6a9281f1f..657978b54 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewEditor.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewEditor.ts @@ -191,7 +191,8 @@ export class WebviewEditor extends BaseWebviewEditor { allowSvgs: true, enableWrappedPostMessage: true, useSameOriginForRoot: false, - localResourceRoots: input.options.localResourceRoots || this.getDefaultLocalResourceRoots() + localResourceRoots: input.options.localResourceRoots || this.getDefaultLocalResourceRoots(), + extensionLocation: input.extensionLocation }, input.options.retainContextWhenHidden); if (this._webviewContent) { @@ -233,7 +234,8 @@ export class WebviewEditor extends BaseWebviewEditor { this._partService.getContainer(Parts.EDITOR_PART), { enableWrappedPostMessage: true, - useSameOriginForRoot: false + useSameOriginForRoot: false, + extensionLocation: input.extensionLocation }); this._webview.mountTo(this._webviewContent); input.webview = this._webview; diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewEditorService.ts b/src/vs/workbench/parts/webview/electron-browser/webviewEditorService.ts index 5824d631d..624aa9aff 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewEditorService.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewEditorService.ts @@ -5,7 +5,6 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IEditorService, ACTIVE_GROUP_TYPE, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; @@ -66,7 +65,7 @@ export interface WebviewReviver { reviveWebview( webview: WebviewEditorInput - ): TPromise; + ): Promise; } export interface WebviewEvents { @@ -138,15 +137,15 @@ export class WebviewEditorService implements IWebviewEditorService { canRevive: (_webview) => { return true; }, - reviveWebview: (webview: WebviewEditorInput): TPromise => { - return TPromise.wrap(this.tryRevive(webview)).then(didRevive => { + reviveWebview: (webview: WebviewEditorInput): Promise => { + return this.tryRevive(webview).then(didRevive => { if (didRevive) { - return TPromise.as(void 0); + return Promise.resolve(void 0); } // A reviver may not be registered yet. Put into queue and resolve promise when we can revive let resolve: (value: void) => void; - const promise = new TPromise(r => { resolve = r; }); + const promise = new Promise(r => { resolve = r; }); this._awaitingRevival.push({ input: webview, resolve }); return promise; }); diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts b/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts index 4485ce76a..f2201182a 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts @@ -23,6 +23,7 @@ export interface WebviewOptions { readonly enableWrappedPostMessage?: boolean; readonly useSameOriginForRoot?: boolean; readonly localResourceRoots?: ReadonlyArray; + readonly extensionLocation?: URI; } export class WebviewElement extends Disposable { @@ -356,11 +357,11 @@ export class WebviewElement extends Disposable { const appRootUri = URI.file(this._environmentService.appRoot); - registerFileProtocol(contents, WebviewProtocol.CoreResource, this._fileService, () => [ + registerFileProtocol(contents, WebviewProtocol.CoreResource, this._fileService, null, () => [ appRootUri ]); - registerFileProtocol(contents, WebviewProtocol.VsCodeResource, this._fileService, () => + registerFileProtocol(contents, WebviewProtocol.VsCodeResource, this._fileService, this._options.extensionLocation, () => (this._options.localResourceRoots || []) ); } diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewProtocols.ts b/src/vs/workbench/parts/webview/electron-browser/webviewProtocols.ts index 5b2fadb82..4a7c4ba39 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewProtocols.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewProtocols.ts @@ -8,32 +8,52 @@ import { nativeSep } from 'vs/base/common/paths'; import { startsWith } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { IFileService } from 'vs/platform/files/common/files'; +import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; export const enum WebviewProtocol { CoreResource = 'vscode-core-resource', VsCodeResource = 'vscode-resource' } +function resolveContent(fileService: IFileService, resource: URI, mime: string, callback: any): void { + fileService.resolveContent(resource, { encoding: 'binary' }).then(contents => { + callback({ + data: Buffer.from(contents.value, contents.encoding), + mimeType: mime + }); + }, (err) => { + console.log(err); + callback({ error: -2 /* FAILED: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ }); + }); +} + export function registerFileProtocol( contents: Electron.WebContents, protocol: WebviewProtocol, fileService: IFileService, + extensionLocation: URI | null | undefined, getRoots: () => ReadonlyArray ) { contents.session.protocol.registerBufferProtocol(protocol, (request, callback: any) => { + if (extensionLocation && extensionLocation.scheme === REMOTE_HOST_SCHEME) { + const requestUri = URI.parse(request.url); + const redirectedUri = URI.from({ + scheme: REMOTE_HOST_SCHEME, + authority: extensionLocation.authority, + path: '/vscode-resource', + query: JSON.stringify({ + requestResourcePath: requestUri.path + }) + }); + resolveContent(fileService, redirectedUri, getMimeType(requestUri), callback); + return; + } + const requestPath = URI.parse(request.url).path; const normalizedPath = URI.file(requestPath); for (const root of getRoots()) { if (startsWith(normalizedPath.fsPath, root.fsPath + nativeSep)) { - fileService.resolveContent(normalizedPath, { encoding: 'binary' }).then(contents => { - const mime = getMimeType(normalizedPath); - callback({ - data: Buffer.from(contents.value, contents.encoding), - mimeType: mime - }); - }, () => { - callback({ error: -2 /* FAILED: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ }); - }); + resolveContent(fileService, normalizedPath, getMimeType(normalizedPath), callback); return; } } @@ -52,5 +72,5 @@ const webviewMimeTypes = { function getMimeType(normalizedPath: URI) { const ext = extname(normalizedPath.fsPath).toLowerCase(); - return webviewMimeTypes[ext] || getMediaMime(normalizedPath.fsPath) || guessMimeTypes(normalizedPath.fsPath)[0]; + return webviewMimeTypes[ext] || getMediaMime(normalizedPath.fsPath) || guessMimeTypes(normalizedPath.fsPath, undefined, true)[0]; } diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.ts b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.ts index e85679a26..2bec09180 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.ts @@ -9,7 +9,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { WelcomePageContribution, WelcomePageAction, WelcomeInputFactory } from 'vs/workbench/parts/welcome/page/electron-browser/welcomePage'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; @@ -20,11 +20,13 @@ Registry.as(ConfigurationExtensions.Configuration) 'title': localize('workbenchConfigurationTitle', "Workbench"), 'properties': { 'workbench.startupEditor': { + 'scope': ConfigurationScope.APPLICATION, // Make sure repositories cannot trigger opening a README for tracking. 'type': 'string', - 'enum': ['none', 'welcomePage', 'newUntitledFile', 'welcomePageInEmptyWorkbench'], + 'enum': ['none', 'welcomePage', 'readme', 'newUntitledFile', 'welcomePageInEmptyWorkbench'], 'enumDescriptions': [ localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.none' }, "Start without an editor."), localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePage' }, "Open the Welcome page (default)."), + localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.readme' }, "Open the README when opening a folder that contains one, fallback to 'welcomePage' otherwise."), localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.newUntitledFile' }, "Open a new untitled file (only applies when opening an empty workspace)."), localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePageInEmptyWorkbench' }, "Open the Welcome page when opening an empty workbench."), ], diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts index a19d39a6d..c36a0f951 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts @@ -5,7 +5,9 @@ import 'vs/css!./welcomePage'; import { URI } from 'vs/base/common/uri'; +import * as strings from 'vs/base/common/strings'; import * as path from 'path'; +import { ICommandService } from 'vs/platform/commands/common/commands'; import * as arrays from 'vs/base/common/arrays'; import { WalkThroughInput } from 'vs/workbench/parts/welcome/walkThrough/node/walkThroughInput'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -39,6 +41,7 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/ import { TimeoutTimer } from 'vs/base/common/async'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ILabelService } from 'vs/platform/label/common/label'; +import { IFileService } from 'vs/platform/files/common/files'; used(); @@ -53,16 +56,49 @@ export class WelcomePageContribution implements IWorkbenchContribution { @IConfigurationService configurationService: IConfigurationService, @IEditorService editorService: IEditorService, @IBackupFileService backupFileService: IBackupFileService, + @IFileService fileService: IFileService, @IWorkspaceContextService contextService: IWorkspaceContextService, @ILifecycleService lifecycleService: ILifecycleService, + @ICommandService private commandService: ICommandService, ) { const enabled = isWelcomePageEnabled(configurationService, contextService); if (enabled && lifecycleService.startupKind !== StartupKind.ReloadedWindow) { backupFileService.hasBackups().then(hasBackups => { const activeEditor = editorService.activeEditor; if (!activeEditor && !hasBackups) { - return instantiationService.createInstance(WelcomePage) - .openEditor(); + const openWithReadme = configurationService.getValue(configurationKey) === 'readme'; + if (openWithReadme) { + return Promise.all(contextService.getWorkspace().folders.map(folder => { + const folderUri = folder.uri; + return fileService.readFolder(folderUri) + .then(files => { + const file = arrays.find(files.sort(), file => strings.startsWith(file.toLowerCase(), 'readme')); + if (file) { + return folderUri.with({ + path: path.posix.join(folderUri.path, file) + }); + } + return undefined; + }, onUnexpectedError); + })).then(results => results.filter(result => !!result)) + .then(readmes => { + if (!editorService.activeEditor) { + if (readmes.length) { + const isMarkDown = (readme: URI) => strings.endsWith(readme.path.toLowerCase(), '.md'); + return Promise.all([ + this.commandService.executeCommand('markdown.showPreview', null, readmes.filter(isMarkDown), { locked: true }), + editorService.openEditors(readmes.filter(readme => !isMarkDown(readme)) + .map(readme => ({ resource: readme }))), + ]); + } else { + return instantiationService.createInstance(WelcomePage).openEditor(); + } + } + return undefined; + }); + } else { + return instantiationService.createInstance(WelcomePage).openEditor(); + } } return undefined; }).then(null, onUnexpectedError); @@ -78,7 +114,7 @@ function isWelcomePageEnabled(configurationService: IConfigurationService, conte return welcomeEnabled.value; } } - return startupEditor.value === 'welcomePage' || startupEditor.value === 'welcomePageInEmptyWorkbench' && contextService.getWorkbenchState() === WorkbenchState.EMPTY; + return startupEditor.value === 'welcomePage' || startupEditor.value === 'readme' || startupEditor.value === 'welcomePageInEmptyWorkbench' && contextService.getWorkbenchState() === WorkbenchState.EMPTY; } export class WelcomePageAction extends Action { @@ -590,8 +626,13 @@ export class WelcomeInputFactory implements IEditorInputFactory { export const buttonBackground = registerColor('welcomePage.buttonBackground', { dark: null, light: null, hc: null }, localize('welcomePage.buttonBackground', 'Background color for the buttons on the Welcome page.')); export const buttonHoverBackground = registerColor('welcomePage.buttonHoverBackground', { dark: null, light: null, hc: null }, localize('welcomePage.buttonHoverBackground', 'Hover background color for the buttons on the Welcome page.')); +export const welcomePageBackground = registerColor('welcomePage.background', { light: null, dark: null, hc: null }, localize('welcomePage.background', 'Background color for the Welcome page.')); registerThemingParticipant((theme, collector) => { + const backgroundColor = theme.getColor(welcomePageBackground); + if (backgroundColor) { + collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePageContainer { background-color: ${backgroundColor}; }`); + } const foregroundColor = theme.getColor(foreground); if (foregroundColor) { collector.addRule(`.monaco-workbench > .part.editor > .content .welcomePage .caption { color: ${foregroundColor}; }`); diff --git a/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts b/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts index 7895c98f5..7b4e8721a 100644 --- a/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts +++ b/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { URI } from 'vs/base/common/uri'; import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -26,9 +25,9 @@ export class WalkThroughContentProvider implements ITextModelContentProvider, IW this.textModelResolverService.registerTextModelContentProvider(Schemas.walkThrough, this); } - public provideTextContent(resource: URI): TPromise { + public provideTextContent(resource: URI): Thenable { const query = resource.query ? JSON.parse(resource.query) : {}; - const content: TPromise = (query.moduleId ? new TPromise((resolve, reject) => { + const content: Thenable = (query.moduleId ? new Promise((resolve, reject) => { require([query.moduleId], content => { try { resolve(content.default()); @@ -40,7 +39,7 @@ export class WalkThroughContentProvider implements ITextModelContentProvider, IW return content.then(content => { let codeEditorModel = this.modelService.getModel(resource); if (!codeEditorModel) { - codeEditorModel = this.modelService.createModel(content, this.modeService.getOrCreateModeByFilepathOrFirstLine(resource.fsPath), resource); + codeEditorModel = this.modelService.createModel(content, this.modeService.createByFilepathOrFirstLine(resource.fsPath), resource); } else { this.modelService.updateModel(codeEditorModel, content); } @@ -61,7 +60,7 @@ export class WalkThroughSnippetContentProvider implements ITextModelContentProvi this.textModelResolverService.registerTextModelContentProvider(Schemas.walkThroughSnippet, this); } - public provideTextContent(resource: URI): TPromise { + public provideTextContent(resource: URI): Thenable { return this.textFileService.resolveTextContent(URI.file(resource.fsPath)).then(content => { let codeEditorModel = this.modelService.getModel(resource); if (!codeEditorModel) { @@ -85,9 +84,9 @@ export class WalkThroughSnippetContentProvider implements ITextModelContentProvi const markdown = textBuffer.getValueInRange(range, EndOfLinePreference.TextDefined); marked(markdown, { renderer }); - const modeId = this.modeService.getModeIdForLanguageName(languageName); - const mode = this.modeService.getOrCreateMode(modeId); - codeEditorModel = this.modelService.createModel(codeSnippet, mode, resource); + const languageId = this.modeService.getModeIdForLanguageName(languageName); + const languageSelection = this.modeService.create(languageId); + codeEditorModel = this.modelService.createModel(codeSnippet, languageSelection, resource); } else { this.modelService.updateModel(codeEditorModel, content.value); } diff --git a/src/vs/workbench/services/actions/electron-browser/menusExtensionPoint.ts b/src/vs/workbench/services/actions/electron-browser/menusExtensionPoint.ts index f351878e5..b7e0f358d 100644 --- a/src/vs/workbench/services/actions/electron-browser/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/electron-browser/menusExtensionPoint.ts @@ -10,7 +10,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { forEach } from 'vs/base/common/collections'; import { IExtensionPointUser, ExtensionMessageCollector, ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { MenuId, MenuRegistry, ILocalizedString } from 'vs/platform/actions/common/actions'; +import { MenuId, MenuRegistry, ILocalizedString, IMenuItem } from 'vs/platform/actions/common/actions'; import { URI } from 'vs/base/common/uri'; namespace schema { @@ -24,7 +24,7 @@ namespace schema { group?: string; } - export function parseMenuId(value: string): MenuId { + export function parseMenuId(value: string): MenuId | undefined { switch (value) { case 'commandPalette': return MenuId.CommandPalette; case 'touchBar': return MenuId.TouchBarContext; @@ -199,7 +199,7 @@ namespace schema { return true; } - function isValidIcon(icon: IUserFriendlyIcon, collector: ExtensionMessageCollector): boolean { + function isValidIcon(icon: IUserFriendlyIcon | undefined, collector: ExtensionMessageCollector): boolean { if (typeof icon === 'undefined') { return true; } @@ -286,7 +286,7 @@ ExtensionsRegistry.registerExtensionPoint 0) { @@ -364,7 +364,7 @@ ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyM group, order, when: ContextKeyExpr.deserialize(item.when) - }); + } as IMenuItem); } }); } diff --git a/src/vs/workbench/services/backup/common/backup.ts b/src/vs/workbench/services/backup/common/backup.ts index 7c6d84d01..a9ff80f9b 100644 --- a/src/vs/workbench/services/backup/common/backup.ts +++ b/src/vs/workbench/services/backup/common/backup.ts @@ -31,7 +31,7 @@ export interface IBackupFileService { * @param resource The resource that is backed up. * @return The backup resource if any. */ - loadBackupResource(resource: Uri): TPromise; + loadBackupResource(resource: Uri): TPromise; /** * Given a resource, returns the associated backup resource. @@ -63,7 +63,7 @@ export interface IBackupFileService { * @param value The contents from a backup resource as stream. * @return The backup file's backed up content as text buffer factory. */ - resolveBackupContent(backup: Uri): TPromise; + resolveBackupContent(backup: Uri): TPromise; /** * Discards the backup associated with a resource if it exists.. diff --git a/src/vs/workbench/services/backup/node/backupFileService.ts b/src/vs/workbench/services/backup/node/backupFileService.ts index 61520c626..43fbee575 100644 --- a/src/vs/workbench/services/backup/node/backupFileService.ts +++ b/src/vs/workbench/services/backup/node/backupFileService.ts @@ -32,7 +32,7 @@ export class BackupSnapshot implements ITextSnapshot { constructor(private snapshot: ITextSnapshot, private preamble: string) { } - read(): string { + read(): string | null { let value = this.snapshot.read(); if (!this.preambleHandled) { this.preambleHandled = true; @@ -145,7 +145,7 @@ export class BackupFileService implements IBackupFileService { }); } - loadBackupResource(resource: Uri): TPromise { + loadBackupResource(resource: Uri): TPromise { return this.ready.then(model => { // Return directly if we have a known backup with that resource @@ -252,7 +252,7 @@ export class InMemoryBackupFileService implements IBackupFileService { return TPromise.as(this.backups.size > 0); } - loadBackupResource(resource: Uri): TPromise { + loadBackupResource(resource: Uri): TPromise { const backupResource = this.toBackupResource(resource); if (this.backups.has(backupResource.toString())) { return TPromise.as(backupResource); @@ -268,7 +268,7 @@ export class InMemoryBackupFileService implements IBackupFileService { return TPromise.as(void 0); } - resolveBackupContent(backupResource: Uri): TPromise { + resolveBackupContent(backupResource: Uri): TPromise { const snapshot = this.backups.get(backupResource.toString()); if (snapshot) { return TPromise.as(createTextBufferFactoryFromSnapshot(snapshot)); diff --git a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts index 6555f087b..212ccb519 100644 --- a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts @@ -415,4 +415,4 @@ export class BulkEditService implements IBulkEditService { } -registerSingleton(IBulkEditService, BulkEditService); +registerSingleton(IBulkEditService, BulkEditService, true); diff --git a/src/vs/workbench/services/commands/common/commandService.ts b/src/vs/workbench/services/commands/common/commandService.ts index 5b91bdb7c..c1b71485e 100644 --- a/src/vs/workbench/services/commands/common/commandService.ts +++ b/src/vs/workbench/services/commands/common/commandService.ts @@ -6,7 +6,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ICommandService, ICommandEvent, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Event, Emitter, filterEvent, toPromise } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; @@ -43,7 +43,11 @@ export class CommandService extends Disposable implements ICommandService { } else { let waitFor = activation; if (!commandIsRegistered) { - waitFor = Promise.all([activation, this._extensionService.activateByEvent(`*`)]); + waitFor = Promise.race([ + // race activation events against command registration + Promise.all([activation, this._extensionService.activateByEvent(`*`)]), + toPromise(filterEvent(CommandsRegistry.onDidRegisterCommand, e => e === id)), + ]); } return (waitFor as Promise).then(_ => this._tryExecuteCommand(id, args)); } diff --git a/src/vs/workbench/services/commands/test/common/commandService.test.ts b/src/vs/workbench/services/commands/test/common/commandService.test.ts index 6fde84b89..ea6bae672 100644 --- a/src/vs/workbench/services/commands/test/common/commandService.test.ts +++ b/src/vs/workbench/services/commands/test/common/commandService.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { CommandService } from 'vs/workbench/services/commands/common/commandService'; import { IExtensionService, ExtensionPointContribution, IExtensionDescription, ProfileSession } from 'vs/workbench/services/extensions/common/extensions'; @@ -20,6 +20,7 @@ class SimpleExtensionService implements IExtensionService { } onDidChangeExtensionsStatus = null; onWillActivateByEvent = null; + onDidChangeResponsiveChange = null; activateByEvent(activationEvent: string): Promise { return this.whenInstalledExtensionsRegistered().then(() => { }); } @@ -141,4 +142,39 @@ suite('CommandService', function () { assert.equal(callCounter, 1); }); }); + + test('Stop waiting for * extensions to activate when trigger is satisfied #62457', function () { + + let callCounter = 0; + let dispoables: IDisposable[] = []; + let events: string[] = []; + let service = new CommandService(new InstantiationService(), new class extends SimpleExtensionService { + + activateByEvent(event: string): Promise { + events.push(event); + if (event === '*') { + return new Promise(() => { }); //forever promise... + } + if (event.indexOf('onCommand:') === 0) { + return new Promise(resolve => { + setTimeout(() => { + let reg = CommandsRegistry.registerCommand(event.substr('onCommand:'.length), () => { + callCounter += 1; + }); + dispoables.push(reg); + resolve(); + }, 0); + }); + } + return Promise.resolve(); + } + + }, new NullLogService()); + + return service.executeCommand('farboo').then(() => { + assert.equal(callCounter, 1); + assert.deepEqual(events.sort(), ['*', 'onCommand:farboo'].sort()); + dispose(dispoables); + }); + }); }); diff --git a/src/vs/workbench/services/configuration/node/configurationService.ts b/src/vs/workbench/services/configuration/node/configurationService.ts index 039dac7f9..159b24809 100644 --- a/src/vs/workbench/services/configuration/node/configurationService.ts +++ b/src/vs/workbench/services/configuration/node/configurationService.ts @@ -82,7 +82,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat this.defaultConfiguration = new DefaultConfigurationModel(); this.userConfiguration = this._register(new UserConfiguration(environmentService.appSettingsPath)); this.workspaceConfiguration = this._register(new WorkspaceConfiguration()); - this._register(this.userConfiguration.onDidChangeConfiguration(() => this.onUserConfigurationChanged())); + this._register(this.userConfiguration.onDidChangeConfiguration(userConfiguration => this.onUserConfigurationChanged(userConfiguration))); this._register(this.workspaceConfiguration.onDidUpdateConfiguration(() => this.onWorkspaceConfigurationChanged())); this._register(Registry.as(Extensions.Configuration).onDidSchemaChange(e => this.registerConfigurationSchemas())); @@ -276,8 +276,8 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat return this.reloadWorkspaceFolderConfiguration(folder, key); } return this.reloadUserConfiguration() - .then(() => this.reloadWorkspaceConfiguration()) - .then(() => this.loadConfiguration()); + .then(userConfigurationModel => this.reloadWorkspaceConfiguration() + .then(() => this.loadConfiguration(userConfigurationModel))); } inspect(key: string, overrides?: IConfigurationOverrides): { @@ -425,10 +425,11 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat private initializeConfiguration(): Promise { this.registerConfigurationSchemas(); - return this.loadConfiguration(); + return this.userConfiguration.initialize() + .then(userConfigurationModel => this.loadConfiguration(userConfigurationModel)); } - private reloadUserConfiguration(key?: string): Promise { + private reloadUserConfiguration(key?: string): Promise { return this.userConfiguration.reload(); } @@ -447,7 +448,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat return this.onWorkspaceFolderConfigurationChanged(folder, key); } - private loadConfiguration(): Promise { + private loadConfiguration(userConfigurationModel: ConfigurationModel): Promise { // reset caches this.cachedFolderConfigs = new ResourceMap(); @@ -460,7 +461,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat folderConfigurations.forEach((folderConfiguration, index) => folderConfigurationModels.set(folders[index].uri, folderConfiguration)); const currentConfiguration = this._configuration; - this._configuration = new Configuration(this.defaultConfiguration, this.userConfiguration.configurationModel, workspaceConfiguration, folderConfigurationModels, new ConfigurationModel(), new ResourceMap(), this.getWorkbenchState() !== WorkbenchState.EMPTY ? this.workspace : null); //TODO: Sandy Avoid passing null + this._configuration = new Configuration(this.defaultConfiguration, userConfigurationModel, workspaceConfiguration, folderConfigurationModels, new ConfigurationModel(), new ResourceMap(), this.getWorkbenchState() !== WorkbenchState.EMPTY ? this.workspace : null); //TODO: Sandy Avoid passing null if (currentConfiguration) { const changedKeys = this._configuration.compare(currentConfiguration); @@ -527,8 +528,8 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat } } - private onUserConfigurationChanged(): void { - let keys = this._configuration.compareAndUpdateUserConfiguration(this.userConfiguration.configurationModel); + private onUserConfigurationChanged(userConfiguration: ConfigurationModel): void { + let keys = this._configuration.compareAndUpdateUserConfiguration(userConfiguration); this.triggerConfigurationChange(keys, ConfigurationTarget.USER); } @@ -623,7 +624,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat case ConfigurationTarget.WORKSPACE_FOLDER: const workspaceFolder = overrides && overrides.resource ? this.workspace.getFolder(overrides.resource) : null; if (workspaceFolder) { - return this.reloadWorkspaceFolderConfiguration(this.workspace.getFolder(overrides.resource), key); + return this.reloadWorkspaceFolderConfiguration(this.workspace.getFolder(overrides.resource), key).then(() => null); } } return null; 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 629b12288..8bd6c1af4 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 @@ -35,7 +35,6 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { ICommandService } from 'vs/platform/commands/common/commands'; import { CommandService } from 'vs/workbench/services/commands/common/commandService'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { createHash } from 'crypto'; class SettingsTestEnvironmentService extends EnvironmentService { @@ -83,14 +82,14 @@ suite('ConfigurationEditingService', () => { .then(() => setUpServices()); }); - function setUpWorkspace(): TPromise { + async function setUpWorkspace(): Promise { const id = uuid.generateUuid(); parentDir = path.join(os.tmpdir(), 'vsctests', id); workspaceDir = path.join(parentDir, 'workspaceconfig', id); globalSettingsFile = path.join(workspaceDir, 'config.json'); workspaceSettingsDir = path.join(workspaceDir, '.vscode'); - return mkdirp(workspaceSettingsDir, 493); + return await mkdirp(workspaceSettingsDir, 493); } function setUpServices(noWorkspace: boolean = false): Promise { diff --git a/src/vs/workbench/services/configurationResolver/common/configurationResolverUtils.ts b/src/vs/workbench/services/configurationResolver/common/configurationResolverUtils.ts new file mode 100644 index 000000000..cb0d1eeb3 --- /dev/null +++ b/src/vs/workbench/services/configurationResolver/common/configurationResolverUtils.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { IJSONSchema } from 'vs/base/common/jsonSchema'; + +export function applyDeprecatedVariableMessage(schema: IJSONSchema) { + schema.pattern = schema.pattern || '^(?!.*\\$\\{(env|config|command)\\.)'; + schema.patternErrorMessage = schema.patternErrorMessage || + nls.localize('deprecatedVariables', "'env.', 'config.' and 'command.' are deprecated, use 'env:', 'config:' and 'command:' instead."); +} \ No newline at end of file diff --git a/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts b/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts index cc219f85a..572dcf836 100644 --- a/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts +++ b/src/vs/workbench/services/contextview/electron-browser/contextmenuService.ts @@ -35,43 +35,42 @@ export class ContextMenuService extends Disposable implements IContextMenuServic } showContextMenu(delegate: IContextMenuDelegate): void { - delegate.getActions().then(actions => { - if (actions.length) { - const onHide = once(() => { - if (delegate.onHide) { - delegate.onHide(undefined); - } + const actions = delegate.getActions(); + if (actions.length) { + const onHide = once(() => { + if (delegate.onHide) { + delegate.onHide(undefined); + } - this._onDidContextMenu.fire(); - }); + this._onDidContextMenu.fire(); + }); - const menu = this.createMenu(delegate, actions, onHide); - const anchor = delegate.getAnchor(); - let x: number, y: number; + const menu = this.createMenu(delegate, actions, onHide); + const anchor = delegate.getAnchor(); + let x: number, y: number; - if (dom.isHTMLElement(anchor)) { - let elementPosition = dom.getDomNodePagePosition(anchor); + if (dom.isHTMLElement(anchor)) { + let elementPosition = dom.getDomNodePagePosition(anchor); - x = elementPosition.left; - y = elementPosition.top + elementPosition.height; - } else { - const pos = <{ x: number; y: number; }>anchor; - x = pos.x + 1; /* prevent first item from being selected automatically under mouse */ - y = pos.y; - } + x = elementPosition.left; + y = elementPosition.top + elementPosition.height; + } else { + const pos = <{ x: number; y: number; }>anchor; + x = pos.x + 1; /* prevent first item from being selected automatically under mouse */ + y = pos.y; + } - let zoom = webFrame.getZoomFactor(); - x *= zoom; - y *= zoom; + let zoom = webFrame.getZoomFactor(); + x *= zoom; + y *= zoom; - popup(menu, { - x: Math.floor(x), - y: Math.floor(y), - positioningItem: delegate.autoSelectFirstItem ? 0 : void 0, - onHide: () => onHide() - }); - } - }); + popup(menu, { + x: Math.floor(x), + y: Math.floor(y), + positioningItem: delegate.autoSelectFirstItem ? 0 : void 0, + onHide: () => onHide() + }); + } } private createMenu(delegate: IContextMenuDelegate, entries: (IAction | ContextSubMenu)[], onHide: () => void): IContextMenuItem[] { diff --git a/src/vs/workbench/services/decorations/browser/decorations.ts b/src/vs/workbench/services/decorations/browser/decorations.ts index ff98b24c9..e8098f948 100644 --- a/src/vs/workbench/services/decorations/browser/decorations.ts +++ b/src/vs/workbench/services/decorations/browser/decorations.ts @@ -46,5 +46,5 @@ export interface IDecorationsService { registerDecorationsProvider(provider: IDecorationsProvider): IDisposable; - getDecoration(uri: URI, includeChildren: boolean, overwrite?: IDecorationData): IDecoration; + getDecoration(uri: URI, includeChildren: boolean, overwrite?: IDecorationData): IDecoration | undefined; } diff --git a/src/vs/workbench/services/decorations/browser/decorationsService.ts b/src/vs/workbench/services/decorations/browser/decorationsService.ts index 32a20903b..1021a4344 100644 --- a/src/vs/workbench/services/decorations/browser/decorationsService.ts +++ b/src/vs/workbench/services/decorations/browser/decorationsService.ts @@ -55,28 +55,28 @@ class DecorationRule { private _appendForOne(data: IDecorationData, element: HTMLStyleElement, theme: ITheme): void { const { color, letter } = data; // label - createCSSRule(`.${this.itemColorClassName}`, `color: ${theme.getColor(color) || 'inherit'};`, element); + createCSSRule(`.${this.itemColorClassName}`, `color: ${getColor(theme, color)};`, element); // letter if (letter) { - createCSSRule(`.${this.itemBadgeClassName}::after`, `content: "${letter}"; color: ${theme.getColor(color) || 'inherit'};`, element); + createCSSRule(`.${this.itemBadgeClassName}::after`, `content: "${letter}"; color: ${getColor(theme, color)};`, element); } } private _appendForMany(data: IDecorationData[], element: HTMLStyleElement, theme: ITheme): void { // label const { color } = data[0]; - createCSSRule(`.${this.itemColorClassName}`, `color: ${theme.getColor(color) || 'inherit'};`, element); + createCSSRule(`.${this.itemColorClassName}`, `color: ${getColor(theme, color)};`, element); // badge const letters = data.filter(d => !isFalsyOrWhitespace(d.letter)).map(d => d.letter); if (letters.length) { - createCSSRule(`.${this.itemBadgeClassName}::after`, `content: "${letters.join(', ')}"; color: ${theme.getColor(color) || 'inherit'};`, element); + createCSSRule(`.${this.itemBadgeClassName}::after`, `content: "${letters.join(', ')}"; color: ${getColor(theme, color)};`, element); } // bubble badge createCSSRule( `.${this.bubbleBadgeClassName}::after`, - `content: "\uf052"; color: ${theme.getColor(color) || 'inherit'}; 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: 14px; opacity: 0.4;`, element ); } @@ -108,13 +108,17 @@ class DecorationStyles { dispose(): void { dispose(this._disposables); - this._styleElement.parentElement.removeChild(this._styleElement); + + const parent = this._styleElement.parentElement; + if (parent) { + parent.removeChild(this._styleElement); + } } asDecoration(data: IDecorationData[], onlyChildren: boolean): IDecoration { // sort by weight - data.sort((a, b) => b.weight - a.weight); + data.sort((a, b) => (b.weight || 0) - (a.weight || 0)); let key = DecorationRule.keyOf(data); let rule = this._decorationRules.get(key); @@ -174,7 +178,7 @@ class DecorationStyles { this._decorationRules.forEach((value, index) => { const { data } = value; if (value.isUnused()) { - let remove: boolean; + let remove: boolean = false; if (Array.isArray(data)) { remove = data.some(data => !usedDecorations.has(DecorationRule.keyOf(data))); } else if (!usedDecorations.has(DecorationRule.keyOf(data))) { @@ -224,7 +228,7 @@ class DecorationDataRequest { class DecorationProviderWrapper { - readonly data = TernarySearchTree.forPaths(); + readonly data = TernarySearchTree.forPaths(); private readonly _dispoable: IDisposable; constructor( @@ -286,7 +290,7 @@ class DecorationProviderWrapper { } } - private _fetchData(uri: URI): IDecorationData { + private _fetchData(uri: URI): IDecorationData | undefined | null { // check for pending request and cancel it const pendingRequest = this.data.get(uri.toString()); @@ -318,9 +322,9 @@ class DecorationProviderWrapper { } } - private _keepItem(uri: URI, data: IDecorationData): IDecorationData { - let deco = data ? data : null; - let old = this.data.set(uri.toString(), deco); + private _keepItem(uri: URI, data: IDecorationData | null | undefined): IDecorationData | null { + const deco = data ? data : null; + const old = this.data.set(uri.toString(), deco); if (deco || old) { // only fire event when something changed this._uriEmitter.fire(uri); @@ -397,9 +401,9 @@ export class FileDecorationsService implements IDecorationsService { }); } - getDecoration(uri: URI, includeChildren: boolean, overwrite?: IDecorationData): IDecoration { + getDecoration(uri: URI, includeChildren: boolean, overwrite?: IDecorationData): IDecoration | undefined { let data: IDecorationData[] = []; - let containsChildren: boolean; + let containsChildren: boolean = false; for (let iter = this._data.iterator(), next = iter.next(); !next.done; next = iter.next()) { next.value.getOrRetrieve(uri, includeChildren, (deco, isChild) => { if (!isChild || deco.bubble) { @@ -427,3 +431,13 @@ export class FileDecorationsService implements IDecorationsService { } } } +function getColor(theme: ITheme, color: string | undefined) { + if (color) { + const foundColor = theme.getColor(color); + if (foundColor) { + return foundColor; + } + } + return 'inherit'; +} + 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 f796cc491..c183860ea 100644 --- a/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts +++ b/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts @@ -93,13 +93,16 @@ suite('DecorationsService', function () { // un-register -> ensure good event let didSeeEvent = false; - let p = toPromise(service.onDidChangeDecorations).then(e => { - assert.equal(e.affectsResource(uri), true); - assert.deepEqual(service.getDecoration(uri, false), undefined); - assert.equal(callCounter, 1); - didSeeEvent = true; + let p = new Promise(resolve => { + service.onDidChangeDecorations(e => { + assert.equal(e.affectsResource(uri), true); + assert.deepEqual(service.getDecoration(uri, false), undefined); + assert.equal(callCounter, 1); + didSeeEvent = true; + resolve(); + }); }); - reg.dispose(); + reg.dispose(); // will clear all data await p; assert.equal(didSeeEvent, true); }); @@ -238,4 +241,80 @@ suite('DecorationsService', function () { reg.dispose(); }); + + test('Folder decorations don\'t go away when file with problems is deleted #61919 (part1)', function () { + + let emitter = new Emitter(); + let gone = false; + let reg = service.registerDecorationsProvider({ + label: 'Test', + onDidChange: emitter.event, + provideDecorations(uri: URI) { + if (!gone && uri.path.match(/file.ts$/)) { + return { tooltip: 'FOO', weight: 17, bubble: true }; + } + return undefined; + } + }); + + let uri = URI.parse('foo:/folder/file.ts'); + let uri2 = URI.parse('foo:/folder/'); + let data = service.getDecoration(uri, true); + assert.equal(data.tooltip, 'FOO'); + + data = service.getDecoration(uri2, true); + assert.ok(data.tooltip); // emphazied items... + + gone = true; + emitter.fire([uri]); + + data = service.getDecoration(uri, true); + assert.equal(data, undefined); + + data = service.getDecoration(uri2, true); + assert.equal(data, undefined); + + reg.dispose(); + }); + + test('Folder decorations don\'t go away when file with problems is deleted #61919 (part2)', function () { + + let emitter = new Emitter(); + let gone = false; + let reg = service.registerDecorationsProvider({ + label: 'Test', + onDidChange: emitter.event, + provideDecorations(uri: URI) { + if (!gone && uri.path.match(/file.ts$/)) { + return { tooltip: 'FOO', weight: 17, bubble: true }; + } + return undefined; + } + }); + + let uri = URI.parse('foo:/folder/file.ts'); + let uri2 = URI.parse('foo:/folder/'); + let data = service.getDecoration(uri, true); + assert.equal(data.tooltip, 'FOO'); + + data = service.getDecoration(uri2, true); + assert.ok(data.tooltip); // emphazied items... + + return new Promise((resolve, reject) => { + let l = service.onDidChangeDecorations(e => { + l.dispose(); + try { + assert.ok(e.affectsResource(uri)); + assert.ok(e.affectsResource(uri2)); + resolve(); + reg.dispose(); + } catch (err) { + reject(err); + reg.dispose(); + } + }); + gone = true; + emitter.fire([uri]); + }); + }); }); diff --git a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts index bdd20aa5e..f6ab8c365 100644 --- a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts @@ -272,17 +272,12 @@ export class FileDialogService implements IFileDialogService { if (defaultUri && defaultUri.scheme !== Schemas.file) { return Promise.reject(new Error('Not supported - Open-dialogs can only be opened on `file`-uris.')); } - const filters = []; - if (options.filters) { - for (let name in options.filters) { - filters.push({ name, extensions: options.filters[name] }); - } - } + const newOptions: OpenDialogOptions = { title: options.title, defaultPath: defaultUri && defaultUri.fsPath, buttonLabel: options.openLabel, - filters, + filters: options.filters, properties: [] }; newOptions.properties.push('createDirectory'); diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 20357f2c9..e70bbcfee 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -6,7 +6,6 @@ import { Event } from 'vs/base/common/event'; import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; @@ -34,6 +33,17 @@ export interface IExtensionDescription { enableProposedApi?: boolean; } +export const nullExtensionDescription = Object.freeze({ + id: 'nullExtensionDescription', + name: 'Null Extension Description', + version: '0.0.0', + publisher: 'vscode', + enableProposedApi: false, + engines: { vscode: '' }, + extensionLocation: URI.parse('void:location'), + isBuiltin: false, +}); + export const IExtensionService = createDecorator('extensionService'); export interface IMessage { @@ -122,7 +132,12 @@ export interface IWillActivateEvent { readonly activation: Thenable; } -export interface IExtensionService { +export interface IResponsiveStateChangeEvent { + target: ICpuProfilerTarget; + isResponsive: boolean; +} + +export interface IExtensionService extends ICpuProfilerTarget { _serviceBrand: any; /** @@ -146,42 +161,38 @@ export interface IExtensionService { */ onWillActivateByEvent: Event; + /** + * An event that is fired when an extension host changes its + * responsive-state. + */ + onDidChangeResponsiveChange: Event; + /** * Send an activation event and activate interested extensions. */ - activateByEvent(activationEvent: string): TPromise; + activateByEvent(activationEvent: string): Thenable; /** * An promise that resolves when the installed extensions are registered after * their extension points got handled. */ - whenInstalledExtensionsRegistered(): TPromise; + whenInstalledExtensionsRegistered(): Promise; /** * Return all registered extensions */ - getExtensions(): TPromise; + getExtensions(): Promise; /** * Read all contributions to an extension point. */ - readExtensionPointContributions(extPoint: IExtensionPoint): TPromise[]>; + readExtensionPointContributions(extPoint: IExtensionPoint): Promise[]>; /** * Get information about extensions status. */ getExtensionsStatus(): { [id: string]: IExtensionsStatus }; - /** - * Check if the extension host can be profiled. - */ - canProfileExtensionHost(): boolean; - - /** - * Begin an extension host process profile session. - */ - startExtensionHostProfile(): TPromise; - /** * Return the inspect port or 0. */ @@ -203,6 +214,29 @@ export interface IExtensionService { stopExtensionHost(): void; } +export interface ICpuProfilerTarget { + + /** + * Check if the extension host can be profiled. + */ + canProfileExtensionHost(): boolean; + + /** + * Begin an extension host process profile session. + */ + startExtensionHostProfile(): Promise; +} + export interface ProfileSession { - stop(): TPromise; + stop(): Promise; +} + +export function checkProposedApiEnabled(extension: IExtensionDescription): void { + if (!extension.enableProposedApi) { + throwProposedApiError(extension); + } +} + +export function throwProposedApiError(extension: IExtensionDescription): never { + throw new Error(`[${extension.id}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${extension.id}`); } diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index 539c21f2d..16adcf10c 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -214,6 +214,11 @@ export const schema = { description: nls.localize('vscode.extension.activationEvents.onDebugResolve', 'An activation event emitted whenever a debug session with the specific type is about to be launched (and a corresponding resolveDebugConfiguration method needs to be called).'), body: 'onDebugResolve:${6:type}' }, + { + label: 'onDebugAdapterProtocolTracker', + description: nls.localize('vscode.extension.activationEvents.onDebugAdapterProtocolTracker', 'An activation event emitted whenever a debug session with the specific type is about to be launched and a debug protocol tracker might be needed.'), + body: 'onDebugAdapterProtocolTracker:${6:type}' + }, { label: 'workspaceContains', description: nls.localize('vscode.extension.activationEvents.workspaceContains', 'An activation event emitted whenever a folder is opened that contains at least a file matching the specified glob pattern.'), diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 3ecc472ed..2a57465eb 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -20,7 +20,7 @@ import { isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { IRemoteConsoleLog, log, parse } from 'vs/base/node/console'; -import { findFreePort } from 'vs/base/node/ports'; +import { findFreePort, randomPort } from 'vs/base/node/ports'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; import { Protocol, generateRandomPipeName } from 'vs/base/parts/ipc/node/ipc.net'; import { IBroadcast, IBroadcastService } from 'vs/platform/broadcast/electron-browser/broadcastService'; @@ -94,7 +94,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { private _messageProtocol: TPromise; constructor( - private readonly _extensions: TPromise, + private readonly _extensions: Promise, private readonly _extensionHostLogsLocation: URI, @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, @INotificationService private readonly _notificationService: INotificationService, @@ -263,7 +263,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { // Help in case we fail to start it let startupTimeoutHandle: any; - if (!this._environmentService.isBuilt || this._isExtensionDevHost) { + if (!this._environmentService.isBuilt && !this._windowService.getConfiguration().remoteAuthority || this._isExtensionDevHost) { startupTimeoutHandle = setTimeout(() => { const msg = this._isExtensionDevDebugBrk ? nls.localize('extensionHostProcess.startupFailDebug', "Extension host did not start in 10 seconds, it might be stopped on the first line and needs a debugger to continue.") @@ -311,11 +311,9 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { */ private _tryFindDebugPort(): Promise<{ expected: number; actual: number }> { let expected: number; - let startPort = 9333; + let startPort = randomPort(); if (typeof this._environmentService.debugExtensionHost.port === 'number') { startPort = expected = this._environmentService.debugExtensionHost.port; - } else { - return Promise.resolve({ expected: undefined, actual: 0 }); } return new Promise(resolve => { return findFreePort(startPort, 10 /* try 10 ports */, 5000 /* try up to 5 seconds */).then(port => { @@ -362,22 +360,38 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { // 2) wait for the incoming `initialized` event. return new Promise((resolve, reject) => { - let handle = setTimeout(() => { - reject('timeout'); - }, 60 * 1000); + let timeoutHandle: NodeJS.Timer; + const installTimeoutCheck = () => { + timeoutHandle = setTimeout(() => { + reject('timeout'); + }, 60 * 1000); + }; + const uninstallTimeoutCheck = () => { + clearTimeout(timeoutHandle); + }; + + // Wait 60s for the ready message + installTimeoutCheck(); const disposable = protocol.onMessage(msg => { if (isMessageOfType(msg, MessageType.Ready)) { // 1) Extension Host is ready to receive messages, initialize it - this._createExtHostInitData().then(data => protocol.send(Buffer.from(JSON.stringify(data)))); + uninstallTimeoutCheck(); + + this._createExtHostInitData().then(data => { + + // Wait 60s for the initialized message + installTimeoutCheck(); + + protocol.send(Buffer.from(JSON.stringify(data))); + }); return; } if (isMessageOfType(msg, MessageType.Initialized)) { // 2) Extension Host is initialized - - clearTimeout(handle); + uninstallTimeoutCheck(); // stop listening for messages here disposable.dispose(); diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts index 6dc5166eb..d7bdc83c6 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts @@ -5,7 +5,6 @@ import { Profile, ProfileNode } from 'v8-inspect-profiler'; import { TernarySearchTree } from 'vs/base/common/map'; -import { TPromise } from 'vs/base/common/winjs.base'; import { realpathSync } from 'vs/base/node/extfs'; import { IExtensionDescription, IExtensionHostProfile, IExtensionService, ProfileSegmentId, ProfileSession } from 'vs/workbench/services/extensions/common/extensions'; @@ -14,20 +13,16 @@ export class ExtensionHostProfiler { constructor(private readonly _port: number, @IExtensionService private readonly _extensionService: IExtensionService) { } - public start(): TPromise { - return TPromise.wrap(import('v8-inspect-profiler')).then(profiler => { - return profiler.startProfiling({ port: this._port }).then(session => { - return { - stop: () => { - return TPromise.wrap(session.stop()).then(profile => { - return this._extensionService.getExtensions().then(extensions => { - return this.distill(profile.profile, extensions); - }); - }); - } - }; - }); - }); + public async start(): Promise { + const profiler = await import('v8-inspect-profiler'); + const session = await profiler.startProfiling({ port: this._port }); + return { + stop: async () => { + const profile = await session.stop(); + const extensions = await this._extensionService.getExtensions(); + return this.distill((profile as any).profile, extensions); + } + }; } private distill(profile: Profile, extensions: IExtensionDescription[]): IExtensionHostProfile { diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index a3b632f74..c0b581c43 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import * as os from 'os'; import * as path from 'path'; import { getPathFromAmdModule } from 'vs/base/common/amd'; -import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; import { Barrier, runWhenIdle } from 'vs/base/common/async'; import * as errors from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; @@ -19,7 +19,6 @@ import * as platform from 'vs/base/common/platform'; import { fsPath, isEqualOrParent } from 'vs/base/common/resources'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as pfs from 'vs/base/node/pfs'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -30,18 +29,16 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import pkg from 'vs/platform/node/package'; import product from 'vs/platform/node/product'; -import { INotificationHandle, INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; import { ExtHostCustomersRegistry } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { ExtHostContext, ExtHostExtensionServiceShape, IExtHostContext, MainContext } from 'vs/workbench/api/node/extHost.protocol'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { ActivationTimes, ExtensionPointContribution, IExtensionDescription, IExtensionService, IExtensionsStatus, IMessage, ProfileSession, IWillActivateEvent } from 'vs/workbench/services/extensions/common/extensions'; +import { ActivationTimes, ExtensionPointContribution, IExtensionDescription, IExtensionService, IExtensionsStatus, IMessage, ProfileSession, IWillActivateEvent, IResponsiveStateChangeEvent } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser, schema } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { ExtensionHostProcessWorker, IExtensionHostStarter } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler'; -import { RuntimeExtensionsInput } from 'vs/workbench/services/extensions/electron-browser/runtimeExtensionsInput'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; import { ExtensionScanner, ExtensionScannerInput, IExtensionReference, IExtensionResolver, ILog, IRelaxedExtensionDescription, Translations } from 'vs/workbench/services/extensions/node/extensionPoints'; import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier'; @@ -80,7 +77,7 @@ class ExtraBuiltInExtensionResolver implements IExtensionResolver { constructor(private builtInExtensions: IBuiltInExtension[], private control: IBuiltInExtensionControl) { } - resolveExtensions(): TPromise { + resolveExtensions(): Promise { const result: IExtensionReference[] = []; for (const ext of this.builtInExtensions) { @@ -98,7 +95,7 @@ class ExtraBuiltInExtensionResolver implements IExtensionResolver { } } - return TPromise.as(result); + return Promise.resolve(result); } } @@ -110,7 +107,7 @@ function messageWithSource(source: string, message: string): string { } const hasOwnProperty = Object.hasOwnProperty; -const NO_OP_VOID_PROMISE = TPromise.wrap(void 0); +const NO_OP_VOID_PROMISE = Promise.resolve(void 0); export class ExtensionHostProcessManager extends Disposable { @@ -129,10 +126,11 @@ export class ExtensionHostProcessManager extends Disposable { /** * winjs believes a proxy is a promise because it has a `then` method, so wrap the result in an object. */ - private _extensionHostProcessProxy: TPromise<{ value: ExtHostExtensionServiceShape; }>; + private _extensionHostProcessProxy: Thenable<{ value: ExtHostExtensionServiceShape; }>; constructor( extensionHostProcessWorker: IExtensionHostStarter, + private readonly _remoteAuthority: string, initialActivationEvents: string[], @IInstantiationService private readonly _instantiationService: IInstantiationService, @IEnvironmentService private readonly _environmentService: IEnvironmentService, @@ -193,6 +191,7 @@ export class ExtensionHostProcessManager extends Disposable { this._extensionHostProcessRPCProtocol = new RPCProtocol(protocol, logger); this._register(this._extensionHostProcessRPCProtocol.onDidChangeResponsiveState((responsiveState: ResponsiveState) => this._onDidChangeResponsiveState.fire(responsiveState))); const extHostContext: IExtHostContext = { + remoteAuthority: this._remoteAuthority, getProxy: (identifier: ProxyIdentifier): T => this._extensionHostProcessRPCProtocol.getProxy(identifier), set: (identifier: ProxyIdentifier, instance: R): R => this._extensionHostProcessRPCProtocol.set(identifier, instance), assertRegistered: (identifiers: ProxyIdentifier[]): void => this._extensionHostProcessRPCProtocol.assertRegistered(identifiers), @@ -222,7 +221,7 @@ export class ExtensionHostProcessManager extends Disposable { return this._extensionHostProcessRPCProtocol.getProxy(ExtHostContext.ExtHostExtensionService); } - public activateByEvent(activationEvent: string): TPromise { + public activateByEvent(activationEvent: string): Thenable { if (this._extensionHostProcessFinishedActivateEvents[activationEvent] || !this._extensionHostProcessProxy) { return NO_OP_VOID_PROMISE; } @@ -238,7 +237,7 @@ export class ExtensionHostProcessManager extends Disposable { }); } - public startExtensionHostProfile(): TPromise { + public startExtensionHostProfile(): Promise { if (this._extensionHostProcessWorker) { let port = this._extensionHostProcessWorker.getInspectPort(); if (port) { @@ -280,7 +279,8 @@ export class ExtensionService extends Disposable implements IExtensionService { private _onWillActivateByEvent = new Emitter(); readonly onWillActivateByEvent: Event = this._onWillActivateByEvent.event; - private _unresponsiveNotificationHandle: INotificationHandle; + private readonly _onDidChangeResponsiveChange = new Emitter(); + readonly onDidChangeResponsiveChange: Event = this._onDidChangeResponsiveChange.event; // --- Members used per extension host process private _extensionHostProcessManagers: ExtensionHostProcessManager[]; @@ -308,8 +308,6 @@ export class ExtensionService extends Disposable implements IExtensionService { this._onDidRegisterExtensions = new Emitter(); - this._unresponsiveNotificationHandle = null; - this._extensionHostProcessManagers = []; this._extensionHostProcessActivationTimes = Object.create(null); this._extensionHostExtensionRuntimeErrors = Object.create(null); @@ -346,6 +344,8 @@ export class ExtensionService extends Disposable implements IExtensionService { public dispose(): void { super.dispose(); + this._onWillActivateByEvent.dispose(); + this._onDidChangeResponsiveChange.dispose(); } public get onDidRegisterExtensions(): Event { @@ -384,9 +384,9 @@ export class ExtensionService extends Disposable implements IExtensionService { this._stopExtensionHostProcess(); const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, this.getExtensions(), this._extensionHostLogsLocation); - const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostProcessWorker, initialActivationEvents); + const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostProcessWorker, null, initialActivationEvents); extHostProcessManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal)); - extHostProcessManager.onDidChangeResponsiveState((responsiveState) => this._onResponsiveStateChanged(responsiveState)); + extHostProcessManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ target: extHostProcessManager, isResponsive: responsiveState === ResponsiveState.Responsive }); }); this._extensionHostProcessManagers.push(extHostProcessManager); } @@ -428,55 +428,9 @@ export class ExtensionService extends Disposable implements IExtensionService { ); } - private _onResponsiveStateChanged(state: ResponsiveState): void { - // Do not show the notification anymore - // See https://github.com/Microsoft/vscode/issues/60318 - const DISABLE_PROMPT = true; - if (this._isDev || DISABLE_PROMPT) { - return; // do not show any notification when developing an extension (https://github.com/Microsoft/vscode/issues/59251) - } - - if (this._unresponsiveNotificationHandle) { - this._unresponsiveNotificationHandle.close(); - this._unresponsiveNotificationHandle = null; - } - - const showRunningExtensions = { - keepOpen: true, - label: nls.localize('extensionHostProcess.unresponsive.inspect', "Show running extensions"), - run: () => { - this._instantiationService.invokeFunction((accessor) => { - const editorService = accessor.get(IEditorService); - editorService.openEditor(this._instantiationService.createInstance(RuntimeExtensionsInput), { revealIfOpened: true }); - }); - } - }; - - const restartExtensionHost = { - label: nls.localize('extensionHostProcess.unresponsive.restart', "Restart Extension Host"), - run: () => { - this.restartExtensionHost(); - } - }; - - if (state === ResponsiveState.Unresponsive) { - this._unresponsiveNotificationHandle = this._notificationService.prompt( - Severity.Warning, - nls.localize('extensionHostProcess.unresponsive', "Extension Host is unresponsive."), - [showRunningExtensions, restartExtensionHost] - ); - } else { - this._unresponsiveNotificationHandle = this._notificationService.prompt( - Severity.Info, - nls.localize('extensionHostProcess.responsive', "Extension Host is now responsive."), - [showRunningExtensions] - ); - } - } - // ---- begin IExtensionService - public activateByEvent(activationEvent: string): TPromise { + public activateByEvent(activationEvent: string): Promise { if (this._installedExtensionsReady.isOpen()) { // Extensions have been scanned and interpreted @@ -499,8 +453,8 @@ export class ExtensionService extends Disposable implements IExtensionService { } } - private _activateByEvent(activationEvent: string): TPromise { - const result = TPromise.join( + private _activateByEvent(activationEvent: string): Promise { + const result = Promise.all( this._extensionHostProcessManagers.map(extHostManager => extHostManager.activateByEvent(activationEvent)) ).then(() => { }); this._onWillActivateByEvent.fire({ @@ -510,17 +464,17 @@ export class ExtensionService extends Disposable implements IExtensionService { return result; } - public whenInstalledExtensionsRegistered(): TPromise { + public whenInstalledExtensionsRegistered(): Promise { return this._installedExtensionsReady.wait(); } - public getExtensions(): TPromise { + public getExtensions(): Promise { return this._installedExtensionsReady.wait().then(() => { return this._registry.getAllExtensionDescriptions(); }); } - public readExtensionPointContributions(extPoint: IExtensionPoint): TPromise[]> { + public readExtensionPointContributions(extPoint: IExtensionPoint): Promise[]> { return this._installedExtensionsReady.wait().then(() => { let availableExtensions = this._registry.getAllExtensionDescriptions(); @@ -564,7 +518,7 @@ export class ExtensionService extends Disposable implements IExtensionService { return false; } - public startExtensionHostProfile(): TPromise { + public startExtensionHostProfile(): Promise { for (let i = 0, len = this._extensionHostProcessManagers.length; i < len; i++) { const extHostProcessManager = this._extensionHostProcessManagers[i]; if (extHostProcessManager.canProfileExtensionHost()) { @@ -608,7 +562,7 @@ export class ExtensionService extends Disposable implements IExtensionService { }); } - private _scanExtensions(): TPromise { + private _scanExtensions(): Promise { const log = new Logger((severity, source, message) => { this._logOrShowMessage(severity, this._isDev ? messageWithSource(source, message) : message); }); @@ -691,7 +645,7 @@ export class ExtensionService extends Disposable implements IExtensionService { return this.extensionManagementService.getInstalled(LocalExtensionType.User) .then(installed => { const toDisable = installed.filter(i => extensionsToDisable.some(e => areSameExtensions({ id: getGalleryExtensionIdFromLocal(i) }, e))); - return TPromise.join(toDisable.map(e => this._extensionEnablementService.setEnablement(e, EnablementState.Disabled))); + return Promise.all(toDisable.map(e => this._extensionEnablementService.setEnablement(e, EnablementState.Disabled))); }) .then(() => { this._storageService.store(BetterMergeDisabledNowKey, true, StorageScope.GLOBAL); @@ -704,7 +658,7 @@ export class ExtensionService extends Disposable implements IExtensionService { } private _updateEnableProposedApi(extension: IExtensionDescription, enableProposedApiForAll: boolean, enableProposedApiFor: string | string[]): IExtensionDescription { - if (!isFalsyOrEmpty(product.extensionAllowedProposedApi) + if (isNonEmptyArray(product.extensionAllowedProposedApi) && product.extensionAllowedProposedApi.indexOf(extension.id) >= 0 ) { // fast lane -> proposed api is available to all extensions @@ -870,9 +824,9 @@ export class ExtensionService extends Disposable implements IExtensionService { return result; } - private static _scanInstalledExtensions(windowService: IWindowService, notificationService: INotificationService, environmentService: IEnvironmentService, extensionEnablementService: IExtensionEnablementService, log: ILog): TPromise<{ system: IExtensionDescription[], user: IExtensionDescription[], development: IExtensionDescription[] }> { + private static _scanInstalledExtensions(windowService: IWindowService, notificationService: INotificationService, environmentService: IEnvironmentService, extensionEnablementService: IExtensionEnablementService, log: ILog): Promise<{ system: IExtensionDescription[], user: IExtensionDescription[], development: IExtensionDescription[] }> { - const translationConfig: TPromise = platform.translationsConfigFile + const translationConfig: Promise = platform.translationsConfigFile ? pfs.readFile(platform.translationsConfigFile, 'utf8').then((content) => { try { return JSON.parse(content) as Translations; @@ -882,7 +836,7 @@ export class ExtensionService extends Disposable implements IExtensionService { }, (err) => { return Object.create(null); }) - : TPromise.as(Object.create(null)); + : Promise.resolve(Object.create(null)); return translationConfig.then((translations) => { const version = pkg.version; @@ -899,7 +853,7 @@ export class ExtensionService extends Disposable implements IExtensionService { log ); - let finalBuiltinExtensions: TPromise = TPromise.wrap(builtinExtensions); + let finalBuiltinExtensions: Promise = builtinExtensions; if (devMode) { const builtInExtensionsFilePath = path.normalize(path.join(getPathFromAmdModule(require, ''), '..', 'build', 'builtInExtensions.json')); @@ -911,39 +865,16 @@ export class ExtensionService extends Disposable implements IExtensionService { .then(raw => JSON.parse(raw), () => ({} as any)); const input = new ExtensionScannerInput(version, commit, locale, devMode, getExtraDevSystemExtensionsRoot(), true, false, translations); - const extraBuiltinExtensions = TPromise.join([builtInExtensions, controlFile]) + const extraBuiltinExtensions = Promise.all([builtInExtensions, controlFile]) .then(([builtInExtensions, control]) => new ExtraBuiltInExtensionResolver(builtInExtensions, control)) .then(resolver => ExtensionScanner.scanExtensions(input, log, resolver)); - finalBuiltinExtensions = TPromise.join([builtinExtensions, extraBuiltinExtensions]).then(([builtinExtensions, extraBuiltinExtensions]) => { - let resultMap: { [id: string]: IExtensionDescription; } = Object.create(null); - for (let i = 0, len = builtinExtensions.length; i < len; i++) { - resultMap[builtinExtensions[i].id] = builtinExtensions[i]; - } - // Overwrite with extensions found in extra - for (let i = 0, len = extraBuiltinExtensions.length; i < len; i++) { - resultMap[extraBuiltinExtensions[i].id] = extraBuiltinExtensions[i]; - } - - let resultArr = Object.keys(resultMap).map((id) => resultMap[id]); - resultArr.sort((a, b) => { - const aLastSegment = path.basename(a.extensionLocation.fsPath); - const bLastSegment = path.basename(b.extensionLocation.fsPath); - if (aLastSegment < bLastSegment) { - return -1; - } - if (aLastSegment > bLastSegment) { - return 1; - } - return 0; - }); - return resultArr; - }); + finalBuiltinExtensions = ExtensionScanner.mergeBuiltinExtensions(builtinExtensions, extraBuiltinExtensions); } const userExtensions = ( extensionEnablementService.allUserExtensionsDisabled || !environmentService.extensionsPath - ? TPromise.as([]) + ? Promise.resolve([]) : this._scanExtensionsWithCache( windowService, notificationService, @@ -955,14 +886,14 @@ export class ExtensionService extends Disposable implements IExtensionService { ); // Always load developed extensions while extensions development - let developedExtensions = TPromise.as([]); + let developedExtensions = Promise.resolve([]); if (environmentService.isExtensionDevelopment && environmentService.extensionDevelopmentLocationURI.scheme === Schemas.file) { developedExtensions = ExtensionScanner.scanOneOrMultipleExtensions( new ExtensionScannerInput(version, commit, locale, devMode, fsPath(environmentService.extensionDevelopmentLocationURI), false, true, translations), log ); } - return TPromise.join([finalBuiltinExtensions, userExtensions, developedExtensions]).then((extensionDescriptions: IExtensionDescription[][]) => { + return Promise.all([finalBuiltinExtensions, userExtensions, developedExtensions]).then((extensionDescriptions: IExtensionDescription[][]) => { const system = extensionDescriptions[0]; const user = extensionDescriptions[1]; const development = extensionDescriptions[2]; diff --git a/src/vs/workbench/services/extensions/node/extensionManagementServerService.ts b/src/vs/workbench/services/extensions/node/extensionManagementServerService.ts index 5de0b1608..a04693e8a 100644 --- a/src/vs/workbench/services/extensions/node/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensions/node/extensionManagementServerService.ts @@ -7,6 +7,10 @@ import { localize } from 'vs/nls'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { IExtensionManagementServer, IExtensionManagementServerService, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/node/extensionManagementIpc'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/node/remoteAgentService'; +import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; +import { IChannel } from 'vs/base/parts/ipc/node/ipc'; const localExtensionManagementServerAuthority: string = 'vscode-local'; @@ -14,20 +18,29 @@ export class ExtensionManagementServerService implements IExtensionManagementSer _serviceBrand: any; - readonly extensionManagementServers: IExtensionManagementServer[]; + readonly localExtensionManagementServer: IExtensionManagementServer; + readonly remoteExtensionManagementServer: IExtensionManagementServer | null = null; constructor( - localExtensionManagementService: IExtensionManagementService + localExtensionManagementService: IExtensionManagementService, + @IRemoteAgentService remoteAgentService: IRemoteAgentService ) { - this.extensionManagementServers = [{ extensionManagementService: localExtensionManagementService, authority: localExtensionManagementServerAuthority, label: localize('local', "Local") }]; + this.localExtensionManagementServer = { extensionManagementService: localExtensionManagementService, authority: localExtensionManagementServerAuthority, label: localize('local', "Local") }; + const remoteAgentConnection = remoteAgentService.getConnection(); + if (remoteAgentConnection) { + const extensionManagementService = new ExtensionManagementChannelClient(remoteAgentConnection.getChannel('extensions')); + this.remoteExtensionManagementServer = { authority: remoteAgentConnection.remoteAuthority, extensionManagementService, label: remoteAgentConnection.remoteAuthority }; + } } - getExtensionManagementServer(location: URI): IExtensionManagementServer { - return this.extensionManagementServers[0]; - } - - getLocalExtensionManagementServer(): IExtensionManagementServer { - return this.extensionManagementServers[0]; + getExtensionManagementServer(location: URI): IExtensionManagementServer | null { + if (location.scheme === Schemas.file) { + return this.localExtensionManagementServer; + } + if (location.scheme === REMOTE_HOST_SCHEME) { + return this.remoteExtensionManagementServer; + } + return null; } } @@ -35,20 +48,22 @@ export class SingleServerExtensionManagementServerService implements IExtensionM _serviceBrand: any; - readonly extensionManagementServers: IExtensionManagementServer[]; constructor( - extensionManagementServer: IExtensionManagementServer + private readonly extensionManagementServer: IExtensionManagementServer ) { - this.extensionManagementServers = [extensionManagementServer]; } - getExtensionManagementServer(location: URI): IExtensionManagementServer { + getExtensionManagementServer(location: URI): IExtensionManagementServer | null { const authority = location.scheme === Schemas.file ? localExtensionManagementServerAuthority : location.authority; - return this.extensionManagementServers.filter(server => authority === server.authority)[0]; + return this.extensionManagementServer.authority === authority ? this.extensionManagementServer : null; + } + + get localExtensionManagementServer(): IExtensionManagementServer | null { + return this.extensionManagementServer.authority === localExtensionManagementServerAuthority ? this.extensionManagementServer : null; } - getLocalExtensionManagementServer(): IExtensionManagementServer { - return this.extensionManagementServers[0]; + get remoteExtensionManagementServer(): IExtensionManagementServer | null { + return this.extensionManagementServer.authority !== localExtensionManagementServerAuthority ? this.extensionManagementServer : null; } } \ No newline at end of file diff --git a/src/vs/workbench/services/extensions/node/extensionPoints.ts b/src/vs/workbench/services/extensions/node/extensionPoints.ts index fa805336b..e5813bafa 100644 --- a/src/vs/workbench/services/extensions/node/extensionPoints.ts +++ b/src/vs/workbench/services/extensions/node/extensionPoints.ts @@ -4,13 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { extname, join, normalize } from 'path'; +import * as path from 'path'; import * as semver from 'semver'; import * as json from 'vs/base/common/json'; import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages'; import * as types from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as pfs from 'vs/base/node/pfs'; import { getGalleryExtensionId, getLocalExtensionId, groupByExtension } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { getIdAndVersionFromLocalExtensionId } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; @@ -75,13 +74,13 @@ abstract class ExtensionManifestHandler { this._absoluteFolderPath = absoluteFolderPath; this._isBuiltin = isBuiltin; this._isUnderDevelopment = isUnderDevelopment; - this._absoluteManifestPath = join(absoluteFolderPath, MANIFEST_FILE); + this._absoluteManifestPath = path.join(absoluteFolderPath, MANIFEST_FILE); } } class ExtensionManifestParser extends ExtensionManifestHandler { - public parse(): TPromise { + public parse(): Promise { return pfs.readFile(this._absoluteManifestPath).then((manifestContents) => { try { const manifest = JSON.parse(manifestContents.toString()); @@ -114,7 +113,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler { this._nlsConfig = nlsConfig; } - public replaceNLS(extensionDescription: IExtensionDescription): TPromise { + public replaceNLS(extensionDescription: IExtensionDescription): Promise { interface MessageBag { [key: string]: string; } @@ -136,12 +135,12 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler { }); }; - let extension = extname(this._absoluteManifestPath); + let extension = path.extname(this._absoluteManifestPath); let basename = this._absoluteManifestPath.substr(0, this._absoluteManifestPath.length - extension.length); const translationId = `${extensionDescription.publisher}.${extensionDescription.name}`; let translationPath = this._nlsConfig.translations[translationId]; - let localizedMessages: TPromise; + let localizedMessages: Promise; if (translationPath) { localizedMessages = pfs.readFile(translationPath, 'utf8').then((content) => { let errors: json.ParseError[] = []; @@ -206,7 +205,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler { * Parses original message bundle, returns null if the original message bundle is null. */ private static resolveOriginalMessageBundle(originalMessageBundle: string, errors: json.ParseError[]) { - return new TPromise<{ [key: string]: string; }>((c, e) => { + return new Promise<{ [key: string]: string; }>((c, e) => { if (originalMessageBundle) { pfs.readFile(originalMessageBundle).then(originalBundleContent => { c(json.parse(originalBundleContent.toString(), errors)); @@ -223,8 +222,8 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler { * Finds localized message bundle and the original (unlocalized) one. * If the localized file is not present, returns null for the original and marks original as localized. */ - private static findMessageBundles(nlsConfig: NlsConfiguration, basename: string): TPromise<{ localized: string, original: string }> { - return new TPromise<{ localized: string, original: string }>((c, e) => { + private static findMessageBundles(nlsConfig: NlsConfiguration, basename: string): Promise<{ localized: string, original: string }> { + return new Promise<{ localized: string, original: string }>((c, e) => { function loop(basename: string, locale: string): void { let toCheck = `${basename}.nls.${locale}.json`; pfs.fileExists(toCheck).then(exists => { @@ -342,7 +341,7 @@ class ExtensionManifestValidator extends ExtensionManifestHandler { // main := absolutePath(`main`) if (extensionDescription.main) { - extensionDescription.main = join(this._absoluteFolderPath, extensionDescription.main); + extensionDescription.main = path.join(this._absoluteFolderPath, extensionDescription.main); } extensionDescription.extensionLocation = URI.file(this._absoluteFolderPath); @@ -410,7 +409,7 @@ class ExtensionManifestValidator extends ExtensionManifestHandler { notices.push(nls.localize('extensionDescription.main1', "property `{0}` can be omitted or must be of type `string`", 'main')); return false; } else { - let normalizedAbsolutePath = join(extensionFolderPath, extensionDescription.main); + let normalizedAbsolutePath = path.join(extensionFolderPath, extensionDescription.main); if (normalizedAbsolutePath.indexOf(extensionFolderPath)) { notices.push(nls.localize('extensionDescription.main2', "Expected `main` ({0}) to be included inside extension's folder ({1}). This might make the extension non-portable.", normalizedAbsolutePath, extensionFolderPath)); @@ -485,16 +484,16 @@ export interface IExtensionReference { } export interface IExtensionResolver { - resolveExtensions(): TPromise; + resolveExtensions(): Promise; } class DefaultExtensionResolver implements IExtensionResolver { constructor(private root: string) { } - resolveExtensions(): TPromise { + resolveExtensions(): Promise { return pfs.readDirsInDir(this.root) - .then(folders => folders.map(name => ({ name, path: join(this.root, name) }))); + .then(folders => folders.map(name => ({ name, path: path.join(this.root, name) }))); } } @@ -503,8 +502,8 @@ export class ExtensionScanner { /** * Read the extension defined in `absoluteFolderPath` */ - public static scanExtension(version: string, log: ILog, absoluteFolderPath: string, isBuiltin: boolean, isUnderDevelopment: boolean, nlsConfig: NlsConfiguration): TPromise { - absoluteFolderPath = normalize(absoluteFolderPath); + public static scanExtension(version: string, log: ILog, absoluteFolderPath: string, isBuiltin: boolean, isUnderDevelopment: boolean, nlsConfig: NlsConfiguration): Promise { + absoluteFolderPath = path.normalize(absoluteFolderPath); let parser = new ExtensionManifestParser(version, log, absoluteFolderPath, isBuiltin, isUnderDevelopment); return parser.parse().then((extensionDescription) => { @@ -540,7 +539,7 @@ export class ExtensionScanner { let obsolete: { [folderName: string]: boolean; } = {}; if (!isBuiltin) { try { - const obsoleteFileContents = await pfs.readFile(join(absoluteFolderPath, '.obsolete'), 'utf8'); + const obsoleteFileContents = await pfs.readFile(path.join(absoluteFolderPath, '.obsolete'), 'utf8'); obsolete = JSON.parse(obsoleteFileContents); } catch (err) { // Don't care @@ -571,7 +570,7 @@ export class ExtensionScanner { } const nlsConfig = ExtensionScannerInput.createNLSConfig(input); - let extensionDescriptions = await TPromise.join(refs.map(r => this.scanExtension(input.ourVersion, log, r.path, isBuiltin, isUnderDevelopment, nlsConfig))); + let extensionDescriptions = await Promise.all(refs.map(r => this.scanExtension(input.ourVersion, log, r.path, isBuiltin, isUnderDevelopment, nlsConfig))); extensionDescriptions = extensionDescriptions.filter(item => item !== null && !obsolete[getLocalExtensionId(getGalleryExtensionId(item.publisher, item.name), item.version)]); if (!isBuiltin) { @@ -597,12 +596,12 @@ export class ExtensionScanner { * Combination of scanExtension and scanExtensions: If an extension manifest is found at root, we load just this extension, * otherwise we assume the folder contains multiple extensions. */ - public static scanOneOrMultipleExtensions(input: ExtensionScannerInput, log: ILog): TPromise { + public static scanOneOrMultipleExtensions(input: ExtensionScannerInput, log: ILog): Promise { const absoluteFolderPath = input.absoluteFolderPath; const isBuiltin = input.isBuiltin; const isUnderDevelopment = input.isUnderDevelopment; - return pfs.fileExists(join(absoluteFolderPath, MANIFEST_FILE)).then((exists) => { + return pfs.fileExists(path.join(absoluteFolderPath, MANIFEST_FILE)).then((exists) => { if (exists) { const nlsConfig = ExtensionScannerInput.createNLSConfig(input); return this.scanExtension(input.ourVersion, log, absoluteFolderPath, isBuiltin, isUnderDevelopment, nlsConfig).then((extensionDescription) => { @@ -618,4 +617,31 @@ export class ExtensionScanner { return []; }); } + + public static mergeBuiltinExtensions(builtinExtensions: Promise, extraBuiltinExtensions: Promise): Promise { + return Promise.all([builtinExtensions, extraBuiltinExtensions]).then(([builtinExtensions, extraBuiltinExtensions]) => { + let resultMap: { [id: string]: IExtensionDescription; } = Object.create(null); + for (let i = 0, len = builtinExtensions.length; i < len; i++) { + resultMap[builtinExtensions[i].id] = builtinExtensions[i]; + } + // Overwrite with extensions found in extra + for (let i = 0, len = extraBuiltinExtensions.length; i < len; i++) { + resultMap[extraBuiltinExtensions[i].id] = extraBuiltinExtensions[i]; + } + + let resultArr = Object.keys(resultMap).map((id) => resultMap[id]); + resultArr.sort((a, b) => { + const aLastSegment = path.basename(a.extensionLocation.fsPath); + const bLastSegment = path.basename(b.extensionLocation.fsPath); + if (aLastSegment < bLastSegment) { + return -1; + } + if (aLastSegment > bLastSegment) { + return 1; + } + return 0; + }); + return resultArr; + }); + } } \ No newline at end of file diff --git a/src/vs/workbench/services/extensions/node/lazyPromise.ts b/src/vs/workbench/services/extensions/node/lazyPromise.ts index 45dd6907b..e6b59dc3a 100644 --- a/src/vs/workbench/services/extensions/node/lazyPromise.ts +++ b/src/vs/workbench/services/extensions/node/lazyPromise.ts @@ -4,13 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { onUnexpectedError } from 'vs/base/common/errors'; -import { ErrorCallback, TPromise, ValueCallback } from 'vs/base/common/winjs.base'; export class LazyPromise implements Thenable { - private _actual: TPromise | null; - private _actualOk: ValueCallback | null; - private _actualErr: ErrorCallback | null; + private _actual: Promise | null; + private _actualOk: ((value?: any) => any) | null; + private _actualErr: ((err?: any) => any) | null; private _hasValue: boolean; private _value: any; @@ -28,9 +27,9 @@ export class LazyPromise implements Thenable { this._err = null; } - private _ensureActual(): TPromise { + private _ensureActual(): Promise { if (!this._actual) { - this._actual = new TPromise((c, e) => { + this._actual = new Promise((c, e) => { this._actualOk = c; this._actualErr = e; diff --git a/src/vs/workbench/services/extensions/node/rpcProtocol.ts b/src/vs/workbench/services/extensions/node/rpcProtocol.ts index a4bd18c6f..dba47b265 100644 --- a/src/vs/workbench/services/extensions/node/rpcProtocol.ts +++ b/src/vs/workbench/services/extensions/node/rpcProtocol.ts @@ -12,11 +12,34 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { MarshalledObject } from 'vs/base/common/marshalling'; import { URI } from 'vs/base/common/uri'; import { IURITransformer } from 'vs/base/common/uriIpc'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; import { LazyPromise } from 'vs/workbench/services/extensions/node/lazyPromise'; import { IRPCProtocol, ProxyIdentifier, getStringIdentifierForProxy } from 'vs/workbench/services/extensions/node/proxyIdentifier'; +export interface JSONStringifyReplacer { + (key: string, value: any): any; +} + +function safeStringify(obj: any, replacer: JSONStringifyReplacer | null): string { + try { + return JSON.stringify(obj, <(key: string, value: any) => any>replacer); + } catch (err) { + return 'null'; + } +} + +function createURIReplacer(transformer: IURITransformer | null): JSONStringifyReplacer | null { + if (!transformer) { + return null; + } + return (key: string, value: any): any => { + if (value && value.$mid === 1) { + return transformer.transformOutgoing(value); + } + return value; + }; +} + function _transformOutgoingURIs(obj: any, transformer: IURITransformer, depth: number): any { if (!obj || depth > 200) { @@ -42,7 +65,7 @@ function _transformOutgoingURIs(obj: any, transformer: IURITransformer, depth: n return null; } -export function transformOutgoingURIs(obj: any, transformer: IURITransformer): any { +export function transformOutgoingURIs(obj: T, transformer: IURITransformer): T { const result = _transformOutgoingURIs(obj, transformer, 0); if (result === null) { // no change @@ -77,7 +100,7 @@ function _transformIncomingURIs(obj: any, transformer: IURITransformer, depth: n return null; } -function transformIncomingURIs(obj: any, transformer: IURITransformer): any { +function transformIncomingURIs(obj: T, transformer: IURITransformer): T { const result = _transformIncomingURIs(obj, transformer, 0); if (result === null) { // no change @@ -105,7 +128,7 @@ const noop = () => { }; export class RPCProtocol extends Disposable implements IRPCProtocol { - private static UNRESPONSIVE_TIME = 10 * 1000; // 10s + private static UNRESPONSIVE_TIME = 3 * 1000; // 3s private readonly _onDidChangeResponsiveState: Emitter = this._register(new Emitter()); public readonly onDidChangeResponsiveState: Event = this._onDidChangeResponsiveState.event; @@ -113,6 +136,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { private readonly _protocol: IMessagePassingProtocol; private readonly _logger: IRPCProtocolLogger | null; private readonly _uriTransformer: IURITransformer | null; + private readonly _uriReplacer: JSONStringifyReplacer | null; private _isDisposed: boolean; private readonly _locals: any[]; private readonly _proxies: any[]; @@ -129,6 +153,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { this._protocol = protocol; this._logger = logger; this._uriTransformer = transformer; + this._uriReplacer = createURIReplacer(this._uriTransformer); this._isDisposed = false; this._locals = []; this._proxies = []; @@ -353,10 +378,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { promise.then((r) => { delete this._cancelInvokedHandlers[callId]; - if (this._uriTransformer) { - r = transformOutgoingURIs(r, this._uriTransformer); - } - const msg = MessageIO.serializeReplyOK(req, r); + const msg = MessageIO.serializeReplyOK(req, r, this._uriReplacer); if (this._logger) { this._logger.logOutgoing(msg.byteLength, req, RequestInitiator.OtherSide, `reply:`, r); } @@ -421,9 +443,9 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { private _invokeHandler(rpcId: number, methodName: string, args: any[]): Thenable { try { - return TPromise.as(this._doInvokeHandler(rpcId, methodName, args)); + return Promise.resolve(this._doInvokeHandler(rpcId, methodName, args)); } catch (err) { - return TPromise.wrapError(err); + return Promise.reject(err); } } @@ -441,7 +463,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { private _remoteCall(rpcId: number, methodName: string, args: any[]): Thenable { if (this._isDisposed) { - return TPromise.wrapError(errors.canceled()); + return Promise.reject(errors.canceled()); } let cancellationToken: CancellationToken | null = null; if (args.length > 0 && CancellationToken.isCancellationToken(args[args.length - 1])) { @@ -450,7 +472,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { if (cancellationToken && cancellationToken.isCancellationRequested) { // No need to do anything... - return TPromise.wrapError(errors.canceled()); + return Promise.reject(errors.canceled()); } const req = ++this._lastMessageId; @@ -469,10 +491,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { this._pendingRPCReplies[callId] = result; this._onWillSendRequest(req); - if (this._uriTransformer) { - args = transformOutgoingURIs(args, this._uriTransformer); - } - const msg = MessageIO.serializeRequest(req, rpcId, methodName, args, !!cancellationToken); + const msg = MessageIO.serializeRequest(req, rpcId, methodName, args, !!cancellationToken, this._uriReplacer); if (this._logger) { this._logger.logOutgoing(msg.byteLength, req, RequestInitiator.LocalSide, `request: ${getStringIdentifierForProxy(rpcId)}.${methodName}(`, args); } @@ -630,7 +649,7 @@ class MessageIO { return false; } - public static serializeRequest(req: number, rpcId: number, method: string, args: any[], usesCancellationToken: boolean): Buffer { + public static serializeRequest(req: number, rpcId: number, method: string, args: any[], usesCancellationToken: boolean, replacer: JSONStringifyReplacer | null): Buffer { if (this._arrayContainsBuffer(args)) { let massagedArgs: (string | Buffer)[] = new Array(args.length); let argsLengths: number[] = new Array(args.length); @@ -640,13 +659,13 @@ class MessageIO { massagedArgs[i] = arg; argsLengths[i] = arg.byteLength; } else { - massagedArgs[i] = safeStringify(arg); + massagedArgs[i] = safeStringify(arg, replacer); argsLengths[i] = Buffer.byteLength(massagedArgs[i], 'utf8'); } } return this._requestMixedArgs(req, rpcId, method, massagedArgs, argsLengths, usesCancellationToken); } - return this._requestJSONArgs(req, rpcId, method, safeStringify(args), usesCancellationToken); + return this._requestJSONArgs(req, rpcId, method, safeStringify(args, replacer), usesCancellationToken); } private static _requestJSONArgs(req: number, rpcId: number, method: string, args: string, usesCancellationToken: boolean): Buffer { @@ -719,14 +738,14 @@ class MessageIO { return MessageBuffer.alloc(MessageType.Cancel, req, 0).buffer; } - public static serializeReplyOK(req: number, res: any): Buffer { + public static serializeReplyOK(req: number, res: any, replacer: JSONStringifyReplacer | null): Buffer { if (typeof res === 'undefined') { return this._serializeReplyOKEmpty(req); } if (Buffer.isBuffer(res)) { return this._serializeReplyOKBuffer(req, res); } - return this._serializeReplyOKJSON(req, safeStringify(res)); + return this._serializeReplyOKJSON(req, safeStringify(res, replacer)); } private static _serializeReplyOKEmpty(req: number): Buffer { @@ -772,7 +791,7 @@ class MessageIO { } private static _serializeReplyErrEror(req: number, _err: Error): Buffer { - const err = safeStringify(errors.transformErrorForSerialization(_err)); + const err = safeStringify(errors.transformErrorForSerialization(_err), null); const errByteLength = Buffer.byteLength(err, 'utf8'); let len = 0; @@ -793,14 +812,6 @@ class MessageIO { } } -function safeStringify(obj: any): string { - try { - return JSON.stringify(obj); - } catch (err) { - return 'null'; - } -} - const enum MessageType { RequestJSONArgs = 1, RequestJSONArgsWithCancellation = 2, diff --git a/src/vs/workbench/services/extensions/test/node/rpcProtocol.test.ts b/src/vs/workbench/services/extensions/test/node/rpcProtocol.test.ts index 68fb156e1..ff8bf3918 100644 --- a/src/vs/workbench/services/extensions/test/node/rpcProtocol.test.ts +++ b/src/vs/workbench/services/extensions/test/node/rpcProtocol.test.ts @@ -6,7 +6,6 @@ import * as assert from 'assert'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier'; import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol'; @@ -34,7 +33,7 @@ suite('RPCProtocol', () => { let bProxy: BClass; class BClass { $m(a1: any, a2: any): Thenable { - return TPromise.as(delegate.call(null, a1, a2)); + return Promise.resolve(delegate.call(null, a1, a2)); } } @@ -131,7 +130,7 @@ suite('RPCProtocol', () => { test('cancelling a call via CancellationToken quickly', function (done) { // this is an implementation which, when cancellation is triggered, will return 7 delegate = (a1: number, token: CancellationToken) => { - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { token.onCancellationRequested((e) => { resolve(7); }); @@ -164,7 +163,7 @@ suite('RPCProtocol', () => { test('error promise', function (done) { delegate = (a1: number, a2: number) => { - return TPromise.wrapError(undefined); + return Promise.reject(undefined); }; bProxy.$m(4, 1).then((res) => { assert.fail('unexpected'); diff --git a/src/vs/workbench/services/files/electron-browser/encoding.ts b/src/vs/workbench/services/files/electron-browser/encoding.ts index ac72e28d5..0f9b92e4f 100644 --- a/src/vs/workbench/services/files/electron-browser/encoding.ts +++ b/src/vs/workbench/services/files/electron-browser/encoding.ts @@ -8,11 +8,12 @@ import * as encoding from 'vs/base/node/encoding'; import { URI as uri } from 'vs/base/common/uri'; import { IResolveContentOptions, isParent, IResourceEncodings } from 'vs/platform/files/common/files'; import { isLinux } from 'vs/base/common/platform'; -import { join, extname } from 'path'; +import { extname } from 'path'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { Disposable } from 'vs/base/common/lifecycle'; +import { joinPath } from 'vs/base/common/resources'; export interface IEncodingOverride { parent?: uri; @@ -49,7 +50,7 @@ export class ResourceEncodings extends Disposable implements IResourceEncodings } getReadEncoding(resource: uri, options: IResolveContentOptions, detected: encoding.IDetectedEncodingResult): string { - let preferredEncoding: string; + let preferredEncoding: string | undefined; // Encoding passed in as option if (options && options.encoding) { @@ -111,13 +112,13 @@ export class ResourceEncodings extends Disposable implements IResourceEncodings // Folder Settings this.contextService.getWorkspace().folders.forEach(folder => { - encodingOverride.push({ parent: uri.file(join(folder.uri.fsPath, '.vscode')), encoding: encoding.UTF8 }); + encodingOverride.push({ parent: joinPath(folder.uri, '.vscode'), encoding: encoding.UTF8 }); }); return encodingOverride; } - private getEncodingOverride(resource: uri): string { + private getEncodingOverride(resource: uri): string | null { if (resource && this.encodingOverride && this.encodingOverride.length) { for (let i = 0; i < this.encodingOverride.length; i++) { const override = this.encodingOverride[i]; diff --git a/src/vs/workbench/services/files/electron-browser/fileService.ts b/src/vs/workbench/services/files/electron-browser/fileService.ts index 3a4184018..0ee6ea2ce 100644 --- a/src/vs/workbench/services/files/electron-browser/fileService.ts +++ b/src/vs/workbench/services/files/electron-browser/fileService.ts @@ -16,7 +16,7 @@ import * as arrays from 'vs/base/common/arrays'; import { TPromise } from 'vs/base/common/winjs.base'; import * as objects from 'vs/base/common/objects'; import * as extfs from 'vs/base/node/extfs'; -import { nfcall, ThrottledDelayer } from 'vs/base/common/async'; +import { nfcall, ThrottledDelayer, timeout } from 'vs/base/common/async'; import { URI as uri } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; @@ -593,24 +593,28 @@ export class FileService extends Disposable implements IFileService { else { // 4.) truncate - let retryFromFailingTruncate = true; return pfs.truncate(absolutePath, 0).then(() => { - retryFromFailingTruncate = false; // 5.) set contents (with r+ mode) and resolve - return this.doSetContentsAndResolve(resource, absolutePath, value, addBom, encodingToWrite, { flag: 'r+' }); - }, error => { - if (retryFromFailingTruncate) { + return this.doSetContentsAndResolve(resource, absolutePath, value, addBom, encodingToWrite, { flag: 'r+' }).then(null, error => { if (this.environmentService.verbose) { - console.error(`Truncate failed (${error}), falling back to normal save`); + console.error(`Truncate succeeded, but save failed (${error}), retrying after 100ms`); } - // we heard from users that fs.truncate() fails (https://github.com/Microsoft/vscode/issues/59561) - // in that case we simply save the file without truncating first (same as macOS and Linux) - return this.doSetContentsAndResolve(resource, absolutePath, value, addBom, encodingToWrite); + // We heard from one user that fs.truncate() succeeds, but the save fails (https://github.com/Microsoft/vscode/issues/61310) + // In that case, the file is now entirely empty and the contents are gone. This can happen if an external file watcher is + // installed that reacts on the truncate and keeps the file busy right after. Our workaround is to retry to save after a + // short timeout, assuming that the file is free to write then. + return timeout(100).then(() => this.doSetContentsAndResolve(resource, absolutePath, value, addBom, encodingToWrite, { flag: 'r+' })); + }); + }, error => { + if (this.environmentService.verbose) { + console.error(`Truncate failed (${error}), falling back to normal save`); } - return TPromise.wrapError(error); + // we heard from users that fs.truncate() fails (https://github.com/Microsoft/vscode/issues/59561) + // in that case we simply save the file without truncating first (same as macOS and Linux) + return this.doSetContentsAndResolve(resource, absolutePath, value, addBom, encodingToWrite); }); } }); @@ -747,6 +751,11 @@ export class FileService extends Disposable implements IFileService { }); } + readFolder(resource: uri): TPromise { + const absolutePath = this.toAbsolutePath(resource); + return pfs.readdir(absolutePath); + } + createFolder(resource: uri): TPromise { // 1.) Create folder diff --git a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts index 3738495c3..9b7e53595 100644 --- a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts +++ b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { flatten, isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { flatten, isNonEmptyArray } from 'vs/base/common/arrays'; import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { TernarySearchTree } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; @@ -76,7 +76,7 @@ export function toDeepIFileStat(provider: IFileSystemProvider, tuple: [URI, ISta const trie = TernarySearchTree.forPaths(); trie.set(tuple[0].toString(), true); - if (!isFalsyOrEmpty(to)) { + if (isNonEmptyArray(to)) { to.forEach(uri => trie.set(uri.toString(), true)); } @@ -520,6 +520,16 @@ export class RemoteFileService extends FileService { } } + readFolder(resource: URI): TPromise { + if (resource.scheme === Schemas.file) { + return super.readFolder(resource); + } else { + return this._withProvider(resource).then(provider => { + return provider.readdir(resource); + }).then(list => list.map(l => l[0])); + } + } + createFolder(resource: URI): TPromise { if (resource.scheme === Schemas.file) { return super.createFolder(resource); diff --git a/src/vs/workbench/services/files/node/watcher/nsfw/watcherApp.ts b/src/vs/workbench/services/files/node/watcher/nsfw/watcherApp.ts index b16b7d8dc..49c892fb5 100644 --- a/src/vs/workbench/services/files/node/watcher/nsfw/watcherApp.ts +++ b/src/vs/workbench/services/files/node/watcher/nsfw/watcherApp.ts @@ -7,7 +7,7 @@ import { Server } from 'vs/base/parts/ipc/node/ipc.cp'; import { WatcherChannel } from 'vs/workbench/services/files/node/watcher/nsfw/watcherIpc'; import { NsfwWatcherService } from 'vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService'; -const server = new Server(); +const server = new Server('watcher'); const service = new NsfwWatcherService(); const channel = new WatcherChannel(service); server.registerChannel('watcher', channel); \ No newline at end of file diff --git a/src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts b/src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts index 83ea7009d..e57440b63 100644 --- a/src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts +++ b/src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts @@ -4,45 +4,37 @@ *--------------------------------------------------------------------------------------------*/ import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; import { IWatcherRequest, IWatcherService, IWatcherOptions, IWatchError } from './watcher'; import { Event } from 'vs/base/common/event'; import { IRawFileChange } from 'vs/workbench/services/files/node/watcher/common'; -export interface IWatcherChannel extends IChannel { - listen(event: 'watch', verboseLogging: boolean): Event; - listen(event: string, arg?: any): Event; - - call(command: 'setRoots', request: IWatcherRequest[]): TPromise; - call(command: 'setVerboseLogging', enable: boolean): TPromise; - call(command: 'stop'): TPromise; - call(command: string, arg?: any): TPromise; -} - -export class WatcherChannel implements IWatcherChannel { +export class WatcherChannel implements IServerChannel { constructor(private service: IWatcherService) { } - listen(event: string, arg?: any): Event { + listen(_, event: string, arg?: any): Event { switch (event) { case 'watch': return this.service.watch(arg); } - throw new Error('No events'); + + throw new Error(`Event not found: ${event}`); } - call(command: string, arg?: any): TPromise { + call(_, command: string, arg?: any): TPromise { switch (command) { case 'setRoots': return this.service.setRoots(arg); case 'setVerboseLogging': return this.service.setVerboseLogging(arg); case 'stop': return this.service.stop(); } - return undefined; + + throw new Error(`Call not found: ${command}`); } } export class WatcherChannelClient implements IWatcherService { - constructor(private channel: IWatcherChannel) { } + constructor(private channel: IChannel) { } watch(options: IWatcherOptions): Event { return this.channel.listen('watch', options); diff --git a/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts b/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts index f41fa26c9..bd201a386 100644 --- a/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts @@ -6,7 +6,7 @@ import { getNextTickChannel } from 'vs/base/parts/ipc/node/ipc'; import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; import { toFileChangesEvent, IRawFileChange } from 'vs/workbench/services/files/node/watcher/common'; -import { IWatcherChannel, WatcherChannelClient } from 'vs/workbench/services/files/node/watcher/nsfw/watcherIpc'; +import { WatcherChannelClient } from 'vs/workbench/services/files/node/watcher/nsfw/watcherIpc'; import { FileChangesEvent, IFilesConfiguration } from 'vs/platform/files/common/files'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -65,7 +65,7 @@ export class FileWatcher { }, null, this.toDispose); // Initialize watcher - const channel = getNextTickChannel(client.getChannel('watcher')); + const channel = getNextTickChannel(client.getChannel('watcher')); this.service = new WatcherChannelClient(channel); const options = { verboseLogging: this.verboseLogging }; diff --git a/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts b/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts index 3fdca2266..6e1d94c64 100644 --- a/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts @@ -43,7 +43,7 @@ export class ChokidarWatcherService implements IWatcherService { private _watchers: { [watchPath: string]: IWatcher }; private _watcherCount: number; - private _pollingInterval: number; + private _pollingInterval?: number; private _verboseLogging: boolean; private spamCheckStartTime: number; @@ -63,7 +63,7 @@ export class ChokidarWatcherService implements IWatcherService { public setVerboseLogging(enabled: boolean): TPromise { this._verboseLogging = enabled; - return TPromise.as(null); + return TPromise.as(void 0); } public setRoots(requests: IWatcherRequest[]): TPromise { @@ -93,7 +93,7 @@ export class ChokidarWatcherService implements IWatcherService { } this._watchers = watchers; - return TPromise.as(null); + return TPromise.as(void 0); } // for test purposes @@ -134,7 +134,7 @@ export class ChokidarWatcherService implements IWatcherService { console.warn(`Watcher basePath does not match version on disk and was corrected (original: ${basePath}, real: ${realBasePath})`); } - let chokidarWatcher = chokidar.watch(realBasePath, watcherOpts); + let chokidarWatcher: chokidar.FSWatcher | null = chokidar.watch(realBasePath, watcherOpts); this._watcherCount++; // Detect if for some reason the native watcher library fails to load @@ -143,7 +143,7 @@ export class ChokidarWatcherService implements IWatcherService { } let undeliveredFileEvents: watcherCommon.IRawFileChange[] = []; - let fileEventDelayer = new ThrottledDelayer(ChokidarWatcherService.FS_EVENT_DELAY); + let fileEventDelayer: ThrottledDelayer | null = new ThrottledDelayer(ChokidarWatcherService.FS_EVENT_DELAY); const watcher: IWatcher = { requests, @@ -228,24 +228,26 @@ export class ChokidarWatcherService implements IWatcherService { // Add to buffer undeliveredFileEvents.push(event); - // Delay and send buffer - fileEventDelayer.trigger(() => { - const events = undeliveredFileEvents; - undeliveredFileEvents = []; + if (fileEventDelayer) { + // Delay and send buffer + fileEventDelayer.trigger(() => { + const events = undeliveredFileEvents; + undeliveredFileEvents = []; - // Broadcast to clients normalized - const res = watcherCommon.normalize(events); - this._onWatchEvent.fire(res); + // Broadcast to clients normalized + const res = watcherCommon.normalize(events); + this._onWatchEvent.fire(res); - // Logging - if (this._verboseLogging) { - res.forEach(r => { - console.log(` >> normalized ${r.type === FileChangeType.ADDED ? '[ADDED]' : r.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]'} ${r.path}`); - }); - } + // Logging + if (this._verboseLogging) { + res.forEach(r => { + console.log(` >> normalized ${r.type === FileChangeType.ADDED ? '[ADDED]' : r.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]'} ${r.path}`); + }); + } - return TPromise.as(null); - }); + return TPromise.as(void 0); + }); + } }); chokidarWatcher.on('error', (error: Error) => { diff --git a/src/vs/workbench/services/files/node/watcher/unix/watcherApp.ts b/src/vs/workbench/services/files/node/watcher/unix/watcherApp.ts index b79e98705..01473fb5c 100644 --- a/src/vs/workbench/services/files/node/watcher/unix/watcherApp.ts +++ b/src/vs/workbench/services/files/node/watcher/unix/watcherApp.ts @@ -7,7 +7,7 @@ import { Server } from 'vs/base/parts/ipc/node/ipc.cp'; import { WatcherChannel } from 'vs/workbench/services/files/node/watcher/unix/watcherIpc'; import { ChokidarWatcherService } from 'vs/workbench/services/files/node/watcher/unix/chokidarWatcherService'; -const server = new Server(); +const server = new Server('watcher'); const service = new ChokidarWatcherService(); const channel = new WatcherChannel(service); server.registerChannel('watcher', channel); \ No newline at end of file diff --git a/src/vs/workbench/services/files/node/watcher/unix/watcherIpc.ts b/src/vs/workbench/services/files/node/watcher/unix/watcherIpc.ts index 83ea7009d..e57440b63 100644 --- a/src/vs/workbench/services/files/node/watcher/unix/watcherIpc.ts +++ b/src/vs/workbench/services/files/node/watcher/unix/watcherIpc.ts @@ -4,45 +4,37 @@ *--------------------------------------------------------------------------------------------*/ import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; import { IWatcherRequest, IWatcherService, IWatcherOptions, IWatchError } from './watcher'; import { Event } from 'vs/base/common/event'; import { IRawFileChange } from 'vs/workbench/services/files/node/watcher/common'; -export interface IWatcherChannel extends IChannel { - listen(event: 'watch', verboseLogging: boolean): Event; - listen(event: string, arg?: any): Event; - - call(command: 'setRoots', request: IWatcherRequest[]): TPromise; - call(command: 'setVerboseLogging', enable: boolean): TPromise; - call(command: 'stop'): TPromise; - call(command: string, arg?: any): TPromise; -} - -export class WatcherChannel implements IWatcherChannel { +export class WatcherChannel implements IServerChannel { constructor(private service: IWatcherService) { } - listen(event: string, arg?: any): Event { + listen(_, event: string, arg?: any): Event { switch (event) { case 'watch': return this.service.watch(arg); } - throw new Error('No events'); + + throw new Error(`Event not found: ${event}`); } - call(command: string, arg?: any): TPromise { + call(_, command: string, arg?: any): TPromise { switch (command) { case 'setRoots': return this.service.setRoots(arg); case 'setVerboseLogging': return this.service.setVerboseLogging(arg); case 'stop': return this.service.stop(); } - return undefined; + + throw new Error(`Call not found: ${command}`); } } export class WatcherChannelClient implements IWatcherService { - constructor(private channel: IWatcherChannel) { } + constructor(private channel: IChannel) { } watch(options: IWatcherOptions): Event { return this.channel.listen('watch', options); diff --git a/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts b/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts index d06d203c7..7c52f2165 100644 --- a/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts @@ -6,7 +6,7 @@ import { getNextTickChannel } from 'vs/base/parts/ipc/node/ipc'; import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; import { toFileChangesEvent, IRawFileChange } from 'vs/workbench/services/files/node/watcher/common'; -import { IWatcherChannel, WatcherChannelClient } from 'vs/workbench/services/files/node/watcher/unix/watcherIpc'; +import { WatcherChannelClient } from 'vs/workbench/services/files/node/watcher/unix/watcherIpc'; import { FileChangesEvent, IFilesConfiguration } from 'vs/platform/files/common/files'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -67,7 +67,7 @@ export class FileWatcher { } }, null, this.toDispose); - const channel = getNextTickChannel(client.getChannel('watcher')); + const channel = getNextTickChannel(client.getChannel('watcher')); this.service = new WatcherChannelClient(channel); const options = { verboseLogging: this.verboseLogging }; diff --git a/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts b/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts index a4105f153..a0a07804c 100644 --- a/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts +++ b/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts @@ -3,8 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IssueReporterStyles, IIssueService, IssueReporterData, ProcessExplorerData } from 'vs/platform/issue/common/issue'; -import { TPromise } from 'vs/base/common/winjs.base'; +import { IssueReporterStyles, IIssueService, IssueReporterData, ProcessExplorerData, IssueReporterExtensionData } from 'vs/platform/issue/common/issue'; import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; import { textLinkForeground, inputBackground, inputBorder, inputForeground, buttonBackground, buttonHoverBackground, buttonForeground, inputValidationErrorBorder, foreground, inputActiveOptionBorder, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, editorBackground, editorForeground, listHoverBackground, listHoverForeground, listHighlightForeground, textLinkActiveForeground } from 'vs/platform/theme/common/colorRegistry'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; @@ -26,15 +25,31 @@ export class WorkbenchIssueService implements IWorkbenchIssueService { ) { } - openReporter(dataOverrides: Partial = {}): TPromise { + openReporter(dataOverrides: Partial = {}): Promise { return this.extensionManagementService.getInstalled(LocalExtensionType.User).then(extensions => { const enabledExtensions = extensions.filter(extension => this.extensionEnablementService.isEnabled(extension)); + const extensionData: IssueReporterExtensionData[] = enabledExtensions.map(extension => { + const { manifest } = extension; + const manifestKeys = manifest.contributes ? Object.keys(manifest.contributes) : []; + const isTheme = !manifest.activationEvents && manifestKeys.length === 1 && manifestKeys[0] === 'themes'; + + return { + name: manifest.name, + publisher: manifest.publisher, + version: manifest.version, + repositoryUrl: manifest.repository && manifest.repository.url, + bugsUrl: manifest.bugs && manifest.bugs.url, + displayName: manifest.displayName, + id: extension.identifier.id, + isTheme: isTheme + }; + }); const theme = this.themeService.getTheme(); const issueReporterData: IssueReporterData = assign( { styles: getIssueReporterStyles(theme), zoomLevel: webFrame.getZoomLevel(), - enabledExtensions + enabledExtensions: extensionData }, dataOverrides); @@ -42,7 +57,7 @@ export class WorkbenchIssueService implements IWorkbenchIssueService { }); } - openProcessExplorer(): TPromise { + openProcessExplorer(): Thenable { const theme = this.themeService.getTheme(); const data: ProcessExplorerData = { pid: this.windowService.getConfiguration().mainPid, diff --git a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts index d72765837..b7e3ec193 100644 --- a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts +++ b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts @@ -11,7 +11,6 @@ import { Edit } from 'vs/base/common/jsonFormatter'; import { Disposable, IReference } from 'vs/base/common/lifecycle'; import { isArray } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; @@ -33,11 +32,11 @@ export interface IKeybindingEditingService { _serviceBrand: ServiceIdentifier; - editKeybinding(key: string, keybindingItem: ResolvedKeybindingItem): TPromise; + editKeybinding(key: string, keybindingItem: ResolvedKeybindingItem): Thenable; - removeKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise; + removeKeybinding(keybindingItem: ResolvedKeybindingItem): Thenable; - resetKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise; + resetKeybinding(keybindingItem: ResolvedKeybindingItem): Thenable; } export class KeybindingsEditingService extends Disposable implements IKeybindingEditingService { @@ -58,19 +57,19 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding this.queue = new Queue(); } - editKeybinding(key: string, keybindingItem: ResolvedKeybindingItem): TPromise { + editKeybinding(key: string, keybindingItem: ResolvedKeybindingItem): Thenable { return this.queue.queue(() => this.doEditKeybinding(key, keybindingItem)); // queue up writes to prevent race conditions } - resetKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise { + resetKeybinding(keybindingItem: ResolvedKeybindingItem): Thenable { return this.queue.queue(() => this.doResetKeybinding(keybindingItem)); // queue up writes to prevent race conditions } - removeKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise { + removeKeybinding(keybindingItem: ResolvedKeybindingItem): Thenable { return this.queue.queue(() => this.doRemoveKeybinding(keybindingItem)); // queue up writes to prevent race conditions } - private doEditKeybinding(key: string, keybindingItem: ResolvedKeybindingItem): TPromise { + private doEditKeybinding(key: string, keybindingItem: ResolvedKeybindingItem): Thenable { return this.resolveAndValidate() .then(reference => { const model = reference.object.textEditorModel; @@ -84,7 +83,7 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding }); } - private doRemoveKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise { + private doRemoveKeybinding(keybindingItem: ResolvedKeybindingItem): Thenable { return this.resolveAndValidate() .then(reference => { const model = reference.object.textEditorModel; @@ -97,7 +96,7 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding }); } - private doResetKeybinding(keybindingItem: ResolvedKeybindingItem): TPromise { + private doResetKeybinding(keybindingItem: ResolvedKeybindingItem): Thenable { return this.resolveAndValidate() .then(reference => { const model = reference.object.textEditorModel; @@ -109,7 +108,7 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding }); } - private save(): TPromise { + private save(): Thenable { return this.textFileService.save(this.resource); } @@ -198,20 +197,20 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding } - private resolveModelReference(): TPromise> { + private resolveModelReference(): Thenable> { return this.fileService.existsFile(this.resource) .then(exists => { const EOL = this.configurationService.getValue('files', { overrideIdentifier: 'json' })['eol']; - const result = exists ? TPromise.as(null) : this.fileService.updateContent(this.resource, this.getEmptyContent(EOL), { encoding: 'utf8' }); + const result: Thenable = exists ? Promise.resolve(null) : this.fileService.updateContent(this.resource, this.getEmptyContent(EOL), { encoding: 'utf8' }); return result.then(() => this.textModelResolverService.createModelReference(this.resource)); }); } - private resolveAndValidate(): TPromise> { + private resolveAndValidate(): Thenable> { // Target cannot be dirty if not writing into buffer if (this.textFileService.isDirty(this.resource)) { - return TPromise.wrapError>(new Error(localize('errorKeybindingsFileDirty', "Unable to write because the keybindings configuration file is dirty. Please save it first and then try again."))); + return Promise.reject(new Error(localize('errorKeybindingsFileDirty', "Unable to write because the keybindings configuration file is dirty. Please save it first and then try again."))); } return this.resolveModelReference() @@ -221,11 +220,11 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding if (model.getValue()) { const parsed = this.parse(model); if (parsed.parseErrors.length) { - return TPromise.wrapError>(new Error(localize('parseErrors', "Unable to write to the keybindings configuration file. Please open it to correct errors/warnings in the file and try again."))); + return Promise.reject(new Error(localize('parseErrors', "Unable to write to the keybindings configuration file. Please open it to correct errors/warnings in the file and try again."))); } if (parsed.result) { if (!isArray(parsed.result)) { - return TPromise.wrapError>(new Error(localize('errorInvalidConfiguration', "Unable to write to the keybindings configuration file. It has an object which is not of type Array. Please open the file to clean up and try again."))); + return Promise.reject(new Error(localize('errorInvalidConfiguration', "Unable to write to the keybindings configuration file. It has an object which is not of type Array. Please open the file to clean up and try again."))); } } else { const content = EOL + '[]'; @@ -246,6 +245,6 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding } private getEmptyContent(EOL: string): string { - return '// ' + localize('emptyKeybindingsHeader', "Place your key bindings in this file to overwrite the defaults") + EOL + '[]'; + return '// ' + localize('emptyKeybindingsHeader', "Place your key bindings in this file to override the defaults") + EOL + '[]'; } } 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 83b519f99..7ea35c141 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 @@ -11,7 +11,6 @@ import * as json from 'vs/base/common/json'; import { ChordKeybinding, KeyCode, SimpleKeybinding } from 'vs/base/common/keyCodes'; import { OS } from 'vs/base/common/platform'; import * as uuid from 'vs/base/common/uuid'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as extfs from 'vs/base/node/extfs'; import { mkdirp } from 'vs/base/node/pfs'; import { IModeService } from 'vs/editor/common/services/modeService'; @@ -104,13 +103,13 @@ suite('KeybindingsEditing', () => { }); }); - function setUpWorkspace(): TPromise { + async function setUpWorkspace(): Promise { testDir = path.join(os.tmpdir(), 'vsctests', uuid.generateUuid()); - return mkdirp(testDir, 493); + return await mkdirp(testDir, 493); } teardown(() => { - return new TPromise((c, e) => { + return new Promise((c, e) => { if (testDir) { extfs.del(testDir, os.tmpdir(), () => c(null), () => c(null)); } else { @@ -222,7 +221,7 @@ suite('KeybindingsEditing', () => { .then(() => assert.deepEqual(getUserKeybindings(), [])); }); - test('reset mulitple removed keybindings', () => { + test('reset multiple removed keybindings', () => { writeToKeybindingsFile({ key: 'alt+c', command: '-b' }); writeToKeybindingsFile({ key: 'alt+shift+c', command: '-b' }); writeToKeybindingsFile({ key: 'escape', command: '-b' }); diff --git a/src/vs/workbench/services/keybinding/test/keyboardMapperTestUtils.ts b/src/vs/workbench/services/keybinding/test/keyboardMapperTestUtils.ts index b81564797..a49ecccfd 100644 --- a/src/vs/workbench/services/keybinding/test/keyboardMapperTestUtils.ts +++ b/src/vs/workbench/services/keybinding/test/keyboardMapperTestUtils.ts @@ -8,19 +8,18 @@ import * as path from 'path'; import { getPathFromAmdModule } from 'vs/base/common/amd'; import { Keybinding, ResolvedKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes'; import { ScanCodeBinding } from 'vs/base/common/scanCode'; -import { TPromise } from 'vs/base/common/winjs.base'; import { readFile, writeFile } from 'vs/base/node/pfs'; import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; export interface IResolvedKeybinding { - label: string; - ariaLabel: string; - electronAccelerator: string; - userSettingsLabel: string; + label: string | null; + ariaLabel: string | null; + electronAccelerator: string | null; + userSettingsLabel: string | null; isWYSIWYG: boolean; isChord: boolean; - dispatchParts: [string, string]; + dispatchParts: [string | null, string | null]; } function toIResolvedKeybinding(kb: ResolvedKeybinding): IResolvedKeybinding { @@ -50,7 +49,7 @@ export function assertResolveUserBinding(mapper: IKeyboardMapper, firstPart: Sim assert.deepEqual(actual, expected); } -export function readRawMapping(file: string): TPromise { +export function readRawMapping(file: string): Promise { return readFile(getPathFromAmdModule(require, `vs/workbench/services/keybinding/test/${file}.js`)).then((buff) => { let contents = buff.toString(); let func = new Function('define', contents); @@ -58,11 +57,11 @@ export function readRawMapping(file: string): TPromise { func(function (value: T) { rawMappings = value; }); - return rawMappings; + return rawMappings!; }); } -export function assertMapping(writeFileIfDifferent: boolean, mapper: IKeyboardMapper, file: string): TPromise { +export function assertMapping(writeFileIfDifferent: boolean, mapper: IKeyboardMapper, file: string): Promise { const filePath = path.normalize(getPathFromAmdModule(require, `vs/workbench/services/keybinding/test/${file}`)); return readFile(filePath).then((buff) => { diff --git a/src/vs/workbench/services/keybinding/test/macLinuxKeyboardMapper.test.ts b/src/vs/workbench/services/keybinding/test/macLinuxKeyboardMapper.test.ts index fb6a602a4..0e0c5c7be 100644 --- a/src/vs/workbench/services/keybinding/test/macLinuxKeyboardMapper.test.ts +++ b/src/vs/workbench/services/keybinding/test/macLinuxKeyboardMapper.test.ts @@ -8,14 +8,13 @@ import { KeyChord, KeyCode, KeyMod, SimpleKeybinding, createKeybinding } from 'v import { UserSettingsLabelProvider } from 'vs/base/common/keybindingLabels'; import { OperatingSystem } from 'vs/base/common/platform'; import { ScanCode, ScanCodeBinding, ScanCodeUtils } from 'vs/base/common/scanCode'; -import { TPromise } from 'vs/base/common/winjs.base'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; import { IMacLinuxKeyboardMapping, MacLinuxKeyboardMapper } from 'vs/workbench/services/keybinding/common/macLinuxKeyboardMapper'; import { IResolvedKeybinding, assertMapping, assertResolveKeybinding, assertResolveKeyboardEvent, assertResolveUserBinding, readRawMapping } from 'vs/workbench/services/keybinding/test/keyboardMapperTestUtils'; const WRITE_FILE_IF_DIFFERENT = false; -async function createKeyboardMapper(isUSStandard: boolean, file: string, OS: OperatingSystem): TPromise { +async function createKeyboardMapper(isUSStandard: boolean, file: string, OS: OperatingSystem): Promise { const rawMappings = await readRawMapping(file); return new MacLinuxKeyboardMapper(isUSStandard, rawMappings, OS); } diff --git a/src/vs/workbench/services/keybinding/test/windowsKeyboardMapper.test.ts b/src/vs/workbench/services/keybinding/test/windowsKeyboardMapper.test.ts index 42a2626b1..5fafd8cf1 100644 --- a/src/vs/workbench/services/keybinding/test/windowsKeyboardMapper.test.ts +++ b/src/vs/workbench/services/keybinding/test/windowsKeyboardMapper.test.ts @@ -6,13 +6,12 @@ import { KeyChord, KeyCode, KeyMod, SimpleKeybinding, createKeybinding } from 'vs/base/common/keyCodes'; import { OperatingSystem } from 'vs/base/common/platform'; import { ScanCode, ScanCodeBinding } from 'vs/base/common/scanCode'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IWindowsKeyboardMapping, WindowsKeyboardMapper } from 'vs/workbench/services/keybinding/common/windowsKeyboardMapper'; import { IResolvedKeybinding, assertMapping, assertResolveKeybinding, assertResolveKeyboardEvent, assertResolveUserBinding, readRawMapping } from 'vs/workbench/services/keybinding/test/keyboardMapperTestUtils'; const WRITE_FILE_IF_DIFFERENT = false; -async function createKeyboardMapper(isUSStandard: boolean, file: string): TPromise { +async function createKeyboardMapper(isUSStandard: boolean, file: string): Promise { const rawMappings = await readRawMapping(file); return new WindowsKeyboardMapper(isUSStandard, rawMappings); } diff --git a/src/vs/workbench/services/mode/common/workbenchModeService.ts b/src/vs/workbench/services/mode/common/workbenchModeService.ts index 281932f24..7e9140e69 100644 --- a/src/vs/workbench/services/mode/common/workbenchModeService.ts +++ b/src/vs/workbench/services/mode/common/workbenchModeService.ts @@ -137,11 +137,15 @@ export class WorkbenchModeServiceImpl extends ModeServiceImpl { }); + this.updateMime(); this._configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(FILES_ASSOCIATIONS_CONFIG)) { this.updateMime(); } }); + this._extensionService.whenInstalledExtensionsRegistered().then(() => { + this.updateMime(); + }); this.onDidCreateMode((mode) => { this._extensionService.activateByEvent(`onLanguage:${mode.getId()}`); @@ -151,10 +155,7 @@ export class WorkbenchModeServiceImpl extends ModeServiceImpl { protected _onReady(): Promise { if (!this._onReadyPromise) { this._onReadyPromise = Promise.resolve( - this._extensionService.whenInstalledExtensionsRegistered().then(() => { - this.updateMime(); - return true; - }) + this._extensionService.whenInstalledExtensionsRegistered().then(() => true) ); } @@ -176,6 +177,8 @@ export class WorkbenchModeServiceImpl extends ModeServiceImpl { mime.registerTextMime({ id: langId, mime: mimetype, filepattern: pattern, userConfigured: true }); }); } + + this._onLanguagesMaybeChanged.fire(); } } diff --git a/src/vs/workbench/services/notification/common/notificationService.ts b/src/vs/workbench/services/notification/common/notificationService.ts index b8eadaddd..48e02e5db 100644 --- a/src/vs/workbench/services/notification/common/notificationService.ts +++ b/src/vs/workbench/services/notification/common/notificationService.ts @@ -63,8 +63,14 @@ export class NotificationService extends Disposable implements INotificationServ choices.forEach((choice, index) => { const action = new ChoiceAction(`workbench.dialog.choice.${index}`, choice); if (!choice.isSecondary) { + if (!actions.primary) { + actions.primary = []; + } actions.primary.push(action); } else { + if (!actions.secondary) { + actions.secondary = []; + } actions.secondary.push(action); } diff --git a/src/vs/workbench/services/panel/common/panelService.ts b/src/vs/workbench/services/panel/common/panelService.ts index e55ede4d2..204cf1f81 100644 --- a/src/vs/workbench/services/panel/common/panelService.ts +++ b/src/vs/workbench/services/panel/common/panelService.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IPanel } from 'vs/workbench/common/panel'; import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; @@ -26,7 +25,7 @@ export interface IPanelService { /** * Opens a panel with the given identifier and pass keyboard focus to it if specified. */ - openPanel(id: string, focus?: boolean): TPromise; + openPanel(id: string, focus?: boolean): IPanel; /** * Returns the current active panel or null if none diff --git a/src/vs/workbench/services/part/common/partService.ts b/src/vs/workbench/services/part/common/partService.ts index 94cc9a6ef..c5a5ac378 100644 --- a/src/vs/workbench/services/part/common/partService.ts +++ b/src/vs/workbench/services/part/common/partService.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { Event } from 'vs/base/common/event'; import { MenuBarVisibility } from 'vs/platform/windows/common/windows'; @@ -89,12 +88,12 @@ export interface IPartService { /** * Set sidebar hidden or not */ - setSideBarHidden(hidden: boolean): TPromise; + setSideBarHidden(hidden: boolean): void; /** * Set panel part hidden or not */ - setPanelHidden(hidden: boolean): TPromise; + setPanelHidden(hidden: boolean): void; /** * Maximizes the panel height if the panel is not already maximized. @@ -125,7 +124,7 @@ export interface IPartService { /** * Sets the panel position. */ - setPanelPosition(position: Position): TPromise; + setPanelPosition(position: Position): void; /** * Returns the element that contains the workbench. diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index 820b9377b..11068ba79 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -10,7 +10,6 @@ import * as network from 'vs/base/common/network'; import { assign } from 'vs/base/common/objects'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { getCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { IPosition, Position } from 'vs/editor/common/core/position'; @@ -104,12 +103,12 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE_FOLDER, resource); } - resolveModel(uri: URI): TPromise { + resolveModel(uri: URI): Thenable { if (this.isDefaultSettingsResource(uri)) { const target = this.getConfigurationTargetFromDefaultSettingsResource(uri); - const mode = this.modeService.getOrCreateMode('jsonc'); - const model = this._register(this.modelService.createModel('', mode, uri)); + const languageSelection = this.modeService.create('jsonc'); + const model = this._register(this.modelService.createModel('', languageSelection, uri)); let defaultSettings: DefaultSettings; this.configurationService.onDidChangeConfiguration(e => { @@ -131,27 +130,27 @@ export class PreferencesService extends Disposable implements IPreferencesServic this.modelService.updateModel(model, defaultSettings.getContent(true)); } - return TPromise.as(model); + return Promise.resolve(model); } if (this.defaultSettingsRawResource.toString() === uri.toString()) { let defaultSettings: DefaultSettings = this.getDefaultSettings(ConfigurationTarget.USER); - const mode = this.modeService.getOrCreateMode('jsonc'); - const model = this._register(this.modelService.createModel(defaultSettings.raw, mode, uri)); - return TPromise.as(model); + const languageSelection = this.modeService.create('jsonc'); + const model = this._register(this.modelService.createModel(defaultSettings.raw, languageSelection, uri)); + return Promise.resolve(model); } if (this.defaultKeybindingsResource.toString() === uri.toString()) { const defaultKeybindingsEditorModel = this.instantiationService.createInstance(DefaultKeybindingsEditorModel, uri); - const mode = this.modeService.getOrCreateMode('jsonc'); - const model = this._register(this.modelService.createModel(defaultKeybindingsEditorModel.content, mode, uri)); - return TPromise.as(model); + const languageSelection = this.modeService.create('jsonc'); + const model = this._register(this.modelService.createModel(defaultKeybindingsEditorModel.content, languageSelection, uri)); + return Promise.resolve(model); } - return TPromise.as(null); + return Promise.resolve(null); } - createPreferencesEditorModel(uri: URI): TPromise> { + createPreferencesEditorModel(uri: URI): Thenable> { if (this.isDefaultSettingsResource(uri)) { return this.createDefaultSettingsEditorModel(uri); } @@ -169,18 +168,18 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE_FOLDER, uri); } - return TPromise.wrap>(null); + return Promise.resolve>(null); } - openRawDefaultSettings(): TPromise { + openRawDefaultSettings(): Thenable { return this.editorService.openEditor({ resource: this.defaultSettingsRawResource }); } - openRawUserSettings(): TPromise { + openRawUserSettings(): Thenable { return this.editorService.openEditor({ resource: this.userSettingsResource }); } - openSettings(jsonEditor?: boolean): TPromise { + openSettings(jsonEditor?: boolean): Thenable { jsonEditor = typeof jsonEditor === 'undefined' ? this.configurationService.getValue('workbench.settings.editor') === 'json' : jsonEditor; @@ -195,13 +194,13 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.openOrSwitchSettings(target, resource); } - private openSettings2(): TPromise { + private openSettings2(): Thenable { const input = this.settingsEditor2Input; return this.editorGroupService.activeGroup.openEditor(input) .then(() => this.editorGroupService.activeGroup.activeControl); } - openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): TPromise { + openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Thenable { jsonEditor = typeof jsonEditor === 'undefined' ? this.configurationService.getValue('workbench.settings.editor') === 'json' : jsonEditor; @@ -211,14 +210,14 @@ export class PreferencesService extends Disposable implements IPreferencesServic this.openOrSwitchSettings2(ConfigurationTarget.USER, undefined, options, group); } - openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): TPromise { + openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Thenable { jsonEditor = typeof jsonEditor === 'undefined' ? this.configurationService.getValue('workbench.settings.editor') === 'json' : jsonEditor; if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { this.notificationService.info(nls.localize('openFolderFirst', "Open a folder first to create workspace settings")); - return TPromise.as(null); + return Promise.resolve(null); } return jsonEditor ? @@ -226,7 +225,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic this.openOrSwitchSettings2(ConfigurationTarget.WORKSPACE, undefined, options, group); } - openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): TPromise { + openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Thenable { jsonEditor = typeof jsonEditor === 'undefined' ? this.configurationService.getValue('workbench.settings.editor') === 'json' : jsonEditor; @@ -236,7 +235,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic this.openOrSwitchSettings2(ConfigurationTarget.WORKSPACE_FOLDER, folder, options, group); } - switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): TPromise { + switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): Thenable { if (!jsonEditor) { return this.doOpenSettings2(target, resource).then(() => null); } @@ -249,7 +248,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic } } - openGlobalKeybindingSettings(textual: boolean): TPromise { + openGlobalKeybindingSettings(textual: boolean): Thenable { /* __GDPR__ "openKeybindings" : { "textual" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } @@ -257,7 +256,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic */ this.telemetryService.publicLog('openKeybindings', { textual }); if (textual) { - const emptyContents = '// ' + nls.localize('emptyKeybindingsHeader', "Place your key bindings in this file to overwrite the defaults") + '\n[\n]'; + const emptyContents = '// ' + nls.localize('emptyKeybindingsHeader', "Place your key bindings in this file to override the defaults") + '\n[\n]'; const editableKeybindings = URI.file(this.environmentService.appKeybindingsPath); const openDefaultKeybindings = !!this.configurationService.getValue('workbench.settings.openDefaultKeybindings'); @@ -266,7 +265,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic if (openDefaultKeybindings) { const activeEditorGroup = this.editorGroupService.activeGroup; const sideEditorGroup = this.editorGroupService.addGroup(activeEditorGroup.id, GroupDirection.RIGHT); - return TPromise.join([ + return Promise.all([ this.editorService.openEditor({ resource: this.defaultKeybindingsResource, options: { pinned: true, preserveFocus: true }, label: nls.localize('defaultKeybindings', "Default Keybindings"), description: '' }), this.editorService.openEditor({ resource: editableKeybindings, options: { pinned: true } }, sideEditorGroup.id) ]).then(editors => void 0); @@ -279,7 +278,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.editorService.openEditor(this.instantiationService.createInstance(KeybindingsEditorInput), { pinned: true }).then(() => null); } - openDefaultKeybindingsFile(): TPromise { + openDefaultKeybindingsFile(): Thenable { return this.editorService.openEditor({ resource: this.defaultKeybindingsResource, label: nls.localize('defaultKeybindings', "Default Keybindings") }); } @@ -301,7 +300,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic })); } - private openOrSwitchSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): TPromise { + private openOrSwitchSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Thenable { const editorInput = this.getActiveSettingsEditorInput(group); if (editorInput && editorInput.master.getResource().fsPath !== resource.fsPath) { return this.doSwitchSettings(configurationTarget, resource, editorInput, group, options); @@ -309,11 +308,11 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.doOpenSettings(configurationTarget, resource, options, group); } - private openOrSwitchSettings2(configurationTarget: ConfigurationTarget, folderUri?: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): TPromise { + private openOrSwitchSettings2(configurationTarget: ConfigurationTarget, folderUri?: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Thenable { return this.doOpenSettings2(configurationTarget, folderUri, options, group); } - private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group?: IEditorGroup): TPromise { + private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group?: IEditorGroup): Thenable { const openDefaultSettings = !!this.configurationService.getValue(DEFAULT_SETTINGS_EDITOR_SETTING); return this.getOrCreateEditableSettingsEditorInput(configurationTarget, resource) .then(editableSettingsEditorInput => { @@ -337,7 +336,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.instantiationService.createInstance(Settings2EditorModel, this.getDefaultSettings(ConfigurationTarget.USER)); } - private doOpenSettings2(target: ConfigurationTarget, folderUri: URI | undefined, options?: IEditorOptions, group?: IEditorGroup): TPromise { + private doOpenSettings2(target: ConfigurationTarget, folderUri: URI | undefined, options?: IEditorOptions, group?: IEditorGroup): Thenable { const input = this.settingsEditor2Input; const settingsOptions: ISettingsEditorOptions = { ...options, @@ -348,7 +347,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.editorService.openEditor(input, SettingsEditorOptions.create(settingsOptions), group); } - private doSwitchSettings(target: ConfigurationTarget, resource: URI, input: PreferencesEditorInput, group: IEditorGroup, options?: ISettingsEditorOptions): TPromise { + private doSwitchSettings(target: ConfigurationTarget, resource: URI, input: PreferencesEditorInput, group: IEditorGroup, options?: ISettingsEditorOptions): Thenable { return this.getOrCreateEditableSettingsEditorInput(target, this.getEditableSettingsURI(target, resource)) .then(toInput => { return group.openEditor(input).then(() => { @@ -423,12 +422,12 @@ export class PreferencesService extends Disposable implements IPreferencesServic return target === ConfigurationTarget.WORKSPACE_FOLDER ? nls.localize('folderSettingsName', "{0} (Folder Settings)", name) : name; } - private getOrCreateEditableSettingsEditorInput(target: ConfigurationTarget, resource: URI): TPromise { + private getOrCreateEditableSettingsEditorInput(target: ConfigurationTarget, resource: URI): Thenable { return this.createSettingsIfNotExists(target, resource) .then(() => this.editorService.createInput({ resource })); } - private createEditableSettingsEditorModel(configurationTarget: ConfigurationTarget, resource: URI): TPromise { + private createEditableSettingsEditorModel(configurationTarget: ConfigurationTarget, resource: URI): Thenable { const settingsUri = this.getEditableSettingsURI(configurationTarget, resource); if (settingsUri) { const workspace = this.contextService.getWorkspace(); @@ -439,10 +438,10 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.textModelResolverService.createModelReference(settingsUri) .then(reference => this.instantiationService.createInstance(SettingsEditorModel, reference, configurationTarget)); } - return TPromise.wrap(null); + return Promise.resolve(null); } - private createDefaultSettingsEditorModel(defaultSettingsUri: URI): TPromise { + private createDefaultSettingsEditorModel(defaultSettingsUri: URI): Thenable { return this.textModelResolverService.createModelReference(defaultSettingsUri) .then(reference => { const target = this.getConfigurationTargetFromDefaultSettingsResource(defaultSettingsUri); @@ -486,9 +485,14 @@ export class PreferencesService extends Disposable implements IPreferencesServic return null; } - private createSettingsIfNotExists(target: ConfigurationTarget, resource: URI): TPromise { + private createSettingsIfNotExists(target: ConfigurationTarget, resource: URI): Thenable { if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE && target === ConfigurationTarget.WORKSPACE) { - return this.fileService.resolveContent(this.contextService.getWorkspace().configuration) + const workspaceConfig = this.contextService.getWorkspace().configuration; + if (!workspaceConfig) { + return Promise.resolve(null); + } + + return this.fileService.resolveContent(workspaceConfig) .then(content => { if (Object.keys(parse(content.value)).indexOf('settings') === -1) { return this.jsonEditingService.write(resource, { key: 'settings', value: {} }, true).then(null, () => { }); @@ -499,15 +503,15 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.createIfNotExists(resource, emptyEditableSettingsContent).then(() => { }); } - private createIfNotExists(resource: URI, contents: string): TPromise { + private createIfNotExists(resource: URI, contents: string): Thenable { return this.fileService.resolveContent(resource, { acceptTextOnly: true }).then(null, error => { if ((error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND) { return this.fileService.updateContent(resource, contents).then(null, error => { - return TPromise.wrapError(new Error(nls.localize('fail.createSettings', "Unable to create '{0}' ({1}).", this.labelService.getUriLabel(resource, { relative: true }), error))); + return Promise.reject(new Error(nls.localize('fail.createSettings', "Unable to create '{0}' ({1}).", this.labelService.getUriLabel(resource, { relative: true }), error))); }); } - return TPromise.wrapError(error); + return Promise.reject(error); }); } @@ -527,7 +531,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic ]; } - private getPosition(language: string, settingsModel: IPreferencesEditorModel, codeEditor: ICodeEditor): TPromise { + private getPosition(language: string, settingsModel: IPreferencesEditorModel, codeEditor: ICodeEditor): Thenable { const languageKey = `[${language}]`; let setting = settingsModel.getPreference(languageKey); const model = codeEditor.getModel(); @@ -544,9 +548,9 @@ export class PreferencesService extends Disposable implements IPreferencesServic } const editOperation = EditOperation.insert(new Position(lastSetting.valueRange.endLineNumber, lastSetting.valueRange.endColumn), content); model.pushEditOperations([], [editOperation], () => []); - return TPromise.as({ lineNumber: lastSetting.valueRange.endLineNumber + 1, column: model.getLineMaxColumn(lastSetting.valueRange.endLineNumber + 1) }); + return Promise.resolve({ lineNumber: lastSetting.valueRange.endLineNumber + 1, column: model.getLineMaxColumn(lastSetting.valueRange.endLineNumber + 1) }); } - return TPromise.as({ lineNumber: setting.valueRange.startLineNumber, column: setting.valueRange.startColumn + 1 }); + return Promise.resolve({ lineNumber: setting.valueRange.startLineNumber, column: setting.valueRange.startColumn + 1 }); } return this.configurationService.updateValue(languageKey, {}, ConfigurationTarget.USER) .then(() => { diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index 80453e0f3..e816d3166 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -3,23 +3,22 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IStringDictionary } from 'vs/base/common/collections'; +import { Event } from 'vs/base/common/event'; +import { join } from 'vs/base/common/paths'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IEditorOptions } from 'vs/platform/editor/common/editor'; -import { IEditor, EditorOptions } from 'vs/workbench/common/editor'; -import { ITextModel } from 'vs/editor/common/model'; import { IRange } from 'vs/editor/common/core/range'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { join } from 'vs/base/common/paths'; +import { ITextModel } from 'vs/editor/common/model'; +import { localize } from 'vs/nls'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { Event } from 'vs/base/common/event'; -import { IStringDictionary } from 'vs/base/common/collections'; +import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { localize } from 'vs/nls'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { EditorOptions, IEditor } from 'vs/workbench/common/editor'; import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; import { Settings2EditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; -import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; export enum SettingValueType { Null = 'null', @@ -202,18 +201,18 @@ export interface IPreferencesService { workspaceSettingsResource: URI; getFolderSettingsResource(resource: URI): URI; - resolveModel(uri: URI): TPromise; - createPreferencesEditorModel(uri: URI): TPromise>; + resolveModel(uri: URI): Thenable; + createPreferencesEditorModel(uri: URI): Thenable>; createSettings2EditorModel(): Settings2EditorModel; // TODO - openRawDefaultSettings(): TPromise; - openSettings(jsonEditor?: boolean): TPromise; - openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): TPromise; - openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): TPromise; - openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): TPromise; - switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): TPromise; - openGlobalKeybindingSettings(textual: boolean): TPromise; - openDefaultKeybindingsFile(): TPromise; + openRawDefaultSettings(): Thenable; + openSettings(jsonEditor?: boolean): Thenable; + openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Thenable; + openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Thenable; + openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Thenable; + switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): Thenable; + openGlobalKeybindingSettings(textual: boolean): Thenable; + openDefaultKeybindingsFile(): Thenable; configureSettingsForLanguage(language: string): void; } diff --git a/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts b/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts index 4bd8f3110..4934e25e2 100644 --- a/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts +++ b/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts @@ -5,7 +5,6 @@ import { OS } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import * as nls from 'vs/nls'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -70,8 +69,8 @@ export class KeybindingsEditorInput extends EditorInput { return nls.localize('keybindingsInputName', "Keyboard Shortcuts"); } - resolve(): TPromise { - return TPromise.as(this.keybindingsModel); + resolve(): Promise { + return Promise.resolve(this.keybindingsModel); } matches(otherInput: any): boolean { @@ -104,8 +103,8 @@ export class SettingsEditor2Input extends EditorInput { return nls.localize('settingsEditor2InputName', "Settings"); } - resolve(): TPromise { - return TPromise.as(this._settingsModel); + resolve(): Promise { + return Promise.resolve(this._settingsModel); } public getResource(): URI { diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index 7888be41f..0ba508964 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -722,6 +722,10 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements } protected update(): IFilterResult { + if (this._model.isDisposed()) { + return null; + } + // Grab current result groups, only render non-empty groups const resultGroups = map .values(this._currentResultGroups) @@ -1126,7 +1130,7 @@ function escapeInvisibleChars(enumValue: string): string { } export function defaultKeybindingsContents(keybindingService: IKeybindingService): string { - const defaultsHeader = '// ' + nls.localize('defaultKeybindingsHeader', "Overwrite key bindings by placing them into your key bindings file."); + const defaultsHeader = '// ' + nls.localize('defaultKeybindingsHeader', "Override key bindings by placing them into your key bindings file."); return defaultsHeader + '\n' + keybindingService.getDefaultKeybindingsContent(); } diff --git a/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts b/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts index caf3501a2..83430e628 100644 --- a/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts +++ b/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts @@ -189,7 +189,7 @@ suite('KeybindingsEditorModel test', () => { assert.equal(actual.keybindingItem.when, ''); }); - test('convert with title and wihtout binding to entry', async () => { + test('convert with title and without binding to entry', async () => { const id = 'a' + uuid.generateUuid(); registerCommandWithTitle(id, 'some title'); prepareKeybindingService(); diff --git a/src/vs/workbench/services/progress/browser/progressService.ts b/src/vs/workbench/services/progress/browser/progressService.ts index 642810a59..f2432e817 100644 --- a/src/vs/workbench/services/progress/browser/progressService.ts +++ b/src/vs/workbench/services/progress/browser/progressService.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from 'vs/base/common/lifecycle'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as types from 'vs/base/common/types'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -16,7 +15,7 @@ interface ProgressState { total?: number; worked?: number; done?: boolean; - whilePromise?: TPromise; + whilePromise?: Thenable; whileStart?: number; whileDelay?: number; } @@ -206,7 +205,7 @@ export class ScopedProgressService extends ScopedService implements IProgressSer }; } - showWhile(promise: TPromise, delay?: number): TPromise { + showWhile(promise: Thenable, delay?: number): Thenable { let stack: boolean = !!this.progressState.whilePromise; // Reset State @@ -216,7 +215,7 @@ export class ScopedProgressService extends ScopedService implements IProgressSer // Otherwise join with existing running promise to ensure progress is accurate else { - promise = TPromise.join([promise, this.progressState.whilePromise]); + promise = Promise.all([promise, this.progressState.whilePromise]); } // Keep Promise in State @@ -287,7 +286,7 @@ export class ProgressService implements IProgressService { }; } - showWhile(promise: TPromise, delay?: number): TPromise { + showWhile(promise: Thenable, delay?: number): Thenable { const stop = () => { this.progressbar.stop().hide(); }; diff --git a/src/vs/workbench/services/progress/test/progressService.test.ts b/src/vs/workbench/services/progress/test/progressService.test.ts index 1c701476b..f2ac81a64 100644 --- a/src/vs/workbench/services/progress/test/progressService.test.ts +++ b/src/vs/workbench/services/progress/test/progressService.test.ts @@ -69,8 +69,8 @@ class TestPanelService implements IPanelService { onDidPanelOpen = new Emitter<{ panel: IPanel, focus: boolean }>().event; onDidPanelClose = new Emitter().event; - public openPanel(id: string, focus?: boolean): TPromise { - return TPromise.as(null); + public openPanel(id: string, focus?: boolean): IPanel { + return null; } public getPanels(): any[] { diff --git a/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts b/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts new file mode 100644 index 000000000..6d21a8fa6 --- /dev/null +++ b/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts @@ -0,0 +1,85 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IChannel, getDelayedChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { Client } from 'vs/base/parts/ipc/node/ipc.net'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { connectRemoteAgentManagement, RemoteAgentConnectionContext } from 'vs/platform/remote/node/remoteAgentConnection'; +import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; +import { RemoteExtensionEnvironmentChannelClient } from 'vs/workbench/services/remote/node/remoteAgentEnvironmentChannel'; +import { IRemoteAgentConnection, IRemoteAgentEnvironment, IRemoteAgentService } from 'vs/workbench/services/remote/node/remoteAgentService'; +import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; + +export class RemoteAgentService implements IRemoteAgentService { + + _serviceBrand: any; + + private readonly _connection: IRemoteAgentConnection | null = null; + + constructor( + window: IWindowConfiguration, + @INotificationService notificationService: INotificationService, + @IEnvironmentService environmentService: IEnvironmentService, + @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService + ) { + if (window.remoteAuthority) { + this._connection = new RemoteAgentConnection(window.remoteAuthority, notificationService, environmentService, remoteAuthorityResolverService); + } + } + + getConnection(): IRemoteAgentConnection | null { + return this._connection; + } +} + +class RemoteAgentConnection extends Disposable implements IRemoteAgentConnection { + + readonly remoteAuthority: string; + private _connection: Thenable> | null; + private _environment: Thenable | null; + + constructor( + remoteAuthority: string, + private _notificationService: INotificationService, + private _environmentService: IEnvironmentService, + private _remoteAuthorityResolverService: IRemoteAuthorityResolverService + ) { + super(); + this.remoteAuthority = remoteAuthority; + this._connection = null; + this._environment = null; + } + + getEnvironment(): Thenable { + if (!this._environment) { + const client = new RemoteExtensionEnvironmentChannelClient(this.getChannel('remoteextensionsenvironment')); + + // Let's cover the case where connecting to fetch the remote extension info fails + this._environment = client.getEnvironmentData(this.remoteAuthority, this._environmentService.extensionDevelopmentLocationURI) + .then(undefined, err => { this._notificationService.error(localize('connectionError', "Failed to connect to the remote extension host agent (Error: {0})", err ? err.message : '')); return null; }); + } + return this._environment; + } + + getChannel(channelName: string): T { + return getDelayedChannel(this._getOrCreateConnection().then(c => c.getChannel(channelName))); + } + + registerChannel>(channelName: string, channel: T): void { + this._getOrCreateConnection().then(client => client.registerChannel(channelName, channel)); + } + + private _getOrCreateConnection(): Thenable> { + if (!this._connection) { + this._connection = this._remoteAuthorityResolverService.resolveAuthority(this.remoteAuthority).then((resolvedAuthority) => { + return connectRemoteAgentManagement(this.remoteAuthority, resolvedAuthority.host, resolvedAuthority.port, `renderer`); + }); + } + return this._connection; + } +} diff --git a/src/vs/workbench/services/remote/node/remoteAgentEnvironmentChannel.ts b/src/vs/workbench/services/remote/node/remoteAgentEnvironmentChannel.ts new file mode 100644 index 000000000..1db9073ca --- /dev/null +++ b/src/vs/workbench/services/remote/node/remoteAgentEnvironmentChannel.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { OperatingSystem } from 'vs/base/common/platform'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { IRemoteAgentEnvironment } from 'vs/workbench/services/remote/node/remoteAgentService'; + +export interface IRemoteAgentEnvironmentDTO { + pid: number; + appRoot: UriComponents; + appSettingsHome: UriComponents; + logsPath: UriComponents; + extensionsPath: UriComponents; + extensionHostLogsPath: UriComponents; + extensions: IExtensionDescription[]; + os: OperatingSystem; +} + +export class RemoteExtensionEnvironmentChannelClient { + + constructor(private channel: IChannel) { } + + getEnvironmentData(remoteAuthority: string, extensionDevelopmentPath?: URI): Thenable { + return this.channel.call('getEnvironmentData', [remoteAuthority, extensionDevelopmentPath]) + .then((data: IRemoteAgentEnvironmentDTO): IRemoteAgentEnvironment => { + return { + pid: data.pid, + appRoot: URI.revive(data.appRoot), + appSettingsHome: URI.revive(data.appSettingsHome), + logsPath: URI.revive(data.logsPath), + extensionsPath: URI.revive(data.extensionsPath), + extensionHostLogsPath: URI.revive(data.extensionHostLogsPath), + extensions: data.extensions.map(ext => { (ext).extensionLocation = URI.revive(ext.extensionLocation); return ext; }), + os: data.os + }; + }); + } +} diff --git a/src/vs/workbench/services/remote/node/remoteAgentService.ts b/src/vs/workbench/services/remote/node/remoteAgentService.ts new file mode 100644 index 000000000..2cf1bf656 --- /dev/null +++ b/src/vs/workbench/services/remote/node/remoteAgentService.ts @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { OperatingSystem } from 'vs/base/common/platform'; +import { URI } from 'vs/base/common/uri'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { RemoteAgentConnectionContext } from 'vs/platform/remote/node/remoteAgentConnection'; + +export const RemoteExtensionLogFileName = 'remoteagent'; + +export const IRemoteAgentService = createDecorator('remoteAgentService'); + +export interface IRemoteAgentEnvironment { + pid: number; + appRoot: URI; + appSettingsHome: URI; + logsPath: URI; + extensionsPath: URI; + extensionHostLogsPath: URI; + extensions: IExtensionDescription[]; + os: OperatingSystem; +} + +export interface IRemoteAgentService { + _serviceBrand: any; + + getConnection(): IRemoteAgentConnection | null; +} + +export interface IRemoteAgentConnection { + readonly remoteAuthority: string; + + getEnvironment(): Thenable; + + getChannel(channelName: string): T; + registerChannel>(channelName: string, channel: T); +} diff --git a/src/vs/workbench/services/scm/common/scm.ts b/src/vs/workbench/services/scm/common/scm.ts index b359fd41f..88882ad8c 100644 --- a/src/vs/workbench/services/scm/common/scm.ts +++ b/src/vs/workbench/services/scm/common/scm.ts @@ -90,6 +90,9 @@ export interface ISCMInput { validateInput: IInputValidator; readonly onDidChangeValidateInput: Event; + + visible: boolean; + readonly onDidChangeVisibility: Event; } export interface ISCMRepository extends IDisposable { diff --git a/src/vs/workbench/services/scm/common/scmService.ts b/src/vs/workbench/services/scm/common/scmService.ts index 1137b195a..618d669a2 100644 --- a/src/vs/workbench/services/scm/common/scmService.ts +++ b/src/vs/workbench/services/scm/common/scmService.ts @@ -40,6 +40,20 @@ class SCMInput implements ISCMInput { private _onDidChangePlaceholder = new Emitter(); get onDidChangePlaceholder(): Event { return this._onDidChangePlaceholder.event; } + private _visible = true; + + get visible(): boolean { + return this._visible; + } + + set visible(visible: boolean) { + this._visible = visible; + this._onDidChangeVisibility.fire(visible); + } + + private _onDidChangeVisibility = new Emitter(); + get onDidChangeVisibility(): Event { return this._onDidChangeVisibility.event; } + private _validateInput: IInputValidator = () => TPromise.as(undefined); get validateInput(): IInputValidator { diff --git a/src/vs/workbench/services/search/common/searchHelpers.ts b/src/vs/workbench/services/search/common/searchHelpers.ts new file mode 100644 index 000000000..55119e2cc --- /dev/null +++ b/src/vs/workbench/services/search/common/searchHelpers.ts @@ -0,0 +1,92 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Range } from 'vs/editor/common/core/range'; +import { FindMatch, ITextModel } from 'vs/editor/common/model'; +import { ITextSearchPreviewOptions, TextSearchMatch, ITextSearchResult, ITextSearchMatch, ITextQuery, ITextSearchContext } from 'vs/platform/search/common/search'; + +function editorMatchToTextSearchResult(matches: FindMatch[], model: ITextModel, previewOptions?: ITextSearchPreviewOptions): TextSearchMatch { + const firstLine = matches[0].range.startLineNumber; + const lastLine = matches[matches.length - 1].range.endLineNumber; + + const lineTexts: string[] = []; + for (let i = firstLine; i <= lastLine; i++) { + lineTexts.push(model.getLineContent(i)); + } + + return new TextSearchMatch( + lineTexts.join('\n') + '\n', + matches.map(m => new Range(m.range.startLineNumber - 1, m.range.startColumn - 1, m.range.endLineNumber - 1, m.range.endColumn - 1)), + previewOptions); +} + +/** + * Combine a set of FindMatches into a set of TextSearchResults. They should be grouped by matches that start on the same line that the previous match ends on. + */ +export function editorMatchesToTextSearchResults(matches: FindMatch[], model: ITextModel, previewOptions?: ITextSearchPreviewOptions): TextSearchMatch[] { + let previousEndLine = -1; + const groupedMatches: FindMatch[][] = []; + let currentMatches: FindMatch[] = []; + matches.forEach((match) => { + if (match.range.startLineNumber !== previousEndLine) { + currentMatches = []; + groupedMatches.push(currentMatches); + } + + currentMatches.push(match); + previousEndLine = match.range.endLineNumber; + }); + + return groupedMatches.map(sameLineMatches => { + return editorMatchToTextSearchResult(sameLineMatches, model, previewOptions); + }); +} + +export function addContextToEditorMatches(matches: ITextSearchMatch[], model: ITextModel, query: ITextQuery): ITextSearchResult[] { + const results: ITextSearchResult[] = []; + + let prevLine = -1; + for (let i = 0; i < matches.length; i++) { + const { start: matchStartLine, end: matchEndLine } = getMatchStartEnd(matches[i]); + if (typeof query.beforeContext === 'number' && query.beforeContext > 0) { + const beforeContextStartLine = Math.max(prevLine + 1, matchStartLine - query.beforeContext); + for (let b = beforeContextStartLine; b < matchStartLine; b++) { + results.push({ + text: model.getLineContent(b + 1), + lineNumber: b + }); + } + } + + results.push(matches[i]); + + const nextMatch = matches[i + 1]; + let nextMatchStartLine = nextMatch ? getMatchStartEnd(nextMatch).start : Number.MAX_VALUE; + if (typeof query.afterContext === 'number' && query.afterContext > 0) { + const afterContextToLine = Math.min(nextMatchStartLine - 1, matchEndLine + query.afterContext, model.getLineCount() - 1); + for (let a = matchEndLine + 1; a <= afterContextToLine; a++) { + results.push({ + text: model.getLineContent(a + 1), + lineNumber: a + }); + } + } + + prevLine = matchEndLine; + } + + return results; +} + +function getMatchStartEnd(match: ITextSearchMatch): { start: number, end: number } { + const matchRanges = match.ranges; + const matchStartLine = Array.isArray(matchRanges) ? matchRanges[0].startLineNumber : matchRanges.startLineNumber; + const matchEndLine = Array.isArray(matchRanges) ? matchRanges[matchRanges.length - 1].endLineNumber : matchRanges.endLineNumber; + + return { + start: matchStartLine, + end: matchEndLine + }; +} \ No newline at end of file diff --git a/src/vs/workbench/services/search/node/fileSearch.ts b/src/vs/workbench/services/search/node/fileSearch.ts index 52deb2ff8..9981f3391 100644 --- a/src/vs/workbench/services/search/node/fileSearch.ts +++ b/src/vs/workbench/services/search/node/fileSearch.ts @@ -19,7 +19,6 @@ import { StopWatch } from 'vs/base/common/stopwatch'; import * as strings from 'vs/base/common/strings'; import * as types from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as extfs from 'vs/base/node/extfs'; import * as flow from 'vs/base/node/flow'; import { IFileQuery, IFolderQuery, IProgress, ISearchEngineStats } from 'vs/platform/search/common/search'; @@ -697,7 +696,7 @@ class AbsoluteAndRelativeParsedExpression { this.relativeParsedExpr = relativeGlobExpr && glob.parse(relativeGlobExpr, { trimForExclusions: true }); } - public test(_path: string, basename?: string, hasSibling?: (name: string) => boolean | TPromise): string | TPromise { + public test(_path: string, basename?: string, hasSibling?: (name: string) => boolean | Promise): string | Promise { return (this.relativeParsedExpr && this.relativeParsedExpr(_path, basename, hasSibling)) || (this.absoluteParsedExpr && this.absoluteParsedExpr(path.join(this.root, _path), basename, hasSibling)); } diff --git a/src/vs/workbench/services/search/node/fileSearchManager.ts b/src/vs/workbench/services/search/node/fileSearchManager.ts index 7acdf19bd..2713a180f 100644 --- a/src/vs/workbench/services/search/node/fileSearchManager.ts +++ b/src/vs/workbench/services/search/node/fileSearchManager.ts @@ -10,7 +10,6 @@ import * as glob from 'vs/base/common/glob'; import * as resources from 'vs/base/common/resources'; import { StopWatch } from 'vs/base/common/stopwatch'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { IFileMatch, IFileSearchProviderStats, IFolderQuery, ISearchCompleteStats, IFileQuery } from 'vs/platform/search/common/search'; import { QueryGlobTester, resolvePatternsForProvider } from 'vs/workbench/services/search/node/search'; import * as vscode from 'vscode'; @@ -36,25 +35,23 @@ export interface IDirectoryTree { class FileSearchEngine { - private filePattern: string; - private includePattern: glob.ParsedExpression; - private maxResults: number; - private exists: boolean; - private isLimitHit: boolean; - private resultCount: number; - private isCanceled: boolean; + private filePattern?: string; + private includePattern?: glob.ParsedExpression; + private maxResults?: number; + private exists?: boolean; + private isLimitHit = false; + private resultCount = 0; + private isCanceled = false; private activeCancellationTokens: Set; - private globalExcludePattern: glob.ParsedExpression; + private globalExcludePattern?: glob.ParsedExpression; constructor(private config: IFileQuery, private provider: vscode.FileSearchProvider) { this.filePattern = config.filePattern; this.includePattern = config.includePattern && glob.parse(config.includePattern); - this.maxResults = config.maxResults || null; + this.maxResults = config.maxResults || undefined; this.exists = config.exists; - this.resultCount = 0; - this.isLimitHit = false; this.activeCancellationTokens = new Set(); this.globalExcludePattern = config.excludePattern && glob.parse(config.excludePattern); @@ -66,10 +63,10 @@ class FileSearchEngine { this.activeCancellationTokens = new Set(); } - public search(_onResult: (match: IInternalFileMatch) => void): TPromise { - const folderQueries = this.config.folderQueries; + public search(_onResult: (match: IInternalFileMatch) => void): Promise { + const folderQueries = this.config.folderQueries || []; - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { const onResult = (match: IInternalFileMatch) => { this.resultCount++; _onResult(match); @@ -96,26 +93,22 @@ class FileSearchEngine { } // For each root folder - TPromise.join(folderQueries.map(fq => { + Promise.all(folderQueries.map(fq => { return this.searchInFolder(fq, onResult); })).then(stats => { resolve({ limitHit: this.isLimitHit, - stats: stats[0] // Only looking at single-folder workspace stats... + stats: stats[0] || undefined // Only looking at single-folder workspace stats... }); - }, (errs: Error[]) => { - const errMsg = errs - .map(err => toErrorMessage(err)) - .filter(msg => !!msg)[0]; - - reject(new Error(errMsg)); + }, (err: Error) => { + reject(new Error(toErrorMessage(err))); }); }); } - private searchInFolder(fq: IFolderQuery, onResult: (match: IInternalFileMatch) => void): TPromise { + private searchInFolder(fq: IFolderQuery, onResult: (match: IInternalFileMatch) => void): Promise { let cancellation = new CancellationTokenSource(); - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { const options = this.getSearchOptionsForFolder(fq); const tree = this.initDirectoryTree(); @@ -123,7 +116,7 @@ class FileSearchEngine { const noSiblingsClauses = !queryTester.hasSiblingExcludeClauses(); let providerSW: StopWatch; - new TPromise(_resolve => process.nextTick(_resolve)) + new Promise(_resolve => process.nextTick(_resolve)) .then(() => { this.activeCancellationTokens.add(cancellation); @@ -267,7 +260,7 @@ class FileSearchEngine { } private matchFile(onResult: (result: IInternalFileMatch) => void, candidate: IInternalFileMatch): void { - if (!this.includePattern || this.includePattern(candidate.relativePath, candidate.basename)) { + if (!this.includePattern || (candidate.relativePath && this.includePattern(candidate.relativePath, candidate.basename))) { if (this.exists || (this.maxResults && this.resultCount >= this.maxResults)) { this.isLimitHit = true; this.cancel(); @@ -289,7 +282,7 @@ export class FileSearchManager { private static readonly BATCH_SIZE = 512; - fileSearch(config: IFileQuery, provider: vscode.FileSearchProvider, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): TPromise { + fileSearch(config: IFileQuery, provider: vscode.FileSearchProvider, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): Promise { const engine = new FileSearchEngine(config, provider); let resultCount = 0; @@ -325,7 +318,7 @@ export class FileSearchManager { } } - private doSearch(engine: FileSearchEngine, batchSize: number, onResultBatch: (matches: IInternalFileMatch[]) => void, token: CancellationToken): TPromise { + private doSearch(engine: FileSearchEngine, batchSize: number, onResultBatch: (matches: IInternalFileMatch[]) => void, token: CancellationToken): Promise { token.onCancellationRequested(() => { engine.cancel(); }); @@ -352,7 +345,7 @@ export class FileSearchManager { onResultBatch(batch); } - return TPromise.wrapError(error); + return Promise.reject(error); }); } } diff --git a/src/vs/workbench/services/search/node/legacy/textSearchWorkerProvider.ts b/src/vs/workbench/services/search/node/legacy/textSearchWorkerProvider.ts index 617bd4ed0..ac848db10 100644 --- a/src/vs/workbench/services/search/node/legacy/textSearchWorkerProvider.ts +++ b/src/vs/workbench/services/search/node/legacy/textSearchWorkerProvider.ts @@ -8,7 +8,7 @@ import * as os from 'os'; import * as ipc from 'vs/base/parts/ipc/node/ipc'; import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; -import { ISearchWorker, ISearchWorkerChannel, SearchWorkerChannelClient } from './worker/searchWorkerIpc'; +import { ISearchWorker, SearchWorkerChannelClient } from './worker/searchWorkerIpc'; import { getPathFromAmdModule } from 'vs/base/common/amd'; export interface ITextSearchWorkerProvider { @@ -42,7 +42,7 @@ export class TextSearchWorkerProvider implements ITextSearchWorkerProvider { useQueue: true }); - const channel = ipc.getNextTickChannel(client.getChannel('searchWorker')); + const channel = ipc.getNextTickChannel(client.getChannel('searchWorker')); const channelClient = new SearchWorkerChannelClient(channel); this.workers.push(channelClient); diff --git a/src/vs/workbench/services/search/node/legacy/worker/searchWorker.ts b/src/vs/workbench/services/search/node/legacy/worker/searchWorker.ts index 66cd9f8ae..4ae556fff 100644 --- a/src/vs/workbench/services/search/node/legacy/worker/searchWorker.ts +++ b/src/vs/workbench/services/search/node/legacy/worker/searchWorker.ts @@ -7,10 +7,9 @@ import * as fs from 'fs'; import * as gracefulFs from 'graceful-fs'; import { onUnexpectedError } from 'vs/base/common/errors'; import * as strings from 'vs/base/common/strings'; -import { TPromise } from 'vs/base/common/winjs.base'; import { bomLength, decode, detectEncodingFromBuffer, encodingExists, UTF16be, UTF16le, UTF8, UTF8_with_bom } from 'vs/base/node/encoding'; import { Range } from 'vs/editor/common/core/range'; -import { ITextSearchPreviewOptions, TextSearchResult } from 'vs/platform/search/common/search'; +import { ITextSearchPreviewOptions, TextSearchMatch } from 'vs/platform/search/common/search'; import { ISearchWorker, ISearchWorkerSearchArgs, ISearchWorkerSearchResult } from './searchWorkerIpc'; import { FileMatch } from 'vs/workbench/services/search/node/search'; @@ -32,21 +31,21 @@ function onError(error: any): void { export class SearchWorker implements ISearchWorker { private currentSearchEngine: SearchWorkerEngine; - initialize(): TPromise { + initialize(): Promise { this.currentSearchEngine = new SearchWorkerEngine(); - return TPromise.wrap(undefined); + return Promise.resolve(undefined); } - cancel(): TPromise { + cancel(): Promise { // Cancel the current search. It will stop searching and close its open files. if (this.currentSearchEngine) { this.currentSearchEngine.cancel(); } - return TPromise.wrap(null); + return Promise.resolve(null); } - search(args: ISearchWorkerSearchArgs): TPromise { + search(args: ISearchWorkerSearchArgs): Promise { if (!this.currentSearchEngine) { // Worker timed out during search this.initialize(); @@ -66,13 +65,13 @@ const LF = 0x0a; const CR = 0x0d; export class SearchWorkerEngine { - private nextSearch = TPromise.wrap(null); + private nextSearch = Promise.resolve(null); private isCanceled = false; /** * Searches some number of the given paths concurrently, and starts searches in other paths when those complete. */ - searchBatch(args: ISearchWorkerSearchArgs): TPromise { + searchBatch(args: ISearchWorkerSearchArgs): Promise { const contentPattern = strings.createRegExp(args.pattern.pattern, args.pattern.isRegExp, { matchCase: args.pattern.isCaseSensitive, wholeWord: args.pattern.isWordMatch, multiline: false, global: true }); const fileEncoding = encodingExists(args.fileEncoding) ? args.fileEncoding : UTF8; return this.nextSearch = @@ -80,12 +79,12 @@ export class SearchWorkerEngine { } - private _searchBatch(args: ISearchWorkerSearchArgs, contentPattern: RegExp, fileEncoding: string): TPromise { + private _searchBatch(args: ISearchWorkerSearchArgs, contentPattern: RegExp, fileEncoding: string): Promise { if (this.isCanceled) { - return TPromise.wrap(null); + return Promise.resolve(null); } - return new TPromise(batchDone => { + return new Promise(batchDone => { const result: ISearchWorkerSearchResult = { matches: [], numMatches: 0, @@ -93,7 +92,7 @@ export class SearchWorkerEngine { }; // Search in the given path, and when it's finished, search in the next path in absolutePaths - const startSearchInFile = (absolutePath: string): TPromise => { + const startSearchInFile = (absolutePath: string): Promise => { return this.searchInFile(absolutePath, contentPattern, fileEncoding, args.maxResults && (args.maxResults - result.numMatches), args.previewOptions).then(fileResult => { // Finish early if search is canceled if (this.isCanceled) { @@ -113,7 +112,7 @@ export class SearchWorkerEngine { }, onError); }; - TPromise.join(args.absolutePaths.map(startSearchInFile)).then(() => { + Promise.all(args.absolutePaths.map(startSearchInFile)).then(() => { batchDone(result); }); }); @@ -123,7 +122,7 @@ export class SearchWorkerEngine { this.isCanceled = true; } - private searchInFile(absolutePath: string, contentPattern: RegExp, fileEncoding: string, maxResults?: number, previewOptions?: ITextSearchPreviewOptions): TPromise { + private searchInFile(absolutePath: string, contentPattern: RegExp, fileEncoding: string, maxResults?: number, previewOptions?: ITextSearchPreviewOptions): Promise { let fileMatch: FileMatch | null = null; let limitReached = false; let numMatches = 0; @@ -137,7 +136,7 @@ export class SearchWorkerEngine { fileMatch = new FileMatch(absolutePath); } - const lineMatch = new TextSearchResult(line, new Range(lineNumber, match.index, lineNumber, match.index + match[0].length), previewOptions); + const lineMatch = new TextSearchMatch(line, new Range(lineNumber, match.index, lineNumber, match.index + match[0].length), previewOptions); fileMatch.addMatch(lineMatch); numMatches++; @@ -154,8 +153,8 @@ export class SearchWorkerEngine { () => fileMatch ? { match: fileMatch, limitReached, numMatches } : null); } - private readlinesAsync(filename: string, perLineCallback: (line: string, lineNumber: number) => void, options: ReadLinesOptions): TPromise { - return new TPromise((resolve, reject) => { + private readlinesAsync(filename: string, perLineCallback: (line: string, lineNumber: number) => void, options: ReadLinesOptions): Promise { + return new Promise((resolve, reject) => { fs.open(filename, 'r', null, (error: Error, fd: number) => { if (error) { return resolve(null); diff --git a/src/vs/workbench/services/search/node/legacy/worker/searchWorkerApp.ts b/src/vs/workbench/services/search/node/legacy/worker/searchWorkerApp.ts index da7c64e7b..3abda129a 100644 --- a/src/vs/workbench/services/search/node/legacy/worker/searchWorkerApp.ts +++ b/src/vs/workbench/services/search/node/legacy/worker/searchWorkerApp.ts @@ -7,7 +7,7 @@ import { Server } from 'vs/base/parts/ipc/node/ipc.cp'; import { SearchWorkerChannel } from './searchWorkerIpc'; import { SearchWorker } from './searchWorker'; -const server = new Server(); +const server = new Server('searchWorker'); const worker = new SearchWorker(); const channel = new SearchWorkerChannel(worker); server.registerChannel('searchWorker', channel); diff --git a/src/vs/workbench/services/search/node/legacy/worker/searchWorkerIpc.ts b/src/vs/workbench/services/search/node/legacy/worker/searchWorkerIpc.ts index 0e0cd8470..1a8fb436f 100644 --- a/src/vs/workbench/services/search/node/legacy/worker/searchWorkerIpc.ts +++ b/src/vs/workbench/services/search/node/legacy/worker/searchWorkerIpc.ts @@ -3,8 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; import { IPatternInfo, ITextSearchPreviewOptions } from 'vs/platform/search/common/search'; import { SearchWorker } from './searchWorker'; import { Event } from 'vs/base/common/event'; @@ -25,48 +24,41 @@ export interface ISearchWorkerSearchResult { } export interface ISearchWorker { - initialize(): TPromise; - search(args: ISearchWorkerSearchArgs): TPromise; - cancel(): TPromise; + initialize(): Thenable; + search(args: ISearchWorkerSearchArgs): Thenable; + cancel(): Thenable; } -export interface ISearchWorkerChannel extends IChannel { - call(command: 'initialize'): TPromise; - call(command: 'search', args: ISearchWorkerSearchArgs): TPromise; - call(command: 'cancel'): TPromise; - call(command: string, arg?: any): TPromise; -} - -export class SearchWorkerChannel implements ISearchWorkerChannel { +export class SearchWorkerChannel implements IServerChannel { constructor(private worker: SearchWorker) { } - listen(event: string, arg?: any): Event { + listen(): Event { throw new Error('No events'); } - call(command: string, arg?: any): TPromise { + call(_, command: string, arg?: any): Promise { switch (command) { case 'initialize': return this.worker.initialize(); case 'search': return this.worker.search(arg); case 'cancel': return this.worker.cancel(); } - return undefined; + throw new Error(`Call not found: ${command}`); } } export class SearchWorkerChannelClient implements ISearchWorker { - constructor(private channel: ISearchWorkerChannel) { } + constructor(private channel: IChannel) { } - initialize(): TPromise { + initialize(): Thenable { return this.channel.call('initialize'); } - search(args: ISearchWorkerSearchArgs): TPromise { + search(args: ISearchWorkerSearchArgs): Thenable { return this.channel.call('search', args); } - cancel(): TPromise { + cancel(): Thenable { return this.channel.call('cancel'); } } diff --git a/src/vs/workbench/services/search/node/rawSearchService.ts b/src/vs/workbench/services/search/node/rawSearchService.ts index 0a6f6efa9..d8c2a7eac 100644 --- a/src/vs/workbench/services/search/node/rawSearchService.ts +++ b/src/vs/workbench/services/search/node/rawSearchService.ts @@ -15,7 +15,6 @@ import * as objects from 'vs/base/common/objects'; import { StopWatch } from 'vs/base/common/stopwatch'; import * as strings from 'vs/base/common/strings'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { compareItemsByScore, IItemAccessor, prepareQuery, ScorerCache } from 'vs/base/parts/quickopen/common/quickOpenScorer'; import { MAX_FILE_SIZE } from 'vs/platform/files/node/files'; import { ICachedSearchStats, IFileQuery, IFileSearchStats, IFolderQuery, IProgress, IRawFileQuery, IRawQuery, IRawTextQuery, ITextQuery } from 'vs/platform/search/common/search'; @@ -89,11 +88,11 @@ export class SearchService implements IRawSearchService { return engine.search(token, progressCallback, progressCallback); } - doFileSearch(config: IFileQuery, progressCallback: IProgressCallback, token?: CancellationToken): TPromise { + doFileSearch(config: IFileQuery, progressCallback: IProgressCallback, token?: CancellationToken): Promise { return this.doFileSearchWithEngine(FileSearchEngine, config, progressCallback, token); } - doFileSearchWithEngine(EngineClass: { new(config: IFileQuery): ISearchEngine; }, config: IFileQuery, progressCallback: IProgressCallback, token?: CancellationToken, batchSize = SearchService.BATCH_SIZE): TPromise { + doFileSearchWithEngine(EngineClass: { new(config: IFileQuery): ISearchEngine; }, config: IFileQuery, progressCallback: IProgressCallback, token?: CancellationToken, batchSize = SearchService.BATCH_SIZE): Promise { let resultCount = 0; const fileProgressCallback: IFileProgressCallback = progress => { if (Array.isArray(progress)) { @@ -115,7 +114,7 @@ export class SearchService implements IRawSearchService { sortedSearch = this.doSortedSearch(engine, config, progressCallback, fileProgressCallback, token); } - return new TPromise((c, e) => { + return new Promise((c, e) => { sortedSearch.then(([result, rawMatches]) => { const serializedMatches = rawMatches.map(rawMatch => this.rawMatchToSearchItem(rawMatch)); this.sendProgress(serializedMatches, progressCallback, batchSize); @@ -145,7 +144,7 @@ export class SearchService implements IRawSearchService { return { path: match.base ? join(match.base, match.relativePath) : match.relativePath }; } - private doSortedSearch(engine: ISearchEngine, config: IFileQuery, progressCallback: IProgressCallback, fileProgressCallback: IFileProgressCallback, token?: CancellationToken): TPromise<[ISerializedSearchSuccess, IRawFileMatch[]]> { + private doSortedSearch(engine: ISearchEngine, config: IFileQuery, progressCallback: IProgressCallback, fileProgressCallback: IFileProgressCallback, token?: CancellationToken): Promise<[ISerializedSearchSuccess, IRawFileMatch[]]> { const emitter = new Emitter(); let allResultsPromise = createCancelablePromise(token => { @@ -184,31 +183,29 @@ export class SearchService implements IRawSearchService { allResultsPromise = this.preventCancellation(allResultsPromise); } - return TPromise.wrap<[ISerializedSearchSuccess, IRawFileMatch[]]>( - allResultsPromise.then(([result, results]) => { - const scorerCache: ScorerCache = cache ? cache.scorerCache : Object.create(null); - const sortSW = (typeof config.maxResults !== 'number' || config.maxResults > 0) && StopWatch.create(false); - return this.sortResults(config, results, scorerCache, token) - .then<[ISerializedSearchSuccess, IRawFileMatch[]]>(sortedResults => { - // sortingTime: -1 indicates a "sorted" search that was not sorted, i.e. populating the cache when quickopen is opened. - // Contrasting with findFiles which is not sorted and will have sortingTime: undefined - const sortingTime = sortSW ? sortSW.elapsed() : -1; - - return [{ - type: 'success', - stats: { - detailStats: result.stats, - sortingTime, - fromCache: false, - type: 'searchProcess', - workspaceFolderCount: config.folderQueries.length, - resultCount: sortedResults.length - }, - limitHit: result.limitHit || typeof config.maxResults === 'number' && results.length > config.maxResults - } as ISerializedSearchSuccess, sortedResults]; - }); - }) - ); + return allResultsPromise.then(([result, results]) => { + const scorerCache: ScorerCache = cache ? cache.scorerCache : Object.create(null); + const sortSW = (typeof config.maxResults !== 'number' || config.maxResults > 0) && StopWatch.create(false); + return this.sortResults(config, results, scorerCache, token) + .then<[ISerializedSearchSuccess, IRawFileMatch[]]>(sortedResults => { + // sortingTime: -1 indicates a "sorted" search that was not sorted, i.e. populating the cache when quickopen is opened. + // Contrasting with findFiles which is not sorted and will have sortingTime: undefined + const sortingTime = sortSW ? sortSW.elapsed() : -1; + + return [{ + type: 'success', + stats: { + detailStats: result.stats, + sortingTime, + fromCache: false, + type: 'searchProcess', + workspaceFolderCount: config.folderQueries.length, + resultCount: sortedResults.length + }, + limitHit: result.limitHit || typeof config.maxResults === 'number' && results.length > config.maxResults + } as ISerializedSearchSuccess, sortedResults]; + }); + }); } private getOrCreateCache(cacheKey: string): Cache { @@ -219,7 +216,7 @@ export class SearchService implements IRawSearchService { return this.caches[cacheKey] = new Cache(); } - private trySortedSearchFromCache(config: IFileQuery, progressCallback: IFileProgressCallback, token?: CancellationToken): TPromise<[ISerializedSearchSuccess, IRawFileMatch[]]> { + private trySortedSearchFromCache(config: IFileQuery, progressCallback: IFileProgressCallback, token?: CancellationToken): Promise<[ISerializedSearchSuccess, IRawFileMatch[]]> { const cache = config.cacheKey && this.caches[config.cacheKey]; if (!cache) { return undefined; @@ -254,7 +251,7 @@ export class SearchService implements IRawSearchService { return undefined; } - private sortResults(config: IFileQuery, results: IRawFileMatch[], scorerCache: ScorerCache, token?: CancellationToken): TPromise { + private sortResults(config: IFileQuery, results: IRawFileMatch[], scorerCache: ScorerCache, token?: CancellationToken): Promise { // we use the same compare function that is used later when showing the results using fuzzy scoring // this is very important because we are also limiting the number of results by config.maxResults // and as such we want the top items to be included in this result set if the number of items @@ -275,7 +272,7 @@ export class SearchService implements IRawSearchService { } } - private getResultsFromCache(cache: Cache, searchValue: string, progressCallback: IFileProgressCallback, token?: CancellationToken): TPromise<[ISearchEngineSuccess, IRawFileMatch[], ICachedSearchStats]> { + private getResultsFromCache(cache: Cache, searchValue: string, progressCallback: IFileProgressCallback, token?: CancellationToken): Promise<[ISearchEngineSuccess, IRawFileMatch[], ICachedSearchStats]> { const cacheLookupSW = StopWatch.create(false); // Find cache entries by prefix of search value @@ -312,7 +309,7 @@ export class SearchService implements IRawSearchService { }); } - return TPromise.wrap(cachedRow.promise.then<[ISearchEngineSuccess, IRawFileMatch[], ICachedSearchStats]>(([complete, cachedEntries]) => { + return cachedRow.promise.then<[ISearchEngineSuccess, IRawFileMatch[], ICachedSearchStats]>(([complete, cachedEntries]) => { if (token && token.isCancellationRequested) { throw canceled(); } @@ -337,13 +334,13 @@ export class SearchService implements IRawSearchService { cacheFilterTime: cacheFilterSW.elapsed(), cacheEntryCount: cachedEntries.length }]; - })); + }); } - private doSearch(engine: ISearchEngine, progressCallback: IFileProgressCallback, batchSize: number, token?: CancellationToken): TPromise { - return new TPromise((c, e) => { + private doSearch(engine: ISearchEngine, progressCallback: IFileProgressCallback, batchSize: number, token?: CancellationToken): Promise { + return new Promise((c, e) => { let batch: IRawFileMatch[] = []; if (token) { token.onCancellationRequested(() => engine.cancel()); @@ -377,9 +374,9 @@ export class SearchService implements IRawSearchService { }); } - public clearCache(cacheKey: string): TPromise { + public clearCache(cacheKey: string): Promise { delete this.caches[cacheKey]; - return TPromise.as(undefined); + return Promise.resolve(undefined); } /** diff --git a/src/vs/workbench/services/search/node/ripgrepFileSearch.ts b/src/vs/workbench/services/search/node/ripgrepFileSearch.ts index 950b35ccb..d722e44eb 100644 --- a/src/vs/workbench/services/search/node/ripgrepFileSearch.ts +++ b/src/vs/workbench/services/search/node/ripgrepFileSearch.ts @@ -44,7 +44,7 @@ function getRgArgs(config: IFileQuery, folderQuery: IFolderQuery, includePattern } }); - let siblingClauses: glob.IExpression; + let siblingClauses: glob.IExpression | null; const rgGlobs = foldersToRgExcludeGlobs([folderQuery], excludePattern, undefined, false); rgGlobs.globArgs.forEach(globArg => { @@ -85,7 +85,7 @@ function getRgArgs(config: IFileQuery, folderQuery: IFolderQuery, includePattern export interface IRgGlobResult { globArgs: string[]; - siblingClauses: glob.IExpression; + siblingClauses: glob.IExpression | null; } export function foldersToRgExcludeGlobs(folderQueries: IFolderQuery[], globalExclude: glob.IExpression, excludesToSkip?: Set, absoluteGlobs = true): IRgGlobResult { @@ -93,7 +93,7 @@ export function foldersToRgExcludeGlobs(folderQueries: IFolderQuery[], globalExc let siblingClauses: glob.IExpression = {}; folderQueries.forEach(folderQuery => { const totalExcludePattern = objects.assign({}, folderQuery.excludePattern || {}, globalExclude || {}); - const result = globExprsToRgGlobs(totalExcludePattern, absoluteGlobs && folderQuery.folder.fsPath, excludesToSkip); + const result = globExprsToRgGlobs(totalExcludePattern, absoluteGlobs ? folderQuery.folder.fsPath : undefined, excludesToSkip); globArgs.push(...result.globArgs); if (result.siblingClauses) { siblingClauses = objects.assign(siblingClauses, result.siblingClauses); @@ -107,7 +107,7 @@ export function foldersToIncludeGlobs(folderQueries: IFolderQuery[], globalInclu const globArgs: string[] = []; folderQueries.forEach(folderQuery => { const totalIncludePattern = objects.assign({}, globalInclude || {}, folderQuery.includePattern || {}); - const result = globExprsToRgGlobs(totalIncludePattern, absoluteGlobs && folderQuery.folder.fsPath); + const result = globExprsToRgGlobs(totalIncludePattern, absoluteGlobs ? folderQuery.folder.fsPath : undefined); globArgs.push(...result.globArgs); }); @@ -116,7 +116,7 @@ export function foldersToIncludeGlobs(folderQueries: IFolderQuery[], globalInclu function globExprsToRgGlobs(patterns: glob.IExpression, folder?: string, excludesToSkip?: Set): IRgGlobResult { const globArgs: string[] = []; - let siblingClauses: glob.IExpression = null; + let siblingClauses: glob.IExpression | null = null; Object.keys(patterns) .forEach(key => { if (excludesToSkip && excludesToSkip.has(key)) { diff --git a/src/vs/workbench/services/search/node/ripgrepSearchUtils.ts b/src/vs/workbench/services/search/node/ripgrepSearchUtils.ts index 601d679d5..9b2bad635 100644 --- a/src/vs/workbench/services/search/node/ripgrepSearchUtils.ts +++ b/src/vs/workbench/services/search/node/ripgrepSearchUtils.ts @@ -5,8 +5,9 @@ import { startsWith } from 'vs/base/common/strings'; import { ILogService } from 'vs/platform/log/common/log'; -import { SearchRange, TextSearchResult } from 'vs/platform/search/common/search'; +import { SearchRange, TextSearchMatch } from 'vs/platform/search/common/search'; import * as vscode from 'vscode'; +import { mapArrayOrNot } from 'vs/base/common/arrays'; export type Maybe = T | null | undefined; @@ -14,26 +15,32 @@ export function anchorGlob(glob: string): string { return startsWith(glob, '**') || startsWith(glob, '/') ? glob : `/${glob}`; } -export function createTextSearchResult(uri: vscode.Uri, text: string, range: Range, previewOptions?: vscode.TextSearchPreviewOptions): vscode.TextSearchResult { - const searchRange: SearchRange = { - startLineNumber: range.start.line, - startColumn: range.start.character, - endLineNumber: range.end.line, - endColumn: range.end.character, - }; +/** + * Create a vscode.TextSearchResult by using our internal TextSearchResult type for its previewOptions logic. + */ +export function createTextSearchResult(uri: vscode.Uri, text: string, range: Range | Range[], previewOptions?: vscode.TextSearchPreviewOptions): vscode.TextSearchMatch { + const searchRange = mapArrayOrNot(range, rangeToSearchRange); - const internalResult = new TextSearchResult(text, searchRange, previewOptions); - const internalPreviewRange = internalResult.preview.match; + const internalResult = new TextSearchMatch(text, searchRange, previewOptions); + const internalPreviewRange = internalResult.preview.matches; return { - range: new Range(internalResult.range.startLineNumber, internalResult.range.startColumn, internalResult.range.endLineNumber, internalResult.range.endColumn), + ranges: mapArrayOrNot(searchRange, searchRangeToRange), uri, preview: { text: internalResult.preview.text, - match: new Range(internalPreviewRange.startLineNumber, internalPreviewRange.startColumn, internalPreviewRange.endLineNumber, internalPreviewRange.endColumn), + matches: mapArrayOrNot(internalPreviewRange, searchRangeToRange) } }; } +function rangeToSearchRange(range: Range): SearchRange { + return new SearchRange(range.start.line, range.start.character, range.end.line, range.end.character); +} + +function searchRangeToRange(range: SearchRange): Range { + return new Range(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn); +} + export class Position { constructor(public readonly line, public readonly character) { } @@ -45,10 +52,10 @@ export class Position { compareTo(other: Position): number { return 0; } translate(lineDelta?: number, characterDelta?: number): Position; translate(change: { lineDelta?: number; characterDelta?: number; }): Position; - translate(_: any) { return null; } + translate(_?: any, _2?: any): Position { return new Position(0, 0); } with(line?: number, character?: number): Position; with(change: { line?: number; character?: number; }): Position; - with(_: any): Position { return null; } + with(_: any): Position { return new Position(0, 0); } } export class Range { @@ -64,12 +71,12 @@ export class Range { isSingleLine: boolean; contains(positionOrRange: Position | Range): boolean { return false; } isEqual(other: Range): boolean { return false; } - intersection(range: Range): Range | undefined { return null; } - union(other: Range): Range { return null; } + intersection(range: Range): Range | undefined { return undefined; } + union(other: Range): Range { return new Range(0, 0, 0, 0); } with(start?: Position, end?: Position): Range; with(change: { start?: Position, end?: Position }): Range; - with(_: any): Range { return null; } + with(_: any): Range { return new Range(0, 0, 0, 0); } } export interface IOutputChannel { diff --git a/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts b/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts index 0d3623f1c..8f3aa5955 100644 --- a/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts +++ b/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts @@ -7,12 +7,12 @@ import * as cp from 'child_process'; import { EventEmitter } from 'events'; import * as path from 'path'; import { NodeStringDecoder, StringDecoder } from 'string_decoder'; -import { startsWith } from 'vs/base/common/strings'; +import { createRegExp, startsWith, startsWithUTF8BOM, stripUTF8BOM, escapeRegExpCharacters } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; +import { IExtendedExtensionSearchOptions, SearchError, SearchErrorCode, serializeSearchError } from 'vs/platform/search/common/search'; import * as vscode from 'vscode'; import { rgPath } from 'vscode-ripgrep'; import { anchorGlob, createTextSearchResult, IOutputChannel, Maybe, Range } from './ripgrepSearchUtils'; -import { IExtendedExtensionSearchOptions } from 'vs/platform/search/common/search'; // If vscode-ripgrep is in an .asar file, then the binary is unpacked. const rgDiskPath = rgPath.replace(/\bnode_modules\.asar\b/, 'node_modules.asar.unpacked'); @@ -45,7 +45,7 @@ export class RipgrepTextSearchEngine { rgProc.on('error', e => { console.error(e); this.outputChannel.appendLine('Error: ' + (e && e.message)); - reject(e); + reject(serializeSearchError(new SearchError(e && e.message, SearchErrorCode.rgProcessError))); }); let gotResult = false; @@ -98,9 +98,9 @@ export class RipgrepTextSearchEngine { // Trigger last result ripgrepParser.flush(); rgProc = null; - let displayMsg: Maybe; - if (stderr && !gotData && (displayMsg = rgErrorMsgForDisplay(stderr))) { - reject(new Error(displayMsg)); + let searchError: Maybe; + if (stderr && !gotData && (searchError = rgErrorMsgForDisplay(stderr))) { + reject(serializeSearchError(new SearchError(searchError.message, searchError.code))); } else { resolve({ limitHit }); } @@ -115,21 +115,26 @@ export class RipgrepTextSearchEngine { * Ripgrep produces stderr output which is not from a fatal error, and we only want the search to be * "failed" when a fatal error was produced. */ -export function rgErrorMsgForDisplay(msg: string): Maybe { +export function rgErrorMsgForDisplay(msg: string): Maybe { const firstLine = msg.split('\n')[0].trim(); if (startsWith(firstLine, 'regex parse error')) { - return 'Regex parse error'; + return new SearchError('Regex parse error', SearchErrorCode.regexParseError); } let match = firstLine.match(/grep config error: unknown encoding: (.*)/); if (match) { - return `Unknown encoding: ${match[1]}`; + return new SearchError(`Unknown encoding: ${match[1]}`, SearchErrorCode.unknownEncoding); } - if (startsWith(firstLine, 'error parsing glob') || startsWith(firstLine, 'the literal')) { + if (startsWith(firstLine, 'error parsing glob')) { // Uppercase first letter - return firstLine.charAt(0).toUpperCase() + firstLine.substr(1); + return new SearchError(firstLine.charAt(0).toUpperCase() + firstLine.substr(1), SearchErrorCode.globParseError); + } + + if (startsWith(firstLine, 'the literal')) { + // Uppercase first letter + return new SearchError(firstLine.charAt(0).toUpperCase() + firstLine.substr(1), SearchErrorCode.invalidLiteral); } return undefined; @@ -138,6 +143,7 @@ export function rgErrorMsgForDisplay(msg: string): Maybe { export class RipgrepParser extends EventEmitter { private remainder = ''; private isDone = false; + private hitLimit = false; private stringDecoder: NodeStringDecoder; private numResults = 0; @@ -182,7 +188,7 @@ export class RipgrepParser extends EventEmitter { return; } - let parsedLine: any; + let parsedLine: IRgMessage; try { parsedLine = JSON.parse(outputLine); } catch (e) { @@ -190,56 +196,83 @@ export class RipgrepParser extends EventEmitter { } if (parsedLine.type === 'match') { - let hitLimit = false; - const uri = URI.file(path.join(this.rootFolder, parsedLine.data.path.text)); - parsedLine.data.submatches.map((match: any) => { - if (hitLimit) { - return null; - } - - if (this.numResults >= this.maxResults) { - // Finish the line, then report the result below - hitLimit = true; - } - - return this.submatchToResult(parsedLine, match, uri); - }).forEach((result: any) => { - if (result) { - this.onResult(result); - } - }); + const matchPath = bytesOrTextToString(parsedLine.data.path); + const uri = URI.file(path.join(this.rootFolder, matchPath)); + const result = this.createTextSearchMatch(parsedLine.data, uri); + this.onResult(result); - if (hitLimit) { + if (this.hitLimit) { this.cancel(); this.emit('hitLimit'); } + } else if (parsedLine.type === 'context') { + const contextPath = bytesOrTextToString(parsedLine.data.path); + const uri = URI.file(path.join(this.rootFolder, contextPath)); + const result = this.createTextSearchContext(parsedLine.data, uri); + result.forEach(r => this.onResult(r)); } } - private submatchToResult(parsedLine: any, match: any, uri: vscode.Uri): vscode.TextSearchResult { - const lineNumber = parsedLine.data.line_number - 1; - let lineText = bytesOrTextToString(parsedLine.data.lines); - let matchText = bytesOrTextToString(match.match); - const newlineMatches = matchText.match(/\n/g); - const newlines = newlineMatches ? newlineMatches.length : 0; + private createTextSearchMatch(data: IRgMatch, uri: vscode.Uri): vscode.TextSearchMatch { + const lineNumber = data.line_number - 1; + const fullText = bytesOrTextToString(data.lines); + const fullTextBytes = Buffer.from(fullText); + + let prevMatchEnd = 0; + let prevMatchEndCol = 0; + let prevMatchEndLine = lineNumber; + const ranges = data.submatches.map((match, i) => { + if (this.hitLimit) { + return null; + } - const textBytes = new Buffer(lineText); - let startCol = textBytes.slice(0, match.start).toString().length; - const endChars = startCol + textBytes.slice(match.start, match.end).toString().length; + this.numResults++; + if (this.numResults >= this.maxResults) { + // Finish the line, then report the result below + this.hitLimit = true; + } - const endLineNumber = lineNumber + newlines; - let endCol = endChars - (lineText.lastIndexOf('\n', lineText.length - 2) + 1); + let matchText = bytesOrTextToString(match.match); + const inBetweenChars = fullTextBytes.slice(prevMatchEnd, match.start).toString().length; + let startCol = prevMatchEndCol + inBetweenChars; - if (lineNumber === 0) { - if (startsWithUTF8BOM(matchText)) { + const stats = getNumLinesAndLastNewlineLength(matchText); + let startLineNumber = prevMatchEndLine; + let endLineNumber = stats.numLines + startLineNumber; + let endCol = stats.numLines > 0 ? + stats.lastLineLength : + stats.lastLineLength + startCol; + + if (lineNumber === 0 && i === 0 && startsWithUTF8BOM(matchText)) { matchText = stripUTF8BOM(matchText); startCol -= 3; endCol -= 3; } - } - const range = new Range(lineNumber, startCol, endLineNumber, endCol); - return createTextSearchResult(uri, lineText, range, this.previewOptions); + prevMatchEnd = match.end; + prevMatchEndCol = endCol; + prevMatchEndLine = endLineNumber; + + return new Range(startLineNumber, startCol, endLineNumber, endCol); + }) + .filter(r => !!r); + + return createTextSearchResult(uri, fullText, ranges, this.previewOptions); + } + + private createTextSearchContext(data: IRgMatch, uri: URI): vscode.TextSearchContext[] { + const text = bytesOrTextToString(data.lines); + const startLine = data.line_number; + return text + .replace(/\r?\n$/, '') + .split('\n') + .map((line, i) => { + return { + text: line, + uri, + lineNumber: startLine + i + }; + }); } private onResult(match: vscode.TextSearchResult): void { @@ -249,12 +282,29 @@ export class RipgrepParser extends EventEmitter { function bytesOrTextToString(obj: any): string { return obj.bytes ? - new Buffer(obj.bytes, 'base64').toString() : + Buffer.from(obj.bytes, 'base64').toString() : obj.text; } +function getNumLinesAndLastNewlineLength(text: string): { numLines: number, lastLineLength: number } { + const re = /\n/g; + let numLines = 0; + let lastNewlineIdx = -1; + let match: ReturnType; + while (match = re.exec(text)) { + numLines++; + lastNewlineIdx = match.index; + } + + const lastLineLength = lastNewlineIdx >= 0 ? + text.length - lastNewlineIdx - 1 : + text.length; + + return { numLines, lastLineLength }; +} + function getRgArgs(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions): string[] { - const args = ['--hidden', '--heading', '--line-number', '--color', 'ansi', '--colors', 'path:none', '--colors', 'line:none', '--colors', 'match:fg:red', '--colors', 'match:style:nobold']; + const args = ['--hidden']; args.push(query.isCaseSensitive ? '--case-sensitive' : '--ignore-case'); options.includes @@ -280,26 +330,45 @@ function getRgArgs(query: vscode.TextSearchQuery, options: vscode.TextSearchOpti args.push('--follow'); } - if (options.encoding) { + if (options.encoding && options.encoding !== 'utf8') { args.push('--encoding', options.encoding); } + let pattern = query.pattern; + // Ripgrep handles -- as a -- arg separator. Only --. - // - is ok, --- is ok, --some-flag is handled as query text. Need to special case. - if (query.pattern === '--') { + // - is ok, --- is ok, --some-flag is also ok. Need to special case. + if (pattern === '--') { + query.isRegExp = true; + pattern = '\\-\\-'; + } + + if (query.isMultiline && !query.isRegExp) { + query.pattern = escapeRegExpCharacters(query.pattern); query.isRegExp = true; - query.pattern = '\\-\\-'; + } + + if ((options).usePCRE2) { + args.push('--pcre2'); + + if (query.isRegExp) { + pattern = unicodeEscapesToPCRE2(pattern); + } } let searchPatternAfterDoubleDashes: Maybe; if (query.isWordMatch) { - const regexp = createRegExp(query.pattern, !!query.isRegExp, { wholeWord: query.isWordMatch }); + const regexp = createRegExp(pattern, !!query.isRegExp, { wholeWord: query.isWordMatch }); const regexpStr = regexp.source.replace(/\\\//g, '/'); // RegExp.source arbitrarily returns escaped slashes. Search and destroy. args.push('--regexp', regexpStr); } else if (query.isRegExp) { - args.push('--regexp', query.pattern); + let fixedRegexpQuery = fixRegexEndingPattern(query.pattern); + fixedRegexpQuery = fixRegexNewline(fixedRegexpQuery); + fixedRegexpQuery = fixRegexCRMatchingNonWordClass(fixedRegexpQuery, !!query.isMultiline); + fixedRegexpQuery = fixRegexCRMatchingWhitespaceClass(fixedRegexpQuery, !!query.isMultiline); + args.push('--regexp', fixedRegexpQuery); } else { - searchPatternAfterDoubleDashes = query.pattern; + searchPatternAfterDoubleDashes = pattern; args.push('--fixed-strings'); } @@ -308,17 +377,18 @@ function getRgArgs(query: vscode.TextSearchQuery, options: vscode.TextSearchOpti args.push('--no-ignore-global'); } - // Match \r\n with $ - args.push('--crlf'); - args.push('--json'); if (query.isMultiline) { args.push('--multiline'); } - if ((options).usePCRE2) { - args.push('--pcre2'); + if (options.beforeContext) { + args.push('--before-context', options.beforeContext + ''); + } + + if (options.afterContext) { + args.push('--after-context', options.afterContext + ''); } // Folder to search @@ -334,57 +404,60 @@ function getRgArgs(query: vscode.TextSearchQuery, options: vscode.TextSearchOpti return args; } -interface RegExpOptions { - matchCase?: boolean; - wholeWord?: boolean; - multiline?: boolean; - global?: boolean; +export function unicodeEscapesToPCRE2(pattern: string): string { + const reg = /((?:[^\\]|^)(?:\\\\)*)\\u([a-z0-9]{4})(?!\d)/g; + // Replace an unescaped $ at the end of the pattern with \r?$ + // Match $ preceeded by none or even number of literal \ + while (pattern.match(reg)) { + pattern = pattern.replace(reg, `$1\\x{$2}`); + } + + return pattern; } -function createRegExp(searchString: string, isRegex: boolean, options: RegExpOptions = {}): RegExp { - if (!searchString) { - throw new Error('Cannot create regex from empty string'); - } - if (!isRegex) { - searchString = escapeRegExpCharacters(searchString); - } - if (options.wholeWord) { - if (!/\B/.test(searchString.charAt(0))) { - searchString = '\\b' + searchString; - } - if (!/\B/.test(searchString.charAt(searchString.length - 1))) { - searchString = searchString + '\\b'; - } - } - let modifiers = ''; - if (options.global) { - modifiers += 'g'; - } - if (!options.matchCase) { - modifiers += 'i'; - } - if (options.multiline) { - modifiers += 'm'; - } +interface IRgMessage { + type: 'match' | 'context' | string; + data: IRgMatch; +} - return new RegExp(searchString, modifiers); +interface IRgMatch { + path: IRgBytesOrText; + lines: IRgBytesOrText; + line_number: number; + absolute_offset: number; + submatches: IRgSubmatch[]; } -/** - * Escapes regular expression characters in a given string - */ -function escapeRegExpCharacters(value: string): string { - return value.replace(/[\-\\\{\}\*\+\?\|\^\$\.\[\]\(\)\#]/g, '\\$&'); +interface IRgSubmatch { + match: IRgBytesOrText; + start: number; + end: number; } -// -- UTF-8 BOM +type IRgBytesOrText = { bytes: string } | { text: string }; -const UTF8_BOM = 65279; +export function fixRegexEndingPattern(pattern: string): string { + // Replace an unescaped $ at the end of the pattern with \r?$ + // Match $ preceeded by none or even number of literal \ + return pattern.match(/([^\\]|^)(\\\\)*\$$/) ? + pattern.replace(/\$$/, '\\r?$') : + pattern; +} + +export function fixRegexNewline(pattern: string): string { + // Replace an unescaped $ at the end of the pattern with \r?$ + // Match $ preceeded by none or even number of literal \ + return pattern.replace(/([^\\]|^)(\\\\)*\\n/g, '$1$2\\r?\\n'); +} -function startsWithUTF8BOM(str: string): boolean { - return !!(str && str.length > 0 && str.charCodeAt(0) === UTF8_BOM); +export function fixRegexCRMatchingWhitespaceClass(pattern: string, isMultiline: boolean): string { + return isMultiline ? + pattern.replace(/([^\\]|^)((?:\\\\)*)\\s/g, '$1$2(\\r?\\n|[^\\S\\r])') : + pattern.replace(/([^\\]|^)((?:\\\\)*)\\s/g, '$1$2[ \\t\\f]'); } -function stripUTF8BOM(str: string): string { - return startsWithUTF8BOM(str) ? str.substr(1) : str; +export function fixRegexCRMatchingNonWordClass(pattern: string, isMultiline: boolean): string { + return isMultiline ? + pattern.replace(/([^\\]|^)((?:\\\\)*)\\W/g, '$1$2(\\r?\\n|[^\\w\\r])') : + pattern.replace(/([^\\]|^)((?:\\\\)*)\\W/g, '$1$2[^\\w\\r]'); } diff --git a/src/vs/workbench/services/search/node/search.ts b/src/vs/workbench/services/search/node/search.ts index 4fc697c22..631a515b4 100644 --- a/src/vs/workbench/services/search/node/search.ts +++ b/src/vs/workbench/services/search/node/search.ts @@ -5,8 +5,7 @@ import { Event } from 'vs/base/common/event'; import * as glob from 'vs/base/common/glob'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { IFileSearchStats, IFolderQuery, IProgress, IRawTextQuery, ISearchEngineStats, ISearchQuery, ITextSearchResult, ITextSearchStats, IRawFileQuery } from 'vs/platform/search/common/search'; +import { IFileSearchStats, IFolderQuery, IProgress, IRawFileQuery, IRawTextQuery, ISearchEngineStats, ISearchQuery, ITextSearchMatch, ITextSearchStats, ITextSearchResult } from 'vs/platform/search/common/search'; import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; export interface ITelemetryEvent { @@ -17,7 +16,7 @@ export interface ITelemetryEvent { export interface IRawSearchService { fileSearch(search: IRawFileQuery): Event; textSearch(search: IRawTextQuery): Event; - clearCache(cacheKey: string): TPromise; + clearCache(cacheKey: string): Thenable; } export interface IRawFileMatch { @@ -35,7 +34,7 @@ export interface ISearchEngine { export interface ISerializedSearchSuccess { type: 'success'; limitHit: boolean; - stats: IFileSearchStats | ITextSearchStats; + stats: IFileSearchStats | ITextSearchStats | null; } export interface ISearchEngineSuccess { @@ -72,8 +71,8 @@ export function isSerializedFileMatch(arg: ISerializedSearchProgressItem): arg i } export interface ISerializedFileMatch { - path: string; - matches?: ITextSearchResult[]; + path?: string; + results?: ITextSearchResult[]; numMatches?: number; } @@ -84,22 +83,22 @@ export type IFileSearchProgressItem = IRawFileMatch | IRawFileMatch[] | IProgres export class FileMatch implements ISerializedFileMatch { path: string; - matches: ITextSearchResult[]; + results: ITextSearchMatch[]; constructor(path: string) { this.path = path; - this.matches = []; + this.results = []; } - addMatch(match: ITextSearchResult): void { - this.matches.push(match); + addMatch(match: ITextSearchMatch): void { + this.results.push(match); } serialize(): ISerializedFileMatch { return { path: this.path, - matches: this.matches, - numMatches: this.matches.length + results: this.results, + numMatches: this.results.length }; } } @@ -107,7 +106,7 @@ export class FileMatch implements ISerializedFileMatch { /** * Computes the patterns that the provider handles. Discards sibling clauses and 'false' patterns */ -export function resolvePatternsForProvider(globalPattern: glob.IExpression, folderPattern: glob.IExpression): string[] { +export function resolvePatternsForProvider(globalPattern: glob.IExpression | undefined, folderPattern: glob.IExpression | undefined): string[] { const merged = { ...(globalPattern || {}), ...(folderPattern || {}) @@ -170,10 +169,10 @@ export class QueryGlobTester { /** * Guaranteed async. */ - public includedInQuery(testPath: string, basename?: string, hasSibling?: (name: string) => boolean | TPromise): TPromise { + public includedInQuery(testPath: string, basename?: string, hasSibling?: (name: string) => boolean | Promise): Promise { const excludeP = this._parsedExcludeExpression ? - TPromise.as(this._parsedExcludeExpression(testPath, basename, hasSibling)).then(result => !!result) : - TPromise.wrap(false); + Promise.resolve(this._parsedExcludeExpression(testPath, basename, hasSibling)).then(result => !!result) : + Promise.resolve(false); return excludeP.then(excluded => { if (excluded) { @@ -181,8 +180,8 @@ export class QueryGlobTester { } return this._parsedIncludeExpression ? - TPromise.as(this._parsedIncludeExpression(testPath, basename, hasSibling)).then(result => !!result) : - TPromise.wrap(true); + Promise.resolve(this._parsedIncludeExpression(testPath, basename, hasSibling)).then(result => !!result) : + Promise.resolve(true); }).then(included => { return included; }); diff --git a/src/vs/workbench/services/search/node/searchApp.ts b/src/vs/workbench/services/search/node/searchApp.ts index d4a163cc7..37b8a367d 100644 --- a/src/vs/workbench/services/search/node/searchApp.ts +++ b/src/vs/workbench/services/search/node/searchApp.ts @@ -7,7 +7,7 @@ import { Server } from 'vs/base/parts/ipc/node/ipc.cp'; import { SearchChannel } from './searchIpc'; import { SearchService } from './rawSearchService'; -const server = new Server(); +const server = new Server('search'); const service = new SearchService(); const channel = new SearchChannel(service); server.registerChannel('search', channel); \ No newline at end of file diff --git a/src/vs/workbench/services/search/node/searchIpc.ts b/src/vs/workbench/services/search/node/searchIpc.ts index 4703fa664..484aea618 100644 --- a/src/vs/workbench/services/search/node/searchIpc.ts +++ b/src/vs/workbench/services/search/node/searchIpc.ts @@ -4,23 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; import { IRawFileQuery, IRawTextQuery } from 'vs/platform/search/common/search'; import { IRawSearchService, ISerializedSearchComplete, ISerializedSearchProgressItem } from './search'; -export interface ISearchChannel extends IChannel { - listen(event: 'fileSearch', search: IRawFileQuery): Event; - listen(event: 'textSearch', search: IRawTextQuery): Event; - call(command: 'clearCache', cacheKey: string): TPromise; - call(command: string, arg: any): TPromise; -} - -export class SearchChannel implements ISearchChannel { +export class SearchChannel implements IServerChannel { constructor(private service: IRawSearchService) { } - listen(event: string, arg?: any): Event { + listen(_, event: string, arg?: any): Event { switch (event) { case 'fileSearch': return this.service.fileSearch(arg); case 'textSearch': return this.service.textSearch(arg); @@ -28,7 +20,7 @@ export class SearchChannel implements ISearchChannel { throw new Error('Event not found'); } - call(command: string, arg?: any): TPromise { + call(_, command: string, arg?: any): Thenable { switch (command) { case 'clearCache': return this.service.clearCache(arg); } @@ -38,7 +30,7 @@ export class SearchChannel implements ISearchChannel { export class SearchChannelClient implements IRawSearchService { - constructor(private channel: ISearchChannel) { } + constructor(private channel: IChannel) { } fileSearch(search: IRawFileQuery): Event { return this.channel.listen('fileSearch', search); @@ -48,7 +40,7 @@ export class SearchChannelClient implements IRawSearchService { return this.channel.listen('textSearch', search); } - clearCache(cacheKey: string): TPromise { + clearCache(cacheKey: string): Thenable { return this.channel.call('clearCache', cacheKey); } } \ No newline at end of file diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index b3c24eef3..260df5e35 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -14,23 +14,21 @@ import { Schemas } from 'vs/base/common/network'; import * as objects from 'vs/base/common/objects'; import { StopWatch } from 'vs/base/common/stopwatch'; import { URI as uri } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import * as pfs from 'vs/base/node/pfs'; import { getNextTickChannel } from 'vs/base/parts/ipc/node/ipc'; import { Client, IIPCOptions } from 'vs/base/parts/ipc/node/ipc.cp'; -import { Range } from 'vs/editor/common/core/range'; -import { FindMatch, ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IDebugParams, IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ILogService } from 'vs/platform/log/common/log'; -import { FileMatch, ICachedSearchStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, IProgress, ISearchComplete, ISearchConfiguration, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, ITextQuery, ITextSearchPreviewOptions, pathIncludedInQuery, QueryType, SearchProviderType, TextSearchResult } from 'vs/platform/search/common/search'; +import { deserializeSearchError, FileMatch, ICachedSearchStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, IProgress, ISearchComplete, ISearchConfiguration, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, ITextQuery, pathIncludedInQuery, QueryType, SearchError, SearchErrorCode, SearchProviderType } from 'vs/platform/search/common/search'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { addContextToEditorMatches, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IRawSearchService, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess } from './search'; -import { ISearchChannel, SearchChannelClient } from './searchIpc'; +import { SearchChannelClient } from './searchIpc'; export class SearchService extends Disposable implements ISearchService { public _serviceBrand: any; @@ -87,7 +85,7 @@ export class SearchService extends Disposable implements ISearchService { } } - public textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): TPromise { + public textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): Promise { // Get local results from dirty/untitled const localResults = this.getLocalResults(query); @@ -116,23 +114,23 @@ export class SearchService extends Disposable implements ISearchService { return this.doSearch(query, token, onProviderProgress); } - public fileSearch(query: IFileQuery, token?: CancellationToken): TPromise { + public fileSearch(query: IFileQuery, token?: CancellationToken): Promise { return this.doSearch(query, token); } - private doSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): TPromise { + private doSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): Promise { const schemesInQuery = this.getSchemesInQuery(query); - const providerActivations: TPromise[] = [TPromise.wrap(null)]; + const providerActivations: Thenable[] = [Promise.resolve(null)]; schemesInQuery.forEach(scheme => providerActivations.push(this.extensionService.activateByEvent(`onSearch:${scheme}`))); providerActivations.push(this.extensionService.activateByEvent('onSearch:file')); - const providerPromise = TPromise.join(providerActivations) + const providerPromise = Promise.all(providerActivations) .then(() => this.extensionService.whenInstalledExtensionsRegistered()) .then(() => { // Cancel faster if search was canceled while waiting for extensions if (token && token.isCancellationRequested) { - return TPromise.wrapError(canceled()); + return Promise.reject(canceled()); } const progressCallback = (item: ISearchProgressItem) => { @@ -150,7 +148,10 @@ export class SearchService extends Disposable implements ISearchService { .then(completes => { completes = completes.filter(c => !!c); if (!completes.length) { - return null; + return { + limitHit: false, + results: [] + }; } return { @@ -158,16 +159,9 @@ export class SearchService extends Disposable implements ISearchService { stats: completes[0].stats, results: arrays.flatten(completes.map(c => c.results)) }; - }, errs => { - if (!Array.isArray(errs)) { - errs = [errs]; - } - - errs = errs.filter(e => !!e); - return TPromise.wrapError(errs[0]); }); - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { if (token) { token.onCancellationRequested(() => { reject(canceled()); @@ -195,7 +189,7 @@ export class SearchService extends Disposable implements ISearchService { const e2eSW = StopWatch.create(false); const diskSearchQueries: IFolderQuery[] = []; - const searchPs: TPromise[] = []; + const searchPs: Thenable[] = []; const fqs = this.groupFolderQueriesByScheme(query); keys(fqs).forEach(scheme => { @@ -238,24 +232,20 @@ export class SearchService extends Disposable implements ISearchService { this.diskSearch.textSearch(diskSearchQuery, onProviderProgress, token)); } - return TPromise.join(searchPs).then(completes => { + return Promise.all(searchPs).then(completes => { const endToEndTime = e2eSW.elapsed(); this.logService.trace(`SearchService#search: ${endToEndTime}ms`); completes.forEach(complete => { this.sendTelemetry(query, endToEndTime, complete); }); return completes; - }, errs => { + }, err => { const endToEndTime = e2eSW.elapsed(); this.logService.trace(`SearchService#search: ${endToEndTime}ms`); - errs = errs - .filter(e => !!e); - - errs.forEach(e => { - this.sendTelemetry(query, endToEndTime, null, e); - }); + const searchError = deserializeSearchError(err.message); + this.sendTelemetry(query, endToEndTime, null, searchError); - throw errs[0]; + throw searchError; }); } @@ -272,7 +262,7 @@ export class SearchService extends Disposable implements ISearchService { return queries; } - private sendTelemetry(query: ISearchQuery, endToEndTime: number, complete?: ISearchComplete, err?: Error): void { + private sendTelemetry(query: ISearchQuery, endToEndTime: number, complete?: ISearchComplete, err?: SearchError): void { const fileSchemeOnly = query.folderQueries.every(fq => fq.folder.scheme === 'file'); const otherSchemeOnly = query.folderQueries.every(fq => fq.folder.scheme !== 'file'); const scheme = fileSchemeOnly ? 'file' : @@ -353,11 +343,12 @@ export class SearchService extends Disposable implements ISearchService { } else if (query.type === QueryType.Text) { let errorType: string; if (err) { - errorType = err.message.indexOf('Regex parse error') >= 0 ? 'regex' : - err.message.indexOf('Unknown encoding') >= 0 ? 'encoding' : - err.message.indexOf('Error parsing glob') >= 0 ? 'glob' : - err.message.indexOf('The literal') >= 0 ? 'literal' : - 'other'; + errorType = err.code === SearchErrorCode.regexParseError ? 'regex' : + err.code === SearchErrorCode.unknownEncoding ? 'encoding' : + err.code === SearchErrorCode.globParseError ? 'glob' : + err.code === SearchErrorCode.invalidLiteral ? 'literal' : + err.code === SearchErrorCode.other ? 'other' : + 'unknown'; } /* __GDPR__ @@ -378,7 +369,7 @@ export class SearchService extends Disposable implements ISearchService { scheme, error: errorType, useRipgrep: query.useRipgrep, - usePCRE2: query.usePCRE2 + usePCRE2: !!query.usePCRE2 }); } } @@ -423,9 +414,8 @@ export class SearchService extends Disposable implements ISearchService { let fileMatch = new FileMatch(resource); localResults.set(resource, fileMatch); - matches.forEach((match) => { - fileMatch.matches.push(editorMatchToTextSearchResult(match, model, query.previewOptions)); - }); + const textSearchResults = editorMatchesToTextSearchResults(matches, model, query.previewOptions); + fileMatch.results = addContextToEditorMatches(textSearchResults, model, query); } else { localResults.set(resource, null); } @@ -446,13 +436,13 @@ export class SearchService extends Disposable implements ISearchService { return pathIncludedInQuery(query, resource.fsPath); } - public clearCache(cacheKey: string): TPromise { + public clearCache(cacheKey: string): Promise { const clearPs = [ this.diskSearch, ...values(this.fileIndexProviders) ].map(provider => provider && provider.clearCache(cacheKey)); - return TPromise.join(clearPs) + return Promise.all(clearPs) .then(() => { }); } } @@ -474,7 +464,8 @@ export class DiskSearch implements ISearchResultProvider { AMD_ENTRYPOINT: 'vs/workbench/services/search/node/searchApp', PIPE_LOGGING: 'true', VERBOSE_LOGGING: verboseLogging - } + }, + useQueue: true }; if (searchDebug) { @@ -489,13 +480,13 @@ export class DiskSearch implements ISearchResultProvider { getPathFromAmdModule(require, 'bootstrap-fork'), opts); - const channel = getNextTickChannel(client.getChannel('search')); + const channel = getNextTickChannel(client.getChannel('search')); this.raw = new SearchChannelClient(channel); } - textSearch(query: ITextQuery, onProgress?: (p: ISearchProgressItem) => void, token?: CancellationToken): TPromise { + textSearch(query: ITextQuery, onProgress?: (p: ISearchProgressItem) => void, token?: CancellationToken): Promise { const folderQueries = query.folderQueries || []; - return TPromise.join(folderQueries.map(q => q.folder.scheme === Schemas.file && pfs.exists(q.folder.fsPath))) + return Promise.all(folderQueries.map(q => q.folder.scheme === Schemas.file && pfs.exists(q.folder.fsPath))) .then(exists => { if (token && token.isCancellationRequested) { throw canceled(); @@ -508,9 +499,9 @@ export class DiskSearch implements ISearchResultProvider { }); } - fileSearch(query: IFileQuery, token?: CancellationToken): TPromise { + fileSearch(query: IFileQuery, token?: CancellationToken): Promise { const folderQueries = query.folderQueries || []; - return TPromise.join(folderQueries.map(q => q.folder.scheme === Schemas.file && pfs.exists(q.folder.fsPath))) + return Promise.all(folderQueries.map(q => q.folder.scheme === Schemas.file && pfs.exists(q.folder.fsPath))) .then(exists => { if (token && token.isCancellationRequested) { throw canceled(); @@ -524,11 +515,11 @@ export class DiskSearch implements ISearchResultProvider { }); } - public static collectResultsFromEvent(event: Event, onProgress?: (p: ISearchProgressItem) => void, token?: CancellationToken): TPromise { + public static collectResultsFromEvent(event: Event, onProgress?: (p: ISearchProgressItem) => void, token?: CancellationToken): Promise { let result: IFileMatch[] = []; let listener: IDisposable; - return new TPromise((c, e) => { + return new Promise((c, e) => { if (token) { token.onCancellationRequested(() => { if (listener) { @@ -582,21 +573,15 @@ export class DiskSearch implements ISearchResultProvider { } private static createFileMatch(data: ISerializedFileMatch): FileMatch { - let fileMatch = new FileMatch(uri.file(data.path)); - if (data.matches) { - fileMatch.matches.push(...data.matches); // TODO why + const fileMatch = new FileMatch(uri.file(data.path)); + if (data.results) { + // const matches = data.results.filter(resultIsMatch); + fileMatch.results.push(...data.results); } return fileMatch; } - public clearCache(cacheKey: string): TPromise { + public clearCache(cacheKey: string): Thenable { return this.raw.clearCache(cacheKey); } } - -function editorMatchToTextSearchResult(match: FindMatch, model: ITextModel, previewOptions: ITextSearchPreviewOptions): TextSearchResult { - return new TextSearchResult( - model.getLineContent(match.range.startLineNumber), - new Range(match.range.startLineNumber - 1, match.range.startColumn - 1, match.range.endLineNumber - 1, match.range.endColumn - 1), - previewOptions); -} diff --git a/src/vs/workbench/services/search/node/textSearchAdapter.ts b/src/vs/workbench/services/search/node/textSearchAdapter.ts index 6759cd6e8..02117a2ed 100644 --- a/src/vs/workbench/services/search/node/textSearchAdapter.ts +++ b/src/vs/workbench/services/search/node/textSearchAdapter.ts @@ -5,7 +5,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import * as extfs from 'vs/base/node/extfs'; -import { IFileMatch, IProgress, ITextQuery, ITextSearchStats } from 'vs/platform/search/common/search'; +import { IFileMatch, IProgress, ITextQuery, ITextSearchStats, ITextSearchMatch } from 'vs/platform/search/common/search'; import { RipgrepTextSearchEngine } from 'vs/workbench/services/search/node/ripgrepTextSearchEngine'; import { TextSearchManager } from 'vs/workbench/services/search/node/textSearchManager'; import { ISerializedFileMatch, ISerializedSearchSuccess } from './search'; @@ -16,7 +16,7 @@ export class TextSearchEngineAdapter { } search(token: CancellationToken, onResult: (matches: ISerializedFileMatch[]) => void, onMessage: (message: IProgress) => void): Promise { - if (!this.query.folderQueries.length && !this.query.extraFileResources.length) { + if ((!this.query.folderQueries || !this.query.folderQueries.length) && (!this.query.extraFileResources || !this.query.extraFileResources.length)) { return Promise.resolve({ type: 'success', limitHit: false, @@ -40,7 +40,7 @@ export class TextSearchEngineAdapter { }, token) .then( - () => resolve({ limitHit: false, stats: null, type: 'success' }), + c => resolve({ limitHit: c.limitHit, stats: null, type: 'success' } as ISerializedSearchSuccess), reject); }); } @@ -48,8 +48,15 @@ export class TextSearchEngineAdapter { function fileMatchToSerialized(match: IFileMatch): ISerializedFileMatch { return { - path: match.resource.fsPath, - matches: match.matches, - numMatches: match.matches.length + path: match.resource ? match.resource.fsPath : undefined, + results: match.results, + numMatches: (match.results || []).reduce((sum, r) => { + if (!!(r).ranges) { + const m = r; + return sum + (Array.isArray(m.ranges) ? m.ranges.length : 1); + } else { + return sum + 1; + } + }, 0) }; -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/search/node/textSearchManager.ts b/src/vs/workbench/services/search/node/textSearchManager.ts index 51e997d26..0f15a7709 100644 --- a/src/vs/workbench/services/search/node/textSearchManager.ts +++ b/src/vs/workbench/services/search/node/textSearchManager.ts @@ -4,15 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as path from 'path'; +import { mapArrayOrNot } from 'vs/base/common/arrays'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import * as glob from 'vs/base/common/glob'; import * as resources from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { toCanonicalName } from 'vs/base/node/encoding'; import * as extfs from 'vs/base/node/extfs'; -import { IExtendedExtensionSearchOptions, IFileMatch, IFolderQuery, IPatternInfo, ISearchCompleteStats, ITextQuery, ITextSearchResult } from 'vs/platform/search/common/search'; +import { IExtendedExtensionSearchOptions, IFileMatch, IFolderQuery, IPatternInfo, ISearchCompleteStats, ITextQuery, ITextSearchMatch, ITextSearchContext, ITextSearchResult } from 'vs/platform/search/common/search'; import { QueryGlobTester, resolvePatternsForProvider } from 'vs/workbench/services/search/node/search'; import * as vscode from 'vscode'; @@ -26,12 +26,12 @@ export class TextSearchManager { constructor(private query: ITextQuery, private provider: vscode.TextSearchProvider, private _extfs: typeof extfs = extfs) { } - public search(onProgress: (matches: IFileMatch[]) => void, token: CancellationToken): TPromise { - const folderQueries = this.query.folderQueries; + public search(onProgress: (matches: IFileMatch[]) => void, token: CancellationToken): Promise { + const folderQueries = this.query.folderQueries || []; const tokenSource = new CancellationTokenSource(); token.onCancellationRequested(() => tokenSource.cancel()); - return new TPromise((resolve, reject) => { + return new Promise((resolve, reject) => { this.collector = new TextSearchResultsCollector(onProgress); let isCanceled = false; @@ -40,7 +40,7 @@ export class TextSearchManager { return; } - if (this.resultCount >= this.query.maxResults) { + if (typeof this.query.maxResults === 'number' && this.resultCount >= this.query.maxResults) { this.isLimitHit = true; isCanceled = true; tokenSource.cancel(); @@ -53,38 +53,39 @@ export class TextSearchManager { }; // For each root folder - TPromise.join(folderQueries.map((fq, i) => { + Promise.all(folderQueries.map((fq, i) => { return this.searchInFolder(fq, r => onResult(r, i), tokenSource.token); })).then(results => { tokenSource.dispose(); this.collector.flush(); - const someFolderHitLImit = results.some(result => result && result.limitHit); + const someFolderHitLImit = results.some(result => !!result && !!result.limitHit); resolve({ limitHit: this.isLimitHit || someFolderHitLImit, stats: { type: 'textSearchProvider' } }); - }, (errs: Error[]) => { + }, (err: Error) => { tokenSource.dispose(); - const errMsg = errs - .map(err => toErrorMessage(err)) - .filter(msg => !!msg)[0]; - + const errMsg = toErrorMessage(err); reject(new Error(errMsg)); }); }); } - private searchInFolder(folderQuery: IFolderQuery, onResult: (result: vscode.TextSearchResult) => void, token: CancellationToken): TPromise { + private searchInFolder(folderQuery: IFolderQuery, onResult: (result: vscode.TextSearchResult) => void, token: CancellationToken): Promise { const queryTester = new QueryGlobTester(this.query, folderQuery); - const testingPs: TPromise[] = []; + const testingPs: Promise[] = []; const progress = { report: (result: vscode.TextSearchResult) => { - const hasSibling = folderQuery.folder.scheme === 'file' && glob.hasSiblingPromiseFn(() => { - return this.readdir(path.dirname(result.uri.fsPath)); - }); + // TODO: validate result.ranges vs result.preview.matches + + const hasSibling = folderQuery.folder.scheme === 'file' ? + glob.hasSiblingPromiseFn(() => { + return this.readdir(path.dirname(result.uri.fsPath)); + }) : + undefined; const relativePath = path.relative(folderQuery.folder.fsPath, result.uri.fsPath); testingPs.push( @@ -98,16 +99,16 @@ export class TextSearchManager { }; const searchOptions = this.getSearchOptionsForFolder(folderQuery); - return new TPromise(resolve => process.nextTick(resolve)) + return new Promise(resolve => process.nextTick(resolve)) .then(() => this.provider.provideTextSearchResults(patternInfoToQuery(this.query.contentPattern), searchOptions, progress, token)) .then(result => { - return TPromise.join(testingPs) + return Promise.all(testingPs) .then(() => result); }); } - private readdir(dirname: string): TPromise { - return new TPromise((resolve, reject) => { + private readdir(dirname: string): Promise { + return new Promise((resolve, reject) => { this._extfs.readdir(dirname, (err, files) => { if (err) { return reject(err); @@ -122,7 +123,7 @@ export class TextSearchManager { const includes = resolvePatternsForProvider(this.query.includePattern, fq.includePattern); const excludes = resolvePatternsForProvider(this.query.excludePattern, fq.excludePattern); - const options = { + const options = { folder: URI.from(fq.folder), excludes, includes, @@ -132,7 +133,9 @@ export class TextSearchManager { encoding: fq.fileEncoding && toCanonicalName(fq.fileEncoding), maxFileSize: this.query.maxFileSize, maxResults: this.query.maxResults, - previewOptions: this.query.previewOptions + previewOptions: this.query.previewOptions, + afterContext: this.query.afterContext, + beforeContext: this.query.beforeContext }; (options).usePCRE2 = this.query.usePCRE2; return options; @@ -154,7 +157,7 @@ export class TextSearchResultsCollector { private _currentFolderIdx: number; private _currentUri: URI; - private _currentFileMatch: IFileMatch; + private _currentFileMatch: IFileMatch | null = null; constructor(private _onResult: (result: IFileMatch[]) => void) { this._batchedCollector = new BatchedCollector(512, items => this.sendItems(items)); @@ -173,18 +176,18 @@ export class TextSearchResultsCollector { this._currentFolderIdx = folderIdx; this._currentFileMatch = { resource: data.uri, - matches: [] + results: [] }; } - this._currentFileMatch.matches.push(extensionResultToFrontendResult(data)); + this._currentFileMatch.results!.push(extensionResultToFrontendResult(data)); } private pushToCollector(): void { - const size = this._currentFileMatch ? - this._currentFileMatch.matches.length : + const size = this._currentFileMatch && this._currentFileMatch.results ? + this._currentFileMatch.results.length : 0; - this._batchedCollector.addItem(this._currentFileMatch, size); + this._batchedCollector.addItem(this._currentFileMatch!, size); } flush(): void { @@ -199,23 +202,34 @@ export class TextSearchResultsCollector { function extensionResultToFrontendResult(data: vscode.TextSearchResult): ITextSearchResult { // Warning: result from RipgrepTextSearchEH has fake vscode.Range. Don't depend on any other props beyond these... - return { - preview: { - match: { - startLineNumber: data.preview.match.start.line, - startColumn: data.preview.match.start.character, - endLineNumber: data.preview.match.end.line, - endColumn: data.preview.match.end.character + if (extensionResultIsMatch(data)) { + return { + preview: { + matches: mapArrayOrNot(data.preview.matches, m => ({ + startLineNumber: m.start.line, + startColumn: m.start.character, + endLineNumber: m.end.line, + endColumn: m.end.character + })), + text: data.preview.text }, - text: data.preview.text - }, - range: { - startLineNumber: data.range.start.line, - startColumn: data.range.start.character, - endLineNumber: data.range.end.line, - endColumn: data.range.end.character - } - }; + ranges: mapArrayOrNot(data.ranges, r => ({ + startLineNumber: r.start.line, + startColumn: r.start.character, + endLineNumber: r.end.line, + endColumn: r.end.character + })) + }; + } else { + return { + text: data.text, + lineNumber: data.lineNumber + }; + } +} + +export function extensionResultIsMatch(data: vscode.TextSearchResult): data is vscode.TextSearchMatch { + return !!(data).preview; } /** diff --git a/src/vs/workbench/services/search/test/common/searchHelpers.test.ts b/src/vs/workbench/services/search/test/common/searchHelpers.test.ts new file mode 100644 index 000000000..758ccd272 --- /dev/null +++ b/src/vs/workbench/services/search/test/common/searchHelpers.test.ts @@ -0,0 +1,190 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { ITextModel, FindMatch } from 'vs/editor/common/model'; +import { editorMatchesToTextSearchResults, addContextToEditorMatches } from 'vs/workbench/services/search/common/searchHelpers'; +import { Range } from 'vs/editor/common/core/range'; +import { ITextQuery, QueryType, ITextSearchContext } from 'vs/platform/search/common/search'; + +suite('SearchHelpers', () => { + suite('editorMatchesToTextSearchResults', () => { + const mockTextModel: ITextModel = { + getLineContent(lineNumber: number): string { + return '' + lineNumber; + } + }; + + test('simple', () => { + const results = editorMatchesToTextSearchResults([new FindMatch(new Range(6, 1, 6, 2), null)], mockTextModel); + assert.equal(results.length, 1); + assert.equal(results[0].preview.text, '6\n'); + assert.deepEqual(results[0].preview.matches, [new Range(0, 0, 0, 1)]); + assert.deepEqual(results[0].ranges, [new Range(5, 0, 5, 1)]); + }); + + test('multiple', () => { + const results = editorMatchesToTextSearchResults( + [ + new FindMatch(new Range(6, 1, 6, 2), null), + new FindMatch(new Range(6, 4, 8, 2), null), + new FindMatch(new Range(9, 1, 10, 3), null), + ], + mockTextModel); + assert.equal(results.length, 2); + assert.deepEqual(results[0].preview.matches, [ + new Range(0, 0, 0, 1), + new Range(0, 3, 2, 1), + ]); + assert.deepEqual(results[0].ranges, [ + new Range(5, 0, 5, 1), + new Range(5, 3, 7, 1), + ]); + assert.equal(results[0].preview.text, '6\n7\n8\n'); + + assert.deepEqual(results[1].preview.matches, [ + new Range(0, 0, 1, 2), + ]); + assert.deepEqual(results[1].ranges, [ + new Range(8, 0, 9, 2), + ]); + assert.equal(results[1].preview.text, '9\n10\n'); + }); + }); + + suite('addContextToEditorMatches', () => { + const MOCK_LINE_COUNT = 100; + + const mockTextModel: ITextModel = { + getLineContent(lineNumber: number): string { + if (lineNumber < 1 || lineNumber > MOCK_LINE_COUNT) { + throw new Error(`invalid line count: ${lineNumber}`); + } + + return '' + lineNumber; + }, + + getLineCount(): number { + return MOCK_LINE_COUNT; + } + }; + + function getQuery(beforeContext?: number, afterContext?: number): ITextQuery { + return { + type: QueryType.Text, + contentPattern: { pattern: 'test' }, + beforeContext, + afterContext + }; + } + + test('no context', () => { + const matches = [{ + preview: { + text: 'foo', + matches: new Range(0, 0, 0, 10) + }, + ranges: new Range(0, 0, 0, 10) + }]; + + assert.deepEqual(addContextToEditorMatches(matches, mockTextModel, getQuery()), matches); + }); + + test('simple', () => { + const matches = [{ + preview: { + text: 'foo', + matches: new Range(0, 0, 0, 10) + }, + ranges: new Range(1, 0, 1, 10) + }]; + + assert.deepEqual(addContextToEditorMatches(matches, mockTextModel, getQuery(1, 2)), [ + { + text: '1', + lineNumber: 0 + }, + ...matches, + { + text: '3', + lineNumber: 2 + }, + { + text: '4', + lineNumber: 3 + }, + ]); + }); + + test('multiple matches next to each other', () => { + const matches = [ + { + preview: { + text: 'foo', + matches: new Range(0, 0, 0, 10) + }, + ranges: new Range(1, 0, 1, 10) + }, + { + preview: { + text: 'bar', + matches: new Range(0, 0, 0, 10) + }, + ranges: new Range(2, 0, 2, 10) + }]; + + assert.deepEqual(addContextToEditorMatches(matches, mockTextModel, getQuery(1, 2)), [ + { + text: '1', + lineNumber: 0 + }, + ...matches, + { + text: '4', + lineNumber: 3 + }, + { + text: '5', + lineNumber: 4 + }, + ]); + }); + + test('boundaries', () => { + const matches = [ + { + preview: { + text: 'foo', + matches: new Range(0, 0, 0, 10) + }, + ranges: new Range(0, 0, 0, 10) + }, + { + preview: { + text: 'bar', + matches: new Range(0, 0, 0, 10) + }, + ranges: new Range(MOCK_LINE_COUNT - 1, 0, MOCK_LINE_COUNT - 1, 10) + }]; + + assert.deepEqual(addContextToEditorMatches(matches, mockTextModel, getQuery(1, 2)), [ + matches[0], + { + text: '2', + lineNumber: 1 + }, + { + text: '3', + lineNumber: 2 + }, + { + text: '' + (MOCK_LINE_COUNT - 1), + lineNumber: MOCK_LINE_COUNT - 2 + }, + matches[1] + ]); + }); + }); +}); \ No newline at end of file diff --git a/src/vs/workbench/services/search/test/node/searchService.test.ts b/src/vs/workbench/services/search/test/node/rawSearchService.test.ts similarity index 99% rename from src/vs/workbench/services/search/test/node/searchService.test.ts rename to src/vs/workbench/services/search/test/node/rawSearchService.test.ts index f8544972b..cf3b096d2 100644 --- a/src/vs/workbench/services/search/test/node/searchService.test.ts +++ b/src/vs/workbench/services/search/test/node/rawSearchService.test.ts @@ -74,7 +74,7 @@ class TestSearchEngine implements ISearchEngine { const testTimeout = 5000; -suite('SearchService', () => { +suite('RawSearchService', () => { const rawSearch: IFileQuery = { type: QueryType.File, diff --git a/src/vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts b/src/vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts new file mode 100644 index 000000000..39f4b9171 --- /dev/null +++ b/src/vs/workbench/services/search/test/node/ripgrepTextSearchEngine.test.ts @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { unicodeEscapesToPCRE2, fixRegexEndingPattern, fixRegexCRMatchingWhitespaceClass, fixRegexCRMatchingNonWordClass, fixRegexNewline } from 'vs/workbench/services/search/node/ripgrepTextSearchEngine'; + +suite('RipgrepTextSearchEngine', () => { + test('unicodeEscapesToPCRE2', async () => { + assert.equal(unicodeEscapesToPCRE2('\\u1234'), '\\x{1234}'); + assert.equal(unicodeEscapesToPCRE2('\\u1234\\u0001'), '\\x{1234}\\x{0001}'); + assert.equal(unicodeEscapesToPCRE2('foo\\u1234bar'), 'foo\\x{1234}bar'); + assert.equal(unicodeEscapesToPCRE2('\\\\\\u1234'), '\\\\\\x{1234}'); + assert.equal(unicodeEscapesToPCRE2('foo\\\\\\u1234'), 'foo\\\\\\x{1234}'); + + assert.equal(unicodeEscapesToPCRE2('\\u123'), '\\u123'); + assert.equal(unicodeEscapesToPCRE2('\\u12345'), '\\u12345'); + assert.equal(unicodeEscapesToPCRE2('\\\\u12345'), '\\\\u12345'); + assert.equal(unicodeEscapesToPCRE2('foo'), 'foo'); + assert.equal(unicodeEscapesToPCRE2(''), ''); + }); + + test('fixRegexEndingPattern', () => { + function testFixRegexEndingPattern([input, expectedResult]: string[]): void { + assert.equal(fixRegexEndingPattern(input), expectedResult); + } + + [ + ['foo', 'foo'], + ['', ''], + ['^foo.*bar\\s+', '^foo.*bar\\s+'], + ['foo$', 'foo\\r?$'], + ['$', '\\r?$'], + ['foo\\$', 'foo\\$'], + ['foo\\\\$', 'foo\\\\\\r?$'], + ].forEach(testFixRegexEndingPattern); + }); + + test('fixRegexCRMatchingWhitespaceClass', () => { + function testFixRegexCRMatchingWhitespaceClass([inputReg, isMultiline, testStr, shouldMatch]): void { + const fixed = fixRegexCRMatchingWhitespaceClass(inputReg, isMultiline); + const reg = new RegExp(fixed); + assert.equal(reg.test(testStr), shouldMatch, `${inputReg} => ${reg}, ${testStr}, ${shouldMatch}`); + } + + [ + ['foo', false, 'foo', true], + + ['foo\\s', false, 'foo\r\n', false], + ['foo\\sabc', true, 'foo\r\nabc', true], + + ['foo\\s', false, 'foo\n', false], + ['foo\\s', true, 'foo\n', true], + + ['foo\\s\\n', true, 'foo\r\n', false], + ['foo\\r\\s', true, 'foo\r\n', true], + + ['foo\\s+abc', true, 'foo \r\nabc', true], + ['foo\\s+abc', false, 'foo \t abc', true], + ].forEach(testFixRegexCRMatchingWhitespaceClass); + }); + + test('fixRegexCRMatchingNonWordClass', () => { + function testRegexCRMatchingNonWordClass([inputReg, isMultiline, testStr, shouldMatch]): void { + const fixed = fixRegexCRMatchingNonWordClass(inputReg, isMultiline); + const reg = new RegExp(fixed); + assert.equal(reg.test(testStr), shouldMatch, `${inputReg} => ${reg}, ${testStr}, ${shouldMatch}`); + } + + [ + ['foo', false, 'foo', true], + + ['foo\\W', false, 'foo\r\n', false], + ['foo\\Wabc', true, 'foo\r\nabc', true], + + ['foo\\W', false, 'foo\n', true], + ['foo\\W', true, 'foo\n', true], + + ['foo\\W\\n', true, 'foo\r\n', false], + ['foo\\r\\W', true, 'foo\r\n', true], + + ['foo\\W+abc', true, 'foo \r\nabc', true], + ['foo\\W+abc', false, 'foo .-\t abc', true], + ].forEach(testRegexCRMatchingNonWordClass); + }); + + test('fixRegexNewline', () => { + function testFixRegexNewline([inputReg, testStr, shouldMatch]): void { + const fixed = fixRegexNewline(inputReg); + const reg = new RegExp(fixed); + assert.equal(reg.test(testStr), shouldMatch, `${inputReg} => ${reg}, ${testStr}, ${shouldMatch}`); + } + + [ + ['foo', 'foo', true], + + ['foo\\n', 'foo\r\n', true], + ['foo\\n', 'foo\n', true], + ['foo\\nabc', 'foo\r\nabc', true], + ['foo\\nabc', 'foo\nabc', true], + ['foo\\r\\n', 'foo\r\n', true], + + ['foo\\n+abc', 'foo\r\nabc', true], + ['foo\\n+abc', 'foo\n\n\nabc', true], + ].forEach(testFixRegexNewline); + }); +}); diff --git a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts index aaa56cd0e..f4edf8a3b 100644 --- a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts +++ b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts @@ -9,8 +9,7 @@ import { getPathFromAmdModule } from 'vs/base/common/amd'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import * as glob from 'vs/base/common/glob'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { IFolderQuery, ITextQuery, QueryType } from 'vs/platform/search/common/search'; +import { IFolderQuery, ISearchRange, ITextQuery, ITextSearchMatch, QueryType, ITextSearchContext, deserializeSearchError, SearchErrorCode } from 'vs/platform/search/common/search'; import { LegacyTextSearchService } from 'vs/workbench/services/search/node/legacy/rawLegacyTextSearchService'; import { ISerializedFileMatch } from 'vs/workbench/services/search/node/search'; import { TextSearchEngineAdapter } from 'vs/workbench/services/search/node/textSearchAdapter'; @@ -32,7 +31,7 @@ const MULTIROOT_QUERIES: IFolderQuery[] = [ { folder: URI.file(MORE_FIXTURES) } ]; -function doLegacySearchTest(config: ITextQuery, expectedResultCount: number | Function): TPromise { +function doLegacySearchTest(config: ITextQuery, expectedResultCount: number | Function): Promise { const engine = new LegacyTextSearchService(); let c = 0; @@ -49,7 +48,7 @@ function doLegacySearchTest(config: ITextQuery, expectedResultCount: number | Fu }); } -function doRipgrepSearchTest(query: ITextQuery, expectedResultCount: number | Function): TPromise { +function doRipgrepSearchTest(query: ITextQuery, expectedResultCount: number | Function): Promise { let engine = new TextSearchEngineAdapter(query); let c = 0; @@ -312,6 +311,60 @@ suite('Search-integration', function () { return doSearchTest(config, 286); }); + test('Text: 语', () => { + const config = { + type: QueryType.Text, + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: '语' } + }; + + return doRipgrepSearchTest(config, 1).then(results => { + const matchRange = (results[0].results[0]).ranges; + assert.deepEqual(matchRange, [{ + startLineNumber: 0, + startColumn: 1, + endLineNumber: 0, + endColumn: 2 + }]); + }); + }); + + test('Multiple matches on line: h\\d,', () => { + const config = { + type: QueryType.Text, + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'h\\d,', isRegExp: true } + }; + + return doRipgrepSearchTest(config, 15).then(results => { + assert.equal(results.length, 3); + assert.equal(results[0].results.length, 1); + const match = results[0].results[0]; + assert.equal((match.ranges).length, 5); + }); + }); + + test('Search with context matches', () => { + const config = { + type: QueryType.Text, + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: 'compiler.typeCheck();' }, + beforeContext: 1, + afterContext: 2 + }; + + return doRipgrepSearchTest(config, 4).then(results => { + assert.equal(results.length, 4); + assert.equal((results[0].results[0]).lineNumber, 25); + assert.equal((results[0].results[0]).text, ' compiler.addUnit(prog,"input.ts");'); + // assert.equal((results[1].results[0]).preview.text, ' compiler.typeCheck();\n'); // See https://github.com/BurntSushi/ripgrep/issues/1095 + assert.equal((results[2].results[0]).lineNumber, 27); + assert.equal((results[2].results[0]).text, ' compiler.emit();'); + assert.equal((results[3].results[0]).lineNumber, 28); + assert.equal((results[3].results[0]).text, ''); + }); + }); + suite('error messages', () => { test('invalid encoding', () => { const config = { @@ -328,7 +381,9 @@ suite('Search-integration', function () { return doRipgrepSearchTest(config, 0).then(() => { throw new Error('expected fail'); }, err => { - assert.equal(err.message, 'Unknown encoding: invalidEncoding'); + const searchError = deserializeSearchError(err.message); + assert.equal(searchError.message, 'Unknown encoding: invalidEncoding'); + assert.equal(searchError.code, SearchErrorCode.unknownEncoding); }); }); @@ -342,7 +397,9 @@ suite('Search-integration', function () { return doRipgrepSearchTest(config, 0).then(() => { throw new Error('expected fail'); }, err => { - assert.equal(err.message, 'Regex parse error'); + const searchError = deserializeSearchError(err.message); + assert.equal(searchError.message, 'Regex parse error'); + assert.equal(searchError.code, SearchErrorCode.regexParseError); }); }); @@ -359,7 +416,9 @@ suite('Search-integration', function () { return doRipgrepSearchTest(config, 0).then(() => { throw new Error('expected fail'); }, err => { - assert.equal(err.message, 'Error parsing glob \'***\': invalid use of **; must be one path component'); + const searchError = deserializeSearchError(err.message); + assert.equal(searchError.message, 'Error parsing glob \'***\': invalid use of **; must be one path component'); + assert.equal(searchError.code, SearchErrorCode.globParseError); }); }); @@ -373,25 +432,9 @@ suite('Search-integration', function () { return doRipgrepSearchTest(config, 0).then(() => { throw new Error('expected fail'); }, err => { - assert.equal(err.message, 'The literal \'"\\n"\' is not allowed in a regex'); - }); - }); - - test('Text: 语', () => { - const config = { - type: QueryType.Text, - folderQueries: ROOT_FOLDER_QUERY, - contentPattern: { pattern: '语' } - }; - - return doRipgrepSearchTest(config, 1).then(results => { - const matchRange = results[0].matches[0].range; - assert.deepEqual(matchRange, { - startLineNumber: 0, - startColumn: 1, - endLineNumber: 0, - endColumn: 2 - }); + const searchError = deserializeSearchError(err.message); + assert.equal(searchError.message, 'The literal \'"\\n"\' is not allowed in a regex'); + assert.equal(searchError.code, SearchErrorCode.invalidLiteral); }); }); }); diff --git a/src/vs/workbench/services/search/test/node/textSearchManager.test.ts b/src/vs/workbench/services/search/test/node/textSearchManager.test.ts index ed136e644..2b2dcf137 100644 --- a/src/vs/workbench/services/search/test/node/textSearchManager.test.ts +++ b/src/vs/workbench/services/search/test/node/textSearchManager.test.ts @@ -12,7 +12,7 @@ import * as vscode from 'vscode'; suite('TextSearchManager', () => { test('fixes encoding', async () => { - let correctEncoding: boolean; + let correctEncoding = false; const provider: vscode.TextSearchProvider = { provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): vscode.ProviderResult { correctEncoding = options.encoding === 'windows-1252'; diff --git a/src/vs/workbench/services/textMate/electron-browser/OSSREADME.json b/src/vs/workbench/services/textMate/electron-browser/OSSREADME.json deleted file mode 100644 index 07f8a8a5e..000000000 --- a/src/vs/workbench/services/textMate/electron-browser/OSSREADME.json +++ /dev/null @@ -1,37 +0,0 @@ -// Listing in here dependencies that come in at build time from vscode-textmate - -[{ - "name": "lib-oniguruma", - "version": "5.9.3", - "license": "BSD", // according to wikipedia: https://en.wikipedia.org/wiki/Oniguruma - "repositoryURL": "https://github.com/kkos/oniguruma", - "licenseDetail": [ - "Copyright (c) 2002-2007 K.Kosako. All rights reserved.", - "", - "The BSD License", - "", - "Redistribution and use in source and binary forms, with or without", - "modification, are permitted provided that the following conditions", - "are met:", - "", - "1. Redistributions of source code must retain the above copyright", - " notice, this list of conditions and the following disclaimer.", - "", - "2. Redistributions in binary form must reproduce the above copyright", - " notice, this list of conditions and the following disclaimer in the", - " documentation and/or other materials provided with the distribution.", - "", - "THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND", - "ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE", - "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR", - "PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS", - "BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR", - "CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF", - "SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR", - "BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,", - "WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE", - "OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN", - "IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." - ], - "isProd": true -}] diff --git a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts index 2f3a80785..a0e060e0d 100644 --- a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts +++ b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts @@ -11,7 +11,6 @@ import { Emitter, Event } from 'vs/base/common/event'; import * as resources from 'vs/base/common/resources'; import * as types from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; import { TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token'; import { IState, ITokenizationSupport, LanguageId, TokenMetadata, TokenizationRegistry } from 'vs/editor/common/modes'; import { nullTokenize2 } from 'vs/editor/common/modes/nullMode'; @@ -20,6 +19,7 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { IEmbeddedLanguagesMap, ITMSyntaxExtensionPoint, TokenTypesContribution, grammarsExtPoint } from 'vs/workbench/services/textMate/electron-browser/TMGrammars'; import { ITextMateService } from 'vs/workbench/services/textMate/electron-browser/textMateService'; @@ -135,7 +135,7 @@ interface ICreateGrammarResult { export class TextMateService implements ITextMateService { public _serviceBrand: any; - private _grammarRegistry: TPromise<[Registry, StackElement]>; + private _grammarRegistry: Promise<[Registry, StackElement]>; private _modeService: IModeService; private _themeService: IWorkbenchThemeService; private _fileService: IFileService; @@ -157,7 +157,8 @@ export class TextMateService implements ITextMateService { @IWorkbenchThemeService themeService: IWorkbenchThemeService, @IFileService fileService: IFileService, @INotificationService notificationService: INotificationService, - @ILogService logService: ILogService + @ILogService logService: ILogService, + @IExtensionService extensionService: IExtensionService ) { this._styleElement = dom.createStyleSheet(); this._styleElement.className = 'vscode-tokens-styles'; @@ -202,15 +203,18 @@ export class TextMateService implements ITextMateService { this._modeService.onDidCreateMode((mode) => { let modeId = mode.getId(); - if (this._languageToScope.has(modeId)) { - this.registerDefinition(modeId); - } + // Modes can be instantiated before the extension points have finished registering + extensionService.whenInstalledExtensionsRegistered().then(() => { + if (this._languageToScope.has(modeId)) { + this.registerDefinition(modeId); + } + }); }); } - private _getOrCreateGrammarRegistry(): TPromise<[Registry, StackElement]> { + private _getOrCreateGrammarRegistry(): Promise<[Registry, StackElement]> { if (!this._grammarRegistry) { - this._grammarRegistry = TPromise.wrap(import('vscode-textmate')).then(({ Registry, INITIAL, parseRawGrammar }) => { + this._grammarRegistry = import('vscode-textmate').then(({ Registry, INITIAL, parseRawGrammar }) => { const grammarRegistry = new Registry({ loadGrammar: (scopeName: string) => { const location = this._scopeRegistry.getGrammarLocation(scopeName); @@ -226,7 +230,13 @@ export class TextMateService implements ITextMateService { }); }, getInjections: (scopeName: string) => { - return this._injections[scopeName]; + const scopeParts = scopeName.split('.'); + let injections: string[] = []; + for (let i = 1; i <= scopeParts.length; i++) { + const subScopeName = scopeParts.slice(0, i).join('.'); + injections = [...injections, ...this._injections[subScopeName]]; + } + return injections; } }); this._updateTheme(grammarRegistry); @@ -358,16 +368,16 @@ export class TextMateService implements ITextMateService { return result; } - public createGrammar(modeId: string): TPromise { + public createGrammar(modeId: string): Promise { return this._createGrammar(modeId).then(r => r.grammar); } - private _createGrammar(modeId: string): TPromise { + private _createGrammar(modeId: string): Promise { let scopeName = this._languageToScope.get(modeId); let languageRegistration = this._scopeRegistry.getLanguageRegistration(scopeName); if (!languageRegistration) { // No TM grammar defined - return TPromise.wrapError(new Error(nls.localize('no-tm-grammar', "No TM Grammar registered for this language."))); + return Promise.reject(new Error(nls.localize('no-tm-grammar', "No TM Grammar registered for this language."))); } let embeddedLanguages = this._resolveEmbeddedLanguages(languageRegistration.embeddedLanguages); let rawInjectedEmbeddedLanguages = this._injectedEmbeddedLanguages[scopeName]; diff --git a/src/vs/workbench/services/textMate/electron-browser/cgmanifest.json b/src/vs/workbench/services/textMate/electron-browser/cgmanifest.json new file mode 100644 index 000000000..4dead8495 --- /dev/null +++ b/src/vs/workbench/services/textMate/electron-browser/cgmanifest.json @@ -0,0 +1,46 @@ +{ + "registrations": [ + { + "component": { + "type": "other", + "other": { + "name": "lib-oniguruma", + "downloadUrl": "http://dl.fedoraproject.org/pub/epel/7/SRPMS/Packages/o/oniguruma-5.9.5-3.el7.src.rpm", + "version": "5.9.3" + } + }, + "licenseDetail": [ + "Copyright (c) 2002-2007 K.Kosako. All rights reserved.", + "", + "The BSD License", + "", + "Redistribution and use in source and binary forms, with or without", + "modification, are permitted provided that the following conditions", + "are met:", + "", + "1. Redistributions of source code must retain the above copyright", + " notice, this list of conditions and the following disclaimer.", + "", + "2. Redistributions in binary form must reproduce the above copyright", + " notice, this list of conditions and the following disclaimer in the", + " documentation and/or other materials provided with the distribution.", + "", + "THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND", + "ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE", + "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR", + "PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS", + "BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR", + "CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF", + "SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR", + "BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,", + "WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE", + "OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN", + "IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + ], + "isOnlyProductionDependency": true, + "license": "BSD", + "version": "5.9.3" + } + ], + "version": 1 +} diff --git a/src/vs/workbench/services/textMate/electron-browser/textMateService.ts b/src/vs/workbench/services/textMate/electron-browser/textMateService.ts index 7efc30297..79c0a7e06 100644 --- a/src/vs/workbench/services/textMate/electron-browser/textMateService.ts +++ b/src/vs/workbench/services/textMate/electron-browser/textMateService.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { TPromise } from 'vs/base/common/winjs.base'; import { LanguageId } from 'vs/editor/common/modes'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IGrammar } from 'vscode-textmate'; @@ -16,5 +15,5 @@ export interface ITextMateService { onDidEncounterLanguage: Event; - createGrammar(modeId: string): TPromise; + createGrammar(modeId: string): Promise; } diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index f6e908bed..9bc56c2fc 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -11,7 +11,6 @@ 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 { IMode } from 'vs/editor/common/modes'; 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, IRawTextContent, ILoadOptions, LoadReason } from 'vs/workbench/services/textfile/common/textfiles'; @@ -20,7 +19,7 @@ import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel' import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { IFileService, IFileStat, FileOperationError, FileOperationResult, CONTENT_CHANGE_EVENT_BUFFER_DELAY, FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IModeService } from 'vs/editor/common/services/modeService'; +import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { RunOnceScheduler, timeout } from 'vs/base/common/async'; @@ -32,6 +31,7 @@ import { isLinux } from 'vs/base/common/platform'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; import { isEqual, isEqualOrParent } from 'vs/base/common/resources'; +import { onUnexpectedError } from 'vs/base/common/errors'; /** * The text file editor model listens to changes to its underlying code editor model and saves these changes through the file service back to the disk. @@ -196,9 +196,9 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } const firstLineText = this.getFirstLineText(this.textEditorModel); - const mode = this.getOrCreateMode(this.modeService, void 0, firstLineText); + const languageSelection = this.getOrCreateMode(this.modeService, void 0, firstLineText); - this.modelService.setMode(this.textEditorModel, mode); + this.modelService.setMode(this.textEditorModel, languageSelection); } getVersionId(): number { @@ -492,8 +492,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return this.backupFileService.resolveBackupContent(backup).then(backupContent => backupContent, error => null /* ignore errors */); } - protected getOrCreateMode(modeService: IModeService, preferredModeIds: string, firstLineText?: string): Promise { - return modeService.getOrCreateModeByFilepathOrFirstLine(this.resource.fsPath, firstLineText); + protected getOrCreateMode(modeService: IModeService, preferredModeIds: string, firstLineText?: string): ILanguageSelection { + return modeService.createByFilepathOrFirstLine(this.resource.fsPath, firstLineText); } private onModelContentChanged(): void { @@ -846,7 +846,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Updated resolved stat with updated stat since touching it might have changed mtime this.updateLastResolvedDiskStat(stat); - }, () => void 0 /* gracefully ignore errors if just touching */)); + }, error => onUnexpectedError(error) /* just log any error but do not notify the user since the file was not dirty */)); } private setDirty(dirty: boolean): () => void { diff --git a/src/vs/workbench/services/textfile/electron-browser/textResourcePropertiesService.ts b/src/vs/workbench/services/textfile/electron-browser/textResourcePropertiesService.ts index 8114527b4..8147cc379 100644 --- a/src/vs/workbench/services/textfile/electron-browser/textResourcePropertiesService.ts +++ b/src/vs/workbench/services/textfile/electron-browser/textResourcePropertiesService.ts @@ -7,21 +7,49 @@ import { URI } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; import { OperatingSystem, OS } from 'vs/base/common/platform'; +import { IRemoteAgentService, IRemoteAgentEnvironment } from 'vs/workbench/services/remote/node/remoteAgentService'; +import { Schemas } from 'vs/base/common/network'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IWindowService } from 'vs/platform/windows/common/windows'; export class TextResourcePropertiesService implements ITextResourcePropertiesService { _serviceBrand: any; + private remoteEnvironment: IRemoteAgentEnvironment | null = null; + constructor( - @IConfigurationService private configurationService: IConfigurationService - ) { } + @IConfigurationService private configurationService: IConfigurationService, + @IRemoteAgentService remoteAgentService: IRemoteAgentService, + @IWindowService private windowService: IWindowService, + @IStorageService private storageService: IStorageService + ) { + const remoteAgentConnection = remoteAgentService.getConnection(); + if (remoteAgentConnection) { + remoteAgentConnection.getEnvironment().then(remoteEnv => this.remoteEnvironment = remoteEnv); + } + } 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; } - return OS === OperatingSystem.Linux || OS === OperatingSystem.Macintosh ? '\n' : '\r\n'; + const os = this.getOS(resource); + return os === OperatingSystem.Linux || os === OperatingSystem.Macintosh ? '\n' : '\r\n'; + } + + private getOS(resource: URI): OperatingSystem { + let os = OS; + const remoteAuthority = this.windowService.getConfiguration().remoteAuthority; + if (remoteAuthority) { + if (resource.scheme !== Schemas.file) { + const osCacheKey = `resource.authority.os.${remoteAuthority}`; + os = this.remoteEnvironment ? this.remoteEnvironment.os : /* Get it from cache */ this.storageService.getInteger(osCacheKey, StorageScope.WORKSPACE, OS); + this.storageService.store(osCacheKey, os, StorageScope.WORKSPACE); + } + } + return os; } } \ No newline at end of file diff --git a/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts b/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts index acf1e5bfc..d36b54ecd 100644 --- a/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts +++ b/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts @@ -59,8 +59,8 @@ suite('Workbench - TextModelResolverService', () => { provideTextContent: function (resource: URI): TPromise { if (resource.scheme === 'test') { let modelContent = 'Hello Test'; - let mode = accessor.modeService.getOrCreateMode('json'); - return TPromise.as(accessor.modelService.createModel(modelContent, mode, resource)); + let languageSelection = accessor.modeService.create('json'); + return TPromise.as(accessor.modelService.createModel(modelContent, languageSelection, resource)); } return TPromise.as(null); @@ -131,14 +131,14 @@ suite('Workbench - TextModelResolverService', () => { test('even loading documents should be refcounted', async () => { let resolveModel: Function; - let waitForIt = new TPromise(c => resolveModel = c); + let waitForIt = new Promise(c => resolveModel = c); const disposable = accessor.textModelResolverService.registerTextModelContentProvider('test', { provideTextContent: (resource: URI): TPromise => { return waitForIt.then(_ => { let modelContent = 'Hello Test'; - let mode = accessor.modeService.getOrCreateMode('json'); - return accessor.modelService.createModel(modelContent, mode, resource); + let languageSelection = accessor.modeService.create('json'); + return accessor.modelService.createModel(modelContent, languageSelection, resource); }); } }); diff --git a/src/vs/workbench/services/timer/electron-browser/timerService.ts b/src/vs/workbench/services/timer/electron-browser/timerService.ts index c69fa9b6d..d9f444120 100644 --- a/src/vs/workbench/services/timer/electron-browser/timerService.ts +++ b/src/vs/workbench/services/timer/electron-browser/timerService.ts @@ -13,7 +13,7 @@ import { IWindowService, IWindowsService } from 'vs/platform/windows/common/wind import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; import { IUpdateService } from 'vs/platform/update/common/update'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -53,6 +53,7 @@ export interface IMemoryInfo { "timers.ellapsedExtensions" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedExtensionsReady" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedRequire" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "timers.ellapsedWorkspaceStorageRequire" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedWorkspaceStorageInit" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedViewletRestore" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedPanelRestore" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, @@ -194,7 +195,16 @@ export interface IStartupMetrics { ellapsedWindowLoadToRequire: number; /** - * The time it took to connect to the workspace storage DB and load the initial set of values. + * The time it took to require the workspace storage DB. + * + * * Happens in the renderer-process + * * Measured with the `willRequireSQLite` and `didRequireSQLite` performance marks. + */ + ellapsedWorkspaceStorageRequire: number; + + /** + * The time it took to require the workspace storage DB, connect to it + * and load the initial set of values. * * * Happens in the renderer-process * * Measured with the `willInitWorkspaceStorage` and `didInitWorkspaceStorage` performance marks. @@ -378,6 +388,7 @@ class TimerService implements ITimerService { ellapsedWindowLoad: initialStartup ? perf.getDuration('main:appReady', 'main:loadWindow') : undefined, ellapsedWindowLoadToRequire: perf.getDuration('main:loadWindow', 'willLoadWorkbenchMain'), ellapsedRequire: perf.getDuration('willLoadWorkbenchMain', 'didLoadWorkbenchMain'), + ellapsedWorkspaceStorageRequire: perf.getDuration('willRequireSQLite', 'didRequireSQLite'), ellapsedWorkspaceStorageInit: perf.getDuration('willInitWorkspaceStorage', 'didInitWorkspaceStorage'), ellapsedExtensions: perf.getDuration('willLoadExtensions', 'didLoadExtensions'), ellapsedEditorRestore: perf.getDuration('willRestoreEditors', 'didRestoreEditors'), @@ -407,7 +418,7 @@ class TimerService implements ITimerService { export const ITimerService = createDecorator('timerService'); -registerSingleton(ITimerService, TimerService); +registerSingleton(ITimerService, TimerService, true); //#region cached data logic @@ -419,7 +430,7 @@ export function didUseCachedData(): boolean { // whenever cached data is produced or rejected a onNodeCachedData-callback is invoked. That callback // stores data in the `MonacoEnvironment.onNodeCachedData` global. See: // https://github.com/Microsoft/vscode/blob/efe424dfe76a492eab032343e2fa4cfe639939f0/src/vs/workbench/electron-browser/bootstrap/index.js#L299 - if (!isFalsyOrEmpty(MonacoEnvironment.onNodeCachedData)) { + if (isNonEmptyArray(MonacoEnvironment.onNodeCachedData)) { return false; } return true; diff --git a/src/vs/workbench/services/viewlet/browser/viewletService.ts b/src/vs/workbench/services/viewlet/browser/viewletService.ts index 8ed4196a2..7b2a42f73 100644 --- a/src/vs/workbench/services/viewlet/browser/viewletService.ts +++ b/src/vs/workbench/services/viewlet/browser/viewletService.ts @@ -70,7 +70,7 @@ export class ViewletService extends Disposable implements IViewletService { openViewlet(id: string, focus?: boolean): Thenable { if (this.getViewlet(id)) { - return this.sidebarPart.openViewlet(id, focus); + return Promise.resolve(this.sidebarPart.openViewlet(id, focus)); } return this.extensionService.whenInstalledExtensionsRegistered() .then(() => { diff --git a/src/vs/workbench/test/common/editor/editorDiffModel.test.ts b/src/vs/workbench/test/common/editor/editorDiffModel.test.ts index 68953e620..4594a22b0 100644 --- a/src/vs/workbench/test/common/editor/editorDiffModel.test.ts +++ b/src/vs/workbench/test/common/editor/editorDiffModel.test.ts @@ -41,8 +41,8 @@ suite('Workbench editor model', () => { provideTextContent: function (resource: URI): TPromise { if (resource.scheme === 'test') { let modelContent = 'Hello Test'; - let mode = accessor.modeService.getOrCreateMode('json'); - return TPromise.as(accessor.modelService.createModel(modelContent, mode, resource)); + let languageSelection = accessor.modeService.create('json'); + return TPromise.as(accessor.modelService.createModel(modelContent, languageSelection, resource)); } return TPromise.as(null); diff --git a/src/vs/workbench/test/common/editor/resourceEditorInput.test.ts b/src/vs/workbench/test/common/editor/resourceEditorInput.test.ts index b43a41d52..873c498d0 100644 --- a/src/vs/workbench/test/common/editor/resourceEditorInput.test.ts +++ b/src/vs/workbench/test/common/editor/resourceEditorInput.test.ts @@ -33,7 +33,7 @@ suite('Workbench resource editor input', () => { test('simple', () => { let resource = URI.from({ scheme: 'inmemory', authority: null, path: 'thePath' }); - accessor.modelService.createModel('function test() {}', accessor.modeService.getOrCreateMode('text'), resource); + accessor.modelService.createModel('function test() {}', accessor.modeService.create('text'), resource); let input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, 'The Name', 'The Description', resource); return input.resolve().then((model: ResourceEditorModel) => { 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 d69dd86e8..863ca25a0 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts @@ -30,6 +30,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import 'vs/workbench/parts/search/electron-browser/search.contribution'; import { NullLogService } from 'vs/platform/log/common/log'; import { ITextModel } from 'vs/editor/common/model'; +import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; const defaultSelector = { scheme: 'far' }; const model: ITextModel = EditorModel.createFromString( @@ -158,7 +159,7 @@ suite('ExtHostLanguageFeatureCommands', function () { test('WorkspaceSymbols, back and forth', function () { - disposables.push(extHost.registerWorkspaceSymbolProvider({ + disposables.push(extHost.registerWorkspaceSymbolProvider(nullExtensionDescription, { provideWorkspaceSymbols(query): any { return [ new types.SymbolInformation(query, types.SymbolKind.Array, new types.Range(0, 0, 1, 1), URI.parse('far://testing/first')), @@ -167,7 +168,7 @@ suite('ExtHostLanguageFeatureCommands', function () { } })); - disposables.push(extHost.registerWorkspaceSymbolProvider({ + disposables.push(extHost.registerWorkspaceSymbolProvider(nullExtensionDescription, { provideWorkspaceSymbols(query): any { return [ new types.SymbolInformation(query, types.SymbolKind.Array, new types.Range(0, 0, 1, 1), URI.parse('far://testing/first')) @@ -190,7 +191,7 @@ suite('ExtHostLanguageFeatureCommands', function () { test('executeWorkspaceSymbolProvider should accept empty string, #39522', async function () { - disposables.push(extHost.registerWorkspaceSymbolProvider({ + disposables.push(extHost.registerWorkspaceSymbolProvider(nullExtensionDescription, { provideWorkspaceSymbols(query) { return [new types.SymbolInformation('hello', types.SymbolKind.Array, new types.Range(0, 0, 0, 0), URI.parse('foo:bar'))]; } @@ -220,12 +221,12 @@ suite('ExtHostLanguageFeatureCommands', function () { test('Definition, back and forth', function () { - disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerDefinitionProvider(nullExtensionDescription, defaultSelector, { provideDefinition(doc: any): any { return new types.Location(doc.uri, new types.Range(0, 0, 0, 0)); } })); - disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerDefinitionProvider(nullExtensionDescription, defaultSelector, { provideDefinition(doc: any): any { return [ new types.Location(doc.uri, new types.Range(0, 0, 0, 0)), @@ -246,6 +247,36 @@ suite('ExtHostLanguageFeatureCommands', function () { }); }); + // --- declaration + + test('Declaration, back and forth', function () { + + disposables.push(extHost.registerDeclarationProvider(nullExtensionDescription, defaultSelector, { + provideDeclaration(doc: any): any { + return new types.Location(doc.uri, new types.Range(0, 0, 0, 0)); + } + })); + disposables.push(extHost.registerDeclarationProvider(nullExtensionDescription, defaultSelector, { + provideDeclaration(doc: any): any { + return [ + new types.Location(doc.uri, new types.Range(0, 0, 0, 0)), + new types.Location(doc.uri, new types.Range(0, 0, 0, 0)), + new types.Location(doc.uri, new types.Range(0, 0, 0, 0)), + ]; + } + })); + + return rpcProtocol.sync().then(() => { + return commands.executeCommand('vscode.executeDeclarationProvider', model.uri, new types.Position(0, 0)).then(values => { + assert.equal(values.length, 4); + for (let v of values) { + assert.ok(v.uri instanceof URI); + assert.ok(v.range instanceof types.Range); + } + }); + }); + }); + // --- type definition test('Type Definition, invalid arguments', function () { @@ -261,12 +292,12 @@ suite('ExtHostLanguageFeatureCommands', function () { test('Type Definition, back and forth', function () { - disposables.push(extHost.registerTypeDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerTypeDefinitionProvider(nullExtensionDescription, defaultSelector, { provideTypeDefinition(doc: any): any { return new types.Location(doc.uri, new types.Range(0, 0, 0, 0)); } })); - disposables.push(extHost.registerTypeDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerTypeDefinitionProvider(nullExtensionDescription, defaultSelector, { provideTypeDefinition(doc: any): any { return [ new types.Location(doc.uri, new types.Range(0, 0, 0, 0)), @@ -291,7 +322,7 @@ suite('ExtHostLanguageFeatureCommands', function () { test('reference search, back and forth', function () { - disposables.push(extHost.registerReferenceProvider(defaultSelector, { + disposables.push(extHost.registerReferenceProvider(nullExtensionDescription, defaultSelector, { provideReferences(doc: any) { return [ new types.Location(URI.parse('some:uri/path'), new types.Range(0, 1, 0, 5)) @@ -313,7 +344,7 @@ suite('ExtHostLanguageFeatureCommands', function () { // --- outline test('Outline, back and forth', function () { - disposables.push(extHost.registerDocumentSymbolProvider(defaultSelector, { + disposables.push(extHost.registerDocumentSymbolProvider(nullExtensionDescription, defaultSelector, { provideDocumentSymbols(): any { return [ new types.SymbolInformation('testing1', types.SymbolKind.Enum, new types.Range(1, 0, 1, 0)), @@ -335,14 +366,14 @@ suite('ExtHostLanguageFeatureCommands', function () { }); test('vscode.executeDocumentSymbolProvider command only returns SymbolInformation[] rather than DocumentSymbol[] #57984', function () { - disposables.push(extHost.registerDocumentSymbolProvider(defaultSelector, { + disposables.push(extHost.registerDocumentSymbolProvider(nullExtensionDescription, defaultSelector, { provideDocumentSymbols(): any { return [ new types.SymbolInformation('SymbolInformation', types.SymbolKind.Enum, new types.Range(1, 0, 1, 0)) ]; } })); - disposables.push(extHost.registerDocumentSymbolProvider(defaultSelector, { + disposables.push(extHost.registerDocumentSymbolProvider(nullExtensionDescription, defaultSelector, { provideDocumentSymbols(): any { let root = new types.DocumentSymbol('DocumentSymbol', 'DocumentSymbol#detail', types.SymbolKind.Enum, new types.Range(1, 0, 1, 0), new types.Range(1, 0, 1, 0)); root.children = [new types.DocumentSymbol('DocumentSymbol#child', 'DocumentSymbol#detail#child', types.SymbolKind.Enum, new types.Range(1, 0, 1, 0), new types.Range(1, 0, 1, 0))]; @@ -367,7 +398,7 @@ suite('ExtHostLanguageFeatureCommands', function () { // --- suggest test('Suggest, back and forth', function () { - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(nullExtensionDescription, defaultSelector, { provideCompletionItems(doc, pos): any { let a = new types.CompletionItem('item1'); let b = new types.CompletionItem('item2'); @@ -425,7 +456,7 @@ suite('ExtHostLanguageFeatureCommands', function () { }); test('Suggest, return CompletionList !array', function () { - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(nullExtensionDescription, defaultSelector, { provideCompletionItems(): any { let a = new types.CompletionItem('item1'); let b = new types.CompletionItem('item2'); @@ -445,7 +476,7 @@ suite('ExtHostLanguageFeatureCommands', function () { let resolveCount = 0; - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(nullExtensionDescription, defaultSelector, { provideCompletionItems(): any { let a = new types.CompletionItem('item1'); let b = new types.CompletionItem('item2'); @@ -475,7 +506,7 @@ suite('ExtHostLanguageFeatureCommands', function () { }); test('"vscode.executeCompletionItemProvider" doesnot return a preselect field #53749', async function () { - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(nullExtensionDescription, defaultSelector, { provideCompletionItems(): any { let a = new types.CompletionItem('item1'); a.preselect = true; @@ -507,7 +538,7 @@ suite('ExtHostLanguageFeatureCommands', function () { }); test('executeCompletionItemProvider doesn\'t capture commitCharacters #58228', async function () { - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(nullExtensionDescription, defaultSelector, { provideCompletionItems(): any { let a = new types.CompletionItem('item1'); a.commitCharacters = ['a', 'b']; @@ -536,7 +567,7 @@ suite('ExtHostLanguageFeatureCommands', function () { // --- quickfix test('QuickFix, back and forth', function () { - disposables.push(extHost.registerCodeActionProvider(defaultSelector, { + disposables.push(extHost.registerCodeActionProvider(nullExtensionDescription, defaultSelector, { provideCodeActions(): vscode.Command[] { return [{ command: 'testing', title: 'Title', arguments: [1, 2, true] }]; } @@ -554,7 +585,7 @@ suite('ExtHostLanguageFeatureCommands', function () { }); test('vscode.executeCodeActionProvider results seem to be missing their `command` property #45124', function () { - disposables.push(extHost.registerCodeActionProvider(defaultSelector, { + disposables.push(extHost.registerCodeActionProvider(nullExtensionDescription, defaultSelector, { provideCodeActions(document, range): vscode.CodeAction[] { return [{ command: { @@ -592,7 +623,7 @@ suite('ExtHostLanguageFeatureCommands', function () { big: extHost }; - disposables.push(extHost.registerCodeLensProvider(defaultSelector, { + disposables.push(extHost.registerCodeLensProvider(nullExtensionDescription, defaultSelector, { provideCodeLenses(): any { return [new types.CodeLens(new types.Range(0, 0, 1, 1), { title: 'Title', command: 'cmd', arguments: [1, true, complexArg] })]; } @@ -616,7 +647,7 @@ suite('ExtHostLanguageFeatureCommands', function () { let resolveCount = 0; - disposables.push(extHost.registerCodeLensProvider(defaultSelector, { + disposables.push(extHost.registerCodeLensProvider(nullExtensionDescription, defaultSelector, { provideCodeLenses(): any { return [ new types.CodeLens(new types.Range(0, 0, 1, 1)), @@ -648,7 +679,7 @@ suite('ExtHostLanguageFeatureCommands', function () { test('Links, back and forth', function () { - disposables.push(extHost.registerDocumentLinkProvider(defaultSelector, { + disposables.push(extHost.registerDocumentLinkProvider(nullExtensionDescription, defaultSelector, { provideDocumentLinks(): any { return [new types.DocumentLink(new types.Range(0, 0, 0, 20), URI.parse('foo:bar'))]; } @@ -671,7 +702,7 @@ suite('ExtHostLanguageFeatureCommands', function () { test('Color provider', function () { - disposables.push(extHost.registerColorProvider(defaultSelector, { + disposables.push(extHost.registerColorProvider(nullExtensionDescription, defaultSelector, { provideDocumentColors(): vscode.ColorInformation[] { return [new types.ColorInformation(new types.Range(0, 0, 0, 20), new types.Color(0.1, 0.2, 0.3, 0.4))]; }, @@ -721,7 +752,7 @@ suite('ExtHostLanguageFeatureCommands', function () { test('"TypeError: e.onCancellationRequested is not a function" calling hover provider in Insiders #54174', function () { - disposables.push(extHost.registerHoverProvider(defaultSelector, { + disposables.push(extHost.registerHoverProvider(nullExtensionDescription, defaultSelector, { provideHover(): any { return new types.Hover('fofofofo'); } diff --git a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts index f259a4d89..b26fc7427 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts @@ -24,7 +24,7 @@ import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumen import { getDocumentSymbols } from 'vs/editor/contrib/quickOpen/quickOpen'; import * as modes from 'vs/editor/common/modes'; import { getCodeLensData } from 'vs/editor/contrib/codelens/codelens'; -import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition } from 'vs/editor/contrib/goToDefinition/goToDefinition'; +import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition, getDeclarationsAtPosition } from 'vs/editor/contrib/goToDefinition/goToDefinition'; import { getHover } from 'vs/editor/contrib/hover/getHover'; import { getOccurrencesAtPosition } from 'vs/editor/contrib/wordHighlighter/wordHighlighter'; import { provideReferences } from 'vs/editor/contrib/referenceSearch/referenceSearch'; @@ -44,6 +44,7 @@ import { NullLogService } from 'vs/platform/log/common/log'; import { ITextModel, EndOfLineSequence } from 'vs/editor/common/model'; import { getColors } from 'vs/editor/contrib/colorPicker/color'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { nullExtensionDescription as defaultExtension } from 'vs/workbench/services/extensions/common/extensions'; const defaultSelector = { scheme: 'far' }; const model: ITextModel = EditorModel.createFromString( @@ -132,7 +133,7 @@ suite('ExtHostLanguageFeatures', function () { test('DocumentSymbols, register/deregister', function () { assert.equal(modes.DocumentSymbolProviderRegistry.all(model).length, 0); - let d1 = extHost.registerDocumentSymbolProvider(defaultSelector, { + let d1 = extHost.registerDocumentSymbolProvider(defaultExtension, defaultSelector, { provideDocumentSymbols() { return []; } @@ -147,12 +148,12 @@ suite('ExtHostLanguageFeatures', function () { }); test('DocumentSymbols, evil provider', function () { - disposables.push(extHost.registerDocumentSymbolProvider(defaultSelector, { + disposables.push(extHost.registerDocumentSymbolProvider(defaultExtension, defaultSelector, { provideDocumentSymbols(): any { throw new Error('evil document symbol provider'); } })); - disposables.push(extHost.registerDocumentSymbolProvider(defaultSelector, { + disposables.push(extHost.registerDocumentSymbolProvider(defaultExtension, defaultSelector, { provideDocumentSymbols(): any { return [new types.SymbolInformation('test', types.SymbolKind.Field, new types.Range(0, 0, 0, 0))]; } @@ -167,7 +168,7 @@ suite('ExtHostLanguageFeatures', function () { }); test('DocumentSymbols, data conversion', function () { - disposables.push(extHost.registerDocumentSymbolProvider(defaultSelector, { + disposables.push(extHost.registerDocumentSymbolProvider(defaultExtension, defaultSelector, { provideDocumentSymbols(): any { return [new types.SymbolInformation('test', types.SymbolKind.Field, new types.Range(0, 0, 0, 0))]; } @@ -189,12 +190,12 @@ suite('ExtHostLanguageFeatures', function () { test('CodeLens, evil provider', function () { - disposables.push(extHost.registerCodeLensProvider(defaultSelector, { + disposables.push(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, { provideCodeLenses(): any { throw new Error('evil'); } })); - disposables.push(extHost.registerCodeLensProvider(defaultSelector, { + disposables.push(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, { provideCodeLenses() { return [new types.CodeLens(new types.Range(0, 0, 0, 0))]; } @@ -209,7 +210,7 @@ suite('ExtHostLanguageFeatures', function () { test('CodeLens, do not resolve a resolved lens', function () { - disposables.push(extHost.registerCodeLensProvider(defaultSelector, { + disposables.push(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, { provideCodeLenses(): any { return [new types.CodeLens( new types.Range(0, 0, 0, 0), @@ -235,7 +236,7 @@ suite('ExtHostLanguageFeatures', function () { test('CodeLens, missing command', function () { - disposables.push(extHost.registerCodeLensProvider(defaultSelector, { + disposables.push(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, { provideCodeLenses() { return [new types.CodeLens(new types.Range(0, 0, 0, 0))]; } @@ -260,7 +261,7 @@ suite('ExtHostLanguageFeatures', function () { test('Definition, data conversion', function () { - disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, { provideDefinition(): any { return [new types.Location(model.uri, new types.Range(1, 2, 3, 4))]; } @@ -279,12 +280,12 @@ suite('ExtHostLanguageFeatures', function () { test('Definition, one or many', function () { - disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, { provideDefinition(): any { return [new types.Location(model.uri, new types.Range(1, 1, 1, 1))]; } })); - disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, { provideDefinition(): any { return new types.Location(model.uri, new types.Range(1, 1, 1, 1)); } @@ -300,13 +301,13 @@ suite('ExtHostLanguageFeatures', function () { test('Definition, registration order', function () { - disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, { provideDefinition(): any { return [new types.Location(URI.parse('far://first'), new types.Range(2, 3, 4, 5))]; } })); - disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, { provideDefinition(): any { return new types.Location(URI.parse('far://second'), new types.Range(1, 2, 3, 4)); } @@ -326,12 +327,12 @@ suite('ExtHostLanguageFeatures', function () { test('Definition, evil provider', function () { - disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, { provideDefinition(): any { throw new Error('evil provider'); } })); - disposables.push(extHost.registerDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, { provideDefinition(): any { return new types.Location(model.uri, new types.Range(1, 1, 1, 1)); } @@ -345,11 +346,32 @@ suite('ExtHostLanguageFeatures', function () { }); }); + // -- declaration + + test('Declaration, data conversion', function () { + + disposables.push(extHost.registerDeclarationProvider(defaultExtension, defaultSelector, { + provideDeclaration(): any { + return [new types.Location(model.uri, new types.Range(1, 2, 3, 4))]; + } + })); + + return rpcProtocol.sync().then(() => { + + return getDeclarationsAtPosition(model, new EditorPosition(1, 1), CancellationToken.None).then(value => { + assert.equal(value.length, 1); + let [entry] = value; + assert.deepEqual(entry.range, { startLineNumber: 2, startColumn: 3, endLineNumber: 4, endColumn: 5 }); + assert.equal(entry.uri.toString(), model.uri.toString()); + }); + }); + }); + // --- implementation test('Implementation, data conversion', function () { - disposables.push(extHost.registerImplementationProvider(defaultSelector, { + disposables.push(extHost.registerImplementationProvider(defaultExtension, defaultSelector, { provideImplementation(): any { return [new types.Location(model.uri, new types.Range(1, 2, 3, 4))]; } @@ -369,7 +391,7 @@ suite('ExtHostLanguageFeatures', function () { test('Type Definition, data conversion', function () { - disposables.push(extHost.registerTypeDefinitionProvider(defaultSelector, { + disposables.push(extHost.registerTypeDefinitionProvider(defaultExtension, defaultSelector, { provideTypeDefinition(): any { return [new types.Location(model.uri, new types.Range(1, 2, 3, 4))]; } @@ -389,7 +411,7 @@ suite('ExtHostLanguageFeatures', function () { test('HoverProvider, word range at pos', function () { - disposables.push(extHost.registerHoverProvider(defaultSelector, { + disposables.push(extHost.registerHoverProvider(defaultExtension, defaultSelector, { provideHover(): any { return new types.Hover('Hello'); } @@ -407,7 +429,7 @@ suite('ExtHostLanguageFeatures', function () { test('HoverProvider, given range', function () { - disposables.push(extHost.registerHoverProvider(defaultSelector, { + disposables.push(extHost.registerHoverProvider(defaultExtension, defaultSelector, { provideHover(): any { return new types.Hover('Hello', new types.Range(3, 0, 8, 7)); } @@ -425,14 +447,14 @@ suite('ExtHostLanguageFeatures', function () { test('HoverProvider, registration order', function () { - disposables.push(extHost.registerHoverProvider(defaultSelector, { + disposables.push(extHost.registerHoverProvider(defaultExtension, defaultSelector, { provideHover(): any { return new types.Hover('registered first'); } })); - disposables.push(extHost.registerHoverProvider(defaultSelector, { + disposables.push(extHost.registerHoverProvider(defaultExtension, defaultSelector, { provideHover(): any { return new types.Hover('registered second'); } @@ -451,12 +473,12 @@ suite('ExtHostLanguageFeatures', function () { test('HoverProvider, evil provider', function () { - disposables.push(extHost.registerHoverProvider(defaultSelector, { + disposables.push(extHost.registerHoverProvider(defaultExtension, defaultSelector, { provideHover(): any { throw new Error('evil'); } })); - disposables.push(extHost.registerHoverProvider(defaultSelector, { + disposables.push(extHost.registerHoverProvider(defaultExtension, defaultSelector, { provideHover(): any { return new types.Hover('Hello'); } @@ -475,7 +497,7 @@ suite('ExtHostLanguageFeatures', function () { test('Occurrences, data conversion', function () { - disposables.push(extHost.registerDocumentHighlightProvider(defaultSelector, { + disposables.push(extHost.registerDocumentHighlightProvider(defaultExtension, defaultSelector, { provideDocumentHighlights(): any { return [new types.DocumentHighlight(new types.Range(0, 0, 0, 4))]; } @@ -494,12 +516,12 @@ suite('ExtHostLanguageFeatures', function () { test('Occurrences, order 1/2', function () { - disposables.push(extHost.registerDocumentHighlightProvider(defaultSelector, { + disposables.push(extHost.registerDocumentHighlightProvider(defaultExtension, defaultSelector, { provideDocumentHighlights(): any { return []; } })); - disposables.push(extHost.registerDocumentHighlightProvider('*', { + disposables.push(extHost.registerDocumentHighlightProvider(defaultExtension, '*', { provideDocumentHighlights(): any { return [new types.DocumentHighlight(new types.Range(0, 0, 0, 4))]; } @@ -518,12 +540,12 @@ suite('ExtHostLanguageFeatures', function () { test('Occurrences, order 2/2', function () { - disposables.push(extHost.registerDocumentHighlightProvider(defaultSelector, { + disposables.push(extHost.registerDocumentHighlightProvider(defaultExtension, defaultSelector, { provideDocumentHighlights(): any { return [new types.DocumentHighlight(new types.Range(0, 0, 0, 2))]; } })); - disposables.push(extHost.registerDocumentHighlightProvider('*', { + disposables.push(extHost.registerDocumentHighlightProvider(defaultExtension, '*', { provideDocumentHighlights(): any { return [new types.DocumentHighlight(new types.Range(0, 0, 0, 4))]; } @@ -542,13 +564,13 @@ suite('ExtHostLanguageFeatures', function () { test('Occurrences, evil provider', function () { - disposables.push(extHost.registerDocumentHighlightProvider(defaultSelector, { + disposables.push(extHost.registerDocumentHighlightProvider(defaultExtension, defaultSelector, { provideDocumentHighlights(): any { throw new Error('evil'); } })); - disposables.push(extHost.registerDocumentHighlightProvider(defaultSelector, { + disposables.push(extHost.registerDocumentHighlightProvider(defaultExtension, defaultSelector, { provideDocumentHighlights(): any { return [new types.DocumentHighlight(new types.Range(0, 0, 0, 4))]; } @@ -566,13 +588,13 @@ suite('ExtHostLanguageFeatures', function () { test('References, registration order', function () { - disposables.push(extHost.registerReferenceProvider(defaultSelector, { + disposables.push(extHost.registerReferenceProvider(defaultExtension, defaultSelector, { provideReferences(): any { return [new types.Location(URI.parse('far://register/first'), new types.Range(0, 0, 0, 0))]; } })); - disposables.push(extHost.registerReferenceProvider(defaultSelector, { + disposables.push(extHost.registerReferenceProvider(defaultExtension, defaultSelector, { provideReferences(): any { return [new types.Location(URI.parse('far://register/second'), new types.Range(0, 0, 0, 0))]; } @@ -592,7 +614,7 @@ suite('ExtHostLanguageFeatures', function () { test('References, data conversion', function () { - disposables.push(extHost.registerReferenceProvider(defaultSelector, { + disposables.push(extHost.registerReferenceProvider(defaultExtension, defaultSelector, { provideReferences(): any { return [new types.Location(model.uri, new types.Position(0, 0))]; } @@ -613,12 +635,12 @@ suite('ExtHostLanguageFeatures', function () { test('References, evil provider', function () { - disposables.push(extHost.registerReferenceProvider(defaultSelector, { + disposables.push(extHost.registerReferenceProvider(defaultExtension, defaultSelector, { provideReferences(): any { throw new Error('evil'); } })); - disposables.push(extHost.registerReferenceProvider(defaultSelector, { + disposables.push(extHost.registerReferenceProvider(defaultExtension, defaultSelector, { provideReferences(): any { return [new types.Location(model.uri, new types.Range(0, 0, 0, 0))]; } @@ -637,7 +659,7 @@ suite('ExtHostLanguageFeatures', function () { test('Quick Fix, command data conversion', function () { - disposables.push(extHost.registerCodeActionProvider(defaultSelector, { + disposables.push(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, { provideCodeActions(): vscode.Command[] { return [ { command: 'test1', title: 'Testing1' }, @@ -661,7 +683,7 @@ suite('ExtHostLanguageFeatures', function () { test('Quick Fix, code action data conversion', function () { - disposables.push(extHost.registerCodeActionProvider(defaultSelector, { + disposables.push(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, { provideCodeActions(): vscode.CodeAction[] { return [ { @@ -689,7 +711,7 @@ suite('ExtHostLanguageFeatures', function () { test('Cannot read property \'id\' of undefined, #29469', function () { - disposables.push(extHost.registerCodeActionProvider(defaultSelector, { + disposables.push(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, { provideCodeActions(): any { return [ undefined, @@ -708,12 +730,12 @@ suite('ExtHostLanguageFeatures', function () { test('Quick Fix, evil provider', function () { - disposables.push(extHost.registerCodeActionProvider(defaultSelector, { + disposables.push(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, { provideCodeActions(): any { throw new Error('evil'); } })); - disposables.push(extHost.registerCodeActionProvider(defaultSelector, { + disposables.push(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, { provideCodeActions(): any { return [{ command: 'test', title: 'Testing' }]; } @@ -730,13 +752,13 @@ suite('ExtHostLanguageFeatures', function () { test('Navigate types, evil provider', function () { - disposables.push(extHost.registerWorkspaceSymbolProvider({ + disposables.push(extHost.registerWorkspaceSymbolProvider(defaultExtension, { provideWorkspaceSymbols(): any { throw new Error('evil'); } })); - disposables.push(extHost.registerWorkspaceSymbolProvider({ + disposables.push(extHost.registerWorkspaceSymbolProvider(defaultExtension, { provideWorkspaceSymbols(): any { return [new types.SymbolInformation('testing', types.SymbolKind.Array, new types.Range(0, 0, 1, 1))]; } @@ -758,7 +780,7 @@ suite('ExtHostLanguageFeatures', function () { test('Rename, evil provider 0/2', function () { - disposables.push(extHost.registerRenameProvider(defaultSelector, { + disposables.push(extHost.registerRenameProvider(defaultExtension, defaultSelector, { provideRenameEdits(): any { throw new class Foo { }; } @@ -776,7 +798,7 @@ suite('ExtHostLanguageFeatures', function () { test('Rename, evil provider 1/2', function () { - disposables.push(extHost.registerRenameProvider(defaultSelector, { + disposables.push(extHost.registerRenameProvider(defaultExtension, defaultSelector, { provideRenameEdits(): any { throw Error('evil'); } @@ -792,13 +814,13 @@ suite('ExtHostLanguageFeatures', function () { test('Rename, evil provider 2/2', function () { - disposables.push(extHost.registerRenameProvider('*', { + disposables.push(extHost.registerRenameProvider(defaultExtension, '*', { provideRenameEdits(): any { throw Error('evil'); } })); - disposables.push(extHost.registerRenameProvider(defaultSelector, { + disposables.push(extHost.registerRenameProvider(defaultExtension, defaultSelector, { provideRenameEdits(): any { let edit = new types.WorkspaceEdit(); edit.replace(model.uri, new types.Range(0, 0, 0, 0), 'testing'); @@ -816,7 +838,7 @@ suite('ExtHostLanguageFeatures', function () { test('Rename, ordering', function () { - disposables.push(extHost.registerRenameProvider('*', { + disposables.push(extHost.registerRenameProvider(defaultExtension, '*', { provideRenameEdits(): any { let edit = new types.WorkspaceEdit(); edit.replace(model.uri, new types.Range(0, 0, 0, 0), 'testing'); @@ -825,7 +847,7 @@ suite('ExtHostLanguageFeatures', function () { } })); - disposables.push(extHost.registerRenameProvider(defaultSelector, { + disposables.push(extHost.registerRenameProvider(defaultExtension, defaultSelector, { provideRenameEdits(): any { return; } @@ -846,13 +868,13 @@ suite('ExtHostLanguageFeatures', function () { test('Parameter Hints, order', function () { - disposables.push(extHost.registerSignatureHelpProvider(defaultSelector, { + disposables.push(extHost.registerSignatureHelpProvider(defaultExtension, defaultSelector, { provideSignatureHelp(): any { return undefined; } }, [])); - disposables.push(extHost.registerSignatureHelpProvider(defaultSelector, { + disposables.push(extHost.registerSignatureHelpProvider(defaultExtension, defaultSelector, { provideSignatureHelp(): vscode.SignatureHelp { return { signatures: [], @@ -864,14 +886,14 @@ suite('ExtHostLanguageFeatures', function () { return rpcProtocol.sync().then(() => { - return provideSignatureHelp(model, new EditorPosition(1, 1), { triggerReason: modes.SignatureHelpTriggerReason.Invoke }, CancellationToken.None).then(value => { + return provideSignatureHelp(model, new EditorPosition(1, 1), { triggerReason: modes.SignatureHelpTriggerReason.Invoke, isRetrigger: false }, CancellationToken.None).then(value => { assert.ok(value); }); }); }); test('Parameter Hints, evil provider', function () { - disposables.push(extHost.registerSignatureHelpProvider(defaultSelector, { + disposables.push(extHost.registerSignatureHelpProvider(defaultExtension, defaultSelector, { provideSignatureHelp(): any { throw new Error('evil'); } @@ -879,7 +901,7 @@ suite('ExtHostLanguageFeatures', function () { return rpcProtocol.sync().then(() => { - return provideSignatureHelp(model, new EditorPosition(1, 1), { triggerReason: modes.SignatureHelpTriggerReason.Invoke }, CancellationToken.None).then(value => { + return provideSignatureHelp(model, new EditorPosition(1, 1), { triggerReason: modes.SignatureHelpTriggerReason.Invoke, isRetrigger: false }, CancellationToken.None).then(value => { assert.equal(value, undefined); }); }); @@ -889,13 +911,13 @@ suite('ExtHostLanguageFeatures', function () { test('Suggest, order 1/3', function () { - disposables.push(extHost.registerCompletionItemProvider('*', { + disposables.push(extHost.registerCompletionItemProvider(defaultExtension, '*', { provideCompletionItems(): any { return [new types.CompletionItem('testing1')]; } }, [])); - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, { provideCompletionItems(): any { return [new types.CompletionItem('testing2')]; } @@ -911,13 +933,13 @@ suite('ExtHostLanguageFeatures', function () { test('Suggest, order 2/3', function () { - disposables.push(extHost.registerCompletionItemProvider('*', { + disposables.push(extHost.registerCompletionItemProvider(defaultExtension, '*', { provideCompletionItems(): any { return [new types.CompletionItem('weak-selector')]; // weaker selector but result } }, [])); - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, { provideCompletionItems(): any { return []; // stronger selector but not a good result; } @@ -933,13 +955,13 @@ suite('ExtHostLanguageFeatures', function () { test('Suggest, order 2/3', function () { - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, { provideCompletionItems(): any { return [new types.CompletionItem('strong-1')]; } }, [])); - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, { provideCompletionItems(): any { return [new types.CompletionItem('strong-2')]; } @@ -956,13 +978,13 @@ suite('ExtHostLanguageFeatures', function () { test('Suggest, evil provider', function () { - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, { provideCompletionItems(): any { throw new Error('evil'); } }, [])); - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, { provideCompletionItems(): any { return [new types.CompletionItem('testing')]; } @@ -979,7 +1001,7 @@ suite('ExtHostLanguageFeatures', function () { test('Suggest, CompletionList', function () { - disposables.push(extHost.registerCompletionItemProvider(defaultSelector, { + disposables.push(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, { provideCompletionItems(): any { return new types.CompletionList([new types.CompletionItem('hello')], true); } @@ -996,7 +1018,7 @@ suite('ExtHostLanguageFeatures', function () { // --- format test('Format Doc, data conversion', function () { - disposables.push(extHost.registerDocumentFormattingEditProvider(defaultSelector, { + disposables.push(extHost.registerDocumentFormattingEditProvider(defaultExtension, defaultSelector, { provideDocumentFormattingEdits(): any { return [new types.TextEdit(new types.Range(0, 0, 0, 0), 'testing'), types.TextEdit.setEndOfLine(types.EndOfLine.LF)]; } @@ -1011,13 +1033,13 @@ suite('ExtHostLanguageFeatures', function () { assert.equal(second.eol, EndOfLineSequence.LF); assert.equal(second.text, ''); - assert.equal(second.range, undefined); + assert.deepEqual(second.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); }); }); }); test('Format Doc, evil provider', function () { - disposables.push(extHost.registerDocumentFormattingEditProvider(defaultSelector, { + disposables.push(extHost.registerDocumentFormattingEditProvider(defaultExtension, defaultSelector, { provideDocumentFormattingEdits(): any { throw new Error('evil'); } @@ -1030,19 +1052,19 @@ suite('ExtHostLanguageFeatures', function () { test('Format Doc, order', function () { - disposables.push(extHost.registerDocumentFormattingEditProvider(defaultSelector, { + disposables.push(extHost.registerDocumentFormattingEditProvider(defaultExtension, defaultSelector, { provideDocumentFormattingEdits(): any { return undefined; } })); - disposables.push(extHost.registerDocumentFormattingEditProvider(defaultSelector, { + disposables.push(extHost.registerDocumentFormattingEditProvider(defaultExtension, defaultSelector, { provideDocumentFormattingEdits(): any { return [new types.TextEdit(new types.Range(0, 0, 0, 0), 'testing')]; } })); - disposables.push(extHost.registerDocumentFormattingEditProvider(defaultSelector, { + disposables.push(extHost.registerDocumentFormattingEditProvider(defaultExtension, defaultSelector, { provideDocumentFormattingEdits(): any { return undefined; } @@ -1059,7 +1081,7 @@ suite('ExtHostLanguageFeatures', function () { }); test('Format Range, data conversion', function () { - disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultSelector, { + disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultExtension, defaultSelector, { provideDocumentRangeFormattingEdits(): any { return [new types.TextEdit(new types.Range(0, 0, 0, 0), 'testing')]; } @@ -1076,17 +1098,17 @@ suite('ExtHostLanguageFeatures', function () { }); test('Format Range, + format_doc', function () { - disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultSelector, { + disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultExtension, defaultSelector, { provideDocumentRangeFormattingEdits(): any { return [new types.TextEdit(new types.Range(0, 0, 0, 0), 'range')]; } })); - disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultSelector, { + disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultExtension, defaultSelector, { provideDocumentRangeFormattingEdits(): any { return [new types.TextEdit(new types.Range(2, 3, 4, 5), 'range2')]; } })); - disposables.push(extHost.registerDocumentFormattingEditProvider(defaultSelector, { + disposables.push(extHost.registerDocumentFormattingEditProvider(defaultExtension, defaultSelector, { provideDocumentFormattingEdits(): any { return [new types.TextEdit(new types.Range(0, 0, 1, 1), 'doc')]; } @@ -1105,7 +1127,7 @@ suite('ExtHostLanguageFeatures', function () { }); test('Format Range, evil provider', function () { - disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultSelector, { + disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultExtension, defaultSelector, { provideDocumentRangeFormattingEdits(): any { throw new Error('evil'); } @@ -1118,7 +1140,7 @@ suite('ExtHostLanguageFeatures', function () { test('Format on Type, data conversion', function () { - disposables.push(extHost.registerOnTypeFormattingEditProvider(defaultSelector, { + disposables.push(extHost.registerOnTypeFormattingEditProvider(defaultExtension, defaultSelector, { provideOnTypeFormattingEdits(): any { return [new types.TextEdit(new types.Range(0, 0, 0, 0), arguments[2])]; } @@ -1137,7 +1159,7 @@ suite('ExtHostLanguageFeatures', function () { test('Links, data conversion', function () { - disposables.push(extHost.registerDocumentLinkProvider(defaultSelector, { + disposables.push(extHost.registerDocumentLinkProvider(defaultExtension, defaultSelector, { provideDocumentLinks() { return [new types.DocumentLink(new types.Range(0, 0, 1, 1), URI.parse('foo:bar#3'))]; } @@ -1156,13 +1178,13 @@ suite('ExtHostLanguageFeatures', function () { test('Links, evil provider', function () { - disposables.push(extHost.registerDocumentLinkProvider(defaultSelector, { + disposables.push(extHost.registerDocumentLinkProvider(defaultExtension, defaultSelector, { provideDocumentLinks() { return [new types.DocumentLink(new types.Range(0, 0, 1, 1), URI.parse('foo:bar#3'))]; } })); - disposables.push(extHost.registerDocumentLinkProvider(defaultSelector, { + disposables.push(extHost.registerDocumentLinkProvider(defaultExtension, defaultSelector, { provideDocumentLinks(): any { throw new Error(); } @@ -1181,7 +1203,7 @@ suite('ExtHostLanguageFeatures', function () { test('Document colors, data conversion', function () { - disposables.push(extHost.registerColorProvider(defaultSelector, { + disposables.push(extHost.registerColorProvider(defaultExtension, defaultSelector, { provideDocumentColors(): vscode.ColorInformation[] { return [new types.ColorInformation(new types.Range(0, 0, 0, 20), new types.Color(0.1, 0.2, 0.3, 0.4))]; }, 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 da34e6e3c..18c5c7078 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts @@ -3,17 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { mapArrayOrNot } from 'vs/base/common/arrays'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { dispose } from 'vs/base/common/lifecycle'; import { joinPath } from 'vs/base/common/resources'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as extfs from 'vs/base/node/extfs'; -import { IFileMatch, IFileQuery, IPatternInfo, IRawFileMatch2, ISearchCompleteStats, ISearchQuery, ITextQuery, QueryType } from 'vs/platform/search/common/search'; +import { IFileMatch, IFileQuery, IPatternInfo, IRawFileMatch2, ISearchCompleteStats, ISearchQuery, ITextQuery, QueryType, resultIsMatch } from 'vs/platform/search/common/search'; import { MainContext, MainThreadSearchShape } from 'vs/workbench/api/node/extHost.protocol'; import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; import { ExtHostSearch } from 'vs/workbench/api/node/extHostSearch'; import { Range } from 'vs/workbench/api/node/extHostTypes'; +import { extensionResultIsMatch } from 'vs/workbench/services/search/node/textSearchManager'; import { TestRPCProtocol } from 'vs/workbench/test/electron-browser/api/testRPCProtocol'; import { TestLogService } from 'vs/workbench/test/workbenchTestServices'; import * as vscode from 'vscode'; @@ -623,17 +625,17 @@ suite('ExtHostSearch', () => { suite('Text:', () => { - function makePreview(text: string): vscode.TextSearchResult['preview'] { + function makePreview(text: string): vscode.TextSearchMatch['preview'] { return { - match: new Range(0, 0, 0, text.length), + matches: new Range(0, 0, 0, text.length), text }; } - function makeTextResult(baseFolder: URI, relativePath: string): vscode.TextSearchResult { + function makeTextResult(baseFolder: URI, relativePath: string): vscode.TextSearchMatch { return { preview: makePreview('foo'), - range: new Range(0, 0, 0, 3), + ranges: new Range(0, 0, 0, 3), uri: joinPath(baseFolder, relativePath) }; } @@ -659,33 +661,51 @@ suite('ExtHostSearch', () => { const actualTextSearchResults: vscode.TextSearchResult[] = []; for (let fileMatch of actual) { // Make relative - for (let lineMatch of fileMatch.matches) { - actualTextSearchResults.push({ - preview: { - text: lineMatch.preview.text, - match: new Range(lineMatch.preview.match.startLineNumber, lineMatch.preview.match.startColumn, lineMatch.preview.match.endLineNumber, lineMatch.preview.match.endColumn) - }, - range: new Range(lineMatch.range.startLineNumber, lineMatch.range.startColumn, lineMatch.range.endLineNumber, lineMatch.range.endColumn), - uri: fileMatch.resource - }); + for (let lineResult of fileMatch.results) { + if (resultIsMatch(lineResult)) { + actualTextSearchResults.push({ + preview: { + text: lineResult.preview.text, + matches: mapArrayOrNot( + lineResult.preview.matches, + m => new Range(m.startLineNumber, m.startColumn, m.endLineNumber, m.endColumn)) + }, + ranges: mapArrayOrNot( + lineResult.ranges, + r => new Range(r.startLineNumber, r.startColumn, r.endLineNumber, r.endColumn), + ), + uri: fileMatch.resource + }); + } else { + actualTextSearchResults.push({ + text: lineResult.text, + lineNumber: lineResult.lineNumber, + uri: fileMatch.resource + }); + } } } const rangeToString = (r: vscode.Range) => `(${r.start.line}, ${r.start.character}), (${r.end.line}, ${r.end.character})`; const makeComparable = (results: vscode.TextSearchResult[]) => results - .sort((a, b) => b.preview.text.localeCompare(a.preview.text)) - .map(r => ({ - ...r, - ...{ - uri: r.uri.toString(), - range: rangeToString(r.range), - preview: { - text: r.preview.text, - match: null // Don't care about this right now - } + .sort((a, b) => { + const compareKeyA = a.uri.toString() + ': ' + (extensionResultIsMatch(a) ? a.preview.text : a.text); + const compareKeyB = b.uri.toString() + ': ' + (extensionResultIsMatch(b) ? b.preview.text : b.text); + return compareKeyB.localeCompare(compareKeyA); + }) + .map(r => extensionResultIsMatch(r) ? { + uri: r.uri.toString(), + range: mapArrayOrNot(r.ranges, rangeToString), + preview: { + text: r.preview.text, + match: null // Don't care about this right now } - })); + } : { + uri: r.uri.toString(), + text: r.text, + lineNumber: r.lineNumber + }); return assert.deepEqual( makeComparable(actualTextSearchResults), diff --git a/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts index ee9704fd0..b19dac9a8 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts @@ -18,6 +18,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { mock } from 'vs/workbench/test/electron-browser/api/mock'; import { TreeItemCollapsibleState, ITreeItem } from 'vs/workbench/common/views'; import { NullLogService } from 'vs/platform/log/common/log'; +import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; suite('ExtHostTreeView', function () { @@ -72,9 +73,9 @@ suite('ExtHostTreeView', function () { testObject = new ExtHostTreeViews(target, new ExtHostCommands(rpcProtocol, new ExtHostHeapService(), new NullLogService()), new NullLogService()); onDidChangeTreeNode = new Emitter<{ key: string }>(); onDidChangeTreeNodeWithId = new Emitter<{ key: string }>(); - testObject.createTreeView('testNodeTreeProvider', { treeDataProvider: aNodeTreeDataProvider() }); - testObject.createTreeView('testNodeWithIdTreeProvider', { treeDataProvider: aNodeWithIdTreeDataProvider() }); - testObject.createTreeView('testNodeWithHighlightsTreeProvider', { treeDataProvider: aNodeWithHighlightedLabelTreeDataProvider() }); + testObject.createTreeView('testNodeTreeProvider', { treeDataProvider: aNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); + testObject.createTreeView('testNodeWithIdTreeProvider', { treeDataProvider: aNodeWithIdTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); + testObject.createTreeView('testNodeWithHighlightsTreeProvider', { treeDataProvider: aNodeWithHighlightedLabelTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); return loadCompleteTree('testNodeTreeProvider'); }); @@ -445,40 +446,40 @@ suite('ExtHostTreeView', function () { }); test('reveal will throw an error if getParent is not implemented', () => { - const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aNodeTreeDataProvider() }); + const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); return treeView.reveal({ key: 'a' }) .then(() => assert.fail('Reveal should throw an error as getParent is not implemented'), () => null); }); test('reveal will return empty array for root element', () => { const revealTarget = sinon.spy(target, '$reveal'); - const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }); + const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); return treeView.reveal({ key: 'a' }) .then(() => { assert.ok(revealTarget.calledOnce); assert.deepEqual('treeDataProvider', revealTarget.args[0][0]); assert.deepEqual({ handle: '0/0:a', label: { label: 'a' }, collapsibleState: TreeItemCollapsibleState.Collapsed }, removeUnsetKeys(revealTarget.args[0][1])); assert.deepEqual([], revealTarget.args[0][2]); - assert.deepEqual({ select: true, focus: false }, revealTarget.args[0][3]); + assert.deepEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][3]); }); }); test('reveal will return parents array for an element when hierarchy is not loaded', () => { const revealTarget = sinon.spy(target, '$reveal'); - const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }); + const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); return treeView.reveal({ key: 'aa' }) .then(() => { assert.ok(revealTarget.calledOnce); assert.deepEqual('treeDataProvider', revealTarget.args[0][0]); assert.deepEqual({ handle: '0/0:a/0:aa', label: { label: 'aa' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:a' }, removeUnsetKeys(revealTarget.args[0][1])); assert.deepEqual([{ handle: '0/0:a', label: { label: 'a' }, collapsibleState: TreeItemCollapsibleState.Collapsed }], (>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg))); - assert.deepEqual({ select: true, focus: false }, revealTarget.args[0][3]); + assert.deepEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][3]); }); }); test('reveal will return parents array for an element when hierarchy is loaded', () => { const revealTarget = sinon.spy(target, '$reveal'); - const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }); + const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); return testObject.$getChildren('treeDataProvider') .then(() => testObject.$getChildren('treeDataProvider', '0/0:a')) .then(() => treeView.reveal({ key: 'aa' }) @@ -487,7 +488,7 @@ suite('ExtHostTreeView', function () { assert.deepEqual('treeDataProvider', revealTarget.args[0][0]); assert.deepEqual({ handle: '0/0:a/0:aa', label: { label: 'aa' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:a' }, removeUnsetKeys(revealTarget.args[0][1])); assert.deepEqual([{ handle: '0/0:a', label: { label: 'a' }, collapsibleState: TreeItemCollapsibleState.Collapsed }], (>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg))); - assert.deepEqual({ select: true, focus: false }, revealTarget.args[0][3]); + assert.deepEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][3]); })); }); @@ -500,8 +501,8 @@ suite('ExtHostTreeView', function () { } }; const revealTarget = sinon.spy(target, '$reveal'); - const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }); - return treeView.reveal({ key: 'bac' }, { select: false, focus: false }) + const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); + return treeView.reveal({ key: 'bac' }, { select: false, focus: false, expand: false }) .then(() => { assert.ok(revealTarget.calledOnce); assert.deepEqual('treeDataProvider', revealTarget.args[0][0]); @@ -510,13 +511,13 @@ suite('ExtHostTreeView', function () { { handle: '0/0:b', label: { label: 'b' }, collapsibleState: TreeItemCollapsibleState.Collapsed }, { handle: '0/0:b/0:ba', label: { label: 'ba' }, collapsibleState: TreeItemCollapsibleState.Collapsed, parentHandle: '0/0:b' } ], (>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg))); - assert.deepEqual({ select: false, focus: false }, revealTarget.args[0][3]); + assert.deepEqual({ select: false, focus: false, expand: false }, revealTarget.args[0][3]); }); }); test('reveal after first udpate', () => { const revealTarget = sinon.spy(target, '$reveal'); - const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }); + const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); return loadCompleteTree('treeDataProvider') .then(() => { tree = { @@ -537,14 +538,14 @@ suite('ExtHostTreeView', function () { assert.deepEqual('treeDataProvider', revealTarget.args[0][0]); assert.deepEqual({ handle: '0/0:a/0:ac', label: { label: 'ac' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:a' }, removeUnsetKeys(revealTarget.args[0][1])); assert.deepEqual([{ handle: '0/0:a', label: { label: 'a' }, collapsibleState: TreeItemCollapsibleState.Collapsed }], (>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg))); - assert.deepEqual({ select: true, focus: false }, revealTarget.args[0][3]); + assert.deepEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][3]); }); }); }); test('reveal after second udpate', () => { const revealTarget = sinon.spy(target, '$reveal'); - const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }); + const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); return loadCompleteTree('treeDataProvider') .then(() => { tree = { @@ -576,7 +577,7 @@ suite('ExtHostTreeView', function () { assert.deepEqual('treeDataProvider', revealTarget.args[0][0]); assert.deepEqual({ handle: '0/0:b/0:bc', label: { label: 'bc' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:b' }, removeUnsetKeys(revealTarget.args[0][1])); assert.deepEqual([{ handle: '0/0:b', label: { label: 'b' }, collapsibleState: TreeItemCollapsibleState.Collapsed }], (>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg))); - assert.deepEqual({ select: true, focus: false }, revealTarget.args[0][3]); + assert.deepEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][3]); }); }); }); diff --git a/src/vs/workbench/test/electron-browser/api/extHostTypeConverter.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTypeConverter.test.ts new file mode 100644 index 000000000..3b217231a --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/extHostTypeConverter.test.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * 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/workbench/api/node/extHostTypeConverters'; +import { isEmptyObject } from 'vs/base/common/types'; +import { size } from 'vs/base/common/collections'; + +suite('ExtHostTypeConverter', function () { + + test('MarkdownConvert - uris', function () { + + let data = MarkdownString.from('Hello'); + assert.equal(isEmptyObject(data.uris), true); + assert.equal(data.value, 'Hello'); + + data = MarkdownString.from('Hello [link](foo)'); + assert.equal(data.value, 'Hello [link](foo)'); + assert.equal(isEmptyObject(data.uris), true); // no scheme, no uri + + data = MarkdownString.from('Hello [link](www.noscheme.bad)'); + assert.equal(data.value, 'Hello [link](www.noscheme.bad)'); + assert.equal(isEmptyObject(data.uris), true); // no scheme, no uri + + data = MarkdownString.from('Hello [link](foo:path)'); + assert.equal(data.value, 'Hello [link](foo:path)'); + assert.equal(size(data.uris), 1); + assert.ok(!!data.uris['foo:path']); + + data = MarkdownString.from('hello@foo.bar'); + assert.equal(data.value, 'hello@foo.bar'); + assert.equal(size(data.uris), 1); + assert.ok(!!data.uris['mailto:hello@foo.bar']); + + data = MarkdownString.from('*hello* [click](command:me)'); + assert.equal(data.value, '*hello* [click](command:me)'); + assert.equal(size(data.uris), 1); + assert.ok(!!data.uris['command:me']); + + data = MarkdownString.from('*hello* [click](file:///somepath/here). [click](file:///somepath/here)'); + assert.equal(data.value, '*hello* [click](file:///somepath/here). [click](file:///somepath/here)'); + assert.equal(size(data.uris), 1); + assert.ok(!!data.uris['file:///somepath/here']); + + data = MarkdownString.from('*hello* [click](file:///somepath/here). [click](file:///somepath/here)'); + assert.equal(data.value, '*hello* [click](file:///somepath/here). [click](file:///somepath/here)'); + assert.equal(size(data.uris), 1); + assert.ok(!!data.uris['file:///somepath/here']); + + data = MarkdownString.from('*hello* [click](file:///somepath/here). [click](file:///somepath/here2)'); + assert.equal(data.value, '*hello* [click](file:///somepath/here). [click](file:///somepath/here2)'); + assert.equal(size(data.uris), 2); + assert.ok(!!data.uris['file:///somepath/here']); + assert.ok(!!data.uris['file:///somepath/here2']); + }); +}); diff --git a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts index 80ddda659..6e121e81c 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts @@ -384,12 +384,12 @@ suite('ExtHostTypes', function () { const all = edit._allEntries(); assert.equal(all.length, 4); - function isFileChange(thing: [URI, types.TextEdit[]] | [URI, URI, { overwrite?: boolean }]): thing is [URI, URI, { overwrite?: boolean }] { + function isFileChange(thing: [URI, types.TextEdit[]] | [URI?, URI?, { overwrite?: boolean }?]): thing is [URI?, URI?, { overwrite?: boolean }?] { const [f, s] = thing; return URI.isUri(f) && URI.isUri(s); } - function isTextChange(thing: [URI, types.TextEdit[]] | [URI, URI, { overwrite?: boolean }]): thing is [URI, types.TextEdit[]] { + function isTextChange(thing: [URI, types.TextEdit[]] | [URI?, URI?, { overwrite?: boolean }?]): thing is [URI, types.TextEdit[]] { const [f, s] = thing; return URI.isUri(f) && Array.isArray(s); } diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadDocumentContentProviders.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadDocumentContentProviders.test.ts new file mode 100644 index 000000000..b1a4edd3e --- /dev/null +++ b/src/vs/workbench/test/electron-browser/api/mainThreadDocumentContentProviders.test.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { URI } from 'vs/base/common/uri'; +import { MainThreadDocumentContentProviders } from 'vs/workbench/api/electron-browser/mainThreadDocumentContentProviders'; +import { TextModel } from 'vs/editor/common/model/textModel'; +import { mock } from 'vs/workbench/test/electron-browser/api/mock'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { TestRPCProtocol } from 'vs/workbench/test/electron-browser/api/testRPCProtocol'; + +suite('MainThreadDocumentContentProviders', function () { + + test('events are processed properly', function () { + + let uri = URI.parse('test:uri'); + let model = TextModel.createFromString('1', undefined, undefined, uri); + + let providers = new MainThreadDocumentContentProviders(new TestRPCProtocol(), null, null, + new class extends mock() { + getModel(_uri) { + assert.equal(uri.toString(), _uri.toString()); + return model; + } + }, + new class extends mock() { + computeMoreMinimalEdits(_uri, data) { + assert.equal(model.getValue(), '1'); + return Promise.resolve(data); + } + }, + ); + + return new Promise((resolve, reject) => { + let expectedEvents = 1; + model.onDidChangeContent(e => { + expectedEvents -= 1; + try { + assert.ok(expectedEvents >= 0); + } catch (err) { + reject(err); + } + if (model.getValue() === '1\n2\n3') { + resolve(); + } + }); + providers.$onVirtualDocumentChange(uri, '1\n2'); + providers.$onVirtualDocumentChange(uri, '1\n2\n3'); + }); + }); +}); 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 2c39ea8c4..4ec159ed8 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts @@ -19,7 +19,7 @@ import { Range } from 'vs/editor/common/core/range'; import { Position } from 'vs/editor/common/core/position'; import { IModelService } from 'vs/editor/common/services/modelService'; import { EditOperation } from 'vs/editor/common/core/editOperation'; -import { TestFileService, TestEditorService, TestEditorGroupsService, TestEnvironmentService, TestContextService, TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices'; +import { TestFileService, TestEditorService, TestEditorGroupsService, TestEnvironmentService, TestContextService, TestTextResourcePropertiesService, TestWindowService } from 'vs/workbench/test/workbenchTestServices'; import { ResourceTextEdit } from 'vs/editor/common/modes'; import { BulkEditService } from 'vs/workbench/services/bulkEdit/electron-browser/bulkEditService'; import { NullLogService } from 'vs/platform/log/common/log'; @@ -82,7 +82,7 @@ suite('MainThreadEditors', () => { } }; - const bulkEditService = new BulkEditService(new NullLogService(), modelService, new TestEditorService(), textModelService, new TestFileService(), textFileService, new LabelService(TestEnvironmentService, new TestContextService()), configService); + const bulkEditService = new BulkEditService(new NullLogService(), modelService, new TestEditorService(), textModelService, new TestFileService(), textFileService, new LabelService(TestEnvironmentService, new TestContextService(), new TestWindowService()), configService); const rpcProtocol = new TestRPCProtocol(); rpcProtocol.set(ExtHostContext.ExtHostDocuments, new class extends mock() { diff --git a/src/vs/workbench/test/electron-browser/api/testRPCProtocol.ts b/src/vs/workbench/test/electron-browser/api/testRPCProtocol.ts index 3e7225bb4..d2a5259f9 100644 --- a/src/vs/workbench/test/electron-browser/api/testRPCProtocol.ts +++ b/src/vs/workbench/test/electron-browser/api/testRPCProtocol.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier'; import { CharCode } from 'vs/base/common/charCode'; import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; @@ -11,6 +10,7 @@ import { isThenable } from 'vs/base/common/async'; export function SingleProxyRPCProtocol(thing: any): IExtHostContext { return { + remoteAuthority: null, getProxy(): T { return thing; }, @@ -23,6 +23,8 @@ export function SingleProxyRPCProtocol(thing: any): IExtHostContext { export class TestRPCProtocol implements IExtHostContext { + public remoteAuthority = null; + private _callCountValue: number = 0; private _idle: Promise; private _completeIdle: Function; @@ -91,10 +93,10 @@ export class TestRPCProtocol implements IExtHostContext { return value; } - protected _remoteCall(proxyId: string, path: string, args: any[]): TPromise { + protected _remoteCall(proxyId: string, path: string, args: any[]): Promise { this._callCount++; - return new TPromise((c) => { + return new Promise((c) => { setTimeout(c, 0); }).then(() => { const instance = this._locals[proxyId]; @@ -103,9 +105,9 @@ export class TestRPCProtocol implements IExtHostContext { let p: Thenable; try { let result = (instance[path]).apply(instance, wireArgs); - p = isThenable(result) ? result : TPromise.as(result); + p = isThenable(result) ? result : Promise.resolve(result); } catch (err) { - p = TPromise.wrapError(err); + p = Promise.reject(err); } return p.then(result => { @@ -115,7 +117,7 @@ export class TestRPCProtocol implements IExtHostContext { return wireResult; }, err => { this._callCount--; - return TPromise.wrapError(err); + return Promise.reject(err); }); }); } diff --git a/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts b/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts index ce851d814..4b2d8d438 100644 --- a/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts +++ b/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts @@ -19,7 +19,6 @@ import { SearchService } from 'vs/workbench/services/search/node/searchService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { TestEnvironmentService, TestContextService, TestEditorService, TestEditorGroupsService, TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { TPromise } from 'vs/base/common/winjs.base'; import { URI } from 'vs/base/common/uri'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; @@ -78,7 +77,7 @@ suite.skip('TextSearch performance (integration)', () => { }; const searchModel: SearchModel = instantiationService.createInstance(SearchModel); - function runSearch(): TPromise { + function runSearch(): Promise { const queryBuilder: QueryBuilder = instantiationService.createInstance(QueryBuilder); const query = queryBuilder.text({ pattern: 'static_library(' }, [URI.file(testWorkspacePath)], queryOptions); @@ -107,7 +106,7 @@ suite.skip('TextSearch performance (integration)', () => { let resolve; let error; - return new TPromise((_resolve, _error) => { + return new Promise((_resolve, _error) => { resolve = _resolve; error = _error; @@ -155,15 +154,15 @@ class TestTelemetryService implements ITelemetryService { return this.emitter.event; } - public publicLog(eventName: string, data?: any): TPromise { + public publicLog(eventName: string, data?: any): Promise { const event = { name: eventName, data: data }; this.events.push(event); this.emitter.fire(event); - return TPromise.wrap(null); + return Promise.resolve(); } - public getTelemetryInfo(): TPromise { - return TPromise.wrap({ + public getTelemetryInfo(): Promise { + return Promise.resolve({ instanceId: 'someValue.instanceId', sessionId: 'someValue.sessionId', machineId: 'someValue.machineId' diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index b4ebc3a06..6c33565ef 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -58,7 +58,7 @@ import { Range } from 'vs/editor/common/core/range'; import { IConfirmation, IConfirmationResult, IDialogService, IDialogOptions, IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; -import { IExtensionService, ProfileSession, IExtensionsStatus, ExtensionPointContribution, IExtensionDescription, IWillActivateEvent } from '../services/extensions/common/extensions'; +import { IExtensionService, ProfileSession, IExtensionsStatus, ExtensionPointContribution, IExtensionDescription, IWillActivateEvent, IResponsiveStateChangeEvent } from '../services/extensions/common/extensions'; import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IDecorationsService, IResourceDecorationChangeEvent, IDecoration, IDecorationData, IDecorationsProvider } from 'vs/workbench/services/decorations/browser/decorations'; @@ -311,14 +311,15 @@ export class TestExtensionService implements IExtensionService { onDidRegisterExtensions: Event = Event.None; onDidChangeExtensionsStatus: Event = Event.None; onWillActivateByEvent: Event = Event.None; - activateByEvent(_activationEvent: string): TPromise { return TPromise.as(void 0); } - whenInstalledExtensionsRegistered(): TPromise { return TPromise.as(true); } - getExtensions(): TPromise { return TPromise.as([]); } - readExtensionPointContributions(_extPoint: IExtensionPoint): TPromise[]> { return TPromise.as(Object.create(null)); } + onDidChangeResponsiveChange: Event = Event.None; + activateByEvent(_activationEvent: string): Thenable { return Promise.resolve(void 0); } + whenInstalledExtensionsRegistered(): Promise { return Promise.resolve(true); } + getExtensions(): Promise { return Promise.resolve([]); } + readExtensionPointContributions(_extPoint: IExtensionPoint): Promise[]> { return Promise.resolve(Object.create(null)); } getExtensionsStatus(): { [id: string]: IExtensionsStatus; } { return Object.create(null); } canProfileExtensionHost(): boolean { return false; } getInspectPort(): number { return 0; } - startExtensionHostProfile(): TPromise { return TPromise.as(Object.create(null)); } + startExtensionHostProfile(): Promise { return Promise.resolve(Object.create(null)); } restartExtensionHost(): void { } startExtensionHost(): void { } stopExtensionHost(): void { } @@ -531,7 +532,7 @@ export class TestPartService implements IPartService { export class TestStorageService extends StorageService { constructor() { - super(':memory:', new NullLogService(), TestEnvironmentService); + super(':memory:', false, new NullLogService()); } } @@ -873,6 +874,10 @@ export class TestFileService implements IFileService { return TPromise.as(null); } + readFolder(_resource: URI) { + return TPromise.as([]); + } + createFolder(_resource: URI): TPromise { return TPromise.as(null); } diff --git a/test/OSSREADME.json b/test/OSSREADME.json deleted file mode 100644 index 051e768a6..000000000 --- a/test/OSSREADME.json +++ /dev/null @@ -1,35 +0,0 @@ -// ATTENTION - THIS DIRECTORY CONTAINS THIRD PARTY OPEN SOURCE MATERIALS: - -[{ - "name": "Jxck/assert", - "license": "MIT", - "licenseDetail": [ - "The MIT License (MIT)", - "", - "Copyright (c) 2011 Jxck", - "", - "Originally from node.js (http://nodejs.org)", - "Copyright Joyent, Inc.", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all", - "copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." - ], - "version": "1.0.0", - "repositoryURL": "https://github.com/Jxck/assert", - "description": "The file assert.js in this folder is based on https://github.com/Jxck/assert/blob/master/assert.js.", - "isDev": true -}] diff --git a/test/cgmanifest.json b/test/cgmanifest.json new file mode 100644 index 000000000..41425880c --- /dev/null +++ b/test/cgmanifest.json @@ -0,0 +1,43 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "name": "Jxck/assert", + "repositoryUrl": "https://github.com/Jxck/assert", + "commitHash": "a617d24d4e752e4299a6de4f78b1c23bfa9c49e8" + } + }, + "licenseDetail": [ + "The MIT License (MIT)", + "", + "Copyright (c) 2011 Jxck", + "", + "Originally from node.js (http://nodejs.org)", + "Copyright Joyent, Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + ], + "developmentDependency": true, + "license": "MIT", + "version": "1.0.0" + } + ], + "version": 1 +} diff --git a/test/smoke/src/areas/editor/editor.ts b/test/smoke/src/areas/editor/editor.ts index c3facfe17..18e4c2f9b 100644 --- a/test/smoke/src/areas/editor/editor.ts +++ b/test/smoke/src/areas/editor/editor.ts @@ -22,7 +22,7 @@ export class Editor { async findReferences(filename: string, term: string, line: number): Promise { await this.clickOnTerm(filename, term, line); - await this.commands.runCommand('Find All References'); + await this.commands.runCommand('Peek References'); const references = new References(this.code); await references.waitUntilOpen(); return references; @@ -40,7 +40,7 @@ export class Editor { async gotoDefinition(filename: string, term: string, line: number): Promise { await this.clickOnTerm(filename, term, line); - await this.commands.runCommand('Go to Definition'); + await this.commands.runCommand('Go to Implementation'); } async peekDefinition(filename: string, term: string, line: number): Promise { diff --git a/test/smoke/src/areas/extensions/extensions.ts b/test/smoke/src/areas/extensions/extensions.ts index b3ad16827..632077c2b 100644 --- a/test/smoke/src/areas/extensions/extensions.ts +++ b/test/smoke/src/areas/extensions/extensions.ts @@ -34,6 +34,6 @@ export class Extensions extends Viewlet { await this.searchForExtension(name); const ariaLabel = `${name}. Press enter for extension details.`; await this.code.waitAndClick(`div.extensions-viewlet[id="workbench.view.extensions"] .monaco-list-row[aria-label="${ariaLabel}"] .extension li[class='action-item'] .extension-action.install`); - await this.code.waitForElement(`.extension-editor .monaco-action-bar .action-item:not(.disabled) .extension-action.reload`); + await this.code.waitForElement(`.extension-editor .monaco-action-bar .action-item:not(.disabled) .extension-action.uninstall`); } } \ No newline at end of file diff --git a/test/smoke/src/areas/workbench/viewlet.ts b/test/smoke/src/areas/workbench/viewlet.ts index 84ccb6010..5d478e5a8 100644 --- a/test/smoke/src/areas/workbench/viewlet.ts +++ b/test/smoke/src/areas/workbench/viewlet.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import { Code } from '../../vscode/code'; export abstract class Viewlet { diff --git a/test/tree/public/index.html b/test/tree/public/index.html index 511385180..db8d67d4f 100644 --- a/test/tree/public/index.html +++ b/test/tree/public/index.html @@ -27,6 +27,7 @@ +
@@ -42,16 +43,29 @@ require.config({ baseUrl: '/static' }); require(['vs/base/browser/ui/tree/indexTree', 'vs/base/browser/ui/tree/dataTree', 'vs/base/browser/ui/tree/tree', 'vs/base/common/iterator'], ({ IndexTree }, { DataTree }, { TreeVisibility }, { iter }) => { - function createIndexTree() { + function createIndexTree(opts) { + opts = opts || {}; + const delegate = { getHeight() { return 22; }, - getTemplateId() { return 'template'; } + getTemplateId() { return 'template'; }, + hasDynamicHeight() { return true; } }; const renderer = { templateId: 'template', renderTemplate(container) { return container; }, - renderElement(element, index, container) { container.textContent = element.element; }, + renderElement(element, index, container) { + if (opts.supportDynamicHeights) { + let v = []; + for (let i = 1; i <= 3; i++) { + v.push(element.element); + } + container.innerHTML = v.join('
'); + } else { + container.innerHTML = element.element; + } + }, disposeElement() { }, disposeTemplate() { } }; @@ -79,7 +93,7 @@ } }; - const tree = new IndexTree(container, delegate, [renderer], { filter: treeFilter }); + const tree = new IndexTree(container, delegate, [renderer], { ...opts, filter: treeFilter, setRowLineHeight: false }); return { tree, treeFilter }; } @@ -158,6 +172,9 @@ case '?problems': { const { tree, treeFilter } = createIndexTree(); + collapseall.onclick = () => perf('collapse all', () => tree.collapseAll()); + renderwidth.onclick = () => perf('renderwidth', () => tree.layoutWidth(Math.random())); + const files = []; for (let i = 0; i < 100000; i++) { const errors = []; @@ -175,13 +192,36 @@ case '?data': { const { tree, treeFilter } = createDataTree(); + collapseall.onclick = () => perf('collapse all', () => tree.collapseAll()); + renderwidth.onclick = () => perf('renderwidth', () => tree.layoutWidth(Math.random())); + tree.refresh(null); break; } - default: + case '?height': { + const { tree, treeFilter } = createIndexTree({ supportDynamicHeights: true }); + + collapseall.onclick = () => perf('collapse all', () => tree.collapseAll()); + renderwidth.onclick = () => perf('renderwidth', () => tree.layoutWidth(Math.random())); + + const xhr = new XMLHttpRequest(); + xhr.open('GET', '/api/ls?path='); + xhr.send(); + xhr.onreadystatechange = function () { + if (this.readyState == 4 && this.status == 200) { + perf('splice', () => tree.splice([0], 0, [JSON.parse(this.responseText)])); + treeFilter.updatePattern(); + } + }; + break; + } + default: { const { tree, treeFilter } = createIndexTree(); + collapseall.onclick = () => perf('collapse all', () => tree.collapseAll()); + renderwidth.onclick = () => perf('renderwidth', () => tree.layoutWidth(Math.random())); + const xhr = new XMLHttpRequest(); xhr.open('GET', '/api/ls?path='); xhr.send(); @@ -191,9 +231,8 @@ treeFilter.updatePattern(); } }; + } } - - collapseall.onclick = () => perf('collapse all', () => tree.collapseAll()); }); diff --git a/test/tree/server.js b/test/tree/server.js index 7e9084bf1..5431bc1c1 100644 --- a/test/tree/server.js +++ b/test/tree/server.js @@ -17,7 +17,7 @@ async function getTree(fsPath, level) { const element = path.basename(fsPath); const stat = await fs.stat(fsPath); - if (!stat.isDirectory() || element === '.git' || element === '.build' || level >= 5) { + if (!stat.isDirectory() || element === '.git' || element === '.build' || level >= 2) { return { element }; } diff --git a/tslint.json b/tslint.json index 7f1388dfa..476f99d22 100644 --- a/tslint.json +++ b/tslint.json @@ -523,6 +523,7 @@ } ], "duplicate-imports": true, + "no-new-buffer": true, "translation-remind": true, "no-standalone-editor": true }, diff --git a/yarn.lock b/yarn.lock index eb165e3f0..4faece5a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -277,14 +277,6 @@ acorn@^5.2.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.2.1.tgz#317ac7821826c22c702d66189ab8359675f135d7" integrity sha512-jG0u7c4Ly+3QkkW18V+NRDN+4bWHdln30NL1ZL2AvFZZmQe/BfopYCtghCKKVBUSetZ4QKcyA0pY6/4Gw8Pv8w== -agent-base@2: - version "2.1.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-2.1.1.tgz#d6de10d5af6132d5bd692427d46fc538539094c7" - integrity sha1-1t4Q1a9hMtW9aSQn1G/FOFOQlMc= - dependencies: - extend "~3.0.0" - semver "~5.0.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" @@ -292,6 +284,13 @@ agent-base@4, agent-base@^4.1.0: dependencies: es6-promisify "^5.0.0" +agent-base@4.2.1, agent-base@~4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" + integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== + dependencies: + es6-promisify "^5.0.0" + ajv-keywords@^1.0.0: version "1.5.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" @@ -1261,13 +1260,13 @@ chownr@^1.0.1: resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" integrity sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE= -chrome-remote-interface@^0.25.3: - version "0.25.3" - resolved "https://registry.yarnpkg.com/chrome-remote-interface/-/chrome-remote-interface-0.25.3.tgz#b692ae538cd5af3a6dd285636bfab3d29a7006c1" - integrity sha512-+GJLyocWojJbo9ODkRGKkMA/jMgzVZtCUOpYSeVXYlPTWkcjUqsV4PeTgLWbzRy8TIRj69xYm0Gcy2Oo07AkYA== +chrome-remote-interface@^0.26.1: + version "0.26.1" + resolved "https://registry.yarnpkg.com/chrome-remote-interface/-/chrome-remote-interface-0.26.1.tgz#6c7d4479742b6d236752d716a9bc2d322d7d8ad2" + integrity sha512-ela482aJK0riFu05sl+zdbnb3ezMiqzwsqf/f/27HngWds+Fat3vcZWpIoDoeQuWMid/+LfKAteAYWaWPqsweg== dependencies: commander "2.11.x" - ws "3.3.x" + ws "^3.3.3" chrome-trace-event@^1.0.0: version "1.0.0" @@ -1888,11 +1887,6 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -data-uri-to-buffer@0: - version "0.0.4" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-0.0.4.tgz#46e13ab9da8e309745c8d01ce547213ebdb2fe3f" - integrity sha1-RuE6udqOMJdFyNAc5UchPr2y/j8= - date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" @@ -1916,13 +1910,6 @@ debounce@^1.0.0: resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.1.0.tgz#6a1a4ee2a9dc4b7c24bb012558dbcdb05b37f408" integrity sha512-ZQVKfRVlwRfD150ndzEK8M90ABT+Y/JQKs4Y7U4MXdpuoUkkrr4DwKbVux3YjylA5bUMUj0Nc3pMxPJX6N2QQQ== -debug@2, debug@2.6.9, debug@^2.1.1, debug@^2.1.2, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - debug@2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" @@ -1930,6 +1917,13 @@ debug@2.2.0: dependencies: ms "0.7.1" +debug@2.6.9, debug@^2.1.1, debug@^2.1.2, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + debug@3.1.0, debug@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -2309,19 +2303,6 @@ electron-mksnapshot@~2.0.0: electron-download "^4.1.0" extract-zip "^1.6.5" -electron-proxy-agent@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/electron-proxy-agent/-/electron-proxy-agent-1.0.2.tgz#ca2a0d953fbc52c5d1f51c9dc7ff25fc8e057dc8" - integrity sha1-yioNlT+8UsXR9Rydx/8l/I4Ffcg= - dependencies: - agent-base "2" - debug "2" - extend "3" - get-uri "1" - http-proxy-agent "1" - https-proxy-agent "1" - socks-proxy-agent "2" - electron-to-chromium@^1.2.7: version "1.3.27" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.27.tgz#78ecb8a399066187bb374eede35d9c70565a803d" @@ -2784,11 +2765,6 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@3, extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - extend@^3.0.0, extend@~3.0.0, extend@~3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" @@ -2799,6 +2775,11 @@ extend@~1.2.1: resolved "https://registry.yarnpkg.com/extend/-/extend-1.2.1.tgz#a0f5fd6cfc83a5fe49ef698d60ec8a624dd4576c" integrity sha1-oPX9bPyDpf5J72mNYOyKYk3UV2w= +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + external-editor@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.0.tgz#dc35c48c6f98a30ca27a20e9687d7f3c77704bb6" @@ -2930,11 +2911,6 @@ file-entry-cache@^2.0.0: flat-cache "^1.2.1" object-assign "^4.0.1" -file-uri-to-path@0: - version "0.0.2" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-0.0.2.tgz#37cdd1b5b905404b3f05e1b23645be694ff70f82" - integrity sha1-N83RtbkFQEs/BeGyNkW+aU/3D4I= - filename-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775" @@ -3255,14 +3231,6 @@ fsevents@^1.2.2: nan "^2.9.2" node-pre-gyp "^0.10.0" -ftp@~0.3.5: - version "0.3.10" - resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" - integrity sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0= - dependencies: - readable-stream "1.1.x" - xregexp "2.0.0" - function-bind@^1.0.2, function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -3326,18 +3294,6 @@ get-stream@^3.0.0: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= -get-uri@1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-1.1.0.tgz#7375d04daf7fcb584b3632679cbdf339b51bb149" - integrity sha1-c3XQTa9/y1hLNjJnnL3zObUbsUk= - dependencies: - data-uri-to-buffer "0" - debug "2" - extend "3" - file-uri-to-path "0" - ftp "~0.3.5" - readable-stream "2" - 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" @@ -4148,16 +4104,7 @@ http-errors@1.6.2, http-errors@~1.6.2: setprototypeof "1.0.3" statuses ">= 1.3.1 < 2" -http-proxy-agent@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz#cc1ce38e453bf984a0f7702d2dd59c73d081284a" - integrity sha1-zBzjjkU7+YSg93AtLdWcc9CBKEo= - dependencies: - agent-base "2" - debug "2" - extend "3" - -http-proxy-agent@^2.1.0: +http-proxy-agent@2.1.0, http-proxy-agent@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== @@ -4188,16 +4135,7 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= -https-proxy-agent@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz#35f7da6c48ce4ddbfa264891ac593ee5ff8671e6" - integrity sha1-NffabEjOTdv6JkiRrFk+5f+GceY= - dependencies: - agent-base "2" - debug "2" - extend "3" - -https-proxy-agent@^2.2.1: +https-proxy-agent@2.2.1, https-proxy-agent@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0" integrity sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ== @@ -4375,7 +4313,7 @@ invert-kv@^1.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= -ip@^1.1.4: +ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= @@ -7336,7 +7274,7 @@ read@^1.0.7: dependencies: mute-stream "~0.0.4" -"readable-stream@1 || 2", readable-stream@2, readable-stream@^2.0.6, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.6, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== @@ -7349,20 +7287,20 @@ read@^1.0.7: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@1.1.x, readable-stream@^1.1.8, readable-stream@~1.1.9: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= +"readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@~1.0.17: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= dependencies: core-util-is "~1.0.0" inherits "~2.0.1" isarray "0.0.1" string_decoder "~0.10.x" -"readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@~1.0.17: - version "1.0.34" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" - integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= +readable-stream@^1.1.8, readable-stream@~1.1.9: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= dependencies: core-util-is "~1.0.0" inherits "~2.0.1" @@ -7873,11 +7811,6 @@ semver@^5.0.1, semver@^5.4.1, semver@^5.5.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA== -semver@~5.0.1: - version "5.0.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a" - integrity sha1-d0Zt5YnNXTyV8TiqeLxWmjy10no= - send@0.16.1: version "0.16.1" resolved "https://registry.yarnpkg.com/send/-/send-0.16.1.tgz#a70e1ca21d1382c11d0d9f6231deb281080d7ab3" @@ -8042,10 +7975,10 @@ slice-ansi@0.0.4: resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= -smart-buffer@^1.0.13: - version "1.1.15" - resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-1.1.15.tgz#7f114b5b65fab3e2a35aa775bb12f0d1c649bf16" - integrity sha1-fxFLW2X6s+KjWqd1uxLw0cZJvxY= +smart-buffer@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.0.1.tgz#07ea1ca8d4db24eb4cac86537d7d18995221ace3" + integrity sha512-RFqinRVJVcCAL9Uh1oVqE6FZkqsyLiVOYEZ20TqIOjuX7iFVJ+zsbs4RIghnw/pTs7mZvt8ZHhvm1ZUrR4fykg== snapdragon-node@^2.0.1: version "2.1.1" @@ -8091,22 +8024,21 @@ sntp@2.x.x: dependencies: hoek "4.x.x" -socks-proxy-agent@2: - version "2.1.1" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-2.1.1.tgz#86ebb07193258637870e13b7bd99f26c663df3d3" - integrity sha512-sFtmYqdUK5dAMh85H0LEVFUCO7OhJJe1/z2x/Z6mxp3s7/QPf1RkZmpZy+BpuU0bEjcV9npqKjq9Y3kwFUjnxw== +socks-proxy-agent@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz#5936bf8b707a993079c6f37db2091821bffa6473" + integrity sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw== dependencies: - agent-base "2" - extend "3" - socks "~1.1.5" + agent-base "~4.2.0" + socks "~2.2.0" -socks@~1.1.5: - version "1.1.10" - resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.10.tgz#5b8b7fc7c8f341c53ed056e929b7bf4de8ba7b5a" - integrity sha1-W4t/x8jzQcU+0FbpKbe/Tei6e1o= +socks@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.2.1.tgz#68ad678b3642fbc5d99c64c165bc561eab0215f9" + integrity sha512-0GabKw7n9mI46vcNrVfs0o6XzWzjVa3h6GaSo2UPxtWAROXUWavfJWh1M4PR5tnE0dcnQXZIDFP4yrAysLze/w== dependencies: - ip "^1.1.4" - smart-buffer "^1.0.13" + ip "^1.1.5" + smart-buffer "^4.0.1" sort-keys@^1.0.0: version "1.1.2" @@ -8771,25 +8703,20 @@ ts-loader@^4.4.2: micromatch "^3.1.4" semver "^5.0.1" -tslib@^1.7.1: - version "1.8.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.8.0.tgz#dc604ebad64bcbf696d613da6c954aa0e7ea1eb6" - integrity sha512-ymKWWZJST0/CkgduC2qkzjMOWr4bouhuURNXCn/inEX0L57BnRG6FhX76o7FOnsjHazCjfU2LKeSrlS2sIKQJg== - tslib@^1.8.0: version "1.9.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8" integrity sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ== -tslib@^1.9.0: +tslib@^1.8.1, tslib@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== -tslint@^5.9.1: - version "5.9.1" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.9.1.tgz#1255f87a3ff57eb0b0e1f0e610a8b4748046c9ae" - integrity sha1-ElX4ej/1frCw4fDmEKi0dIBGya4= +tslint@^5.11.0: + version "5.11.0" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.11.0.tgz#98f30c02eae3cde7006201e4c33cb08b48581eed" + integrity sha1-mPMMAurjzecAYgHkwzywi0hYHu0= dependencies: babel-code-frame "^6.22.0" builtin-modules "^1.1.1" @@ -8802,14 +8729,14 @@ tslint@^5.9.1: resolve "^1.3.2" semver "^5.3.0" tslib "^1.8.0" - tsutils "^2.12.1" + tsutils "^2.27.2" -tsutils@^2.12.1: - version "2.12.2" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.12.2.tgz#ad58a4865d17ec3ddb6631b6ca53be14a5656ff3" - integrity sha1-rVikhl0X7D3bZjG2ylO+FKVlb/M= +tsutils@^2.27.2: + version "2.29.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" + integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== dependencies: - tslib "^1.7.1" + tslib "^1.8.1" tty-browserify@0.0.0: version "0.0.0" @@ -8881,10 +8808,18 @@ typescript-formatter@7.1.0: commandpost "^1.0.0" editorconfig "^0.15.0" -typescript@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.1.tgz#3362ba9dd1e482ebb2355b02dfe8bcd19a2c7c96" - integrity sha512-Veu0w4dTc/9wlWNf2jeRInNodKlcdLgemvPsrNpfu5Pq39sgfFjvIIgTsvUHCoLBnMhPoUA+tFxsXjU6VexVRQ== +typescript-tslint-plugin@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/typescript-tslint-plugin/-/typescript-tslint-plugin-0.0.7.tgz#675c26a54b0156aec404abc264d0a87642151d5a" + integrity sha512-WH06dLcOjhc6fPwCUwPZKV4Dke7KvsHClZhOfa9WDnWqt08mOXx5h6o6901vakul9r82JFHWz5LSZIijXQZWLQ== + dependencies: + minimatch "^3.0.4" + vscode-languageserver "^5.1.0" + +typescript@3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.4.tgz#c74ef7b3c2da65beff548b903022cb8c3cd997ed" + integrity sha512-JZHJtA6ZL15+Q3Dqkbh8iCUmvxD3iJ7ujXS+fVkKnwIVAdHc5BJTDNM0aTrnr2luKulFjU7W+SRhDZvi66Ru7Q== typescript@^2.6.2: version "2.6.2" @@ -9127,12 +9062,12 @@ v8-compile-cache@^2.0.0: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz#a428b28bb26790734c4fc8bc9fa106fccebf6a6c" integrity sha512-1wFuMUIM16MDJRCrpbpuEPTUGmM5QMUg0cr3KFwra2XgOgFcPGDQHDh3CszSCD2Zewc/dh/pamNEW8CbfDebUw== -v8-inspect-profiler@^0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/v8-inspect-profiler/-/v8-inspect-profiler-0.0.8.tgz#4d6bedb7c3d1bfc69e5bfdc2ded3d6784a5a76a6" - integrity sha1-TWvtt8PRv8aeW/3C3tPWeEpadqY= +v8-inspect-profiler@^0.0.13: + version "0.0.13" + resolved "https://registry.yarnpkg.com/v8-inspect-profiler/-/v8-inspect-profiler-0.0.13.tgz#4bf4e7d51a9df8ac6f9a1df5487a636cf4ec2c5a" + integrity sha512-t6EldtIyV35/AkwFH40Dikhv9ZzySdjFPdnhVlNR4EWA0cVvOXnr1djc35HhG57yFdbFGRb8A2cxAFuccc1uxw== dependencies: - chrome-remote-interface "^0.25.3" + chrome-remote-interface "^0.26.1" v8flags@^2.0.2: version "2.1.1" @@ -9363,18 +9298,18 @@ vsce@1.48.0: yauzl "^2.3.1" yazl "^2.2.2" -vscode-anymatch@^1.3.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/vscode-anymatch/-/vscode-anymatch-1.3.2.tgz#fff357422ea59cc4d12a074f347957f08d251003" - integrity sha512-TKJ8jTIvurXCjhCjhpTmu4m6Z852JTRZdCNT9OmQ7OJjR/kTf0P5E2swRvGNbM9jtmgeiFJ3MN64prFLCIzChQ== +vscode-anymatch@1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/vscode-anymatch/-/vscode-anymatch-1.3.3.tgz#0613d31a949c8025473bbdad848d219f47a44f86" + integrity sha512-LQ4vF4BWb9gwAvbMtN+3HC4HKDxLd+ZyWmAjACOdD05O/ZMcgvvnjO24GseEIQ6cWn8gW+Ft08gHFihnQy1eSw== dependencies: micromatch "^2.1.5" normalize-path "^2.0.0" -vscode-chokidar@1.6.4: - version "1.6.4" - resolved "https://registry.yarnpkg.com/vscode-chokidar/-/vscode-chokidar-1.6.4.tgz#03e5b5f755a1e73b4f15310e66f59b11673fbdd2" - integrity sha512-2A4YQsY2Mm6VAxushKwJTIWCxnDe+1BPTSa+4zQGWcFvyu5W1q+WCp+hTIG6eZ+hc7JHdrHeKzZ0mgUsUFwQgQ== +vscode-chokidar@1.6.5: + version "1.6.5" + resolved "https://registry.yarnpkg.com/vscode-chokidar/-/vscode-chokidar-1.6.5.tgz#f38a1f909fa364a5429a4d4d70ecb40a15b54d0b" + integrity sha512-bc5dNF8tW2R+oesYT/Au//qumyd/0HTwSRqVXcg8ADQW1MsWKFwv+IxfSIuCHckaEy4I81GpSDaRWVGQqtsELw== dependencies: async-each "^1.0.0" glob-parent "^2.0.0" @@ -9383,22 +9318,48 @@ vscode-chokidar@1.6.4: is-glob "^2.0.0" path-is-absolute "^1.0.0" readdirp "^2.0.0" - vscode-anymatch "^1.3.0" + vscode-anymatch "1.3.3" optionalDependencies: - vscode-fsevents "0.3.9" + vscode-fsevents "0.3.10" -vscode-debugprotocol@1.32.0: - version "1.32.0" - resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.32.0.tgz#cca9eccb3f73ded5e525e01621a72ca2bb577dc3" - integrity sha512-x3+HV+BkLqfl1ZuDJEILAv1sT5mDceuPThDXD12hwXAEjAdfc6MLQFvaoVPuO6C6gb+lHQTd0R9FNkCflEJHbA== +vscode-debugprotocol@1.33.0-pre.0: + version "1.33.0-pre.0" + resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.33.0-pre.0.tgz#a6c8fd6655ade772f7cbae8994f068bac51a9381" + integrity sha512-LRTpPDLkq7fcjvRr8Ttzo+1tiFXuxOcYQ5xhN7i/dYN6ISwg9hcQv4W74n8JBxz1nXdVpbS52KLKQMiZhBwpgg== -vscode-fsevents@0.3.9: - version "0.3.9" - resolved "https://registry.yarnpkg.com/vscode-fsevents/-/vscode-fsevents-0.3.9.tgz#edbb66ea2c4eeb102b9194bb602e73bd9512c64c" - integrity sha512-ykvsVNFXeSc8aBNzwp0hIq41i80njAfpCwbQ3h04x69VC4xSK/PqVStNlOg7oRnrGHu8acchZv++k6WHXpSp1A== +vscode-fsevents@0.3.10: + version "0.3.10" + resolved "https://registry.yarnpkg.com/vscode-fsevents/-/vscode-fsevents-0.3.10.tgz#65a586c3c6df3080bea20482146963a0f3a2c58d" + integrity sha512-iNlCKNgEB9A2JZkLf4h4sJlOS1u0lbe4QjM0Dr0PHaTmsttkJEfOaQeci2Ja1wUA7hUUAF6sNbei/Qp2DacFLw== dependencies: nan "^2.10.0" +vscode-jsonrpc@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz#a7bf74ef3254d0a0c272fab15c82128e378b3be9" + integrity sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg== + +vscode-languageserver-protocol@3.13.0: + version "3.13.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.13.0.tgz#710d8e42119bb3affb1416e1e104bd6b4d503595" + integrity sha512-2ZGKwI+P2ovQll2PGAp+2UfJH+FK9eait86VBUdkPd9HRlm8e58aYT9pV/NYanHOcp3pL6x2yTLVCFMcTer0mg== + dependencies: + vscode-jsonrpc "^4.0.0" + vscode-languageserver-types "3.13.0" + +vscode-languageserver-types@3.13.0: + version "3.13.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.13.0.tgz#b704b024cef059f7b326611c99b9c8753c0a18b4" + integrity sha512-BnJIxS+5+8UWiNKCP7W3g9FlE7fErFw0ofP5BXJe7c2tl0VeWh+nNHFbwAS2vmVC4a5kYxHBjRy0UeOtziemVA== + +vscode-languageserver@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.1.0.tgz#012a28f154cc7a848c443d217894942e4c3eeb39" + integrity sha512-CIsrgx2Y5VHS317g/HwkSTWYBIQmy0DwEyZPmB2pEpVOhYFwVsYpbiJwHIIyLQsQtmRaO4eA2xM8KPjNSdXpBw== + dependencies: + vscode-languageserver-protocol "3.13.0" + vscode-uri "^1.0.6" + vscode-nls-dev@3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/vscode-nls-dev/-/vscode-nls-dev-3.2.2.tgz#5855c9b3e566dd00fd6108f9c2e1bd02c925c153" @@ -9426,15 +9387,26 @@ vscode-nsfw@1.1.1: lodash.isundefined "^3.0.1" nan "^2.10.0" +vscode-proxy-agent@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/vscode-proxy-agent/-/vscode-proxy-agent-0.1.1.tgz#dabf44726afe838143d445a88ae03455928cc4a7" + integrity sha512-/sC95JmVy+HjJIFzza131muUAyCy0aT8a5zIgu9bkrntzsh3V/sKtbVX64Ig59dvgyOghsSEF4D9wQKGbmrAQg== + dependencies: + agent-base "4.2.1" + debug "3.1.0" + http-proxy-agent "2.1.0" + https-proxy-agent "2.2.1" + socks-proxy-agent "4.0.1" + vscode-ripgrep@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/vscode-ripgrep/-/vscode-ripgrep-1.2.4.tgz#b3cfbe08ed13f6cf6b134147ea4d982970ab4f70" integrity sha512-TysaK20aCSfsFIQGd0DfMshjkHof0fG6zx7DoO0tdWNAZgsvoqLtOWdqHcocICRZ3RSpdiMiEJRaMK+iOzx16w== -vscode-sqlite3@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/vscode-sqlite3/-/vscode-sqlite3-4.0.2.tgz#35389b7ee973f7ba6a8e57cbbdde1608ffc6d60c" - integrity sha512-QuXKvwtkjsQgIlU2mDhkp+pNIpwKY99MNwE4PJTOFkck7dtfwnYQwLvuuJ666a2QMDMqCEKYHG5+0bhrGh+gZg== +vscode-sqlite3@4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/vscode-sqlite3/-/vscode-sqlite3-4.0.5.tgz#444e4e7a34ad80ab48eb81b205d22b31bbeac7df" + integrity sha512-XJZWpOj05XjuokRQc5gju3ZSQGAB7ynIHnqyZuYOsTmeVWPcV0cTjBovTJ49aV8cNLQJQLVTNU9Vn4et4m28rg== dependencies: nan "~2.10.0" @@ -9445,10 +9417,15 @@ vscode-textmate@^4.0.1: dependencies: oniguruma "^7.0.0" -vscode-xterm@3.9.0-beta11: - version "3.9.0-beta11" - resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.9.0-beta11.tgz#f90bb8bb03f9db2a7369b37832704f23a7720c73" - integrity sha512-2KjtCLADUNj9a0qDYExUOoJwgdL9TgP/hGFolzSO1j1LqZIz785qFhoM5ovnJ7jLjU+BsmfQpeNKYaUnp5fbEg== +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-xterm@3.9.0-beta13: + version "3.9.0-beta13" + resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.9.0-beta13.tgz#ede9b4141eee579ec62f890df23c08b48ada17e8" + integrity sha512-honMQHY3AObEZomW83xrQt4paBFJ3aqCdF7jCABEM/9PQ0DjV7BDJmUQwdPwbKXYCvgOIC3BmATmVOxGey4Gmg== vso-node-api@6.1.2-preview: version "6.1.2-preview" @@ -9641,10 +9618,10 @@ write@^0.2.1: dependencies: mkdirp "^0.5.1" -ws@3.3.x: - version "3.3.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.2.tgz#96c1d08b3fefda1d5c1e33700d3bfaa9be2d5608" - integrity sha512-t+WGpsNxhMR4v6EClXS8r8km5ZljKJzyGhJf7goJz9k5Ye3+b5Bvno5rjqPuIBn5mnn5GBb7o8IrIWHxX1qOLQ== +ws@^3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== dependencies: async-limiter "~1.0.0" safe-buffer "~5.1.0" @@ -9700,11 +9677,6 @@ xmldom@0.1.x: resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw= -xregexp@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" - integrity sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM= - xregexp@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020"