diff --git a/.github/classifier.yml b/.github/classifier.yml index e0f0cec8169..9581f82f716 100644 --- a/.github/classifier.yml +++ b/.github/classifier.yml @@ -15,7 +15,7 @@ css-less-scss: [], debug-console: [], debug: { - assignees: [ isidorn ], + assignees: [ weinand ], assignLabel: false }, diff-editor: [], @@ -52,7 +52,7 @@ editor-symbols: [], editor-textbuffer: [], editor-wrapping: [], - emmet: [ octref, ramya-rao-a ], + emmet: [ octref ], error-list: [], explorer-custom: [], extension-host: [], diff --git a/.github/commands.yml b/.github/commands.yml index f20079caab8..c9279232529 100644 --- a/.github/commands.yml +++ b/.github/commands.yml @@ -105,7 +105,7 @@ { type: 'comment', name: 'a11ymas', - allowUsers: ['AccessibilityTestingTeam-TCS'], + allowUsers: ['AccessibilityTestingTeam-TCS', 'dixitsonali95', 'Mohini78', 'ChitrarupaSharma', 'mspatil110', 'umasarath52', 'v-umnaik'], action: 'updateLabels', addLabel: 'a11ymas' }, diff --git a/.gitignore b/.gitignore index 6a9804cd237..68834eb43ad 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .DS_Store +.cache npm-debug.log Thumbs.db node_modules/ @@ -14,6 +15,14 @@ out-editor-min/ out-monaco-editor-core/ out-vscode/ out-vscode-min/ +out-vscode-reh/ +out-vscode-reh-min/ +out-vscode-reh-pkg/ +out-vscode-web/ +out-vscode-web-min/ +out-vscode-web-pkg/ +src/vs/server +resources/server build/node_modules coverage/ test_data/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 55cfea092a6..b4336e7d127 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -4,6 +4,7 @@ "recommendations": [ "ms-vscode.vscode-typescript-tslint-plugin", "dbaeumer.vscode-eslint", + "EditorConfig.EditorConfig", "msjsdiag.debugger-for-chrome" ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index bd9e290ab21..1d8207865fd 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -148,6 +148,9 @@ "request": "launch", "name": "Launch VS Code (Main Process)", "runtimeExecutable": "${workspaceFolder}/scripts/code.sh", + "windows": { + "runtimeExecutable": "${workspaceFolder}/scripts/code.bat", + }, "runtimeArgs": [ "--no-cached-data" ], @@ -276,4 +279,4 @@ ] }, ] -} +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index af8b3803709..bde5d632541 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -56,5 +56,9 @@ "url": "./.vscode/cglicenses.schema.json" } ], - "git.ignoreLimitWarning": true -} \ No newline at end of file + "git.ignoreLimitWarning": true, + "remote.extensionKind": { + "msjsdiag.debugger-for-chrome": "workspace" + }, + "typescript.experimental.useSeparateSyntaxServer": true +} diff --git a/.yarnrc b/.yarnrc index beab9f70b95..441b5a2e69a 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://atom.io/download/electron" -target "3.1.8" +target "4.2.5" runtime "electron" diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 65c8a42b8d2..00000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1 +0,0 @@ -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/README.md b/README.md index 29b3270f1e5..035ba940a62 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Visual Studio Code - Open Source +# Visual Studio Code - Open Source ("Code - OSS") [![Build Status](https://dev.azure.com/vscode/VSCode/_apis/build/status/VS%20Code?branchName=master)](https://dev.azure.com/vscode/VSCode/_build/latest?definitionId=12) @@ -6,55 +6,63 @@ [![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 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. +## The Repository -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. +This repository ("`Code - OSS`") is where we (Microsoft) develop the [Visual Studio Code](https://code.visualstudio.com) product. Not only do we work on code and issues here, we also publish our roadmap, monthly iteration plans, and our endgame plans. The source code here is available to everyone under the standard [MIT license](https://github.com/microsoft/vscode/blob/master/LICENSE.txt). + +## Visual Studio Code

- VS Code in action + VS Code in action

-The [`vscode`](https://github.com/microsoft/vscode) repository is where VS Code is developed and there are many ways in which you can participate in the project, for example: +[Visual Studio Code](https://code.visualstudio.com) is a distribution of the `Code - OSS` repository with Microsoft specific customizations released under a traditional [Microsoft product license](https://code.visualstudio.com/License/). + +[Visual Studio Code](https://code.visualstudio.com) combines the simplicity of a code editor with what developers need for their core edit-build-debug cycle. It provides comprehensive code editing, navigation, and understanding support along with lightweight debugging, a rich extensibility model, and lightweight integration with existing tools. + +Visual Studio Code is updated monthly with new features and bug fixes. You can download it for Windows, macOS, and Linux on [Visual Studio Code's website](https://code.visualstudio.com/Download). To get the latest releases every day, install the [Insiders build](https://code.visualstudio.com/insiders). + -* [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). -* Review the [documentation](https://github.com/microsoft/vscode-docs) and make pull requests for anything from typos to new content. ## Contributing +There are many ways in which 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) +* Review the [documentation](https://github.com/microsoft/vscode-docs) and make pull requests for anything from typos to new content + If you are interested in fixing issues and contributing directly to the code base, please see the document [How to Contribute](https://github.com/Microsoft/vscode/wiki/How-to-Contribute), which covers the following: * [How to build and run from source](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#build-and-run) * [The development workflow, including debugging and running tests](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#debugging) -* [Coding Guidelines](https://github.com/Microsoft/vscode/wiki/Coding-Guidelines) +* [Coding guidelines](https://github.com/Microsoft/vscode/wiki/Coding-Guidelines) * [Submitting pull requests](https://github.com/Microsoft/vscode/wiki/How-to-Contribute#pull-requests) * [Contributing to translations](https://aka.ms/vscodeloc) -Please also see our [Code of Conduct](CODE_OF_CONDUCT.md). - ## Feedback -* Ask a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/vscode). -* 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 any other feedback. +* Ask a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/vscode) +* [Request a new feature](CONTRIBUTING.md) +* Up vote [popular feature requests](https://github.com/Microsoft/vscode/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc) +* [File an issue](https://github.com/Microsoft/vscode/issues) +* Follow [@code](https://twitter.com/code) and let us know what you think! ## 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) 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). +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. +Code includes a set of built-in extensions located in the [extensions](extensions) folder, including grammars and snippets for many 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`. + +## Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. ## License Copyright (c) Microsoft Corporation. All rights reserved. -Licensed under the [MIT](LICENSE.txt) License. +Licensed under the [MIT](LICENSE.txt) license. diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 2df05cca0d3..1d854430864 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -5,28 +5,28 @@ Do Not Translate or Localize This project incorporates components from the projects listed below. The original copyright notices and the licenses under which Microsoft received such components are set forth below. Microsoft reserves all rights not expressly granted herein, whether by implication, estoppel or otherwise. -1. atom/language-clojure version 0.22.6 (https://github.com/atom/language-clojure) +1. atom/language-clojure version 0.22.7 (https://github.com/atom/language-clojure) 2. atom/language-coffee-script version 0.49.3 (https://github.com/atom/language-coffee-script) -3. atom/language-java version 0.31.1 (https://github.com/atom/language-java) -4. atom/language-objective-c version 0.15.0 (https://github.com/atom/language-objective-c) -5. atom/language-sass version 0.61.4 (https://github.com/atom/language-sass) -6. atom/language-shellscript version 0.26.0 (https://github.com/atom/language-shellscript) -7. atom/language-xml version 0.35.2 (https://github.com/atom/language-xml) -8. Colorsublime-Themes version 0.1.0 (https://github.com/Colorsublime/Colorsublime-Themes) -9. daaain/Handlebars version 1.7.1 (https://github.com/daaain/Handlebars) -10. davidrios/pug-tmbundle (https://github.com/davidrios/pug-tmbundle) -11. definitelytyped (https://github.com/DefinitelyTyped/DefinitelyTyped) -12. demyte/language-cshtml version 0.3.0 (https://github.com/demyte/language-cshtml) -13. Document Object Model version 4.0.0 (https://www.w3.org/DOM/) -14. dotnet/csharp-tmLanguage version 0.1.0 (https://github.com/dotnet/csharp-tmLanguage) -15. expand-abbreviation version 0.5.8 (https://github.com/emmetio/expand-abbreviation) -16. fadeevab/make.tmbundle (https://github.com/fadeevab/make.tmbundle) -17. freebroccolo/atom-language-swift (https://github.com/freebroccolo/atom-language-swift) -18. HTML 5.1 W3C Working Draft version 08 October 2015 (http://www.w3.org/TR/2015/WD-html51-20151008/) -19. Ikuyadeu/vscode-R version 0.5.5 (https://github.com/Ikuyadeu/vscode-R) -20. Ionic documentation version 1.2.4 (https://github.com/ionic-team/ionic-site) -21. ionide/ionide-fsgrammar (https://github.com/ionide/ionide-fsgrammar) -22. jeff-hykin/cpp-textmate-grammar version 1.4.5 (https://github.com/jeff-hykin/cpp-textmate-grammar) +3. atom/language-java version 0.31.2 (https://github.com/atom/language-java) +4. atom/language-sass version 0.61.4 (https://github.com/atom/language-sass) +5. atom/language-shellscript version 0.26.0 (https://github.com/atom/language-shellscript) +6. atom/language-xml version 0.35.2 (https://github.com/atom/language-xml) +7. Colorsublime-Themes version 0.1.0 (https://github.com/Colorsublime/Colorsublime-Themes) +8. daaain/Handlebars version 1.8.0 (https://github.com/daaain/Handlebars) +9. davidrios/pug-tmbundle (https://github.com/davidrios/pug-tmbundle) +10. definitelytyped (https://github.com/DefinitelyTyped/DefinitelyTyped) +11. demyte/language-cshtml version 0.3.0 (https://github.com/demyte/language-cshtml) +12. Document Object Model version 4.0.0 (https://www.w3.org/DOM/) +13. dotnet/csharp-tmLanguage version 0.1.0 (https://github.com/dotnet/csharp-tmLanguage) +14. expand-abbreviation version 0.5.8 (https://github.com/emmetio/expand-abbreviation) +15. fadeevab/make.tmbundle (https://github.com/fadeevab/make.tmbundle) +16. freebroccolo/atom-language-swift (https://github.com/freebroccolo/atom-language-swift) +17. HTML 5.1 W3C Working Draft version 08 October 2015 (http://www.w3.org/TR/2015/WD-html51-20151008/) +18. Ikuyadeu/vscode-R version 0.5.5 (https://github.com/Ikuyadeu/vscode-R) +19. Ionic documentation version 1.2.4 (https://github.com/ionic-team/ionic-site) +20. ionide/ionide-fsgrammar (https://github.com/ionide/ionide-fsgrammar) +21. jeff-hykin/cpp-textmate-grammar version 1.11.0 (https://github.com/jeff-hykin/cpp-textmate-grammar) +22. jeff-hykin/cpp-textmate-grammar version 1.11.7 (https://github.com/jeff-hykin/cpp-textmate-grammar) 23. js-beautify version 1.6.8 (https://github.com/beautify-web/js-beautify) 24. Jxck/assert version 1.0.0 (https://github.com/Jxck/assert) 25. language-docker (https://github.com/moby/moby) @@ -35,14 +35,14 @@ This project incorporates components from the projects listed below. The origina 28. language-php version 0.44.1 (https://github.com/atom/language-php) 29. language-rust version 0.4.12 (https://github.com/zargony/atom-language-rust) 30. MagicStack/MagicPython version 1.1.1 (https://github.com/MagicStack/MagicPython) -31. marked version 0.5.0 (https://github.com/markedjs/marked) +31. marked version 0.6.2 (https://github.com/markedjs/marked) 32. mdn-data version 1.1.12 (https://github.com/mdn/data) 33. Microsoft/TypeScript-TmLanguage version 0.0.1 (https://github.com/Microsoft/TypeScript-TmLanguage) 34. Microsoft/vscode-JSON.tmLanguage (https://github.com/Microsoft/vscode-JSON.tmLanguage) 35. Microsoft/vscode-mssql version 1.4.0 (https://github.com/Microsoft/vscode-mssql) 36. mmims/language-batchfile version 0.7.5 (https://github.com/mmims/language-batchfile) 37. octicons version 8.3.0 (https://github.com/primer/octicons) -38. octref/language-css (https://github.com/octref/language-css) +38. octref/language-css version 0.42.11 (https://github.com/octref/language-css) 39. PowerShell/EditorSyntax (https://github.com/powershell/editorsyntax) 40. promise-polyfill version 8.0.0 (https://github.com/taylorhakes/promise-polyfill) 41. seti-ui version 0.1.0 (https://github.com/jesseweed/seti-ui) @@ -64,7 +64,7 @@ This project incorporates components from the projects listed below. The origina 57. TypeScript-TmLanguage version 1.0.0 (https://github.com/Microsoft/TypeScript-TmLanguage) 58. Unicode version 12.0.0 (http://www.unicode.org/) 59. vscode-logfile-highlighter version 2.4.1 (https://github.com/emilast/vscode-logfile-highlighter) -60. vscode-octicons-font version 1.0.0 (https://github.com/Microsoft/vscode-octicons-font) +60. vscode-octicons-font version 1.3.1 (https://github.com/Microsoft/vscode-octicons-font) 61. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) 62. Web Background Synchronization (https://github.com/WICG/BackgroundSync) @@ -213,43 +213,6 @@ suitability for any purpose. ========================================= END OF atom/language-java NOTICES AND INFORMATION -%% atom/language-objective-c NOTICES AND INFORMATION BEGIN HERE -========================================= -The MIT License (MIT) - -Copyright (c) 2014 GitHub 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. - - -This package was derived from a TextMate bundle located at -https://github.com/textmate/objective-c.tmbundle and distributed under the following -license, located in `README.mdown`: - -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. -========================================= -END OF atom/language-objective-c NOTICES AND INFORMATION - %% atom/language-sass NOTICES AND INFORMATION BEGIN HERE ========================================= The MIT License (MIT) diff --git a/build/.nativeignore b/build/.nativeignore new file mode 100644 index 00000000000..64286dae790 --- /dev/null +++ b/build/.nativeignore @@ -0,0 +1,107 @@ +# cleanup rules for native node modules, .gitignore style + +nan/** +*/node_modules/nan/** + +fsevents/binding.gyp +fsevents/fsevents.cc +fsevents/build/** +fsevents/src/** +fsevents/test/** +!fsevents/**/*.node + +vscode-sqlite3/binding.gyp +vscode-sqlite3/benchmark/** +vscode-sqlite3/cloudformation/** +vscode-sqlite3/deps/** +vscode-sqlite3/test/** +vscode-sqlite3/build/** +vscode-sqlite3/src/** +!vscode-sqlite3/build/Release/*.node + +oniguruma/binding.gyp +oniguruma/build/** +oniguruma/src/** +oniguruma/deps/** +!oniguruma/build/Release/*.node +!oniguruma/src/*.js + +windows-mutex/binding.gyp +windows-mutex/build/** +windows-mutex/src/** +!windows-mutex/**/*.node + +native-keymap/binding.gyp +native-keymap/build/** +native-keymap/src/** +native-keymap/deps/** +!native-keymap/build/Release/*.node + +native-is-elevated/binding.gyp +native-is-elevated/build/** +native-is-elevated/src/** +native-is-elevated/deps/** +!native-is-elevated/build/Release/*.node + +native-watchdog/binding.gyp +native-watchdog/build/** +native-watchdog/src/** +!native-watchdog/build/Release/*.node + +spdlog/binding.gyp +spdlog/build/** +spdlog/deps/** +spdlog/src/** +spdlog/test/** +!spdlog/build/Release/*.node + +jschardet/dist/** + +windows-foreground-love/binding.gyp +windows-foreground-love/build/** +windows-foreground-love/src/** +!windows-foreground-love/**/*.node + +windows-process-tree/binding.gyp +windows-process-tree/build/** +windows-process-tree/src/** +!windows-process-tree/**/*.node + +keytar/binding.gyp +keytar/build/** +keytar/src/** +keytar/script/** +keytar/node_modules/** +!keytar/**/*.node + +node-pty/binding.gyp +node-pty/build/** +node-pty/src/** +node-pty/tools/** +node-pty/deps/** +!node-pty/build/Release/*.exe +!node-pty/build/Release/*.dll +!node-pty/build/Release/*.node + +vscode-nsfw/binding.gyp +vscode-nsfw/build/** +vscode-nsfw/src/** +vscode-nsfw/openpa/** +vscode-nsfw/includes/** +!vscode-nsfw/build/Release/*.node +!vscode-nsfw/**/*.a + +vsda/binding.gyp +vsda/README.md +vsda/build/** +vsda/*.bat +vsda/*.sh +vsda/*.cpp +vsda/*.h +!vsda/build/Release/vsda.node + +vscode-windows-ca-certs/**/* +!vscode-windows-ca-certs/package.json +!vscode-windows-ca-certs/**/*.node + +node-addon-api/**/* diff --git a/build/azure-pipelines/common/installDistro.ts b/build/azure-pipelines/common/installDistro.ts deleted file mode 100644 index 3cb97afd273..00000000000 --- a/build/azure-pipelines/common/installDistro.ts +++ /dev/null @@ -1,18 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as cp from 'child_process'; - -function yarnInstall(packageName: string): void { - cp.execSync(`yarn add --no-lockfile ${packageName}`); -} - -const product = require('../../../product.json'); -const dependencies = product.dependencies || {} as { [name: string]: string; }; - -Object.keys(dependencies).forEach(name => { - const url = dependencies[name]; - yarnInstall(url); -}); \ No newline at end of file diff --git a/build/azure-pipelines/common/installDistroDependencies.ts b/build/azure-pipelines/common/installDistroDependencies.ts new file mode 100644 index 00000000000..a0dd3a295db --- /dev/null +++ b/build/azure-pipelines/common/installDistroDependencies.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as cp from 'child_process'; +import * as path from 'path'; +import * as fs from 'fs'; + +function yarnInstall(packageName: string, cwd: string): void { + console.log(`yarn add --no-lockfile ${packageName}`, cwd); + cp.execSync(`yarn add --no-lockfile ${packageName}`, { cwd, stdio: 'inherit' }); +} + +/** + * Install additional dependencies listed on each quality `package.json` file. + */ +function main() { + const quality = process.env['VSCODE_QUALITY']; + + if (!quality) { + throw new Error('Missing VSCODE_QUALITY, can\'t install distro'); + } + + const rootPath = path.dirname(path.dirname(path.dirname(__dirname))); + const qualityPath = path.join(rootPath, 'quality', quality); + const packagePath = path.join(qualityPath, 'package.json'); + const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8')); + const dependencies = pkg.dependencies || {} as { [name: string]: string; }; + + Object.keys(dependencies).forEach(name => { + const url = dependencies[name]; + const cwd = process.argv.length < 3 ? process.cwd() : path.join(process.cwd(), process.argv[2]); + yarnInstall(url, cwd); + }); +} + +main(); \ No newline at end of file diff --git a/build/azure-pipelines/common/publish.ts b/build/azure-pipelines/common/publish.ts index 724939da318..62113c8f3b3 100644 --- a/build/azure-pipelines/common/publish.ts +++ b/build/azure-pipelines/common/publish.ts @@ -151,13 +151,6 @@ async function publish(commit: string, quality: string, platform: string, type: const queuedBy = process.env['BUILD_QUEUEDBY']!; const sourceBranch = process.env['BUILD_SOURCEBRANCH']!; - const isReleased = ( - // Insiders: nightly build from master - (quality === 'insider' && /^master$|^refs\/heads\/master$/.test(sourceBranch) && /Project Collection Service Accounts|Microsoft.VisualStudio.Services.TFS/.test(queuedBy)) || - - // Exploration: any build from electron-4.0.x branch - (quality === 'exploration' && /^electron-4.0.x$|^refs\/heads\/electron-4.0.x$/.test(sourceBranch)) - ); console.log('Publishing...'); console.log('Quality:', quality); @@ -167,7 +160,6 @@ async function publish(commit: string, quality: string, platform: string, type: console.log('Version:', version); console.log('Commit:', commit); console.log('Is Update:', isUpdate); - console.log('Is Released:', isReleased); console.log('File:', file); const stat = await new Promise((c, e) => fs.stat(file, (err, stat) => err ? e(err) : c(stat))); @@ -226,7 +218,7 @@ async function publish(commit: string, quality: string, platform: string, type: id: commit, timestamp: (new Date()).getTime(), version, - isReleased: config.frozen ? false : isReleased, + isReleased: false, sourceBranch, queuedBy, assets: [] as Array, @@ -245,11 +237,6 @@ 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 commit = process.env['BUILD_SOURCEVERSION']; if (!commit) { diff --git a/build/azure-pipelines/common/release.ts b/build/azure-pipelines/common/release.ts new file mode 100644 index 00000000000..1220bd62e68 --- /dev/null +++ b/build/azure-pipelines/common/release.ts @@ -0,0 +1,109 @@ +/*--------------------------------------------------------------------------------------------- + * 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); + }); + }); +} + +function doRelease(commit: string, quality: string): Promise { + const client = new DocumentClient(process.env['AZURE_DOCUMENTDB_ENDPOINT']!, { masterKey: process.env['AZURE_DOCUMENTDB_MASTERKEY'] }); + const collection = 'dbs/builds/colls/' + quality; + const query = { + query: 'SELECT TOP 1 * FROM c WHERE c.id = @id', + parameters: [{ name: '@id', value: commit }] + }; + + let updateTries = 0; + + function update(): Promise { + updateTries++; + + return new Promise((c, e) => { + client.queryDocuments(collection, query).toArray((err, results) => { + if (err) { return e(err); } + if (results.length !== 1) { return e(new Error('No documents')); } + + const release = results[0]; + release.isReleased = true; + + client.replaceDocument(release._self, release, err => { + if (err && err.code === 409 && updateTries < 5) { return c(update()); } + if (err) { return e(err); } + + console.log('Build successfully updated.'); + c(); + }); + }); + }); + } + + return update(); +} + +async function release(commit: string, quality: string): Promise { + const config = await getConfig(quality); + + console.log('Quality config:', config); + + if (config.frozen) { + console.log(`Skipping release because quality ${quality} is frozen.`); + return; + } + + await doRelease(commit, quality); +} + +function env(name: string): string { + const result = process.env[name]; + + if (!result) { + throw new Error(`Skipping release due to missing env: ${name}`); + } + + return result; +} + +async function main(): Promise { + const commit = env('BUILD_SOURCEVERSION'); + const quality = env('VSCODE_QUALITY'); + + await release(commit, quality); +} + +main().catch(err => { + console.error(err); + process.exit(1); +}); diff --git a/build/azure-pipelines/common/symbols.ts b/build/azure-pipelines/common/symbols.ts index 1a5125295f3..153be4f25b1 100644 --- a/build/azure-pipelines/common/symbols.ts +++ b/build/azure-pipelines/common/symbols.ts @@ -36,7 +36,6 @@ export interface IVersionAccessor extends IApplicationAccessor { enum Platform { WIN_32 = 'win32-ia32', WIN_64 = 'win32-x64', - LINUX_32 = 'linux-ia32', LINUX_64 = 'linux-x64', MAC_OS = 'darwin-x64' } @@ -147,6 +146,10 @@ async function ensureVersionAndSymbols(options: IOptions) { // Check version does not exist console.log(`HockeyApp: checking for existing version ${options.versions.code} (${options.platform})`); const versions = await getVersions({ accessToken: options.access.hockeyAppToken, appId: options.access.hockeyAppId }); + if (!Array.isArray(versions.app_versions)) { + throw new Error(`Unexpected response: ${JSON.stringify(versions)}`); + } + if (versions.app_versions.some(v => v.version === options.versions.code)) { console.log(`HockeyApp: Returning without uploading symbols because version ${options.versions.code} (${options.platform}) was already found`); return; @@ -185,13 +188,17 @@ const hockeyAppToken = process.argv[3]; const is64 = process.argv[4] === 'x64'; const hockeyAppId = process.argv[5]; +if (process.argv.length !== 6) { + throw new Error(`HockeyApp: Unexpected number of arguments. Got ${process.argv}`); +} + let platform: Platform; if (process.platform === 'darwin') { platform = Platform.MAC_OS; } else if (process.platform === 'win32') { platform = is64 ? Platform.WIN_64 : Platform.WIN_32; } else { - platform = is64 ? Platform.LINUX_64 : Platform.LINUX_32; + platform = Platform.LINUX_64; } // Create version and upload symbols in HockeyApp @@ -212,7 +219,9 @@ if (repository && codeVersion && electronVersion && (product.quality === 'stable }).then(() => { console.log('HockeyApp: done'); }).catch(error => { - console.error(`HockeyApp: error (${error})`); + console.error(`HockeyApp: error ${error} (AppID: ${hockeyAppId})`); + + return process.exit(1); }); } else { console.log(`HockeyApp: skipping due to unexpected context (repository: ${repository}, codeVersion: ${codeVersion}, electronVersion: ${electronVersion}, quality: ${product.quality})`); diff --git a/build/azure-pipelines/common/sync-mooncake.ts b/build/azure-pipelines/common/sync-mooncake.ts index af0db530683..1eac8f34e92 100644 --- a/build/azure-pipelines/common/sync-mooncake.ts +++ b/build/azure-pipelines/common/sync-mooncake.ts @@ -153,11 +153,6 @@ async function sync(commit: string, quality: string): Promise { } function main(): void { - if (process.env['VSCODE_BUILD_SKIP_PUBLISH']) { - error('Skipping publish due to VSCODE_BUILD_SKIP_PUBLISH'); - return; - } - const commit = process.env['BUILD_SOURCEVERSION']; if (!commit) { diff --git a/build/azure-pipelines/darwin/build.sh b/build/azure-pipelines/darwin/build.sh index af558b8c145..a47546249fc 100755 --- a/build/azure-pipelines/darwin/build.sh +++ b/build/azure-pipelines/darwin/build.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash set -e yarn gulp vscode-darwin-min +yarn gulp vscode-reh-darwin-min yarn gulp upload-vscode-sourcemaps \ No newline at end of file diff --git a/build/azure-pipelines/darwin/continuous-build-darwin.yml b/build/azure-pipelines/darwin/continuous-build-darwin.yml index 776c1172bea..0588b0ae961 100644 --- a/build/azure-pipelines/darwin/continuous-build-darwin.yml +++ b/build/azure-pipelines/darwin/continuous-build-darwin.yml @@ -11,9 +11,9 @@ steps: inputs: versionSpec: "1.10.1" - script: | - yarn + yarn --frozen-lockfile displayName: Install Dependencies - condition: ne(variables['CacheRestored'], 'true') + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 inputs: keyfile: '**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index f5d5ef626b1..bcd5bfa57ba 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -7,35 +7,77 @@ steps: inputs: versionSpec: "1.10.1" +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + - script: | set -e cat << EOF > ~/.netrc machine monacotools.visualstudio.com - password $(VSO_PAT) + password $(devops-pat) machine github.com login vscode - password $(VSCODE_MIXIN_PASSWORD) + password $(github-distro-mixin-password) EOF git config user.email "vscode@microsoft.com" git config user.name "VSCode" - git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" - git fetch distro - git merge $(node -p "require('./package.json').distro") - - yarn - yarn gulp mixin - yarn gulp hygiene - yarn monaco-compile-check - node build/azure-pipelines/common/installDistro.js - node build/lib/builtInExtensions.js - displayName: Prepare build + displayName: Prepare tooling - script: | set -e - VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" \ - AZURE_STORAGE_ACCESS_KEY="$(AZURE_STORAGE_ACCESS_KEY)" \ + git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" + git fetch distro + git merge $(node -p "require('./package.json').distro") + displayName: Merge distro + +- script: | + set -e + yarn --frozen-lockfile + displayName: Install dependencies + +- script: | + set -e + yarn gulp mixin + displayName: Mix in quality + +- script: | + set -e + yarn gulp hygiene + yarn monaco-compile-check + displayName: Run hygiene checks + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + +- script: | + set -e + node build/azure-pipelines/common/installDistroDependencies.js + node build/azure-pipelines/common/installDistroDependencies.js remote + node build/lib/builtInExtensions.js + displayName: Install distro dependencies and extensions + +- script: | + set -e + cd $BUILD_STAGINGDIRECTORY + git clone https://github.com/microsoft/vscode-telemetry-extractor.git + cd vscode-telemetry-extractor + git checkout 3b04aba5bfdfcca1a5426cd2c51a90d18740d0bc + npm i + npm run setup-extension-repos + node ./out/cli-extract.js --sourceDir $BUILD_SOURCESDIRECTORY --excludedDirPattern extensions --outputDir . --applyEndpoints --includeIsMeasurement --patchWebsiteEvents + node ./out/cli-extract-extensions.js --sourceDir ./src/telemetry-sources --outputDir . --applyEndpoints --includeIsMeasurement + mkdir -p $BUILD_SOURCESDIRECTORY/.build/telemetry + mv declarations-resolved.json $BUILD_SOURCESDIRECTORY/.build/telemetry/telemetry-core.json + mv declarations-extensions-resolved.json $BUILD_SOURCESDIRECTORY/.build/telemetry/telemetry-extensions.json + displayName: Extract Telemetry + +- script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ ./build/azure-pipelines/darwin/build.sh displayName: Build @@ -45,11 +87,13 @@ steps: # APP_NAME="`ls $(agent.builddirectory)/VSCode-darwin | head -n 1`" # yarn smoketest -- --build "$(agent.builddirectory)/VSCode-darwin/$APP_NAME" displayName: Run unit tests + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e ./scripts/test-integration.sh --build --tfs "Integration Tests" displayName: Run integration tests + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e @@ -77,11 +121,11 @@ steps: - script: | set -e - VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" \ - AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ - AZURE_STORAGE_ACCESS_KEY="$(AZURE_STORAGE_ACCESS_KEY)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ - VSCODE_HOCKEYAPP_TOKEN="$(VSCODE_HOCKEYAPP_TOKEN)" \ + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY="$(ticino-storage-key)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + VSCODE_HOCKEYAPP_TOKEN="$(vscode-hockeyapp-token)" \ ./build/azure-pipelines/darwin/publish.sh displayName: Publish diff --git a/build/azure-pipelines/darwin/publish.sh b/build/azure-pipelines/darwin/publish.sh index 96d5967ea4b..6bc80ce04e8 100755 --- a/build/azure-pipelines/darwin/publish.sh +++ b/build/azure-pipelines/darwin/publish.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +set -e # remove pkg from archive zip -d ../VSCode-darwin.zip "*.pkg" @@ -15,6 +16,19 @@ node build/azure-pipelines/common/publish.js \ true \ ../VSCode-darwin.zip +# package Remote Extension Host +pushd .. && mv vscode-reh-darwin vscode-server-darwin && zip -Xry vscode-server-darwin.zip vscode-server-darwin && popd + +# publish Remote Extension Host +node build/azure-pipelines/common/publish.js \ + "$VSCODE_QUALITY" \ + server-darwin \ + archive-unsigned \ + "vscode-server-darwin.zip" \ + $VERSION \ + true \ + ../vscode-server-darwin.zip + # publish hockeyapp symbols node build/azure-pipelines/common/symbols.js "$VSCODE_MIXIN_PASSWORD" "$VSCODE_HOCKEYAPP_TOKEN" "$VSCODE_ARCH" "$VSCODE_HOCKEYAPP_ID_MACOS" diff --git a/build/azure-pipelines/distro-build.yml b/build/azure-pipelines/distro-build.yml index dc55bce808c..639456ad4ce 100644 --- a/build/azure-pipelines/distro-build.yml +++ b/build/azure-pipelines/distro-build.yml @@ -10,13 +10,19 @@ steps: inputs: versionSpec: "10.15.1" +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + - script: | set -e cat << EOF > ~/.netrc machine github.com login vscode - password $(VSCODE_MIXIN_PASSWORD) + password $(github-distro-mixin-password) EOF git config user.email "vscode@microsoft.com" diff --git a/build/azure-pipelines/linux/build-alpine.sh b/build/azure-pipelines/linux/build-alpine.sh new file mode 100755 index 00000000000..4f01bc9a7e5 --- /dev/null +++ b/build/azure-pipelines/linux/build-alpine.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -e +echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/build-arm.sh b/build/azure-pipelines/linux/build-arm.sh new file mode 100755 index 00000000000..4f01bc9a7e5 --- /dev/null +++ b/build/azure-pipelines/linux/build-arm.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -e +echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/build.sh b/build/azure-pipelines/linux/build.sh index bd251ebc789..1c79d764862 100755 --- a/build/azure-pipelines/linux/build.sh +++ b/build/azure-pipelines/linux/build.sh @@ -1,3 +1,7 @@ #!/usr/bin/env bash set -e -yarn gulp "vscode-linux-$VSCODE_ARCH-min" \ No newline at end of file +yarn gulp "vscode-linux-$VSCODE_ARCH-min" + +if [[ "$VSCODE_ARCH" != "ia32" ]]; then + yarn gulp vscode-reh-linux-$VSCODE_ARCH-min +fi \ No newline at end of file diff --git a/build/azure-pipelines/linux/continuous-build-linux.yml b/build/azure-pipelines/linux/continuous-build-linux.yml index ee47a8d4320..d1e38506f5d 100644 --- a/build/azure-pipelines/linux/continuous-build-linux.yml +++ b/build/azure-pipelines/linux/continuous-build-linux.yml @@ -19,9 +19,9 @@ steps: inputs: versionSpec: "1.10.1" - script: | - yarn + yarn --frozen-lockfile displayName: Install Dependencies - condition: ne(variables['CacheRestored'], 'true') + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 inputs: keyfile: '**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' diff --git a/build/azure-pipelines/linux/prebuild-alpine.sh b/build/azure-pipelines/linux/prebuild-alpine.sh new file mode 100755 index 00000000000..4f01bc9a7e5 --- /dev/null +++ b/build/azure-pipelines/linux/prebuild-alpine.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -e +echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/prebuild-arm.sh b/build/azure-pipelines/linux/prebuild-arm.sh new file mode 100755 index 00000000000..4f01bc9a7e5 --- /dev/null +++ b/build/azure-pipelines/linux/prebuild-arm.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -e +echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/product-build-linux-alpine.yml b/build/azure-pipelines/linux/product-build-linux-alpine.yml new file mode 100644 index 00000000000..5bce791dcf6 --- /dev/null +++ b/build/azure-pipelines/linux/product-build-linux-alpine.yml @@ -0,0 +1,85 @@ +steps: +- task: NodeTool@0 + inputs: + versionSpec: "10.15.1" + +- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.10.1" + +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + +- task: Docker@1 + displayName: 'Pull image' + inputs: + azureSubscriptionEndpoint: 'vscode-builds-subscription' + azureContainerRegistry: vscodehub.azurecr.io + command: 'Run an image' + imageName: 'vscode-linux-build-agent:alpine' + containerCommand: uname + +- script: | + set -e + + cat << EOF > ~/.netrc + machine monacotools.visualstudio.com + password $(devops-pat) + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF + + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + displayName: Prepare tooling + +- script: | + set -e + git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" + git fetch distro + git merge $(node -p "require('./package.json').distro") + displayName: Merge distro + +- script: | + set -e + CHILD_CONCURRENCY=1 yarn --frozen-lockfile + displayName: Install dependencies + +- script: | + set -e + yarn gulp mixin + displayName: Mix in quality + +- script: | + set -e + yarn gulp hygiene + yarn monaco-compile-check + displayName: Run hygiene checks + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + +- script: | + set -e + ./build/azure-pipelines/linux/prebuild-alpine.sh + displayName: Prepare build + +- script: | + set -e + ./build/azure-pipelines/linux/build-alpine.sh + displayName: Build + +- script: | + set -e + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + VSCODE_HOCKEYAPP_TOKEN="$(vscode-hockeyapp-token)" \ + ./build/azure-pipelines/linux/publish-alpine.sh + displayName: Publish + +- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: 'Component Detection' + continueOnError: true \ No newline at end of file diff --git a/build/azure-pipelines/linux/product-build-linux-arm.yml b/build/azure-pipelines/linux/product-build-linux-arm.yml new file mode 100644 index 00000000000..6aac72535bc --- /dev/null +++ b/build/azure-pipelines/linux/product-build-linux-arm.yml @@ -0,0 +1,85 @@ +steps: +- task: NodeTool@0 + inputs: + versionSpec: "10.15.1" + +- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.10.1" + +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + +- task: Docker@1 + displayName: 'Pull image' + inputs: + azureSubscriptionEndpoint: 'vscode-builds-subscription' + azureContainerRegistry: vscodehub.azurecr.io + command: 'Run an image' + imageName: 'vscode-linux-build-agent:armhf' + containerCommand: uname + +- script: | + set -e + + cat << EOF > ~/.netrc + machine monacotools.visualstudio.com + password $(devops-pat) + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF + + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + displayName: Prepare tooling + +- script: | + set -e + git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" + git fetch distro + git merge $(node -p "require('./package.json').distro") + displayName: Merge distro + +- script: | + set -e + CHILD_CONCURRENCY=1 yarn --frozen-lockfile + displayName: Install dependencies + +- script: | + set -e + yarn gulp mixin + displayName: Mix in quality + +- script: | + set -e + yarn gulp hygiene + yarn monaco-compile-check + displayName: Run hygiene checks + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + +- script: | + set -e + ./build/azure-pipelines/linux/prebuild-arm.sh + displayName: Prebuild + +- script: | + set -e + ./build/azure-pipelines/linux/build-arm.sh + displayName: Build + +- script: | + set -e + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + VSCODE_HOCKEYAPP_TOKEN="$(vscode-hockeyapp-token)" \ + ./build/azure-pipelines/linux/publish-arm.sh + displayName: Publish + +- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: 'Component Detection' + continueOnError: true \ No newline at end of file diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 342d72e4969..bdd1064a8c9 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -7,38 +7,77 @@ steps: inputs: versionSpec: "1.10.1" +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + - script: | set -e export npm_config_arch="$(VSCODE_ARCH)" - if [[ "$(VSCODE_ARCH)" == "ia32" ]]; then - export PKG_CONFIG_PATH="/usr/lib/i386-linux-gnu/pkgconfig" - fi cat << EOF > ~/.netrc machine monacotools.visualstudio.com - password $(VSO_PAT) + password $(devops-pat) machine github.com login vscode - password $(VSCODE_MIXIN_PASSWORD) + password $(github-distro-mixin-password) EOF git config user.email "vscode@microsoft.com" git config user.name "VSCode" - git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" - git fetch distro - git merge $(node -p "require('./package.json').distro") - - CHILD_CONCURRENCY=1 yarn - yarn gulp mixin - yarn gulp hygiene - yarn monaco-compile-check - node build/azure-pipelines/common/installDistro.js - node build/lib/builtInExtensions.js - displayName: Prepare build + displayName: Prepare tooling - script: | set -e - VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" \ + git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" + git fetch distro + git merge $(node -p "require('./package.json').distro") + displayName: Merge distro + +- script: | + set -e + CHILD_CONCURRENCY=1 yarn --frozen-lockfile + displayName: Install dependencies + +- script: | + set -e + yarn gulp mixin + displayName: Mix in quality + +- script: | + set -e + yarn gulp hygiene + yarn monaco-compile-check + displayName: Run hygiene checks + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + +- script: | + set -e + node build/azure-pipelines/common/installDistroDependencies.js + node build/azure-pipelines/common/installDistroDependencies.js remote + node build/lib/builtInExtensions.js + displayName: Install distro dependencies and extensions + +- script: | + set -e + cd $BUILD_STAGINGDIRECTORY + git clone https://github.com/microsoft/vscode-telemetry-extractor.git + cd vscode-telemetry-extractor + git checkout 3b04aba5bfdfcca1a5426cd2c51a90d18740d0bc + npm i + npm run setup-extension-repos + node ./out/cli-extract.js --sourceDir $BUILD_SOURCESDIRECTORY --excludedDirPattern extensions --outputDir . --applyEndpoints --includeIsMeasurement --patchWebsiteEvents + node ./out/cli-extract-extensions.js --sourceDir ./src/telemetry-sources --outputDir . --applyEndpoints --includeIsMeasurement + mkdir -p $BUILD_SOURCESDIRECTORY/.build/telemetry + mv declarations-resolved.json $BUILD_SOURCESDIRECTORY/.build/telemetry/telemetry-core.json + mv declarations-extensions-resolved.json $BUILD_SOURCESDIRECTORY/.build/telemetry/telemetry-extensions.json + displayName: Extract Telemetry + +- script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ ./build/azure-pipelines/linux/build.sh displayName: Build @@ -52,13 +91,14 @@ steps: DISPLAY=:10 ./scripts/test.sh --build --tfs "Unit Tests" # yarn smoketest -- --build "$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH)" displayName: Run unit tests + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e - AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ - VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" \ - VSCODE_HOCKEYAPP_TOKEN="$(VSCODE_HOCKEYAPP_TOKEN)" \ + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + VSCODE_HOCKEYAPP_TOKEN="$(vscode-hockeyapp-token)" \ ./build/azure-pipelines/linux/publish.sh displayName: Publish diff --git a/build/azure-pipelines/linux/publish-alpine.sh b/build/azure-pipelines/linux/publish-alpine.sh new file mode 100755 index 00000000000..4f01bc9a7e5 --- /dev/null +++ b/build/azure-pipelines/linux/publish-alpine.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -e +echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/publish-arm.sh b/build/azure-pipelines/linux/publish-arm.sh new file mode 100755 index 00000000000..4f01bc9a7e5 --- /dev/null +++ b/build/azure-pipelines/linux/publish-arm.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -e +echo 'noop' \ No newline at end of file diff --git a/build/azure-pipelines/linux/publish.sh b/build/azure-pipelines/linux/publish.sh index ca338a6fc5f..a6b48389d58 100755 --- a/build/azure-pipelines/linux/publish.sh +++ b/build/azure-pipelines/linux/publish.sh @@ -5,8 +5,6 @@ ROOT="$REPO/.." # Publish tarball PLATFORM_LINUX="linux-$VSCODE_ARCH" -[[ "$VSCODE_ARCH" == "ia32" ]] && DEB_ARCH="i386" || DEB_ARCH="amd64" -[[ "$VSCODE_ARCH" == "ia32" ]] && RPM_ARCH="i386" || RPM_ARCH="x86_64" BUILDNAME="VSCode-$PLATFORM_LINUX" BUILD="$ROOT/$BUILDNAME" BUILD_VERSION="$(date +%s)" @@ -20,13 +18,26 @@ rm -rf $ROOT/code-*.tar.* node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_LINUX" archive-unsigned "$TARBALL_FILENAME" "$VERSION" true "$TARBALL_PATH" +# Publish Remote Extension Host +if [[ "$VSCODE_ARCH" != "ia32" ]]; then + LEGACY_SERVER_BUILD_NAME="vscode-reh-$PLATFORM_LINUX" + SERVER_BUILD_NAME="vscode-server-$PLATFORM_LINUX" + SERVER_TARBALL_FILENAME="vscode-server-$PLATFORM_LINUX.tar.gz" + SERVER_TARBALL_PATH="$ROOT/$SERVER_TARBALL_FILENAME" + + rm -rf $ROOT/vscode-server-*.tar.* + (cd $ROOT && mv $LEGACY_SERVER_BUILD_NAME $SERVER_BUILD_NAME && tar -czf $SERVER_TARBALL_PATH $SERVER_BUILD_NAME) + + node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "server-$PLATFORM_LINUX" archive-unsigned "$SERVER_TARBALL_FILENAME" "$VERSION" true "$SERVER_TARBALL_PATH" +fi + # Publish hockeyapp symbols node build/azure-pipelines/common/symbols.js "$VSCODE_MIXIN_PASSWORD" "$VSCODE_HOCKEYAPP_TOKEN" "$VSCODE_ARCH" "$VSCODE_HOCKEYAPP_ID_LINUX64" # Publish DEB yarn gulp "vscode-linux-$VSCODE_ARCH-build-deb" PLATFORM_DEB="linux-deb-$VSCODE_ARCH" -[[ "$VSCODE_ARCH" == "ia32" ]] && DEB_ARCH="i386" || DEB_ARCH="amd64" +DEB_ARCH="amd64" DEB_FILENAME="$(ls $REPO/.build/linux/deb/$DEB_ARCH/deb/)" DEB_PATH="$REPO/.build/linux/deb/$DEB_ARCH/deb/$DEB_FILENAME" @@ -35,7 +46,7 @@ node build/azure-pipelines/common/publish.js "$VSCODE_QUALITY" "$PLATFORM_DEB" p # Publish RPM yarn gulp "vscode-linux-$VSCODE_ARCH-build-rpm" PLATFORM_RPM="linux-rpm-$VSCODE_ARCH" -[[ "$VSCODE_ARCH" == "ia32" ]] && RPM_ARCH="i386" || RPM_ARCH="x86_64" +RPM_ARCH="x86_64" RPM_FILENAME="$(ls $REPO/.build/linux/rpm/$RPM_ARCH/ | grep .rpm)" RPM_PATH="$REPO/.build/linux/rpm/$RPM_ARCH/$RPM_FILENAME" diff --git a/build/azure-pipelines/linux/snap-build-linux.yml b/build/azure-pipelines/linux/snap-build-linux.yml index 9588ebcb36d..9dbe920b87b 100644 --- a/build/azure-pipelines/linux/snap-build-linux.yml +++ b/build/azure-pipelines/linux/snap-build-linux.yml @@ -7,6 +7,12 @@ steps: inputs: versionSpec: "1.10.1" +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + - task: DownloadPipelineArtifact@0 displayName: 'Download Pipeline Artifact' inputs: @@ -41,9 +47,9 @@ steps: 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-* && sudo snapcraft snap --output "$SNAP_PATH") + (cd $SNAP_ROOT/code-* && sudo --preserve-env snapcraft snap --output "$SNAP_PATH") # Publish snap package - AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-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/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 86868f6a2a6..2e55abb4719 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -1,15 +1,15 @@ resources: containers: - container: vscode-x64 - image: joaomoreno/vscode-linux-build-agent:x64 - - container: vscode-ia32 - image: joaomoreno/vscode-linux-build-agent:ia32 + image: vscodehub.azurecr.io/vscode-linux-build-agent:x64 + endpoint: VSCodeHub - container: snapcraft - image: snapcore/snapcraft + image: snapcore/snapcraft:stable jobs: - job: Windows condition: eq(variables['VSCODE_BUILD_WIN32'], 'true') + timeoutInMinutes: 120 pool: vmImage: VS2017-Win2016 variables: @@ -19,6 +19,7 @@ jobs: - job: Windows32 condition: eq(variables['VSCODE_BUILD_WIN32_32BIT'], 'true') + timeoutInMinutes: 120 pool: vmImage: VS2017-Win2016 variables: @@ -28,6 +29,7 @@ jobs: - job: Linux condition: eq(variables['VSCODE_BUILD_LINUX'], 'true') + timeoutInMinutes: 120 pool: vmImage: 'Ubuntu-16.04' variables: @@ -38,6 +40,7 @@ jobs: - job: LinuxSnap condition: eq(variables['VSCODE_BUILD_LINUX'], 'true') + timeoutInMinutes: 120 pool: vmImage: 'Ubuntu-16.04' variables: @@ -47,23 +50,49 @@ jobs: steps: - template: linux/snap-build-linux.yml -- job: Linux32 - condition: eq(variables['VSCODE_BUILD_LINUX_32BIT'], 'true') +- job: LinuxArmhf + condition: and(eq(variables['VSCODE_BUILD_LINUX_ARMHF'], 'true'), ne(variables['VSCODE_QUALITY'], 'stable')) + timeoutInMinutes: 120 pool: vmImage: 'Ubuntu-16.04' variables: - VSCODE_ARCH: ia32 - container: vscode-ia32 + VSCODE_ARCH: armhf steps: - - template: linux/product-build-linux.yml + - template: linux/product-build-linux-arm.yml + +- job: LinuxAlpine + condition: and(eq(variables['VSCODE_BUILD_LINUX_ALPINE'], 'true'), ne(variables['VSCODE_QUALITY'], 'stable')) + timeoutInMinutes: 120 + pool: + vmImage: 'Ubuntu-16.04' + variables: + VSCODE_ARCH: alpine + steps: + - template: linux/product-build-linux-alpine.yml - job: macOS condition: eq(variables['VSCODE_BUILD_MACOS'], 'true') + timeoutInMinutes: 120 pool: vmImage: macOS 10.13 steps: - template: darwin/product-build-darwin.yml +- job: Release + condition: and(succeeded(), or(eq(variables['VSCODE_RELEASE'], 'true'), and(eq(variables['VSCODE_QUALITY'], 'insider'), eq(variables['Build.Reason'], 'Schedule')), and(eq(variables['VSCODE_QUALITY'], 'exploration'), eq(variables['Build.SourceBranch'], 'refs/heads/electron-4.0.x')))) + pool: + vmImage: 'Ubuntu-16.04' + dependsOn: + - Windows + - Windows32 + - Linux + - LinuxSnap + - LinuxArmhf + - LinuxAlpine + - macOS + steps: + - template: release.yml + - job: Mooncake pool: vmImage: 'Ubuntu-16.04' @@ -73,7 +102,8 @@ jobs: - Windows32 - Linux - LinuxSnap - - Linux32 + - LinuxArmhf + - LinuxAlpine - macOS steps: - template: sync-mooncake.yml \ No newline at end of file diff --git a/build/azure-pipelines/publish-types/.gitignore b/build/azure-pipelines/publish-types/.gitignore new file mode 100644 index 00000000000..e94ecda764e --- /dev/null +++ b/build/azure-pipelines/publish-types/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +*.js \ No newline at end of file diff --git a/build/azure-pipelines/publish-types/check-version.ts b/build/azure-pipelines/publish-types/check-version.ts new file mode 100644 index 00000000000..2c730889112 --- /dev/null +++ b/build/azure-pipelines/publish-types/check-version.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * 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 * as cp from 'child_process'; + +let tag = ''; +try { + tag = cp + .execSync('git describe --tags `git rev-list --tags --max-count=1`') + .toString() + .trim(); + + if (!isValidTag(tag)) { + throw Error(`Invalid tag ${tag}`); + } +} catch (err) { + console.error(err); + console.error('Failed to update types'); + process.exit(1); +} + +function isValidTag(t: string) { + if (t.split('.').length !== 3) { + return false; + } + + const [major, minor, bug] = t.split('.'); + + // Only release for tags like 1.34.0 + if (bug !== '0') { + return false; + } + + if (parseInt(major, 10) === NaN || parseInt(minor, 10) === NaN) { + return false; + } + + return true; +} \ No newline at end of file diff --git a/build/azure-pipelines/publish-types/publish-types.yml b/build/azure-pipelines/publish-types/publish-types.yml new file mode 100644 index 00000000000..ff03cecedb1 --- /dev/null +++ b/build/azure-pipelines/publish-types/publish-types.yml @@ -0,0 +1,67 @@ +# Publish @types/vscode for each release + +trigger: + branches: + include: ['refs/tags/*'] + +pr: none + +steps: +- task: NodeTool@0 + inputs: + versionSpec: "10.15.1" + +- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.10.1" + +- bash: | + # Install build dependencies + (cd build && yarn) + node build/azure-pipelines/publish-types/check-version.js + displayName: Check version + +- bash: | + git config --global user.email "vscode@microsoft.com" + git config --global user.name "VSCode" + + git clone https://$(GITHUB_TOKEN)@github.com/DefinitelyTyped/DefinitelyTyped.git --depth=1 + node build/azure-pipelines/publish-types/update-types.js + + TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) + + cd DefinitelyTyped + + git diff --color | cat + git add -A + git status + git checkout -b "vscode-types-$TAG_VERSION" + git commit -m "VS Code $TAG_VERSION Extension API" + git push origin "vscode-types-$TAG_VERSION" + + displayName: Push update to DefinitelyTyped + +- bash: | + TAG_VERSION=$(git describe --tags `git rev-list --tags --max-count=1`) + CHANNEL="G1C14HJ2F" + + MESSAGE="DefinitelyTyped/DefinitelyTyped#vscode-types-$TAG_VERSION created. Endgame master, please open this link, examine changes and create a PR:" + LINK="https://github.com/DefinitelyTyped/DefinitelyTyped/compare/vscode-types-$TAG_VERSION?quick_pull=1&body=Updating%20VS%20Code%20Extension%20API.%20See%20https%3A%2F%2Fgithub.com%2Fmicrosoft%2Fvscode%2Fissues%2F70175%20for%20details." + MESSAGE2="[@octref, @jrieken, @kmaetzel, @egamma]. Please review and merge PR to publish @types/vscode." + + curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \ + -H 'Content-type: application/json; charset=utf-8' \ + --data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$MESSAGE"'"}' \ + https://slack.com/api/chat.postMessage + + curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \ + -H 'Content-type: application/json; charset=utf-8' \ + --data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$LINK"'"}' \ + https://slack.com/api/chat.postMessage + + curl -X POST -H "Authorization: Bearer $(SLACK_TOKEN)" \ + -H 'Content-type: application/json; charset=utf-8' \ + --data '{"channel":"'"$CHANNEL"'", "link_names": true, "text":"'"$MESSAGE2"'"}' \ + https://slack.com/api/chat.postMessage + + displayName: Send message on Slack diff --git a/build/azure-pipelines/publish-types/update-types.ts b/build/azure-pipelines/publish-types/update-types.ts new file mode 100644 index 00000000000..a5ef449b77b --- /dev/null +++ b/build/azure-pipelines/publish-types/update-types.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * 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 * as fs from 'fs'; +import * as cp from 'child_process'; +import * as path from 'path'; + +let tag = ''; +try { + tag = cp + .execSync('git describe --tags `git rev-list --tags --max-count=1`') + .toString() + .trim(); + + const dtsUri = `https://raw.githubusercontent.com/microsoft/vscode/${tag}/src/vs/vscode.d.ts`; + const outPath = path.resolve(process.cwd(), 'DefinitelyTyped/types/vscode/index.d.ts'); + cp.execSync(`curl ${dtsUri} --output ${outPath}`); + + updateDTSFile(outPath, tag); + + console.log(`Done updating vscode.d.ts at ${outPath}`); +} catch (err) { + console.error(err); + console.error('Failed to update types'); + process.exit(1); +} + +function updateDTSFile(outPath: string, tag: string) { + const oldContent = fs.readFileSync(outPath, 'utf-8'); + const newContent = getNewFileContent(oldContent, tag); + + fs.writeFileSync(outPath, newContent); +} + +function getNewFileContent(content: string, tag: string) { + const oldheader = [ + `/*---------------------------------------------------------------------------------------------`, + ` * Copyright (c) Microsoft Corporation. All rights reserved.`, + ` * Licensed under the MIT License. See License.txt in the project root for license information.`, + ` *--------------------------------------------------------------------------------------------*/` + ].join('\n'); + + return getNewFileHeader(tag) + content.slice(oldheader.length); +} + +function getNewFileHeader(tag: string) { + const [major, minor] = tag.split('.'); + const shorttag = `${major}.${minor}`; + + const header = [ + `// Type definitions for Visual Studio Code ${shorttag}`, + `// Project: https://github.com/microsoft/vscode`, + `// Definitions by: Visual Studio Code Team, Microsoft `, + `// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped`, + ``, + `/*---------------------------------------------------------------------------------------------`, + ` * Copyright (c) Microsoft Corporation. All rights reserved.`, + ` * Licensed under the MIT License.`, + ` * See https://github.com/Microsoft/vscode/blob/master/LICENSE.txt for license information.`, + ` *--------------------------------------------------------------------------------------------*/`, + ``, + `/**`, + ` * Type Definition for Visual Studio Code ${shorttag} Extension API`, + ` * See https://code.visualstudio.com/api for more information`, + ` */` + ].join('\n'); + + return header; +} diff --git a/build/azure-pipelines/release.yml b/build/azure-pipelines/release.yml new file mode 100644 index 00000000000..eee54a1d8b7 --- /dev/null +++ b/build/azure-pipelines/release.yml @@ -0,0 +1,22 @@ +steps: +- task: NodeTool@0 + inputs: + versionSpec: "10.x" + +- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.x" + +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + +- script: | + set -e + + (cd build ; yarn) + + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + node build/azure-pipelines/common/release.js diff --git a/build/azure-pipelines/sync-mooncake.yml b/build/azure-pipelines/sync-mooncake.yml index c422839de1c..f3e8bc07856 100644 --- a/build/azure-pipelines/sync-mooncake.yml +++ b/build/azure-pipelines/sync-mooncake.yml @@ -7,12 +7,18 @@ steps: inputs: versionSpec: "1.10.1" +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + - script: | set -e (cd build ; yarn) - AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \ - AZURE_STORAGE_ACCESS_KEY_2="$(AZURE_STORAGE_ACCESS_KEY_2)" \ - MOONCAKE_STORAGE_ACCESS_KEY="$(MOONCAKE_STORAGE_ACCESS_KEY)" \ + AZURE_DOCUMENTDB_MASTERKEY="$(builds-docdb-key-readwrite)" \ + AZURE_STORAGE_ACCESS_KEY_2="$(vscode-storage-key)" \ + MOONCAKE_STORAGE_ACCESS_KEY="$(vscode-mooncake-storage-key)" \ node build/azure-pipelines/common/sync-mooncake.js "$VSCODE_QUALITY" diff --git a/build/azure-pipelines/win32/build.ps1 b/build/azure-pipelines/win32/build.ps1 index 60325ba54ca..8334a87e029 100644 --- a/build/azure-pipelines/win32/build.ps1 +++ b/build/azure-pipelines/win32/build.ps1 @@ -1,4 +1,5 @@ . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-min" } +exec { yarn gulp "vscode-reh-win32-$env:VSCODE_ARCH-min" } exec { yarn gulp "vscode-win32-$env:VSCODE_ARCH-inno-updater" } \ No newline at end of file diff --git a/build/azure-pipelines/win32/continuous-build-win32.yml b/build/azure-pipelines/win32/continuous-build-win32.yml index 36336276814..7f10e74d7c5 100644 --- a/build/azure-pipelines/win32/continuous-build-win32.yml +++ b/build/azure-pipelines/win32/continuous-build-win32.yml @@ -15,9 +15,9 @@ steps: targetfolder: '**/node_modules, !**/node_modules/**/node_modules' vstsFeed: '$(ArtifactFeed)' - powershell: | - yarn + yarn --frozen-lockfile displayName: Install Dependencies - condition: ne(variables['CacheRestored'], 'true') + condition: and(succeeded(), ne(variables['CacheRestored'], 'true')) - task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 inputs: keyfile: '**/yarn.lock, !**/node_modules/**/yarn.lock, !**/.*/**/yarn.lock' diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index a49349150d2..09a0f7af93b 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -12,31 +12,79 @@ steps: versionSpec: '2.x' addToPath: true +- task: AzureKeyVault@1 + displayName: 'Azure Key Vault: Get Secrets' + inputs: + azureSubscription: 'vscode-builds-subscription' + KeyVaultName: vscode + - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - "machine monacotools.visualstudio.com`npassword $(VSO_PAT)`nmachine github.com`nlogin vscode`npassword $(VSCODE_MIXIN_PASSWORD)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII - $env:npm_config_arch="$(VSCODE_ARCH)" - $env:CHILD_CONCURRENCY="1" + "machine monacotools.visualstudio.com`npassword $(devops-pat)`nmachine github.com`nlogin vscode`npassword $(github-distro-mixin-password)" | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII exec { git config user.email "vscode@microsoft.com" } exec { git config user.name "VSCode" } - exec { git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" } - exec { git fetch distro } - exec { git merge $(node -p "require('./package.json').distro") } - - exec { yarn } - exec { yarn gulp mixin } - exec { yarn gulp hygiene } - exec { yarn monaco-compile-check } - exec { node build/azure-pipelines/common/installDistro.js } - exec { node build/lib/builtInExtensions.js } - displayName: Prepare build + displayName: Prepare tooling - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - $env:VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" + exec { git remote add distro "https://github.com/$(VSCODE_MIXIN_REPO).git" } + exec { git fetch distro } + exec { git merge $(node -p "require('./package.json').distro") } + displayName: Merge distro + +- powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $env:npm_config_arch="$(VSCODE_ARCH)" + $env:CHILD_CONCURRENCY="1" + exec { yarn --frozen-lockfile } + displayName: Install dependencies + +- powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn gulp mixin } + displayName: Mix in quality + +- powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { yarn gulp hygiene } + exec { yarn monaco-compile-check } + displayName: Run hygiene checks + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + +- powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { node build/azure-pipelines/common/installDistroDependencies.js } + exec { node build/azure-pipelines/common/installDistroDependencies.js remote } + exec { node build/lib/builtInExtensions.js } + displayName: Install distro dependencies and extensions + +- powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + cd $env:BUILD_STAGINGDIRECTORY + git clone https://github.com/microsoft/vscode-telemetry-extractor.git + cd vscode-telemetry-extractor + git checkout 3b04aba5bfdfcca1a5426cd2c51a90d18740d0bc + npm i + npm run setup-extension-repos + node .\out\cli-extract.js --sourceDir $env:BUILD_SOURCESDIRECTORY --excludedDirPattern extensions --outputDir . --applyEndpoints --includeIsMeasurement --patchWebsiteEvents + node .\out\cli-extract-extensions.js --sourceDir .\src\telemetry-sources --outputDir . --applyEndpoints --includeIsMeasurement + mkdir $env:BUILD_SOURCESDIRECTORY\.build\telemetry -ea 0 + mv declarations-resolved.json $env:BUILD_SOURCESDIRECTORY\.build\telemetry\telemetry-core.json + mv declarations-extensions-resolved.json $env:BUILD_SOURCESDIRECTORY\.build\telemetry\telemetry-extensions.json + displayName: Extract Telemetry + +- powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" .\build\azure-pipelines\win32\build.ps1 displayName: Build @@ -45,8 +93,8 @@ steps: $ErrorActionPreference = "Stop" exec { yarn gulp "electron-$(VSCODE_ARCH)" } exec { .\scripts\test.bat --build --tfs "Unit Tests" } - # yarn smoketest -- --build "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" displayName: Run unit tests + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - powershell: | . build/azure-pipelines/win32/exec.ps1 @@ -54,6 +102,7 @@ steps: exec { yarn gulp "electron-$(VSCODE_ARCH)" } exec { .\scripts\test-integration.bat --build --tfs "Integration Tests" } displayName: Run integration tests + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 inputs: @@ -126,15 +175,16 @@ steps: - powershell: | $ErrorActionPreference = "Stop" - .\build\azure-pipelines\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/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - $env:AZURE_STORAGE_ACCESS_KEY_2 = "$(AZURE_STORAGE_ACCESS_KEY_2)" - $env:AZURE_DOCUMENTDB_MASTERKEY = "$(AZURE_DOCUMENTDB_MASTERKEY)" - $env:VSCODE_HOCKEYAPP_TOKEN = "$(VSCODE_HOCKEYAPP_TOKEN)" + $env:AZURE_STORAGE_ACCESS_KEY_2 = "$(vscode-storage-key)" + $env:AZURE_DOCUMENTDB_MASTERKEY = "$(builds-docdb-key-readwrite)" + $env:VSCODE_HOCKEYAPP_TOKEN = "$(vscode-hockeyapp-token)" + $env:VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" .\build\azure-pipelines\win32\publish.ps1 displayName: Publish diff --git a/build/azure-pipelines/win32/publish.ps1 b/build/azure-pipelines/win32/publish.ps1 index 8e6fa988adf..86da6df4e0a 100644 --- a/build/azure-pipelines/win32/publish.ps1 +++ b/build/azure-pipelines/win32/publish.ps1 @@ -10,8 +10,16 @@ $Root = "$Repo\.." $SystemExe = "$Repo\.build\win32-$Arch\system-setup\VSCodeSetup.exe" $UserExe = "$Repo\.build\win32-$Arch\user-setup\VSCodeSetup.exe" $Zip = "$Repo\.build\win32-$Arch\archive\VSCode-win32-$Arch.zip" +$LegacyServer = "$Root\vscode-reh-win32-$Arch" +$ServerName = "vscode-server-win32-$Arch" +$Server = "$Root\$ServerName" +$ServerZip = "$Repo\.build\vscode-server-win32-$Arch.zip" $Build = "$Root\VSCode-win32-$Arch" +# Create server archive +exec { Rename-Item -Path $LegacyServer -NewName $ServerName } +exec { .\node_modules\7zip\7zip-lite\7z.exe a -tzip $ServerZip $Server -r } + # get version $PackageJson = Get-Content -Raw -Path "$Build\resources\app\package.json" | ConvertFrom-Json $Version = $PackageJson.version @@ -22,6 +30,7 @@ $AssetPlatform = if ("$Arch" -eq "ia32") { "win32" } else { "win32-x64" } exec { node build/azure-pipelines/common/publish.js $Quality "$AssetPlatform-archive" archive "VSCode-win32-$Arch-$Version.zip" $Version true $Zip } exec { node build/azure-pipelines/common/publish.js $Quality "$AssetPlatform" setup "VSCodeSetup-$Arch-$Version.exe" $Version true $SystemExe } exec { node build/azure-pipelines/common/publish.js $Quality "$AssetPlatform-user" setup "VSCodeUserSetup-$Arch-$Version.exe" $Version true $UserExe } +exec { node build/azure-pipelines/common/publish.js $Quality "server-$AssetPlatform" archive "vscode-server-win32-$Arch.zip" $Version true $ServerZip } # publish hockeyapp symbols $hockeyAppId = if ("$Arch" -eq "ia32") { "$env:VSCODE_HOCKEYAPP_ID_WIN32" } else { "$env:VSCODE_HOCKEYAPP_ID_WIN64" } diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index f678b7bec07..7f1c9fd2f6f 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -1,7 +1,7 @@ [ { "name": "ms-vscode.node-debug", - "version": "1.33.3", + "version": "1.35.3", "repo": "https://github.com/Microsoft/vscode-node-debug", "metadata": { "id": "b6ded8fb-a0a0-4c1c-acbd-ab2a3bc995a6", @@ -31,7 +31,7 @@ }, { "name": "ms-vscode.references-view", - "version": "0.0.26", + "version": "0.0.27", "repo": "https://github.com/Microsoft/vscode-reference-view", "metadata": { "id": "dc489f46-520d-4556-ae85-1f9eab3c412d", diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js index 50082693334..b49bc069366 100644 --- a/build/gulpfile.hygiene.js +++ b/build/gulpfile.hygiene.js @@ -175,6 +175,17 @@ gulp.task('tslint', () => { function hygiene(some) { let errorCount = 0; + const productJson = es.through(function (file) { + const product = JSON.parse(file.contents.toString('utf8')); + + if (product.extensionsGallery) { + console.error('product.json: Contains "extensionsGallery"'); + errorCount++; + } + + this.emit('data', file); + }); + const indentation = es.through(function (file) { const lines = file.contents.toString('utf8').split(/\r\n|\r|\n/); file.__lines = lines; @@ -258,8 +269,13 @@ function hygiene(some) { input = some; } + const productJsonFilter = filter('product.json', { restore: true }); + const result = input .pipe(filter(f => !f.stat.isDirectory())) + .pipe(productJsonFilter) + .pipe(process.env['BUILD_SOURCEVERSION'] ? es.through() : productJson) + .pipe(productJsonFilter.restore) .pipe(filter(indentationFilter)) .pipe(indentation) .pipe(filter(copyrightFilter)) diff --git a/build/gulpfile.mixin.js b/build/gulpfile.mixin.js index 690cc874899..0f9b1d990f3 100644 --- a/build/gulpfile.mixin.js +++ b/build/gulpfile.mixin.js @@ -27,7 +27,8 @@ gulp.task('mixin', function () { fancyLog(ansiColors.blue('[mixin]'), `Mixing in sources:`); return vfs .src(`quality/${quality}/**`, { base: `quality/${quality}` }) - .pipe(filter(function (f) { return !f.isDirectory(); })) + .pipe(filter(f => !f.isDirectory())) + .pipe(filter(['**', '!**/package.json'])) .pipe(productJsonFilter) .pipe(buffer()) .pipe(json(o => Object.assign({}, require('../product.json'), o))) diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js new file mode 100644 index 00000000000..2773a8cb430 --- /dev/null +++ b/build/gulpfile.reh.js @@ -0,0 +1,162 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +const gulp = require('gulp'); + +const path = require('path'); +const es = require('event-stream'); +const util = require('./lib/util'); +const task = require('./lib/task'); + +const vfs = require('vinyl-fs'); +const flatmap = require('gulp-flatmap'); +const gunzip = require('gulp-gunzip'); +const untar = require('gulp-untar'); +const File = require('vinyl'); +const fs = require('fs'); + +const cp = require('child_process'); + +const REPO_ROOT = path.dirname(__dirname); + +const noop = () => { return Promise.resolve(); }; + +gulp.task('vscode-reh-win32-ia32-min', noop); +gulp.task('vscode-reh-win32-x64-min', noop); +gulp.task('vscode-reh-darwin-min', noop); +gulp.task('vscode-reh-linux-x64-min', noop); +gulp.task('vscode-reh-linux-armhf-min', noop); +gulp.task('vscode-reh-linux-alpine-min', noop); + + +function getNodeVersion() { + const yarnrc = fs.readFileSync(path.join(REPO_ROOT, 'remote', '.yarnrc'), 'utf8'); + const target = /^target "(.*)"$/m.exec(yarnrc)[1]; + return target; +} + +function ensureDirs(dirPath) { + if (!fs.existsSync(dirPath)) { + ensureDirs(path.dirname(dirPath)); + fs.mkdirSync(dirPath); + } +} + +/* Downloads the node executable used for the remote server to ./build/node-remote */ +gulp.task(task.define('node-remote', () => { + const VERSION = getNodeVersion(); + const nodePath = path.join('.build', 'node-remote'); + const nodeVersionPath = path.join(nodePath, 'version'); + if (!fs.existsSync(nodeVersionPath) || fs.readFileSync(nodeVersionPath).toString() !== VERSION) { + ensureDirs(nodePath); + util.rimraf(nodePath); + fs.writeFileSync(nodeVersionPath, VERSION); + return nodejs(process.platform, process.arch).pipe(vfs.dest(nodePath)); + } + return vfs.src(nodePath); +})); + +function nodejs(platform, arch) { + const VERSION = getNodeVersion(); + + if (arch === 'ia32') { + arch = 'x86'; + } + + if (platform === 'win32') { + const downloadPath = `/dist/v${VERSION}/win-${arch}/node.exe`; + + return ( + util.download({ host: 'nodejs.org', path: downloadPath }) + .pipe(es.through(function (data) { + // base comes in looking like `https:\nodejs.org\dist\v10.2.1\win-x64\node.exe` + this.emit('data', new File({ + path: data.path, + base: data.base.replace(/\\node\.exe$/, ''), + contents: data.contents, + stat: { + isFile: true, + mode: /* 100755 */ 33261 + } + })); + })) + ); + } + + if (arch === 'alpine') { + return es.readArray([ + new File({ + path: 'node', + contents: cp.execSync(`docker run --rm node:${VERSION}-alpine /bin/sh -c 'cat \`which node\`'`, { maxBuffer: 100 * 1024 * 1024, encoding: 'buffer' }), + stat: { + mode: parseInt('755', 8) + } + }) + ]); + } + + if (platform === 'darwin') { + arch = 'x64'; + } + + if (arch === 'armhf') { + arch = 'armv7l'; + } + + const downloadPath = `/dist/v${VERSION}/node-v${VERSION}-${platform}-${arch}.tar.gz`; + + return ( + util.download({ host: 'nodejs.org', path: downloadPath }) + .pipe(flatmap(stream => stream.pipe(gunzip()).pipe(untar()))) + .pipe(es.through(function (data) { + // base comes in looking like `https:/nodejs.org/dist/v8.9.3/node-v8.9.3-darwin-x64.tar.gz` + // => we must remove the `.tar.gz` + // Also, keep only bin/node + if (/\/bin\/node$/.test(data.path)) { + this.emit('data', new File({ + path: data.path.replace(/bin\/node$/, 'node'), + base: data.base.replace(/\.tar\.gz$/, ''), + contents: data.contents, + stat: { + isFile: true, + mode: /* 100755 */ 33261 + } + })); + } + })) + ); +} + +function mixinServer(watch) { + const packageJSONPath = path.join(path.dirname(__dirname), 'package.json'); + function exec(cmdLine) { + console.log(cmdLine); + cp.execSync(cmdLine, { stdio: "inherit" }); + } + function checkout() { + const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath).toString()); + exec('git fetch distro'); + exec(`git checkout ${packageJSON['distro']} -- src/vs/server resources/server`); + exec('git reset HEAD src/vs/server resources/server'); + } + checkout(); + if (watch) { + console.log('Enter watch mode (observing package.json)'); + const watcher = fs.watch(packageJSONPath); + watcher.addListener('change', () => { + try { + checkout(); + } catch (e) { + console.log(e); + } + }); + } + return Promise.resolve(); +} + +gulp.task(task.define('mixin-server', () => mixinServer(false))); +gulp.task(task.define('mixin-server-watch', () => mixinServer(true))); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index ae37a72fca0..0673fdb0306 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -315,6 +315,8 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op // TODO the API should be copied to `out` during compile, not here const api = gulp.src('src/vs/vscode.d.ts').pipe(rename('out/vs/vscode.d.ts')); + const telemetry = gulp.src('.build/telemetry/**', { base: '.build/telemetry', dot: true }); + const depsSrc = [ ..._.flatten(productionDependencies.map(d => path.relative(root, d.path)).map(d => [`${d}/**`, `!${d}/**/{test,tests}/**`])), // @ts-ignore JSON checking: dependencies is optional @@ -323,24 +325,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op const deps = gulp.src(depsSrc, { base: '.', dot: true }) .pipe(filter(['**', '!**/package-lock.json'])) - .pipe(util.cleanNodeModule('fsevents', ['binding.gyp', 'fsevents.cc', 'build/**', 'src/**', 'test/**'], ['**/*.node'])) - .pipe(util.cleanNodeModule('vscode-sqlite3', ['binding.gyp', 'benchmark/**', 'cloudformation/**', 'deps/**', 'test/**', 'build/**', 'src/**'], ['build/Release/*.node'])) - .pipe(util.cleanNodeModule('oniguruma', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/*.node', 'src/*.js'])) - .pipe(util.cleanNodeModule('windows-mutex', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node'])) - .pipe(util.cleanNodeModule('native-keymap', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/*.node'])) - .pipe(util.cleanNodeModule('native-is-elevated', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/*.node'])) - .pipe(util.cleanNodeModule('native-watchdog', ['binding.gyp', 'build/**', 'src/**'], ['build/Release/*.node'])) - .pipe(util.cleanNodeModule('spdlog', ['binding.gyp', 'build/**', 'deps/**', 'src/**', 'test/**'], ['build/Release/*.node'])) - .pipe(util.cleanNodeModule('jschardet', ['dist/**'])) - .pipe(util.cleanNodeModule('windows-foreground-love', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node'])) - .pipe(util.cleanNodeModule('windows-process-tree', ['binding.gyp', 'build/**', 'src/**'], ['**/*.node'])) - .pipe(util.cleanNodeModule('gc-signals', ['binding.gyp', 'build/**', 'src/**', 'deps/**'], ['build/Release/*.node', 'src/index.js'])) - .pipe(util.cleanNodeModule('keytar', ['binding.gyp', 'build/**', 'src/**', 'script/**', 'node_modules/**'], ['**/*.node'])) - .pipe(util.cleanNodeModule('node-pty', ['binding.gyp', 'build/**', 'src/**', 'tools/**'], ['build/Release/*.exe', 'build/Release/*.dll', 'build/Release/*.node'])) - .pipe(util.cleanNodeModule('vscode-nsfw', ['binding.gyp', 'build/**', 'src/**', 'openpa/**', 'includes/**'], ['build/Release/*.node', '**/*.a'])) - .pipe(util.cleanNodeModule('vsda', ['binding.gyp', 'README.md', 'build/**', '*.bat', '*.sh', '*.cpp', '*.h'], ['build/Release/vsda.node'])) - .pipe(util.cleanNodeModule('vscode-windows-ca-certs', ['**/*'], ['package.json', '**/*.node'])) - .pipe(util.cleanNodeModule('node-addon-api', ['**/*'])) + .pipe(util.cleanNodeModules(path.join(__dirname, '.nativeignore'))) .pipe(createAsar(path.join(process.cwd(), 'node_modules'), ['**/*.node', '**/vscode-ripgrep/bin/*', '**/node-pty/build/Release/*'], 'app/node_modules.asar')); let all = es.merge( @@ -348,6 +333,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op productJsonStream, license, api, + telemetry, sources, deps ); @@ -397,7 +383,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op .pipe(util.skipDirectories()) .pipe(util.fixWin32DirectoryPermissions()) .pipe(electron(_.extend({}, config, { platform, arch, ffmpegChromium: true }))) - .pipe(filter(['**', '!LICENSE', '!LICENSES.chromium.html', '!version'])); + .pipe(filter(['**', '!LICENSE', '!LICENSES.chromium.html', '!version'], { dot: true })); // result = es.merge(result, gulp.src('resources/completions/**', { base: '.' })); @@ -414,6 +400,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op .pipe(replace('@@VERSION@@', version)) .pipe(replace('@@COMMIT@@', commit)) .pipe(replace('@@APPNAME@@', product.applicationName)) + .pipe(replace('@@DATAFOLDER@@', product.dataFolderName)) .pipe(replace('@@QUALITY@@', quality)) .pipe(rename(function (f) { f.basename = product.applicationName; f.extname = ''; }))); @@ -554,14 +541,14 @@ gulp.task('vscode-translations-import', function () { // Sourcemaps gulp.task('upload-vscode-sourcemaps', () => { - const vs = gulp.src('out-vscode-min/**/*.map', { base: 'out-vscode-min' }) + const vs = gulp.src('out-vscode-min/**/*.map', { base: 'out-vscode-min' }) // client source-maps only .pipe(es.mapSync(f => { f.path = `${f.base}/core/${f.relative}`; return f; })); - const extensionsOut = gulp.src('extensions/**/out/**/*.map', { base: '.' }); - const extensionsDist = gulp.src('extensions/**/dist/**/*.map', { base: '.' }); + const extensionsOut = gulp.src(['extensions/**/out/**/*.map', '!extensions/**/node_modules/**'], { base: '.' }); + const extensionsDist = gulp.src(['extensions/**/dist/**/*.map', '!extensions/**/node_modules/**'], { base: '.' }); return es.merge(vs, extensionsOut, extensionsDist) .pipe(es.through(function (data) { diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index 9b0abd18d9a..a718cfb0082 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -23,7 +23,7 @@ const commit = util.getVersion(root); const linuxPackageRevision = Math.floor(new Date().getTime() / 1000); function getDebPackageArch(arch) { - return { x64: 'amd64', ia32: 'i386', arm: 'armhf', arm64: "arm64" }[arch]; + return { x64: 'amd64', arm: 'armhf', arm64: "arm64" }[arch]; } function prepareDebPackage(arch) { @@ -114,7 +114,7 @@ function getRpmBuildPath(rpmArch) { } function getRpmPackageArch(arch) { - return { x64: 'x86_64', ia32: 'i386', arm: 'armhf', arm64: "arm64" }[arch]; + return { x64: 'x86_64', arm: 'armhf', arm64: "arm64" }[arch]; } function prepareRpmPackage(arch) { @@ -238,7 +238,6 @@ function buildSnapPackage(arch) { } const BUILD_TARGETS = [ - { arch: 'ia32' }, { arch: 'x64' }, { arch: 'arm' }, { arch: 'arm64' }, diff --git a/build/gulpfile.vscode.win32.js b/build/gulpfile.vscode.win32.js index 46543316de9..b20efebdb62 100644 --- a/build/gulpfile.vscode.win32.js +++ b/build/gulpfile.vscode.win32.js @@ -25,7 +25,7 @@ const zipDir = arch => path.join(repoPath, '.build', `win32-${arch}`, 'archive') 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 innoSetupPath = path.join(path.dirname(path.dirname(require.resolve('innosetup'))), 'bin', 'ISCC.exe'); const signPS1 = path.join(repoPath, 'build', 'azure-pipelines', 'win32', 'sign.ps1'); function packageInnoSetup(iss, options, cb) { diff --git a/build/lib/electron.js b/build/lib/electron.js index 6387133675b..63e867a98fe 100644 --- a/build/lib/electron.js +++ b/build/lib/electron.js @@ -24,7 +24,7 @@ module.exports.getElectronVersion = getElectronVersion; if (require.main === module) { const version = getElectronVersion(); const versionFile = path.join(root, '.build', 'electron', 'version'); - const isUpToDate = fs.existsSync(versionFile) && fs.readFileSync(versionFile, 'utf8') === `v${version}`; + const isUpToDate = fs.existsSync(versionFile) && fs.readFileSync(versionFile, 'utf8') === `${version}`; process.exit(isUpToDate ? 0 : 1); } diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index ac557db625e..32b0e6fb4c7 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -38,10 +38,6 @@ "name": "vs/workbench/contrib/codeEditor", "project": "vscode-workbench" }, - { - "name": "vs/workbench/contrib/codeinset", - "project": "vscode-workbench" - }, { "name": "vs/workbench/contrib/callHierarchy", "project": "vscode-workbench" @@ -110,6 +106,10 @@ "name": "vs/workbench/contrib/quickopen", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/remote", + "project": "vscode-workbench" + }, { "name": "vs/workbench/contrib/relauncher", "project": "vscode-workbench" diff --git a/build/lib/i18n.ts b/build/lib/i18n.ts index ee75ac1ec01..9f53df17bad 100644 --- a/build/lib/i18n.ts +++ b/build/lib/i18n.ts @@ -25,7 +25,7 @@ function log(message: any, ...rest: any[]): void { export interface Language { id: string; // language id, e.g. zh-tw, de - translationId?: string; // language id used in translation tools, e.g zh-hant, de (optional, if not set, the id is used) + translationId?: string; // language id used in translation tools, e.g. zh-hant, de (optional, if not set, the id is used) folderName?: string; // language specific folder name, e.g. cht, deu (optional, if not set, the id is used) } diff --git a/build/lib/standalone.js b/build/lib/standalone.js index 63e16442d53..da5987963b6 100644 --- a/build/lib/standalone.js +++ b/build/lib/standalone.js @@ -40,6 +40,7 @@ function extractEditor(options) { compilerOptions.noUnusedLocals = false; compilerOptions.preserveConstEnums = false; compilerOptions.declaration = false; + compilerOptions.noImplicitAny = false; compilerOptions.moduleResolution = ts.ModuleResolutionKind.Classic; options.compilerOptions = compilerOptions; let result = tss.shake(options); @@ -90,6 +91,8 @@ function extractEditor(options) { } delete tsConfig.compilerOptions.moduleResolution; writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); + const tsConfigBase = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.base.json')).toString()); + writeOutputFile('tsconfig.base.json', JSON.stringify(tsConfigBase, null, '\t')); [ 'vs/css.build.js', 'vs/css.d.ts', diff --git a/build/lib/standalone.ts b/build/lib/standalone.ts index dfd3c99fe91..79cdeeb455c 100644 --- a/build/lib/standalone.ts +++ b/build/lib/standalone.ts @@ -44,6 +44,7 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str compilerOptions.noUnusedLocals = false; compilerOptions.preserveConstEnums = false; compilerOptions.declaration = false; + compilerOptions.noImplicitAny = false; compilerOptions.moduleResolution = ts.ModuleResolutionKind.Classic; @@ -99,6 +100,8 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str delete tsConfig.compilerOptions.moduleResolution; writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); + const tsConfigBase = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.base.json')).toString()); + writeOutputFile('tsconfig.base.json', JSON.stringify(tsConfigBase, null, '\t')); [ 'vs/css.build.js', diff --git a/build/lib/util.js b/build/lib/util.js index 34ee13695fd..6a210d3decc 100644 --- a/build/lib/util.js +++ b/build/lib/util.js @@ -8,7 +8,6 @@ const es = require("event-stream"); const debounce = require("debounce"); const _filter = require("gulp-filter"); const rename = require("gulp-rename"); -const _ = require("underscore"); const path = require("path"); const fs = require("fs"); const _rimraf = require("rimraf"); @@ -100,22 +99,18 @@ function skipDirectories() { }); } exports.skipDirectories = skipDirectories; -function cleanNodeModule(name, excludes, includes) { - const toGlob = (path) => '**/node_modules/' + name + (path ? '/' + path : ''); - const negate = (str) => '!' + str; - const allFilter = _filter(toGlob('**'), { restore: true }); - const globs = [toGlob('**')].concat(excludes.map(_.compose(negate, toGlob))); +function cleanNodeModules(rulePath) { + const rules = fs.readFileSync(rulePath, 'utf8') + .split(/\r?\n/g) + .map(line => line.trim()) + .filter(line => line && !/^#/.test(line)); + const excludes = rules.filter(line => !/^!/.test(line)).map(line => `!**/node_modules/${line}`); + const includes = rules.filter(line => /^!/.test(line)).map(line => `**/node_modules/${line.substr(1)}`); const input = es.through(); - const nodeModuleInput = input.pipe(allFilter); - let output = nodeModuleInput.pipe(_filter(globs)); - if (includes) { - const includeGlobs = includes.map(toGlob); - output = es.merge(output, nodeModuleInput.pipe(_filter(includeGlobs))); - } - output = output.pipe(allFilter.restore); + const output = es.merge(input.pipe(_filter(['**', ...excludes])), input.pipe(_filter(includes))); return es.duplex(input, output); } -exports.cleanNodeModule = cleanNodeModule; +exports.cleanNodeModules = cleanNodeModules; function loadSourcemaps() { const input = es.through(); const output = input diff --git a/build/lib/util.ts b/build/lib/util.ts index 44ac9d0dc74..e87c0650ec3 100644 --- a/build/lib/util.ts +++ b/build/lib/util.ts @@ -132,23 +132,21 @@ export function skipDirectories(): NodeJS.ReadWriteStream { }); } -export function cleanNodeModule(name: string, excludes: string[], includes?: string[]): NodeJS.ReadWriteStream { - const toGlob = (path: string) => '**/node_modules/' + name + (path ? '/' + path : ''); - const negate = (str: string) => '!' + str; +export function cleanNodeModules(rulePath: string): NodeJS.ReadWriteStream { + const rules = fs.readFileSync(rulePath, 'utf8') + .split(/\r?\n/g) + .map(line => line.trim()) + .filter(line => line && !/^#/.test(line)); - const allFilter = _filter(toGlob('**'), { restore: true }); - const globs = [toGlob('**')].concat(excludes.map(_.compose(negate, toGlob) as (x: string) => string)); + const excludes = rules.filter(line => !/^!/.test(line)).map(line => `!**/node_modules/${line}`); + const includes = rules.filter(line => /^!/.test(line)).map(line => `**/node_modules/${line.substr(1)}`); const input = es.through(); - const nodeModuleInput = input.pipe(allFilter); - let output: NodeJS.ReadWriteStream = nodeModuleInput.pipe(_filter(globs)); + const output = es.merge( + input.pipe(_filter(['**', ...excludes])), + input.pipe(_filter(includes)) + ); - if (includes) { - const includeGlobs = includes.map(toGlob); - output = es.merge(output, nodeModuleInput.pipe(_filter(includeGlobs))); - } - - output = output.pipe(allFilter.restore); return es.duplex(input, output); } diff --git a/build/lib/watch/package.json b/build/lib/watch/package.json index 0d031340153..3afffdec7d3 100644 --- a/build/lib/watch/package.json +++ b/build/lib/watch/package.json @@ -4,6 +4,7 @@ "description": "", "author": "Microsoft ", "private": true, + "license": "MIT", "devDependencies": { "gulp-watch": "^4.3.9" } diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index 02755e171ec..80a4f0eeb5c 100644 --- a/build/npm/postinstall.js +++ b/build/npm/postinstall.js @@ -20,13 +20,10 @@ function yarnInstall(location, opts) { const raw = process.env['npm_config_argv'] || '{}'; const argv = JSON.parse(raw); const original = argv.original || []; - const args = ['install']; + const args = original.filter(arg => arg === '--ignore-optional' || arg === '--frozen-lockfile'); - if (original.indexOf('--ignore-optional') > -1) { - args.push('--ignore-optional'); - } - - console.log('Installing dependencies in \'%s\'.', location); + console.log(`Installing dependencies in ${location}...`); + console.log(`$ yarn ${args.join(' ')}`); const result = cp.spawnSync(yarn, args, opts); if (result.error || result.status !== 0) { @@ -36,6 +33,8 @@ function yarnInstall(location, opts) { yarnInstall('extensions'); // node modules shared by all extensions +yarnInstall('remote'); // node modules used by vscode server + const allExtensionFolders = fs.readdirSync('extensions'); const extensions = allExtensionFolders.filter(e => { try { diff --git a/build/package.json b/build/package.json index 49495b1c1e5..622b7b59351 100644 --- a/build/package.json +++ b/build/package.json @@ -1,6 +1,7 @@ { "name": "code-oss-dev-build", "version": "1.0.0", + "license": "MIT", "devDependencies": { "@types/ansi-colors": "^3.2.0", "@types/azure": "0.9.19", @@ -39,7 +40,7 @@ "minimist": "^1.2.0", "request": "^2.85.0", "tslint": "^5.9.1", - "typescript": "3.4.5", + "typescript": "3.5.2", "vsce": "1.48.0", "xml2js": "^0.4.17" }, diff --git a/build/win32/Cargo.lock b/build/win32/Cargo.lock index dcc1440b35e..3c95fb34cdf 100644 --- a/build/win32/Cargo.lock +++ b/build/win32/Cargo.lock @@ -1,3 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. [[package]] name = "build_const" version = "0.2.0" @@ -27,7 +29,7 @@ dependencies = [ [[package]] name = "inno_updater" -version = "0.7.1" +version = "0.8.0" 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)", diff --git a/build/win32/code.iss b/build/win32/code.iss index ea31a50c9bf..831b31a3c71 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -1034,7 +1034,7 @@ begin AltArch := '32'; end; - if not Result then begin + if not Result and not WizardSilent() then begin MsgBox('Please uninstall the ' + AltArch + '-bit version of {#NameShort} before installing this ' + ThisArch + '-bit version.', mbInformation, MB_OK); end; end; diff --git a/build/win32/i18n/messages.en.isl b/build/win32/i18n/messages.en.isl index 4bd6c75ad43..a6aab59b95a 100644 --- a/build/win32/i18n/messages.en.isl +++ b/build/win32/i18n/messages.en.isl @@ -2,7 +2,7 @@ AddContextMenuFiles=Add "Open with %1" action to Windows Explorer file context menu AddContextMenuFolders=Add "Open with %1" action to Windows Explorer directory context menu AssociateWithFiles=Register %1 as an editor for supported file types -AddToPath=Add to PATH (available after restart) +AddToPath=Add to PATH (requires shell restart) RunAfter=Run %1 after installation Other=Other: SourceFile=%1 Source File \ No newline at end of file diff --git a/build/win32/inno_updater.exe b/build/win32/inno_updater.exe index 140065f673c..baca28361d1 100644 Binary files a/build/win32/inno_updater.exe and b/build/win32/inno_updater.exe differ diff --git a/build/yarn.lock b/build/yarn.lock index 3d6f612789b..3bba51751e1 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -1894,10 +1894,10 @@ typed-rest-client@^0.9.0: tunnel "0.0.4" underscore "1.8.3" -typescript@3.4.5: - version "3.4.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.5.tgz#2d2618d10bb566572b8d7aad5180d84257d70a99" - integrity sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw== +typescript@3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.2.tgz#a09e1dc69bc9551cadf17dba10ee42cf55e5d56c" + integrity sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.5" diff --git a/cglicenses.json b/cglicenses.json index 3d96813f3ef..469cdedc9ad 100644 --- a/cglicenses.json +++ b/cglicenses.json @@ -6,694 +6,742 @@ // DO NOT EDIT THIS FILE UNLESS THE OSS TOOL INDICATES THAT YOU SHOULD. // [ -{ - // Reason: The license at https://github.com/aadsm/jschardet/blob/master/LICENSE - // does not include a clear Copyright statement and does not credit authors. - "name": "jschardet", - "licenseDetail": [ - "Chardet was originally ported from C++ by Mark Pilgrim. It is now maintained", - " by Dan Blanchard and Ian Cordasco, and was formerly maintained by Erik Rose.", - " JSChardet was ported from python to JavaScript by António Afonso ", - " (https://github.com/aadsm/jschardet) and transformed into an npm package by ", - "Markus Ast (https://github.com/brainafk)", - "", - "GNU LESSER GENERAL PUBLIC LICENSE", - "\t\t Version 2.1, February 1999", - "", - " Copyright (C) 1991,", - "1999 Free Software Foundation, Inc.", - " 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA", - " Everyone is permitted to copy and distribute verbatim copies", - " of this license document, but changing it is not allowed.", - "", - "[This is the first released version of the Lesser GPL. It also counts", - " as the successor of the GNU Library Public License, version 2, hence", - " the version number 2.1.", - "]", - "", - "\t\t\t Preamble", - "", - " The licenses for most software are designed to take away your", - "freedom to share and change it. By contrast, the GNU General Public", - "Licenses are intended to guarantee your freedom to share and change", - "free software--to make sure the software is free for all its users.", - "", - " This license, the Lesser General Public License, applies to some", - "specially designated software packages--typically libraries--of the", - "Free Software Foundation and other authors who decide to use it. You", - "can use it too, but we suggest you first think carefully about whether", - "this license or the ordinary General Public License is the better", - "strategy to use in any particular case, based on the explanations below.", - "", - " When we speak of free software, we are referring to freedom of use,", - "not price. Our General Public Licenses are designed to make sure that", - "you have the freedom to distribute copies of free software (and charge", - "for this service if you wish); that you receive source code or can get", - "it if you want it; that you can change the software and use pieces of", - "it in new free programs; and that you are informed that you can do", - "these things.", - "", - " To protect your rights, we need to make restrictions that forbid", - "distributors to deny you these rights or to ask you to surrender these", - "rights. These restrictions translate to certain responsibilities for", - "you if you distribute copies of the library or if you modify it.", - "", - " For example, if you distribute copies of the library, whether gratis", - "or for a fee, you must give the recipients all the rights that we gave", - "you. You must make sure that they, too, receive or can get the source", - "code. If you link other code with the library, you must provide", - "complete object files to the recipients, so that they can relink them", - "with the library after making changes to the library and recompiling", - "it. And you must show them these terms so they know their rights.", - "", - " We protect your rights with a two-step method: (1) we copyright the", - "library, and (2) we offer you this license, which gives you legal", - "permission to copy, distribute and/or modify the library.", - "", - " To protect each distributor, we want to make it very clear that", - "there is no warranty for the free library. Also, if the library is", - "modified by someone else and passed on, the recipients should know", - "that what they have is not the original version, so that the original", - "author's reputation will not be affected by problems that might be", - "introduced by others.", - "", - " Finally, software patents pose a constant threat to the existence of", - "any free program. We wish to make sure that a company cannot", - "effectively restrict the users of a free program by obtaining a", - "restrictive license from a patent holder. Therefore, we insist that", - "any patent license obtained for a version of the library must be", - "consistent with the full freedom of use specified in this license.", - "", - " Most GNU software, including some libraries, is covered by the", - "ordinary GNU General Public License. This license, the GNU Lesser", - "General Public License, applies to certain designated libraries, and", - "is quite different from the ordinary General Public License. We use", - "this license for certain libraries in order to permit linking those", - "libraries into non-free programs.", - "", - " When a program is linked with a library, whether statically or using", - "a shared library, the combination of the two is legally speaking a", - "combined work, a derivative of the original library. The ordinary", - "General Public License therefore permits such linking only if the", - "entire combination fits its criteria of freedom. The Lesser General", - "Public License permits more lax criteria for linking other code with", - "the library.", - "", - " We call this license the \"Lesser\" General Public License because it", - "does Less to protect the user's freedom than the ordinary General", - "Public License. It also provides other free software developers Less", - "of an advantage over competing non-free programs. These disadvantages", - "are the reason we use the ordinary General Public License for many", - "libraries. However, the Lesser license provides advantages in certain", - "special circumstances.", - "", - " For example, on rare occasions, there may be a special need to", - "encourage the widest possible use of a certain library, so that it becomes", - "a de-facto standard. To achieve this, non-free programs must be", - "allowed to use the library. A more frequent case is that a free", - "library does the same job as widely used non-free libraries. In this", - "case, there is little to gain by limiting the free library to free", - "software only, so we use the Lesser General Public License.", - "", - " In other cases, permission to use a particular library in non-free", - "programs enables a greater number of people to use a large body of", - "free software. For example, permission to use the GNU C Library in", - "non-free programs enables many more people to use the whole GNU", - "operating system, as well as its variant, the GNU/Linux operating", - "system.", - "", - " Although the Lesser General Public License is Less protective of the", - "users' freedom, it does ensure that the user of a program that is", - "linked with the Library has the freedom and the wherewithal to run", - "that program using a modified version of the Library.", - "", - " The precise terms and conditions for copying, distribution and", - "modification follow. Pay close attention to the difference between a", - "\"work based on the library\" and a \"work that uses the library\". The", - "former contains code derived from the library, whereas the latter must", - "be combined with the library in order to run.", - "", - "\t\t GNU LESSER GENERAL PUBLIC LICENSE", - " TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION", - "", - " 0. This License Agreement applies to any software library or other", - "program which contains a notice placed by the copyright holder or", - "other authorized party saying it may be distributed under the terms of", - "this Lesser General Public License (also called \"this License\").", - "Each licensee is addressed as \"you\".", - "", - " A \"library\" means a collection of software functions and/or data", - "prepared so as to be conveniently linked with application programs", - "(which use some of those functions and data) to form executables.", - "", - " The \"Library\", below, refers to any such software library or work", - "which has been distributed under these terms. A \"work based on the", - "Library\" means either the Library or any derivative work under", - "copyright law: that is to say, a work containing the Library or a", - "portion of it, either verbatim or with modifications and/or translated", - "straightforwardly into another language. (Hereinafter, translation is", - "included without limitation in the term \"modification\".)", - "", - " \"Source code\" for a work means the preferred form of the work for", - "making modifications to it. For a library, complete source code means", - "all the source code for all modules it contains, plus any associated", - "interface definition files, plus the scripts used to control compilation", - "and installation of the library.", - "", - " Activities other than copying, distribution and modification are not", - "covered by this License; they are outside its scope. The act of", - "running a program using the Library is not restricted, and output from", - "such a program is covered only if its contents constitute a work based", - "on the Library (independent of the use of the Library in a tool for", - "writing it). Whether that is true depends on what the Library does", - "and what the program that uses the Library does.", - "", - " 1. You may copy and distribute verbatim copies of the Library's", - "complete source code as you receive it, in any medium, provided that", - "you conspicuously and appropriately publish on each copy an", - "appropriate copyright notice and disclaimer of warranty; keep intact", - "all the notices that refer to this License and to the absence of any", - "warranty; and distribute a copy of this License along with the", - "Library.", - "", - " You may charge a fee for the physical act of transferring a copy,", - "and you may at your option offer warranty protection in exchange for a", - "fee.", - "", - " 2. You may modify your copy or copies of the Library or any portion", - "of it, thus forming a work based on the Library, and copy and", - "distribute such modifications or work under the terms of Section 1", - "above, provided that you also meet all of these conditions:", - "", - " a) The modified work must itself be a software library.", - "", - " b) You must cause the files modified to carry prominent notices", - " stating that you changed the files and the date of any change.", - "", - " c) You must cause the whole of the work to be licensed at no", - " charge to all third parties under the terms of this License.", - "", - " d) If a facility in the modified Library refers to a function or a", - " table of data to be supplied by an application program that uses", - " the facility, other than as an argument passed when the facility", - " is invoked, then you must make a good faith effort to ensure that,", - " in the event an application does not supply such function or", - " table, the facility still operates, and performs whatever part of", - " its purpose remains meaningful.", - "", - " (For example, a function in a library to compute square roots has", - " a purpose that is entirely well-defined independent of the", - " application. Therefore, Subsection 2d requires that any", - " application-supplied function or table used by this function must", - " be optional: if the application does not supply it, the square", - " root function must still compute square roots.)", - "", - "These requirements apply to the modified work as a whole. If", - "identifiable sections of that work are not derived from the Library,", - "and can be reasonably considered independent and separate works in", - "themselves, then this License, and its terms, do not apply to those", - "sections when you distribute them as separate works. But when you", - "distribute the same sections as part of a whole which is a work based", - "on the Library, the distribution of the whole must be on the terms of", - "this License, whose permissions for other licensees extend to the", - "entire whole, and thus to each and every part regardless of who wrote", - "it.", - "", - "Thus, it is not the intent of this section to claim rights or contest", - "your rights to work written entirely by you; rather, the intent is to", - "exercise the right to control the distribution of derivative or", - "collective works based on the Library.", - "", - "In addition, mere aggregation of another work not based on the Library", - "with the Library (or with a work based on the Library) on a volume of", - "a storage or distribution medium does not bring the other work under", - "the scope of this License.", - "", - " 3. You may opt to apply the terms of the ordinary GNU General Public", - "License instead of this License to a given copy of the Library. To do", - "this, you must alter all the notices that refer to this License, so", - "that they refer to the ordinary GNU General Public License, version 2,", - "instead of to this License. (If a newer version than version 2 of the", - "ordinary GNU General Public License has appeared, then you can specify", - "that version instead if you wish.) Do not make any other change in", - "these notices.", - "", - " Once this change is made in a given copy, it is irreversible for", - "that copy, so the ordinary GNU General Public License applies to all", - "subsequent copies and derivative works made from that copy.", - "", - " This option is useful when you wish to copy part of the code of", - "the Library into a program that is not a library.", - "", - " 4. You may copy and distribute the Library (or a portion or", - "derivative of it, under Section 2) in object code or executable form", - "under the terms of Sections 1 and 2 above provided that you accompany", - "it with the complete corresponding machine-readable source code, which", - "must be distributed under the terms of Sections 1 and 2 above on a", - "medium customarily used for software interchange.", - "", - " If distribution of object code is made by offering access to copy", - "from a designated place, then offering equivalent access to copy the", - "source code from the same place satisfies the requirement to", - "distribute the source code, even though third parties are not", - "compelled to copy the source along with the object code.", - "", - " 5. A program that contains no derivative of any portion of the", - "Library, but is designed to work with the Library by being compiled or", - "linked with it, is called a \"work that uses the Library\". Such a", - "work, in isolation, is not a derivative work of the Library, and", - "therefore falls outside the scope of this License.", - "", - " However, linking a \"work that uses the Library\" with the Library", - "creates an executable that is a derivative of the Library (because it", - "contains portions of the Library), rather than a \"work that uses the", - "library\". The executable is therefore covered by this License.", - "Section 6 states terms for distribution of such executables.", - "", - " When a \"work that uses the Library\" uses material from a header file", - "that is part of the Library, the object code for the work may be a", - "derivative work of the Library even though the source code is not.", - "Whether this is true is especially significant if the work can be", - "linked without the Library, or if the work is itself a library. The", - "threshold for this to be true is not precisely defined by law.", - "", - " If such an object file uses only numerical parameters, data", - "structure layouts and accessors, and small macros and small inline", - "functions (ten lines or less in length), then the use of the object", - "file is unrestricted, regardless of whether it is legally a derivative", - "work. (Executables containing this object code plus portions of the", - "Library will still fall under Section 6.)", - "", - " Otherwise, if the work is a derivative of the Library, you may", - "distribute the object code for the work under the terms of Section 6.", - "Any executables containing that work also fall under Section 6,", - "whether or not they are linked directly with the Library itself.", - "", - " 6. As an exception to the Sections above, you may also combine or", - "link a \"work that uses the Library\" with the Library to produce a", - "work containing portions of the Library, and distribute that work", - "under terms of your choice, provided that the terms permit", - "modification of the work for the customer's own use and reverse", - "engineering for debugging such modifications.", - "", - " You must give prominent notice with each copy of the work that the", - "Library is used in it and that the Library and its use are covered by", - "this License. You must supply a copy of this License. If the work", - "during execution displays copyright notices, you must include the", - "copyright notice for the Library among them, as well as a reference", - "directing the user to the copy of this License. Also, you must do one", - "of these things:", - "", - " a) Accompany the work with the complete corresponding", - " machine-readable source code for the Library including whatever", - " changes were used in the work (which must be distributed under", - " Sections 1 and 2 above); and, if the work is an executable linked", - " with the Library, with the complete machine-readable \"work that", - " uses the Library\", as object code and/or source code, so that the", - " user can modify the Library and then relink to produce a modified", - " executable containing the modified Library. (It is understood", - " that the user who changes the contents of definitions files in the", - " Library will not necessarily be able to recompile the application", - " to use the modified definitions.)", - "", - " b) Use a suitable shared library mechanism for linking with the", - " Library. A suitable mechanism is one that (1) uses at run time a", - " copy of the library already present on the user's computer system,", - " rather than copying library functions into the executable, and (2)", - " will operate properly with a modified version of the library, if", - " the user installs one, as long as the modified version is", - " interface-compatible with the version that the work was made with.", - "", - " c) Accompany the work with a written offer, valid for at", - " least three years, to give the same user the materials", - " specified in Subsection 6a, above, for a charge no more", - " than the cost of performing this distribution.", - "", - " d) If distribution of the work is made by offering access to copy", - " from a designated place, offer equivalent access to copy the above", - " specified materials from the same place.", - "", - " e) Verify that the user has already received a copy of these", - " materials or that you have already sent this user a copy.", - "", - " For an executable, the required form of the \"work that uses the", - "Library\" must include any data and utility programs needed for", - "reproducing the executable from it. However, as a special exception,", - "the materials to be distributed need not include anything that is", - "normally distributed (in either source or binary form) with the major", - "components (compiler, kernel, and so on) of the operating system on", - "which the executable runs, unless that component itself accompanies", - "the executable.", - "", - " It may happen that this requirement contradicts the license", - "restrictions of other proprietary libraries that do not normally", - "accompany the operating system. Such a contradiction means you cannot", - "use both them and the Library together in an executable that you", - "distribute.", - "", - " 7. You may place library facilities that are a work based on the", - "Library side-by-side in a single library together with other library", - "facilities not covered by this License, and distribute such a combined", - "library, provided that the separate distribution of the work based on", - "the Library and of the other library facilities is otherwise", - "permitted, and provided that you do these two things:", - "", - " a) Accompany the combined library with a copy of the same work", - " based on the Library, uncombined with any other library", - " facilities. This must be distributed under the terms of the", - " Sections above.", - "", - " b) Give prominent notice with the combined library of the fact", - " that part of it is a work based on the Library, and explaining", - " where to find the accompanying uncombined form of the same work.", - "", - " 8. You may not copy, modify, sublicense, link with, or distribute", - "the Library except as expressly provided under this License. Any", - "attempt otherwise to copy, modify, sublicense, link with, or", - "distribute the Library is void, and will automatically terminate your", - "rights under this License. However, parties who have received copies,", - "or rights, from you under this License will not have their licenses", - "terminated so long as such parties remain in full compliance.", - "", - " 9. You are not required to accept this License, since you have not", - "signed it. However, nothing else grants you permission to modify or", - "distribute the Library or its derivative works. These actions are", - "prohibited by law if you do not accept this License. Therefore, by", - "modifying or distributing the Library (or any work based on the", - "Library), you indicate your acceptance of this License to do so, and", - "all its terms and conditions for copying, distributing or modifying", - "the Library or works based on it.", - "", - " 10. Each time you redistribute the Library (or any work based on the", - "Library), the recipient automatically receives a license from the", - "original licensor to copy, distribute, link with or modify the Library", - "subject to these terms and conditions. You may not impose any further", - "restrictions on the recipients' exercise of the rights granted herein.", - "You are not responsible for enforcing compliance by third parties with", - "this License.", - "", - " 11. If, as a consequence of a court judgment or allegation of patent", - "infringement or for any other reason (not limited to patent issues),", - "conditions are imposed on you (whether by court order, agreement or", - "otherwise) that contradict the conditions of this License, they do not", - "excuse you from the conditions of this License. If you cannot", - "distribute so as to satisfy simultaneously your obligations under this", - "License and any other pertinent obligations, then as a consequence you", - "may not distribute the Library at all. For example, if a patent", - "license would not permit royalty-free redistribution of the Library by", - "all those who receive copies directly or indirectly through you, then", - "the only way you could satisfy both it and this License would be to", - "refrain entirely from distribution of the Library.", - "", - "If any portion of this section is held invalid or unenforceable under any", - "particular circumstance, the balance of the section is intended to apply,", - "and the section as a whole is intended to apply in other circumstances.", - "", - "It is not the purpose of this section to induce you to infringe any", - "patents or other property right claims or to contest validity of any", - "such claims; this section has the sole purpose of protecting the", - "integrity of the free software distribution system which is", - "implemented by public license practices. Many people have made", - "generous contributions to the wide range of software distributed", - "through that system in reliance on consistent application of that", - "system; it is up to the author/donor to decide if he or she is willing", - "to distribute software through any other system and a licensee cannot", - "impose that choice.", - "", - "This section is intended to make thoroughly clear what is believed to", - "be a consequence of the rest of this License.", - "", - " 12. If the distribution and/or use of the Library is restricted in", - "certain countries either by patents or by copyrighted interfaces, the", - "original copyright holder who places the Library under this License may add", - "an explicit geographical distribution limitation excluding those countries,", - "so that distribution is permitted only in or among countries not thus", - "excluded. In such case, this License incorporates the limitation as if", - "written in the body of this License.", - "", - " 13. The Free Software Foundation may publish revised and/or new", - "versions of the Lesser General Public License from time to time.", - "Such new versions will be similar in spirit to the present version,", - "but may differ in detail to address new problems or concerns.", - "", - "Each version is given a distinguishing version number. If the Library", - "specifies a version number of this License which applies to it and", - "\"any later version\", you have the option of following the terms and", - "conditions either of that version or of any later version published by", - "the Free Software Foundation. If the Library does not specify a", - "license version number, you may choose any version ever published by", - "the Free Software Foundation.", - "", - " 14. If you wish to incorporate parts of the Library into other free", - "programs whose distribution conditions are incompatible with these,", - "write to the author to ask for permission. For software which is", - "copyrighted by the Free Software Foundation, write to the Free", - "Software Foundation; we sometimes make exceptions for this. Our", - "decision will be guided by the two goals of preserving the free status", - "of all derivatives of our free software and of promoting the sharing", - "and reuse of software generally.", - "", - "\t\t\t NO WARRANTY", - "", - " 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO", - "WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.", - "EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR", - "OTHER PARTIES PROVIDE THE LIBRARY \"AS IS\" WITHOUT WARRANTY OF ANY", - "KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE", - "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR", - "PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE", - "LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME", - "THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.", - "", - " 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN", - "WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY", - "AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU", - "FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR", - "CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE", - "LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING", - "RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A", - "FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF", - "SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH", - "DAMAGES.", - "", - "\t\t END OF TERMS AND CONDITIONS", - "", - " How to Apply These Terms to Your New Libraries", - "", - " If you develop a new library, and you want it to be of the greatest", - "possible use to the public, we recommend making it free software that", - "everyone can redistribute and change. You can do so by permitting", - "redistribution under these terms (or, alternatively, under the terms of the", - "ordinary General Public License).", - "", - " To apply these terms, attach the following notices to the library. It is", - "safest to attach them to the start of each source file to most effectively", - "convey the exclusion of warranty; and each file should have at least the", - "\"copyright\" line and a pointer to where the full notice is found.", - "", - " ", - " Copyright (C) ", - "", - " This library is free software; you can redistribute it and/or", - " modify it under the terms of the GNU Lesser General Public", - " License as published by the Free Software Foundation; either", - " version 2.1 of the License, or (at your option) any later version.", - "", - " This library is distributed in the hope that it will be useful,", - " but WITHOUT ANY WARRANTY; without even the implied warranty of", - " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU", - " Lesser General Public License for more details.", - "", - " You should have received a copy of the GNU Lesser General Public", - " License along with this library; if not, write to the Free Software", - " Foundation, Inc.,", - "51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA", - "", - "Also add information on how to contact you by electronic and paper mail.", - "", - "You should also get your employer (if you work as a programmer) or your", - "school, if any, to sign a \"copyright disclaimer\" for the library, if", - "necessary. Here is a sample; alter the names:", - "", - " Yoyodyne, Inc., hereby disclaims all copyright interest in the", - " library `Frob' (a library for tweaking knobs) written by James Random Hacker.", - "", - " ,", - "1 April 1990", - " Ty Coon, President of Vice", - "", - "That's all there is to it!" - ] -}, -{ - // 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 - "name": "@types/node", - "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." - ] -}, -{ - // 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 - "name": "typescript", - "licenseDetail": [ - "Copyright (c) Microsoft Corporation. All rights reserved.", - "", - "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" - ] -}, -{ - // This module comes in from https://github.com/Microsoft/vscode-node-debug2/blob/master/package-lock.json - "name": "@types/source-map", - "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." - ] -}, -{ - "name": "tunnel-agent", - "licenseDetail": [ - "Copyright (c) tunnel-agent authors", - "", - "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" - ] -}, -{ - // Waiting for https://github.com/segmentio/noop-logger/issues/2 - "name": "noop-logger", - "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 + { + // Reason: The license at https://github.com/aadsm/jschardet/blob/master/LICENSE + // does not include a clear Copyright statement and does not credit authors. + "name": "jschardet", + "licenseDetail": [ + "Chardet was originally ported from C++ by Mark Pilgrim. It is now maintained", + " by Dan Blanchard and Ian Cordasco, and was formerly maintained by Erik Rose.", + " JSChardet was ported from python to JavaScript by António Afonso ", + " (https://github.com/aadsm/jschardet) and transformed into an npm package by ", + "Markus Ast (https://github.com/brainafk)", + "", + "GNU LESSER GENERAL PUBLIC LICENSE", + "\t\t Version 2.1, February 1999", + "", + " Copyright (C) 1991,", + "1999 Free Software Foundation, Inc.", + " 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA", + " Everyone is permitted to copy and distribute verbatim copies", + " of this license document, but changing it is not allowed.", + "", + "[This is the first released version of the Lesser GPL. It also counts", + " as the successor of the GNU Library Public License, version 2, hence", + " the version number 2.1.", + "]", + "", + "\t\t\t Preamble", + "", + " The licenses for most software are designed to take away your", + "freedom to share and change it. By contrast, the GNU General Public", + "Licenses are intended to guarantee your freedom to share and change", + "free software--to make sure the software is free for all its users.", + "", + " This license, the Lesser General Public License, applies to some", + "specially designated software packages--typically libraries--of the", + "Free Software Foundation and other authors who decide to use it. You", + "can use it too, but we suggest you first think carefully about whether", + "this license or the ordinary General Public License is the better", + "strategy to use in any particular case, based on the explanations below.", + "", + " When we speak of free software, we are referring to freedom of use,", + "not price. Our General Public Licenses are designed to make sure that", + "you have the freedom to distribute copies of free software (and charge", + "for this service if you wish); that you receive source code or can get", + "it if you want it; that you can change the software and use pieces of", + "it in new free programs; and that you are informed that you can do", + "these things.", + "", + " To protect your rights, we need to make restrictions that forbid", + "distributors to deny you these rights or to ask you to surrender these", + "rights. These restrictions translate to certain responsibilities for", + "you if you distribute copies of the library or if you modify it.", + "", + " For example, if you distribute copies of the library, whether gratis", + "or for a fee, you must give the recipients all the rights that we gave", + "you. You must make sure that they, too, receive or can get the source", + "code. If you link other code with the library, you must provide", + "complete object files to the recipients, so that they can relink them", + "with the library after making changes to the library and recompiling", + "it. And you must show them these terms so they know their rights.", + "", + " We protect your rights with a two-step method: (1) we copyright the", + "library, and (2) we offer you this license, which gives you legal", + "permission to copy, distribute and/or modify the library.", + "", + " To protect each distributor, we want to make it very clear that", + "there is no warranty for the free library. Also, if the library is", + "modified by someone else and passed on, the recipients should know", + "that what they have is not the original version, so that the original", + "author's reputation will not be affected by problems that might be", + "introduced by others.", + "", + " Finally, software patents pose a constant threat to the existence of", + "any free program. We wish to make sure that a company cannot", + "effectively restrict the users of a free program by obtaining a", + "restrictive license from a patent holder. Therefore, we insist that", + "any patent license obtained for a version of the library must be", + "consistent with the full freedom of use specified in this license.", + "", + " Most GNU software, including some libraries, is covered by the", + "ordinary GNU General Public License. This license, the GNU Lesser", + "General Public License, applies to certain designated libraries, and", + "is quite different from the ordinary General Public License. We use", + "this license for certain libraries in order to permit linking those", + "libraries into non-free programs.", + "", + " When a program is linked with a library, whether statically or using", + "a shared library, the combination of the two is legally speaking a", + "combined work, a derivative of the original library. The ordinary", + "General Public License therefore permits such linking only if the", + "entire combination fits its criteria of freedom. The Lesser General", + "Public License permits more lax criteria for linking other code with", + "the library.", + "", + " We call this license the \"Lesser\" General Public License because it", + "does Less to protect the user's freedom than the ordinary General", + "Public License. It also provides other free software developers Less", + "of an advantage over competing non-free programs. These disadvantages", + "are the reason we use the ordinary General Public License for many", + "libraries. However, the Lesser license provides advantages in certain", + "special circumstances.", + "", + " For example, on rare occasions, there may be a special need to", + "encourage the widest possible use of a certain library, so that it becomes", + "a de-facto standard. To achieve this, non-free programs must be", + "allowed to use the library. A more frequent case is that a free", + "library does the same job as widely used non-free libraries. In this", + "case, there is little to gain by limiting the free library to free", + "software only, so we use the Lesser General Public License.", + "", + " In other cases, permission to use a particular library in non-free", + "programs enables a greater number of people to use a large body of", + "free software. For example, permission to use the GNU C Library in", + "non-free programs enables many more people to use the whole GNU", + "operating system, as well as its variant, the GNU/Linux operating", + "system.", + "", + " Although the Lesser General Public License is Less protective of the", + "users' freedom, it does ensure that the user of a program that is", + "linked with the Library has the freedom and the wherewithal to run", + "that program using a modified version of the Library.", + "", + " The precise terms and conditions for copying, distribution and", + "modification follow. Pay close attention to the difference between a", + "\"work based on the library\" and a \"work that uses the library\". The", + "former contains code derived from the library, whereas the latter must", + "be combined with the library in order to run.", + "", + "\t\t GNU LESSER GENERAL PUBLIC LICENSE", + " TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION", + "", + " 0. This License Agreement applies to any software library or other", + "program which contains a notice placed by the copyright holder or", + "other authorized party saying it may be distributed under the terms of", + "this Lesser General Public License (also called \"this License\").", + "Each licensee is addressed as \"you\".", + "", + " A \"library\" means a collection of software functions and/or data", + "prepared so as to be conveniently linked with application programs", + "(which use some of those functions and data) to form executables.", + "", + " The \"Library\", below, refers to any such software library or work", + "which has been distributed under these terms. A \"work based on the", + "Library\" means either the Library or any derivative work under", + "copyright law: that is to say, a work containing the Library or a", + "portion of it, either verbatim or with modifications and/or translated", + "straightforwardly into another language. (Hereinafter, translation is", + "included without limitation in the term \"modification\".)", + "", + " \"Source code\" for a work means the preferred form of the work for", + "making modifications to it. For a library, complete source code means", + "all the source code for all modules it contains, plus any associated", + "interface definition files, plus the scripts used to control compilation", + "and installation of the library.", + "", + " Activities other than copying, distribution and modification are not", + "covered by this License; they are outside its scope. The act of", + "running a program using the Library is not restricted, and output from", + "such a program is covered only if its contents constitute a work based", + "on the Library (independent of the use of the Library in a tool for", + "writing it). Whether that is true depends on what the Library does", + "and what the program that uses the Library does.", + "", + " 1. You may copy and distribute verbatim copies of the Library's", + "complete source code as you receive it, in any medium, provided that", + "you conspicuously and appropriately publish on each copy an", + "appropriate copyright notice and disclaimer of warranty; keep intact", + "all the notices that refer to this License and to the absence of any", + "warranty; and distribute a copy of this License along with the", + "Library.", + "", + " You may charge a fee for the physical act of transferring a copy,", + "and you may at your option offer warranty protection in exchange for a", + "fee.", + "", + " 2. You may modify your copy or copies of the Library or any portion", + "of it, thus forming a work based on the Library, and copy and", + "distribute such modifications or work under the terms of Section 1", + "above, provided that you also meet all of these conditions:", + "", + " a) The modified work must itself be a software library.", + "", + " b) You must cause the files modified to carry prominent notices", + " stating that you changed the files and the date of any change.", + "", + " c) You must cause the whole of the work to be licensed at no", + " charge to all third parties under the terms of this License.", + "", + " d) If a facility in the modified Library refers to a function or a", + " table of data to be supplied by an application program that uses", + " the facility, other than as an argument passed when the facility", + " is invoked, then you must make a good faith effort to ensure that,", + " in the event an application does not supply such function or", + " table, the facility still operates, and performs whatever part of", + " its purpose remains meaningful.", + "", + " (For example, a function in a library to compute square roots has", + " a purpose that is entirely well-defined independent of the", + " application. Therefore, Subsection 2d requires that any", + " application-supplied function or table used by this function must", + " be optional: if the application does not supply it, the square", + " root function must still compute square roots.)", + "", + "These requirements apply to the modified work as a whole. If", + "identifiable sections of that work are not derived from the Library,", + "and can be reasonably considered independent and separate works in", + "themselves, then this License, and its terms, do not apply to those", + "sections when you distribute them as separate works. But when you", + "distribute the same sections as part of a whole which is a work based", + "on the Library, the distribution of the whole must be on the terms of", + "this License, whose permissions for other licensees extend to the", + "entire whole, and thus to each and every part regardless of who wrote", + "it.", + "", + "Thus, it is not the intent of this section to claim rights or contest", + "your rights to work written entirely by you; rather, the intent is to", + "exercise the right to control the distribution of derivative or", + "collective works based on the Library.", + "", + "In addition, mere aggregation of another work not based on the Library", + "with the Library (or with a work based on the Library) on a volume of", + "a storage or distribution medium does not bring the other work under", + "the scope of this License.", + "", + " 3. You may opt to apply the terms of the ordinary GNU General Public", + "License instead of this License to a given copy of the Library. To do", + "this, you must alter all the notices that refer to this License, so", + "that they refer to the ordinary GNU General Public License, version 2,", + "instead of to this License. (If a newer version than version 2 of the", + "ordinary GNU General Public License has appeared, then you can specify", + "that version instead if you wish.) Do not make any other change in", + "these notices.", + "", + " Once this change is made in a given copy, it is irreversible for", + "that copy, so the ordinary GNU General Public License applies to all", + "subsequent copies and derivative works made from that copy.", + "", + " This option is useful when you wish to copy part of the code of", + "the Library into a program that is not a library.", + "", + " 4. You may copy and distribute the Library (or a portion or", + "derivative of it, under Section 2) in object code or executable form", + "under the terms of Sections 1 and 2 above provided that you accompany", + "it with the complete corresponding machine-readable source code, which", + "must be distributed under the terms of Sections 1 and 2 above on a", + "medium customarily used for software interchange.", + "", + " If distribution of object code is made by offering access to copy", + "from a designated place, then offering equivalent access to copy the", + "source code from the same place satisfies the requirement to", + "distribute the source code, even though third parties are not", + "compelled to copy the source along with the object code.", + "", + " 5. A program that contains no derivative of any portion of the", + "Library, but is designed to work with the Library by being compiled or", + "linked with it, is called a \"work that uses the Library\". Such a", + "work, in isolation, is not a derivative work of the Library, and", + "therefore falls outside the scope of this License.", + "", + " However, linking a \"work that uses the Library\" with the Library", + "creates an executable that is a derivative of the Library (because it", + "contains portions of the Library), rather than a \"work that uses the", + "library\". The executable is therefore covered by this License.", + "Section 6 states terms for distribution of such executables.", + "", + " When a \"work that uses the Library\" uses material from a header file", + "that is part of the Library, the object code for the work may be a", + "derivative work of the Library even though the source code is not.", + "Whether this is true is especially significant if the work can be", + "linked without the Library, or if the work is itself a library. The", + "threshold for this to be true is not precisely defined by law.", + "", + " If such an object file uses only numerical parameters, data", + "structure layouts and accessors, and small macros and small inline", + "functions (ten lines or less in length), then the use of the object", + "file is unrestricted, regardless of whether it is legally a derivative", + "work. (Executables containing this object code plus portions of the", + "Library will still fall under Section 6.)", + "", + " Otherwise, if the work is a derivative of the Library, you may", + "distribute the object code for the work under the terms of Section 6.", + "Any executables containing that work also fall under Section 6,", + "whether or not they are linked directly with the Library itself.", + "", + " 6. As an exception to the Sections above, you may also combine or", + "link a \"work that uses the Library\" with the Library to produce a", + "work containing portions of the Library, and distribute that work", + "under terms of your choice, provided that the terms permit", + "modification of the work for the customer's own use and reverse", + "engineering for debugging such modifications.", + "", + " You must give prominent notice with each copy of the work that the", + "Library is used in it and that the Library and its use are covered by", + "this License. You must supply a copy of this License. If the work", + "during execution displays copyright notices, you must include the", + "copyright notice for the Library among them, as well as a reference", + "directing the user to the copy of this License. Also, you must do one", + "of these things:", + "", + " a) Accompany the work with the complete corresponding", + " machine-readable source code for the Library including whatever", + " changes were used in the work (which must be distributed under", + " Sections 1 and 2 above); and, if the work is an executable linked", + " with the Library, with the complete machine-readable \"work that", + " uses the Library\", as object code and/or source code, so that the", + " user can modify the Library and then relink to produce a modified", + " executable containing the modified Library. (It is understood", + " that the user who changes the contents of definitions files in the", + " Library will not necessarily be able to recompile the application", + " to use the modified definitions.)", + "", + " b) Use a suitable shared library mechanism for linking with the", + " Library. A suitable mechanism is one that (1) uses at run time a", + " copy of the library already present on the user's computer system,", + " rather than copying library functions into the executable, and (2)", + " will operate properly with a modified version of the library, if", + " the user installs one, as long as the modified version is", + " interface-compatible with the version that the work was made with.", + "", + " c) Accompany the work with a written offer, valid for at", + " least three years, to give the same user the materials", + " specified in Subsection 6a, above, for a charge no more", + " than the cost of performing this distribution.", + "", + " d) If distribution of the work is made by offering access to copy", + " from a designated place, offer equivalent access to copy the above", + " specified materials from the same place.", + "", + " e) Verify that the user has already received a copy of these", + " materials or that you have already sent this user a copy.", + "", + " For an executable, the required form of the \"work that uses the", + "Library\" must include any data and utility programs needed for", + "reproducing the executable from it. However, as a special exception,", + "the materials to be distributed need not include anything that is", + "normally distributed (in either source or binary form) with the major", + "components (compiler, kernel, and so on) of the operating system on", + "which the executable runs, unless that component itself accompanies", + "the executable.", + "", + " It may happen that this requirement contradicts the license", + "restrictions of other proprietary libraries that do not normally", + "accompany the operating system. Such a contradiction means you cannot", + "use both them and the Library together in an executable that you", + "distribute.", + "", + " 7. You may place library facilities that are a work based on the", + "Library side-by-side in a single library together with other library", + "facilities not covered by this License, and distribute such a combined", + "library, provided that the separate distribution of the work based on", + "the Library and of the other library facilities is otherwise", + "permitted, and provided that you do these two things:", + "", + " a) Accompany the combined library with a copy of the same work", + " based on the Library, uncombined with any other library", + " facilities. This must be distributed under the terms of the", + " Sections above.", + "", + " b) Give prominent notice with the combined library of the fact", + " that part of it is a work based on the Library, and explaining", + " where to find the accompanying uncombined form of the same work.", + "", + " 8. You may not copy, modify, sublicense, link with, or distribute", + "the Library except as expressly provided under this License. Any", + "attempt otherwise to copy, modify, sublicense, link with, or", + "distribute the Library is void, and will automatically terminate your", + "rights under this License. However, parties who have received copies,", + "or rights, from you under this License will not have their licenses", + "terminated so long as such parties remain in full compliance.", + "", + " 9. You are not required to accept this License, since you have not", + "signed it. However, nothing else grants you permission to modify or", + "distribute the Library or its derivative works. These actions are", + "prohibited by law if you do not accept this License. Therefore, by", + "modifying or distributing the Library (or any work based on the", + "Library), you indicate your acceptance of this License to do so, and", + "all its terms and conditions for copying, distributing or modifying", + "the Library or works based on it.", + "", + " 10. Each time you redistribute the Library (or any work based on the", + "Library), the recipient automatically receives a license from the", + "original licensor to copy, distribute, link with or modify the Library", + "subject to these terms and conditions. You may not impose any further", + "restrictions on the recipients' exercise of the rights granted herein.", + "You are not responsible for enforcing compliance by third parties with", + "this License.", + "", + " 11. If, as a consequence of a court judgment or allegation of patent", + "infringement or for any other reason (not limited to patent issues),", + "conditions are imposed on you (whether by court order, agreement or", + "otherwise) that contradict the conditions of this License, they do not", + "excuse you from the conditions of this License. If you cannot", + "distribute so as to satisfy simultaneously your obligations under this", + "License and any other pertinent obligations, then as a consequence you", + "may not distribute the Library at all. For example, if a patent", + "license would not permit royalty-free redistribution of the Library by", + "all those who receive copies directly or indirectly through you, then", + "the only way you could satisfy both it and this License would be to", + "refrain entirely from distribution of the Library.", + "", + "If any portion of this section is held invalid or unenforceable under any", + "particular circumstance, the balance of the section is intended to apply,", + "and the section as a whole is intended to apply in other circumstances.", + "", + "It is not the purpose of this section to induce you to infringe any", + "patents or other property right claims or to contest validity of any", + "such claims; this section has the sole purpose of protecting the", + "integrity of the free software distribution system which is", + "implemented by public license practices. Many people have made", + "generous contributions to the wide range of software distributed", + "through that system in reliance on consistent application of that", + "system; it is up to the author/donor to decide if he or she is willing", + "to distribute software through any other system and a licensee cannot", + "impose that choice.", + "", + "This section is intended to make thoroughly clear what is believed to", + "be a consequence of the rest of this License.", + "", + " 12. If the distribution and/or use of the Library is restricted in", + "certain countries either by patents or by copyrighted interfaces, the", + "original copyright holder who places the Library under this License may add", + "an explicit geographical distribution limitation excluding those countries,", + "so that distribution is permitted only in or among countries not thus", + "excluded. In such case, this License incorporates the limitation as if", + "written in the body of this License.", + "", + " 13. The Free Software Foundation may publish revised and/or new", + "versions of the Lesser General Public License from time to time.", + "Such new versions will be similar in spirit to the present version,", + "but may differ in detail to address new problems or concerns.", + "", + "Each version is given a distinguishing version number. If the Library", + "specifies a version number of this License which applies to it and", + "\"any later version\", you have the option of following the terms and", + "conditions either of that version or of any later version published by", + "the Free Software Foundation. If the Library does not specify a", + "license version number, you may choose any version ever published by", + "the Free Software Foundation.", + "", + " 14. If you wish to incorporate parts of the Library into other free", + "programs whose distribution conditions are incompatible with these,", + "write to the author to ask for permission. For software which is", + "copyrighted by the Free Software Foundation, write to the Free", + "Software Foundation; we sometimes make exceptions for this. Our", + "decision will be guided by the two goals of preserving the free status", + "of all derivatives of our free software and of promoting the sharing", + "and reuse of software generally.", + "", + "\t\t\t NO WARRANTY", + "", + " 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO", + "WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.", + "EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR", + "OTHER PARTIES PROVIDE THE LIBRARY \"AS IS\" WITHOUT WARRANTY OF ANY", + "KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE", + "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR", + "PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE", + "LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME", + "THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.", + "", + " 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN", + "WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY", + "AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU", + "FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR", + "CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE", + "LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING", + "RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A", + "FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF", + "SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH", + "DAMAGES.", + "", + "\t\t END OF TERMS AND CONDITIONS", + "", + " How to Apply These Terms to Your New Libraries", + "", + " If you develop a new library, and you want it to be of the greatest", + "possible use to the public, we recommend making it free software that", + "everyone can redistribute and change. You can do so by permitting", + "redistribution under these terms (or, alternatively, under the terms of the", + "ordinary General Public License).", + "", + " To apply these terms, attach the following notices to the library. It is", + "safest to attach them to the start of each source file to most effectively", + "convey the exclusion of warranty; and each file should have at least the", + "\"copyright\" line and a pointer to where the full notice is found.", + "", + " ", + " Copyright (C) ", + "", + " This library is free software; you can redistribute it and/or", + " modify it under the terms of the GNU Lesser General Public", + " License as published by the Free Software Foundation; either", + " version 2.1 of the License, or (at your option) any later version.", + "", + " This library is distributed in the hope that it will be useful,", + " but WITHOUT ANY WARRANTY; without even the implied warranty of", + " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU", + " Lesser General Public License for more details.", + "", + " You should have received a copy of the GNU Lesser General Public", + " License along with this library; if not, write to the Free Software", + " Foundation, Inc.,", + "51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA", + "", + "Also add information on how to contact you by electronic and paper mail.", + "", + "You should also get your employer (if you work as a programmer) or your", + "school, if any, to sign a \"copyright disclaimer\" for the library, if", + "necessary. Here is a sample; alter the names:", + "", + " Yoyodyne, Inc., hereby disclaims all copyright interest in the", + " library `Frob' (a library for tweaking knobs) written by James Random Hacker.", + "", + " ,", + "1 April 1990", + " Ty Coon, President of Vice", + "", + "That's all there is to it!" + ] + }, + { + // 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 + "name": "@types/node", + "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." + ] + }, + { + // 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 + "name": "typescript", + "licenseDetail": [ + "Copyright (c) Microsoft Corporation. All rights reserved.", + "", + "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" + ] + }, + { + // This module comes in from https://github.com/Microsoft/vscode-node-debug2/blob/master/package-lock.json + "name": "@types/source-map", + "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." + ] + }, + { + "name": "tunnel-agent", + "licenseDetail": [ + "Copyright (c) tunnel-agent authors", + "", + "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" + ] + }, + { + // Waiting for https://github.com/segmentio/noop-logger/issues/2 + "name": "noop-logger", + "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." + ] + }, + { + "name": "xterm-addon-search", + "licenseDetail": [ + "Copyright (c) 2017, The xterm.js authors (https://github.com/xtermjs/xterm.js)", + "", + "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": "xterm-addon-web-links", + "licenseDetail": [ + "Copyright (c) 2017, The xterm.js authors (https://github.com/xtermjs/xterm.js)", + "", + "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/cgmanifest.json b/cgmanifest.json index 01cdd8df5f1..b8c4f3bfefc 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "chromium", "repositoryUrl": "https://chromium.googlesource.com/chromium/src", - "commitHash": "164c37e3f235134c88e80fac2a182cfba3f07f00" + "commitHash": "c6a08e5368de4352903e702cde750b33239a50ab" } }, "licenseDetail": [ @@ -40,20 +40,7 @@ "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ], "isOnlyProductionDependency": true, - "version": "66.0.3359.181" - }, - { - "component": { - "type": "git", - "git": { - "name": "libchromiumcontent", - "repositoryUrl": "https://github.com/electron/libchromiumcontent", - "commitHash": "7ea271f92018b1eeb8e70ec6de8c29f9758a0c05" - } - }, - "isOnlyProductionDependency": true, - "license": "MIT", - "version": "66.0.3359.181" + "version": "69.0.3497.128" }, { "component": { @@ -61,11 +48,11 @@ "git": { "name": "nodejs", "repositoryUrl": "https://github.com/nodejs/node", - "commitHash": "5cbb905c1af7cea2d709932d59827d7c6d03ef4a" + "commitHash": "8c70b2084ce5f76ea1e3b3c4ccdeee4483fe338b" } }, "isOnlyProductionDependency": true, - "version": "10.2.0" + "version": "10.11.0" }, { "component": { @@ -73,12 +60,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "e84a6860e35e14b4031b88bb9b49841cdb89a305" + "commitHash": "5d67ec3da5376a5058990e8a9557bc9124ad59a8" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "3.1.8" + "version": "4.2.5" }, { "component": { @@ -111,11 +98,11 @@ "git": { "name": "vscode-octicons-font", "repositoryUrl": "https://github.com/Microsoft/vscode-octicons-font", - "commitHash": "4f69de3a233ed501c2098e33047e116ac2fbbf42" + "commitHash": "415cd5b42ab699b6b46c0bf011ada0a2ae50bfb4" } }, "license": "MIT", - "version": "1.1.0" + "version": "1.3.1" }, { "component": { diff --git a/extensions/bat/package.json b/extensions/bat/package.json index c54d1a4ce5d..00bd84e4ae4 100644 --- a/extensions/bat/package.json +++ b/extensions/bat/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, "scripts": { "update-grammar": "node ../../build/npm/update-grammar.js mmims/language-batchfile grammars/batchfile.cson ./syntaxes/batchfile.tmLanguage.json" diff --git a/extensions/clojure/cgmanifest.json b/extensions/clojure/cgmanifest.json index 1a9d69c5633..3a72fefb369 100644 --- a/extensions/clojure/cgmanifest.json +++ b/extensions/clojure/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "atom/language-clojure", "repositoryUrl": "https://github.com/atom/language-clojure", - "commitHash": "ecc790326bc8e14220e4d2d72a392a30876c3219" + "commitHash": "de877502aa4a77ccdc2c7f0c9180436aea3effff" } }, "license": "MIT", - "version": "0.22.6", + "version": "0.22.7", "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/package.json b/extensions/clojure/package.json index 769b9f46798..3342784348b 100644 --- a/extensions/clojure/package.json +++ b/extensions/clojure/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, "scripts": { "update-grammar": "node ../../build/npm/update-grammar.js atom/language-clojure grammars/clojure.cson ./syntaxes/clojure.tmLanguage.json" diff --git a/extensions/clojure/syntaxes/clojure.tmLanguage.json b/extensions/clojure/syntaxes/clojure.tmLanguage.json index 3d059513af0..29c25edfa24 100644 --- a/extensions/clojure/syntaxes/clojure.tmLanguage.json +++ b/extensions/clojure/syntaxes/clojure.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-clojure/commit/ecc790326bc8e14220e4d2d72a392a30876c3219", + "version": "https://github.com/atom/language-clojure/commit/de877502aa4a77ccdc2c7f0c9180436aea3effff", "name": "Clojure", "scopeName": "source.clojure", "patterns": [ @@ -83,7 +83,7 @@ "name": "constant.numeric.ratio.clojure" }, { - "match": "(-?\\d+[rR][0-9a-zA-Z]+)", + "match": "(-?\\d+[rR]\\w+)", "name": "constant.numeric.arbitrary-radix.clojure" }, { @@ -116,17 +116,17 @@ ] }, "keyword": { - "match": "(?<=(\\s|\\(|\\[|\\{)):[a-zA-Z0-9\\#\\.\\-\\_\\:\\+\\=\\>\\<\\/\\!\\?\\*]+(?=(\\s|\\)|\\]|\\}|\\,))", + "match": "(?<=(\\s|\\(|\\[|\\{)):[\\w\\#\\.\\-\\_\\:\\+\\=\\>\\<\\/\\!\\?\\*]+(?=(\\s|\\)|\\]|\\}|\\,))", "name": "constant.keyword.clojure" }, "keyfn": { "patterns": [ { - "match": "(?<=(\\s|\\(|\\[|\\{))(if(-[-a-z\\?]*)?|when(-[-a-z]*)?|for(-[-a-z]*)?|cond|do|let(-[-a-z\\?]*)?|binding|loop|recur|fn|throw[a-z\\-]*|try|catch|finally|([a-z]*case))(?=(\\s|\\)|\\]|\\}))", + "match": "(?<=(\\s|\\(|\\[|\\{))(if(-[-\\p{Ll}\\?]*)?|when(-[-\\p{Ll}]*)?|for(-[-\\p{Ll}]*)?|cond|do|let(-[-\\p{Ll}\\?]*)?|binding|loop|recur|fn|throw[\\p{Ll}\\-]*|try|catch|finally|([\\p{Ll}]*case))(?=(\\s|\\)|\\]|\\}))", "name": "storage.control.clojure" }, { - "match": "(?<=(\\s|\\(|\\[|\\{))(declare-?|(in-)?ns|import|use|require|load|compile|(def[a-z\\-]*))(?=(\\s|\\)|\\]|\\}))", + "match": "(?<=(\\s|\\(|\\[|\\{))(declare-?|(in-)?ns|import|use|require|load|compile|(def[\\p{Ll}\\-]*))(?=(\\s|\\)|\\]|\\}))", "name": "keyword.control.clojure" } ] @@ -309,7 +309,7 @@ "include": "#dynamic-variables" }, { - "match": "([a-zA-Z\\.\\-\\_\\+\\=\\>\\<\\!\\?\\*][\\w\\.\\-\\_\\:\\+\\=\\>\\<\\!\\?\\*\\d]*)", + "match": "([\\p{L}\\.\\-\\_\\+\\=\\>\\<\\!\\?\\*][\\w\\.\\-\\_\\:\\+\\=\\>\\<\\!\\?\\*\\d]*)", "name": "entity.global.clojure" }, { @@ -387,7 +387,7 @@ "namespace-symbol": { "patterns": [ { - "match": "([a-zA-Z\\.\\-\\_\\+\\=\\>\\<\\!\\?\\*][\\w\\.\\-\\_\\:\\+\\=\\>\\<\\!\\?\\*\\d]*)/", + "match": "([\\p{L}\\.\\-\\_\\+\\=\\>\\<\\!\\?\\*][\\w\\.\\-\\_\\:\\+\\=\\>\\<\\!\\?\\*\\d]*)/", "captures": { "1": { "name": "meta.symbol.namespace.clojure" @@ -399,13 +399,13 @@ "symbol": { "patterns": [ { - "match": "([a-zA-Z\\.\\-\\_\\+\\=\\>\\<\\!\\?\\*][\\w\\.\\-\\_\\:\\+\\=\\>\\<\\!\\?\\*\\d]*)", + "match": "([\\p{L}\\.\\-\\_\\+\\=\\>\\<\\!\\?\\*][\\w\\.\\-\\_\\:\\+\\=\\>\\<\\!\\?\\*\\d]*)", "name": "meta.symbol.clojure" } ] }, "var": { - "match": "(?<=(\\s|\\(|\\[|\\{)\\#)'[a-zA-Z0-9\\.\\-\\_\\:\\+\\=\\>\\<\\/\\!\\?\\*]+(?=(\\s|\\)|\\]|\\}))", + "match": "(?<=(\\s|\\(|\\[|\\{)\\#)'[\\w\\.\\-\\_\\:\\+\\=\\>\\<\\/\\!\\?\\*]+(?=(\\s|\\)|\\]|\\}))", "name": "meta.var.clojure" }, "vector": { diff --git a/extensions/coffeescript/package.json b/extensions/coffeescript/package.json index 8d9293a8852..df5f8ff7c1f 100644 --- a/extensions/coffeescript/package.json +++ b/extensions/coffeescript/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, "scripts": { "update-grammar": "node ../../build/npm/update-grammar.js atom/language-coffee-script grammars/coffeescript.cson ./syntaxes/coffeescript.tmLanguage.json" diff --git a/extensions/configuration-editing/package.json b/extensions/configuration-editing/package.json index bb9690a07d4..e555d61457e 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "^1.0.0" }, @@ -96,10 +97,18 @@ { "fileMatch": "/.vscode/extensions.json", "url": "vscode://schemas/extensions" + }, + { + "fileMatch": "/.devcontainer/devcontainer.json", + "url": "./schemas/devContainer.schema.json" + }, + { + "fileMatch": "/.devcontainer.json", + "url": "./schemas/devContainer.schema.json" } ] }, "devDependencies": { - "@types/node": "^10.12.21" + "@types/node": "^10.14.8" } } diff --git a/extensions/configuration-editing/schemas/devContainer.schema.json b/extensions/configuration-editing/schemas/devContainer.schema.json new file mode 100644 index 00000000000..55950a9021e --- /dev/null +++ b/extensions/configuration-editing/schemas/devContainer.schema.json @@ -0,0 +1,181 @@ +{ + "$schema": "http://json-schema.org/schema#", + "description": "Defines a dev container", + "allowComments": true, + "type": "object", + "definitions": { + "devContainerCommon": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "A name to show for the workspace folder." + }, + "extensions": { + "type": "array", + "description": "An array of extensions that should be installed into the container.", + "items": { + "type": "string" + } + }, + "settings": { + "$ref": "vscode://schemas/settings/machine", + "description": "Machine specific settings that should be copied into the container." + }, + "postCreateCommand": { + "type": [ + "string", + "array" + ], + "description": "A command to run after creating the container. If this is a single string, it will be run in a shell. If this is an array of strings, it will be run as a single command without shell.", + "items": { + "type": "string" + } + }, + "devPort": { + "type": "integer", + "description": "The port VS Code can use to connect to its backend." + } + } + }, + "nonComposeBase": { + "type": "object", + "properties": { + "appPort": { + "type": [ + "integer", + "string", + "array" + ], + "description": "Application ports that are exposed by the container. This can be a single port or an array of ports. Each port can be a number or a string. A number is mapped to the same port on the host. A string is passed to Docker unchanged and can be used to map ports differently, e.g. \"8000:8010\".", + "items": { + "type": [ + "integer", + "string" + ] + } + }, + "runArgs": { + "type": "array", + "description": "The arguments required when starting in the container.", + "items": { + "type": "string" + } + }, + "shutdownAction": { + "type": "string", + "enum": [ + "none", + "stopContainer" + ], + "description": "Action to take when VS Code is shutting down. The default is to stop the container." + }, + "overrideCommand": { + "type": "boolean", + "description": "Whether to overwrite the command specified in the image. The default is true." + }, + "workspaceFolder": { + "type": "string", + "description": "The path of the workspace folder inside the container." + }, + "workspaceMount": { + "type": "string", + "description": "The --mount parameter for docker run. The default is to mount the project folder at /workspaces/$project." + } + } + }, + "dockerFileContainer": { + "type": "object", + "properties": { + "dockerFile": { + "type": "string", + "description": "The location of the Dockerfile that defines the contents of the container. The path is relative to the folder containing the `devcontainer.json` file." + }, + "context": { + "type": "string", + "description": "The location of the context folder for building the Docker image. The path is relative to the folder containing the `devcontainer.json` file." + } + }, + "required": [ + "dockerFile" + ] + }, + "imageContainer": { + "type": "object", + "properties": { + "image": { + "type": "string", + "description": "The docker image that will be used to create the container." + } + }, + "required": [ + "image" + ] + }, + "composeContainer": { + "type": "object", + "properties": { + "dockerComposeFile": { + "type": [ + "string", + "array" + ], + "description": "The name of the docker-compose file(s) used to start the services.", + "items": { + "type": "string" + } + }, + "service": { + "type": "string", + "description": "The service you want to work on." + }, + "workspaceFolder": { + "type": "string", + "description": "The path of the workspace folder inside the container." + }, + "shutdownAction": { + "type": "string", + "enum": [ + "none", + "stopCompose" + ], + "description": "Action to take when VS Code is shutting down. The default is to stop the containers." + } + }, + "required": [ + "dockerComposeFile", + "service", + "workspaceFolder" + ] + } + }, + "allOf": [ + { + "oneOf": [ + { + "allOf": [ + { + "oneOf": [ + { + "$ref": "#/definitions/dockerFileContainer" + }, + { + "$ref": "#/definitions/imageContainer" + } + ] + }, + { + "$ref": "#/definitions/nonComposeBase" + } + ] + }, + { + "$ref": "#/definitions/composeContainer" + } + ] + }, + { + "$ref": "#/definitions/devContainerCommon" + } + ] +} \ No newline at end of file diff --git a/extensions/configuration-editing/yarn.lock b/extensions/configuration-editing/yarn.lock index 49c47166066..ce653f097a9 100644 --- a/extensions/configuration-editing/yarn.lock +++ b/extensions/configuration-editing/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@^10.12.21": - version "10.12.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" - integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== +"@types/node@^10.14.8": + version "10.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" + integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== jsonc-parser@2.0.2: version "2.0.2" diff --git a/extensions/cpp/cgmanifest.json b/extensions/cpp/cgmanifest.json index aba5bb7f100..90e88ff8230 100644 --- a/extensions/cpp/cgmanifest.json +++ b/extensions/cpp/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "jeff-hykin/cpp-textmate-grammar", "repositoryUrl": "https://github.com/jeff-hykin/cpp-textmate-grammar", - "commitHash": "3fa2a8862b6a06ca381f8e46eb782e5dd014d426" + "commitHash": "ccdbfcae7454d7f00e9a6ebe0c3df3ab4e19e33b" } }, "license": "MIT", - "version": "1.8.8", + "version": "1.11.7", "description": "The files syntaxes/c.json and syntaxes/c++.json were derived from https://github.com/atom/language-c which was originally converted from the C TextMate bundle https://github.com/textmate/c.tmbundle." }, { diff --git a/extensions/cpp/language-configuration.json b/extensions/cpp/language-configuration.json index e50df32dd11..b0b1de59d45 100644 --- a/extensions/cpp/language-configuration.json +++ b/extensions/cpp/language-configuration.json @@ -13,7 +13,8 @@ { "open": "{", "close": "}" }, { "open": "(", "close": ")" }, { "open": "'", "close": "'", "notIn": ["string", "comment"] }, - { "open": "\"", "close": "\"", "notIn": ["string"] } + { "open": "\"", "close": "\"", "notIn": ["string"] }, + { "open": "/*", "close": " */", "notIn": ["string", "comment"] } ], "surroundingPairs": [ ["{", "}"], diff --git a/extensions/cpp/package.json b/extensions/cpp/package.json index ff7ddb1023d..b4285b3b8c3 100644 --- a/extensions/cpp/package.json +++ b/extensions/cpp/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, "scripts": { "update-grammar": "node ./build/update-grammars.js" diff --git a/extensions/cpp/syntaxes/c.tmLanguage.json b/extensions/cpp/syntaxes/c.tmLanguage.json index e02a6d88391..1f7b3648b20 100644 --- a/extensions/cpp/syntaxes/c.tmLanguage.json +++ b/extensions/cpp/syntaxes/c.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/jeff-hykin/cpp-textmate-grammar/commit/98cbae6aca391825a7612825f9677f22fe70dd68", + "version": "https://github.com/jeff-hykin/cpp-textmate-grammar/commit/5a701cf1028d9c517fa2ee5210628935e0d7b19a", "name": "C", "scopeName": "source.c", "patterns": [ @@ -17,6 +17,9 @@ { "include": "#preprocessor-rule-conditional" }, + { + "include": "#predefined_macros" + }, { "include": "#comments" }, @@ -559,10 +562,6 @@ }, "name": "comment.block.c" }, - { - "match": "\\*/.*\\n", - "name": "invalid.illegal.stray-comment-end.c" - }, { "captures": { "1": { @@ -570,13 +569,13 @@ } }, "match": "^// =(\\s*.*?)\\s*=\\s*$\\n?", - "name": "comment.line.banner.cpp.c" + "name": "comment.line.banner.c" }, { "begin": "(^[ \\t]+)?(?=//)", "beginCaptures": { "1": { - "name": "punctuation.whitespace.comment.leading.cpp.c" + "name": "punctuation.whitespace.comment.leading.c" } }, "end": "(?!\\G)", @@ -585,11 +584,11 @@ "begin": "//", "beginCaptures": { "0": { - "name": "punctuation.definition.comment.cpp.c" + "name": "punctuation.definition.comment.c" } }, "end": "(?=\\n)", - "name": "comment.line.double-slash.cpp.c", + "name": "comment.line.double-slash.c", "patterns": [ { "include": "#line_continuation_character" @@ -624,14 +623,6 @@ } ] }, - "numbers": { - "patterns": [ - { - "match": "\\b((0(x|X)[0-9a-fA-F]([0-9a-fA-F']*[0-9a-fA-F])?)|(0(b|B)[01]([01']*[01])?)|(([0-9]([0-9']*[0-9])?\\.?[0-9]*([0-9']*[0-9])?)|(\\.[0-9]([0-9']*[0-9])?))((e|E)(\\+|-)?[0-9]([0-9']*[0-9])?)?)(L|l|UL|ul|u|U|F|f|ll|LL|ull|ULL)?\\b", - "name": "constant.numeric.c" - } - ] - }, "parens": { "name": "meta.parens.c", "begin": "\\(", @@ -672,7 +663,7 @@ }, { "match": "(?-mix:(?\\[\\]=]))", + "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "patterns": [ { - "include": "#switch_conditional_parentheses" - }, - { - "name": "meta.head.switch.cpp.c", - "begin": "\\G| ", - "end": "((?:\\{|(?=;)))", + "name": "meta.head.switch.c", + "begin": "\\G ?", + "end": "((?:\\{|<%|\\?\\?<|(?=;)))", "endCaptures": { "1": { - "name": "punctuation.section.block.begin.bracket.curly.switch.cpp.c" + "name": "punctuation.section.block.begin.bracket.curly.switch.c" } }, "patterns": [ @@ -2151,17 +2233,17 @@ "include": "#switch_conditional_parentheses" }, { - "include": "$base" + "include": "#root_context" } ] }, { - "name": "meta.body.switch.cpp.c", - "begin": "(?<=\\{)", - "end": "(\\})", + "name": "meta.body.switch.c", + "begin": "(?<=\\{|<%|\\?\\?<)", + "end": "(\\}|%>|\\?\\?>)", "endCaptures": { "1": { - "name": "punctuation.section.block.end.bracket.curly.switch.cpp.c" + "name": "punctuation.section.block.end.bracket.curly.switch.c" } }, "patterns": [ @@ -2172,17 +2254,20 @@ "include": "#case_statement" }, { - "include": "$base" + "include": "#root_context" + }, + { + "include": "#block_innards" } ] }, { - "name": "meta.tail.switch.cpp.c", - "begin": "(?<=})[\\s\\n]*", + "name": "meta.tail.switch.c", + "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", "end": "[\\s\\n]*(?=;)", "patterns": [ { - "include": "$base" + "include": "#root_context" } ] } @@ -2193,13 +2278,13 @@ "begin": "(\\()", "beginCaptures": { "1": { - "name": "punctuation.section.parens.begin.bracket.round.conditional.switch.cpp.c" + "name": "punctuation.section.parens.begin.bracket.round.conditional.switch.c" } }, "end": "(\\))", "endCaptures": { "1": { - "name": "punctuation.section.parens.end.bracket.round.conditional.switch.cpp.c" + "name": "punctuation.section.parens.end.bracket.round.conditional.switch.c" } }, "patterns": [ @@ -2208,15 +2293,72 @@ } ] }, + "static_assert": { + "begin": "(static_assert|_Static_assert)\\s*(\\()", + "beginCaptures": { + "1": { + "name": "keyword.other.static_assert.c" + }, + "2": { + "name": "punctuation.section.arguments.begin.bracket.round.c" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.section.arguments.end.bracket.round.c" + } + }, + "patterns": [ + { + "name": "meta.static_assert.message.c", + "begin": "(,)\\s*(?=(?:L|u8|u|U\\s*\\\")?)", + "beginCaptures": { + "1": { + "name": "comma.c punctuation.separator.delimiter.c" + } + }, + "end": "(?=\\))", + "patterns": [ + { + "include": "#string_context" + }, + { + "include": "#string_context_c" + } + ] + }, + { + "include": "#function_call_context" + } + ] + }, + "backslash_escapes": { + "match": "(?x)\\\\ (\n\\\\\t\t\t |\n[abefnprtv'\"?] |\n[0-3]\\d{,2}\t |\n[4-7]\\d?\t\t|\nx[a-fA-F0-9]{,2} |\nu[a-fA-F0-9]{,4} |\nU[a-fA-F0-9]{,8} )", + "name": "constant.character.escape.c" + }, "conditional_context": { "patterns": [ + { + "include": "$base" + }, + { + "include": "#block_innards" + } + ] + }, + "evalutation_context": { + "patterns": [ + { + "include": "#function-call-innards" + }, { "include": "$base" } ] }, "member_access": { - "match": "((?:[a-zA-Z_]\\w*|(?<=\\]|\\)))\\s*)(?:((?:\\.\\*|\\.))|((?:->\\*|->)))((?:[a-zA-Z_]\\w*\\s*(?-mix:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(\\b(?!(?:void|char|short|int|signed|unsigned|long|float|double|bool|_Bool|_Complex|_Imaginary|u_char|u_short|u_int|u_long|ushort|uint|u_quad_t|quad_t|qaddr_t|caddr_t|daddr_t|div_t|dev_t|fixpt_t|blkcnt_t|blksize_t|gid_t|in_addr_t|in_port_t|ino_t|key_t|mode_t|nlink_t|id_t|pid_t|off_t|segsz_t|swblk_t|uid_t|id_t|clock_t|size_t|ssize_t|time_t|useconds_t|suseconds_t|pthread_attr_t|pthread_cond_t|pthread_condattr_t|pthread_mutex_t|pthread_mutexattr_t|pthread_once_t|pthread_rwlock_t|pthread_rwlockattr_t|pthread_t|pthread_key_t|int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|int_least8_t|int_least16_t|int_least32_t|int_least64_t|uint_least8_t|uint_least16_t|uint_least32_t|uint_least64_t|int_fast8_t|int_fast16_t|int_fast32_t|int_fast64_t|uint_fast8_t|uint_fast16_t|uint_fast32_t|uint_fast64_t|intptr_t|uintptr_t|intmax_t|intmax_t|uintmax_t|uintmax_t|memory_order|atomic_bool|atomic_char|atomic_schar|atomic_uchar|atomic_short|atomic_ushort|atomic_int|atomic_uint|atomic_long|atomic_ulong|atomic_llong|atomic_ullong|atomic_char16_t|atomic_char32_t|atomic_wchar_t|atomic_int_least8_t|atomic_uint_least8_t|atomic_int_least16_t|atomic_uint_least16_t|atomic_int_least32_t|atomic_uint_least32_t|atomic_int_least64_t|atomic_uint_least64_t|atomic_int_fast8_t|atomic_uint_fast8_t|atomic_int_fast16_t|atomic_uint_fast16_t|atomic_int_fast32_t|atomic_uint_fast32_t|atomic_int_fast64_t|atomic_uint_fast64_t|atomic_intptr_t|atomic_uintptr_t|atomic_size_t|atomic_ptrdiff_t|atomic_intmax_t|atomic_uintmax_t))[a-zA-Z_]\\w*\\b(?!\\())", + "match": "((?:[a-zA-Z_]\\w*|(?<=\\]|\\)))\\s*)(?:((?:\\.\\*|\\.))|((?:->\\*|->)))((?:[a-zA-Z_]\\w*\\s*(?:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(\\b(?!(?:void|char|short|int|signed|unsigned|long|float|double|bool|_Bool|_Complex|_Imaginary|u_char|u_short|u_int|u_long|ushort|uint|u_quad_t|quad_t|qaddr_t|caddr_t|daddr_t|div_t|dev_t|fixpt_t|blkcnt_t|blksize_t|gid_t|in_addr_t|in_port_t|ino_t|key_t|mode_t|nlink_t|id_t|pid_t|off_t|segsz_t|swblk_t|uid_t|id_t|clock_t|size_t|ssize_t|time_t|useconds_t|suseconds_t|pthread_attr_t|pthread_cond_t|pthread_condattr_t|pthread_mutex_t|pthread_mutexattr_t|pthread_once_t|pthread_rwlock_t|pthread_rwlockattr_t|pthread_t|pthread_key_t|int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|int_least8_t|int_least16_t|int_least32_t|int_least64_t|uint_least8_t|uint_least16_t|uint_least32_t|uint_least64_t|int_fast8_t|int_fast16_t|int_fast32_t|int_fast64_t|uint_fast8_t|uint_fast16_t|uint_fast32_t|uint_fast64_t|intptr_t|uintptr_t|intmax_t|intmax_t|uintmax_t|uintmax_t|memory_order|atomic_bool|atomic_char|atomic_schar|atomic_uchar|atomic_short|atomic_ushort|atomic_int|atomic_uint|atomic_long|atomic_ulong|atomic_llong|atomic_ullong|atomic_char16_t|atomic_char32_t|atomic_wchar_t|atomic_int_least8_t|atomic_uint_least8_t|atomic_int_least16_t|atomic_uint_least16_t|atomic_int_least32_t|atomic_uint_least32_t|atomic_int_least64_t|atomic_uint_least64_t|atomic_int_fast8_t|atomic_uint_fast8_t|atomic_int_fast16_t|atomic_uint_fast16_t|atomic_int_fast32_t|atomic_uint_fast32_t|atomic_int_fast64_t|atomic_uint_fast64_t|atomic_intptr_t|atomic_uintptr_t|atomic_size_t|atomic_ptrdiff_t|atomic_intmax_t|atomic_uintmax_t))[a-zA-Z_]\\w*\\b(?!\\())", "captures": { "1": { "name": "variable.other.object.access.c" @@ -2257,8 +2399,8 @@ } }, "method_access": { - "contentName": "meta.function-call.member", - "begin": "((?:[a-zA-Z_]\\w*|(?<=\\]|\\)))\\s*)(?:((?:\\.\\*|\\.))|((?:->\\*|->)))((?:[a-zA-Z_]\\w*\\s*(?-mix:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*([a-zA-Z_]\\w*)(\\()", + "contentName": "meta.function-call.member.c", + "begin": "((?:[a-zA-Z_]\\w*|(?<=\\]|\\)))\\s*)(?:((?:\\.\\*|\\.))|((?:->\\*|->)))((?:[a-zA-Z_]\\w*\\s*(?:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*([a-zA-Z_]\\w*)(\\()", "beginCaptures": { "1": { "name": "variable.other.object.access.c" @@ -2311,6 +2453,1270 @@ "include": "#function-call-innards" } ] + }, + "predefined_macros": { + "patterns": [ + { + "match": "\\b__cplusplus\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__cplusplus.c" + }, + { + "match": "\\b__DATE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__DATE__.c" + }, + { + "match": "\\b__FILE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__FILE__.c" + }, + { + "match": "\\b__LINE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__LINE__.c" + }, + { + "match": "\\b__STDC__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__STDC__.c" + }, + { + "match": "\\b__STDC_HOSTED__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__STDC_HOSTED__.c" + }, + { + "match": "\\b__STDC_NO_COMPLEX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__STDC_NO_COMPLEX__.c" + }, + { + "match": "\\b__STDC_VERSION__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__STDC_VERSION__.c" + }, + { + "match": "\\b__STDCPP_THREADS__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__STDCPP_THREADS__.c" + }, + { + "match": "\\b__TIME__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__TIME__.c" + }, + { + "match": "\\bNDEBUG\\b", + "name": "entity.name.other.preprocessor.macro.predefined.NDEBUG.c" + }, + { + "match": "\\b__OBJC__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__OBJC__.c" + }, + { + "match": "\\b__ASSEMBLER__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__ASSEMBLER__.c" + }, + { + "match": "\\b__ATOM__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__ATOM__.c" + }, + { + "match": "\\b__AVX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__AVX__.c" + }, + { + "match": "\\b__AVX2__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__AVX2__.c" + }, + { + "match": "\\b_CHAR_UNSIGNED\\b", + "name": "entity.name.other.preprocessor.macro.predefined._CHAR_UNSIGNED.c" + }, + { + "match": "\\b__CLR_VER\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__CLR_VER.c" + }, + { + "match": "\\b_CONTROL_FLOW_GUARD\\b", + "name": "entity.name.other.preprocessor.macro.predefined._CONTROL_FLOW_GUARD.c" + }, + { + "match": "\\b__COUNTER__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__COUNTER__.c" + }, + { + "match": "\\b__cplusplus_cli\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__cplusplus_cli.c" + }, + { + "match": "\\b__cplusplus_winrt\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__cplusplus_winrt.c" + }, + { + "match": "\\b_CPPRTTI\\b", + "name": "entity.name.other.preprocessor.macro.predefined._CPPRTTI.c" + }, + { + "match": "\\b_CPPUNWIND\\b", + "name": "entity.name.other.preprocessor.macro.predefined._CPPUNWIND.c" + }, + { + "match": "\\b_DEBUG\\b", + "name": "entity.name.other.preprocessor.macro.predefined._DEBUG.c" + }, + { + "match": "\\b_DLL\\b", + "name": "entity.name.other.preprocessor.macro.predefined._DLL.c" + }, + { + "match": "\\b__FUNCDNAME__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__FUNCDNAME__.c" + }, + { + "match": "\\b__FUNCSIG__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__FUNCSIG__.c" + }, + { + "match": "\\b__FUNCTION__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__FUNCTION__.c" + }, + { + "match": "\\b_INTEGRAL_MAX_BITS\\b", + "name": "entity.name.other.preprocessor.macro.predefined._INTEGRAL_MAX_BITS.c" + }, + { + "match": "\\b__INTELLISENSE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INTELLISENSE__.c" + }, + { + "match": "\\b_ISO_VOLATILE\\b", + "name": "entity.name.other.preprocessor.macro.predefined._ISO_VOLATILE.c" + }, + { + "match": "\\b_KERNEL_MODE\\b", + "name": "entity.name.other.preprocessor.macro.predefined._KERNEL_MODE.c" + }, + { + "match": "\\b_M_AMD64\\b", + "name": "entity.name.other.preprocessor.macro.predefined._M_AMD64.c" + }, + { + "match": "\\b_M_ARM\\b", + "name": "entity.name.other.preprocessor.macro.predefined._M_ARM.c" + }, + { + "match": "\\b_M_ARM_ARMV7VE\\b", + "name": "entity.name.other.preprocessor.macro.predefined._M_ARM_ARMV7VE.c" + }, + { + "match": "\\b_M_ARM_FP\\b", + "name": "entity.name.other.preprocessor.macro.predefined._M_ARM_FP.c" + }, + { + "match": "\\b_M_ARM64\\b", + "name": "entity.name.other.preprocessor.macro.predefined._M_ARM64.c" + }, + { + "match": "\\b_M_CEE\\b", + "name": "entity.name.other.preprocessor.macro.predefined._M_CEE.c" + }, + { + "match": "\\b_M_CEE_PURE\\b", + "name": "entity.name.other.preprocessor.macro.predefined._M_CEE_PURE.c" + }, + { + "match": "\\b_M_CEE_SAFE\\b", + "name": "entity.name.other.preprocessor.macro.predefined._M_CEE_SAFE.c" + }, + { + "match": "\\b_M_FP_EXCEPT\\b", + "name": "entity.name.other.preprocessor.macro.predefined._M_FP_EXCEPT.c" + }, + { + "match": "\\b_M_FP_FAST\\b", + "name": "entity.name.other.preprocessor.macro.predefined._M_FP_FAST.c" + }, + { + "match": "\\b_M_FP_PRECISE\\b", + "name": "entity.name.other.preprocessor.macro.predefined._M_FP_PRECISE.c" + }, + { + "match": "\\b_M_FP_STRICT\\b", + "name": "entity.name.other.preprocessor.macro.predefined._M_FP_STRICT.c" + }, + { + "match": "\\b_M_IX86\\b", + "name": "entity.name.other.preprocessor.macro.predefined._M_IX86.c" + }, + { + "match": "\\b_M_IX86_FP\\b", + "name": "entity.name.other.preprocessor.macro.predefined._M_IX86_FP.c" + }, + { + "match": "\\b_M_X64\\b", + "name": "entity.name.other.preprocessor.macro.predefined._M_X64.c" + }, + { + "match": "\\b_MANAGED\\b", + "name": "entity.name.other.preprocessor.macro.predefined._MANAGED.c" + }, + { + "match": "\\b_MSC_BUILD\\b", + "name": "entity.name.other.preprocessor.macro.predefined._MSC_BUILD.c" + }, + { + "match": "\\b_MSC_EXTENSIONS\\b", + "name": "entity.name.other.preprocessor.macro.predefined._MSC_EXTENSIONS.c" + }, + { + "match": "\\b_MSC_FULL_VER\\b", + "name": "entity.name.other.preprocessor.macro.predefined._MSC_FULL_VER.c" + }, + { + "match": "\\b_MSC_VER\\b", + "name": "entity.name.other.preprocessor.macro.predefined._MSC_VER.c" + }, + { + "match": "\\b_MSVC_LANG\\b", + "name": "entity.name.other.preprocessor.macro.predefined._MSVC_LANG.c" + }, + { + "match": "\\b__MSVC_RUNTIME_CHECKS\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__MSVC_RUNTIME_CHECKS.c" + }, + { + "match": "\\b_MT\\b", + "name": "entity.name.other.preprocessor.macro.predefined._MT.c" + }, + { + "match": "\\b_NATIVE_WCHAR_T_DEFINED\\b", + "name": "entity.name.other.preprocessor.macro.predefined._NATIVE_WCHAR_T_DEFINED.c" + }, + { + "match": "\\b_OPENMP\\b", + "name": "entity.name.other.preprocessor.macro.predefined._OPENMP.c" + }, + { + "match": "\\b_PREFAST\\b", + "name": "entity.name.other.preprocessor.macro.predefined._PREFAST.c" + }, + { + "match": "\\b__TIMESTAMP__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__TIMESTAMP__.c" + }, + { + "match": "\\b_VC_NO_DEFAULTLIB\\b", + "name": "entity.name.other.preprocessor.macro.predefined._VC_NO_DEFAULTLIB.c" + }, + { + "match": "\\b_WCHAR_T_DEFINED\\b", + "name": "entity.name.other.preprocessor.macro.predefined._WCHAR_T_DEFINED.c" + }, + { + "match": "\\b_WIN32\\b", + "name": "entity.name.other.preprocessor.macro.predefined._WIN32.c" + }, + { + "match": "\\b_WIN64\\b", + "name": "entity.name.other.preprocessor.macro.predefined._WIN64.c" + }, + { + "match": "\\b_WINRT_DLL\\b", + "name": "entity.name.other.preprocessor.macro.predefined._WINRT_DLL.c" + }, + { + "match": "\\b_ATL_VER\\b", + "name": "entity.name.other.preprocessor.macro.predefined._ATL_VER.c" + }, + { + "match": "\\b_MFC_VER\\b", + "name": "entity.name.other.preprocessor.macro.predefined._MFC_VER.c" + }, + { + "match": "\\b__GFORTRAN__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__GFORTRAN__.c" + }, + { + "match": "\\b__GNUC__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__GNUC__.c" + }, + { + "match": "\\b__GNUC_MINOR__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__GNUC_MINOR__.c" + }, + { + "match": "\\b__GNUC_PATCHLEVEL__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__GNUC_PATCHLEVEL__.c" + }, + { + "match": "\\b__GNUG__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__GNUG__.c" + }, + { + "match": "\\b__STRICT_ANSI__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__STRICT_ANSI__.c" + }, + { + "match": "\\b__BASE_FILE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__BASE_FILE__.c" + }, + { + "match": "\\b__INCLUDE_LEVEL__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INCLUDE_LEVEL__.c" + }, + { + "match": "\\b__ELF__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__ELF__.c" + }, + { + "match": "\\b__VERSION__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__VERSION__.c" + }, + { + "match": "\\b__OPTIMIZE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__OPTIMIZE__.c" + }, + { + "match": "\\b__OPTIMIZE_SIZE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__OPTIMIZE_SIZE__.c" + }, + { + "match": "\\b__NO_INLINE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__NO_INLINE__.c" + }, + { + "match": "\\b__GNUC_STDC_INLINE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__GNUC_STDC_INLINE__.c" + }, + { + "match": "\\b__CHAR_UNSIGNED__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__CHAR_UNSIGNED__.c" + }, + { + "match": "\\b__WCHAR_UNSIGNED__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__WCHAR_UNSIGNED__.c" + }, + { + "match": "\\b__REGISTER_PREFIX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__REGISTER_PREFIX__.c" + }, + { + "match": "\\b__REGISTER_PREFIX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__REGISTER_PREFIX__.c" + }, + { + "match": "\\b__SIZE_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SIZE_TYPE__.c" + }, + { + "match": "\\b__PTRDIFF_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__PTRDIFF_TYPE__.c" + }, + { + "match": "\\b__WCHAR_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__WCHAR_TYPE__.c" + }, + { + "match": "\\b__WINT_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__WINT_TYPE__.c" + }, + { + "match": "\\b__INTMAX_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INTMAX_TYPE__.c" + }, + { + "match": "\\b__UINTMAX_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINTMAX_TYPE__.c" + }, + { + "match": "\\b__SIG_ATOMIC_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SIG_ATOMIC_TYPE__.c" + }, + { + "match": "\\b__INT8_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT8_TYPE__.c" + }, + { + "match": "\\b__INT16_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT16_TYPE__.c" + }, + { + "match": "\\b__INT32_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT32_TYPE__.c" + }, + { + "match": "\\b__INT64_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT64_TYPE__.c" + }, + { + "match": "\\b__UINT8_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT8_TYPE__.c" + }, + { + "match": "\\b__UINT16_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT16_TYPE__.c" + }, + { + "match": "\\b__UINT32_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT32_TYPE__.c" + }, + { + "match": "\\b__UINT64_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT64_TYPE__.c" + }, + { + "match": "\\b__INT_LEAST8_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_LEAST8_TYPE__.c" + }, + { + "match": "\\b__INT_LEAST16_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_LEAST16_TYPE__.c" + }, + { + "match": "\\b__INT_LEAST32_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_LEAST32_TYPE__.c" + }, + { + "match": "\\b__INT_LEAST64_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_LEAST64_TYPE__.c" + }, + { + "match": "\\b__UINT_LEAST8_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT_LEAST8_TYPE__.c" + }, + { + "match": "\\b__UINT_LEAST16_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT_LEAST16_TYPE__.c" + }, + { + "match": "\\b__UINT_LEAST32_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT_LEAST32_TYPE__.c" + }, + { + "match": "\\b__UINT_LEAST64_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT_LEAST64_TYPE__.c" + }, + { + "match": "\\b__INT_FAST8_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_FAST8_TYPE__.c" + }, + { + "match": "\\b__INT_FAST16_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_FAST16_TYPE__.c" + }, + { + "match": "\\b__INT_FAST32_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_FAST32_TYPE__.c" + }, + { + "match": "\\b__INT_FAST64_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_FAST64_TYPE__.c" + }, + { + "match": "\\b__UINT_FAST8_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT_FAST8_TYPE__.c" + }, + { + "match": "\\b__UINT_FAST16_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT_FAST16_TYPE__.c" + }, + { + "match": "\\b__UINT_FAST32_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT_FAST32_TYPE__.c" + }, + { + "match": "\\b__UINT_FAST64_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT_FAST64_TYPE__.c" + }, + { + "match": "\\b__INTPTR_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INTPTR_TYPE__.c" + }, + { + "match": "\\b__UINTPTR_TYPE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINTPTR_TYPE__.c" + }, + { + "match": "\\b__CHAR_BIT__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__CHAR_BIT__.c" + }, + { + "match": "\\b__SCHAR_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SCHAR_MAX__.c" + }, + { + "match": "\\b__WCHAR_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__WCHAR_MAX__.c" + }, + { + "match": "\\b__SHRT_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SHRT_MAX__.c" + }, + { + "match": "\\b__INT_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_MAX__.c" + }, + { + "match": "\\b__LONG_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__LONG_MAX__.c" + }, + { + "match": "\\b__LONG_LONG_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__LONG_LONG_MAX__.c" + }, + { + "match": "\\b__WINT_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__WINT_MAX__.c" + }, + { + "match": "\\b__SIZE_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SIZE_MAX__.c" + }, + { + "match": "\\b__PTRDIFF_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__PTRDIFF_MAX__.c" + }, + { + "match": "\\b__INTMAX_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INTMAX_MAX__.c" + }, + { + "match": "\\b__UINTMAX_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINTMAX_MAX__.c" + }, + { + "match": "\\b__SIG_ATOMIC_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SIG_ATOMIC_MAX__.c" + }, + { + "match": "\\b__INT8_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT8_MAX__.c" + }, + { + "match": "\\b__INT16_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT16_MAX__.c" + }, + { + "match": "\\b__INT32_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT32_MAX__.c" + }, + { + "match": "\\b__INT64_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT64_MAX__.c" + }, + { + "match": "\\b__UINT8_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT8_MAX__.c" + }, + { + "match": "\\b__UINT16_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT16_MAX__.c" + }, + { + "match": "\\b__UINT32_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT32_MAX__.c" + }, + { + "match": "\\b__UINT64_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT64_MAX__.c" + }, + { + "match": "\\b__INT_LEAST8_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_LEAST8_MAX__.c" + }, + { + "match": "\\b__INT_LEAST16_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_LEAST16_MAX__.c" + }, + { + "match": "\\b__INT_LEAST32_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_LEAST32_MAX__.c" + }, + { + "match": "\\b__INT_LEAST64_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_LEAST64_MAX__.c" + }, + { + "match": "\\b__UINT_LEAST8_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT_LEAST8_MAX__.c" + }, + { + "match": "\\b__UINT_LEAST16_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT_LEAST16_MAX__.c" + }, + { + "match": "\\b__UINT_LEAST32_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT_LEAST32_MAX__.c" + }, + { + "match": "\\b__UINT_LEAST64_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT_LEAST64_MAX__.c" + }, + { + "match": "\\b__INT_FAST8_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_FAST8_MAX__.c" + }, + { + "match": "\\b__INT_FAST16_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_FAST16_MAX__.c" + }, + { + "match": "\\b__INT_FAST32_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_FAST32_MAX__.c" + }, + { + "match": "\\b__INT_FAST64_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_FAST64_MAX__.c" + }, + { + "match": "\\b__UINT_FAST8_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT_FAST8_MAX__.c" + }, + { + "match": "\\b__UINT_FAST16_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT_FAST16_MAX__.c" + }, + { + "match": "\\b__UINT_FAST32_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT_FAST32_MAX__.c" + }, + { + "match": "\\b__UINT_FAST64_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINT_FAST64_MAX__.c" + }, + { + "match": "\\b__INTPTR_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INTPTR_MAX__.c" + }, + { + "match": "\\b__UINTPTR_MAX__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__UINTPTR_MAX__.c" + }, + { + "match": "\\b__WCHAR_MIN__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__WCHAR_MIN__.c" + }, + { + "match": "\\b__WINT_MIN__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__WINT_MIN__.c" + }, + { + "match": "\\b__SIG_ATOMIC_MIN__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SIG_ATOMIC_MIN__.c" + }, + { + "match": "\\b__SCHAR_WIDTH__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SCHAR_WIDTH__.c" + }, + { + "match": "\\b__SHRT_WIDTH__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SHRT_WIDTH__.c" + }, + { + "match": "\\b__INT_WIDTH__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_WIDTH__.c" + }, + { + "match": "\\b__LONG_WIDTH__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__LONG_WIDTH__.c" + }, + { + "match": "\\b__LONG_LONG_WIDTH__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__LONG_LONG_WIDTH__.c" + }, + { + "match": "\\b__PTRDIFF_WIDTH__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__PTRDIFF_WIDTH__.c" + }, + { + "match": "\\b__SIG_ATOMIC_WIDTH__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SIG_ATOMIC_WIDTH__.c" + }, + { + "match": "\\b__SIZE_WIDTH__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SIZE_WIDTH__.c" + }, + { + "match": "\\b__WCHAR_WIDTH__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__WCHAR_WIDTH__.c" + }, + { + "match": "\\b__WINT_WIDTH__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__WINT_WIDTH__.c" + }, + { + "match": "\\b__INT_LEAST8_WIDTH__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_LEAST8_WIDTH__.c" + }, + { + "match": "\\b__INT_LEAST16_WIDTH__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_LEAST16_WIDTH__.c" + }, + { + "match": "\\b__INT_LEAST32_WIDTH__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_LEAST32_WIDTH__.c" + }, + { + "match": "\\b__INT_LEAST64_WIDTH__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_LEAST64_WIDTH__.c" + }, + { + "match": "\\b__INT_FAST8_WIDTH__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_FAST8_WIDTH__.c" + }, + { + "match": "\\b__INT_FAST16_WIDTH__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_FAST16_WIDTH__.c" + }, + { + "match": "\\b__INT_FAST32_WIDTH__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_FAST32_WIDTH__.c" + }, + { + "match": "\\b__INT_FAST64_WIDTH__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INT_FAST64_WIDTH__.c" + }, + { + "match": "\\b__INTPTR_WIDTH__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INTPTR_WIDTH__.c" + }, + { + "match": "\\b__INTMAX_WIDTH__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__INTMAX_WIDTH__.c" + }, + { + "match": "\\b__SIZEOF_INT__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SIZEOF_INT__.c" + }, + { + "match": "\\b__SIZEOF_LONG__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SIZEOF_LONG__.c" + }, + { + "match": "\\b__SIZEOF_LONG_LONG__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SIZEOF_LONG_LONG__.c" + }, + { + "match": "\\b__SIZEOF_SHORT__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SIZEOF_SHORT__.c" + }, + { + "match": "\\b__SIZEOF_POINTER__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SIZEOF_POINTER__.c" + }, + { + "match": "\\b__SIZEOF_FLOAT__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SIZEOF_FLOAT__.c" + }, + { + "match": "\\b__SIZEOF_DOUBLE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SIZEOF_DOUBLE__.c" + }, + { + "match": "\\b__SIZEOF_LONG_DOUBLE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SIZEOF_LONG_DOUBLE__.c" + }, + { + "match": "\\b__SIZEOF_SIZE_T__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SIZEOF_SIZE_T__.c" + }, + { + "match": "\\b__SIZEOF_WCHAR_T__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SIZEOF_WCHAR_T__.c" + }, + { + "match": "\\b__SIZEOF_WINT_T__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SIZEOF_WINT_T__.c" + }, + { + "match": "\\b__SIZEOF_PTRDIFF_T__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SIZEOF_PTRDIFF_T__.c" + }, + { + "match": "\\b__BYTE_ORDER__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__BYTE_ORDER__.c" + }, + { + "match": "\\b__ORDER_LITTLE_ENDIAN__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__ORDER_LITTLE_ENDIAN__.c" + }, + { + "match": "\\b__ORDER_BIG_ENDIAN__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__ORDER_BIG_ENDIAN__.c" + }, + { + "match": "\\b__ORDER_PDP_ENDIAN__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__ORDER_PDP_ENDIAN__.c" + }, + { + "match": "\\b__FLOAT_WORD_ORDER__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__FLOAT_WORD_ORDER__.c" + }, + { + "match": "\\b__DEPRECATED\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__DEPRECATED.c" + }, + { + "match": "\\b__EXCEPTIONS\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__EXCEPTIONS.c" + }, + { + "match": "\\b__GXX_RTTI\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__GXX_RTTI.c" + }, + { + "match": "\\b__USING_SJLJ_EXCEPTIONS__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__USING_SJLJ_EXCEPTIONS__.c" + }, + { + "match": "\\b__GXX_EXPERIMENTAL_CXX0X__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__GXX_EXPERIMENTAL_CXX0X__.c" + }, + { + "match": "\\b__GXX_WEAK__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__GXX_WEAK__.c" + }, + { + "match": "\\b__NEXT_RUNTIME__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__NEXT_RUNTIME__.c" + }, + { + "match": "\\b__LP64__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__LP64__.c" + }, + { + "match": "\\b_LP64\\b", + "name": "entity.name.other.preprocessor.macro.predefined._LP64.c" + }, + { + "match": "\\b__SSP__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SSP__.c" + }, + { + "match": "\\b__SSP_ALL__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SSP_ALL__.c" + }, + { + "match": "\\b__SSP_STRONG__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SSP_STRONG__.c" + }, + { + "match": "\\b__SSP_EXPLICIT__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SSP_EXPLICIT__.c" + }, + { + "match": "\\b__SANITIZE_ADDRESS__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SANITIZE_ADDRESS__.c" + }, + { + "match": "\\b__SANITIZE_THREAD__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__SANITIZE_THREAD__.c" + }, + { + "match": "\\b__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1.c" + }, + { + "match": "\\b__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2.c" + }, + { + "match": "\\b__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4.c" + }, + { + "match": "\\b__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8.c" + }, + { + "match": "\\b__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16.c" + }, + { + "match": "\\b__HAVE_SPECULATION_SAFE_VALUE\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__HAVE_SPECULATION_SAFE_VALUE.c" + }, + { + "match": "\\b__GCC_HAVE_DWARF2_CFI_ASM\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__GCC_HAVE_DWARF2_CFI_ASM.c" + }, + { + "match": "\\b__FP_FAST_FMA\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__FP_FAST_FMA.c" + }, + { + "match": "\\b__FP_FAST_FMAF\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__FP_FAST_FMAF.c" + }, + { + "match": "\\b__FP_FAST_FMAL\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__FP_FAST_FMAL.c" + }, + { + "match": "\\b__FP_FAST_FMAF16\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__FP_FAST_FMAF16.c" + }, + { + "match": "\\b__FP_FAST_FMAF32\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__FP_FAST_FMAF32.c" + }, + { + "match": "\\b__FP_FAST_FMAF64\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__FP_FAST_FMAF64.c" + }, + { + "match": "\\b__FP_FAST_FMAF128\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__FP_FAST_FMAF128.c" + }, + { + "match": "\\b__FP_FAST_FMAF32X\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__FP_FAST_FMAF32X.c" + }, + { + "match": "\\b__FP_FAST_FMAF64X\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__FP_FAST_FMAF64X.c" + }, + { + "match": "\\b__FP_FAST_FMAF128X\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__FP_FAST_FMAF128X.c" + }, + { + "match": "\\b__GCC_IEC_559\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__GCC_IEC_559.c" + }, + { + "match": "\\b__GCC_IEC_559_COMPLEX\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__GCC_IEC_559_COMPLEX.c" + }, + { + "match": "\\b__NO_MATH_ERRNO__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__NO_MATH_ERRNO__.c" + }, + { + "match": "\\b__has_builtin\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__has_builtin.c" + }, + { + "match": "\\b__has_feature\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__has_feature.c" + }, + { + "match": "\\b__has_extension\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__has_extension.c" + }, + { + "match": "\\b__has_cpp_attribute\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__has_cpp_attribute.c" + }, + { + "match": "\\b__has_c_attribute\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__has_c_attribute.c" + }, + { + "match": "\\b__has_attribute\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__has_attribute.c" + }, + { + "match": "\\b__has_declspec_attribute\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__has_declspec_attribute.c" + }, + { + "match": "\\b__is_identifier\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__is_identifier.c" + }, + { + "match": "\\b__has_include\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__has_include.c" + }, + { + "match": "\\b__has_include_next\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__has_include_next.c" + }, + { + "match": "\\b__has_warning\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__has_warning.c" + }, + { + "match": "\\b__BASE_FILE__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__BASE_FILE__.c" + }, + { + "match": "\\b__FILE_NAME__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__FILE_NAME__.c" + }, + { + "match": "\\b__clang__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__clang__.c" + }, + { + "match": "\\b__clang_major__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__clang_major__.c" + }, + { + "match": "\\b__clang_minor__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__clang_minor__.c" + }, + { + "match": "\\b__clang_patchlevel__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__clang_patchlevel__.c" + }, + { + "match": "\\b__clang_version__\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__clang_version__.c" + }, + { + "match": "\\b__fp16\\b", + "name": "entity.name.other.preprocessor.macro.predefined.__fp16.c" + }, + { + "match": "\\b_Float16\\b", + "name": "entity.name.other.preprocessor.macro.predefined._Float16.c" + }, + { + "match": "(\\b__([A-Z_])__\\b)", + "captures": { + "1": { + "name": "entity.name.other.preprocessor.macro.predefined.probably.$2.c" + } + } + } + ] + }, + "numbers": { + "begin": "(?\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "beginCaptures": { + "1": { + "name": "keyword.operator.functionlike.cpp keyword.other.decltype.cpp storage.type.decltype.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "6": { + "name": "punctuation.section.arguments.begin.bracket.round.decltype.cpp" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.section.arguments.end.bracket.round.decltype.cpp" + } + }, + "patterns": [ + { + "include": "#evaluation_context" + } + ] + }, "sizeof_operator": { - "contentName": "meta.arguments.operator.sizeof", - "begin": "((?\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.sizeof.cpp" }, "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "6": { "name": "punctuation.section.arguments.begin.bracket.round.operator.sizeof.cpp" } }, @@ -175,13 +92,29 @@ ] }, "alignof_operator": { - "contentName": "meta.arguments.operator.alignof", - "begin": "((?\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.alignof.cpp" }, "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "6": { "name": "punctuation.section.arguments.begin.bracket.round.operator.alignof.cpp" } }, @@ -198,13 +131,29 @@ ] }, "alignas_operator": { - "contentName": "meta.arguments.operator.alignas", - "begin": "((?\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.alignas.cpp" }, "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "6": { "name": "punctuation.section.arguments.begin.bracket.round.operator.alignas.cpp" } }, @@ -221,13 +170,29 @@ ] }, "typeid_operator": { - "contentName": "meta.arguments.operator.typeid", - "begin": "((?\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", "beginCaptures": { "1": { "name": "keyword.operator.functionlike.cpp keyword.operator.typeid.cpp" }, "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "6": { "name": "punctuation.section.arguments.begin.bracket.round.operator.typeid.cpp" } }, @@ -243,28 +208,19 @@ } ] }, - "decltype_specifier": { - "contentName": "meta.arguments.decltype", - "begin": "((?\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(\\()", + "beginCaptures": { + "1": { + "name": "keyword.operator.functionlike.cpp keyword.other.decltype.cpp storage.type.decltype.cpp" + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "6": { + "name": "punctuation.section.arguments.begin.bracket.round.decltype.cpp" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.section.arguments.end.bracket.round.decltype.cpp" + } + }, + "patterns": [ + { + "include": "#evaluation_context" + } + ] + }, + "using_name": { + "match": "(using)\\s+(?!namespace\\b)", + "captures": { + "1": { + "name": "keyword.other.using.directive.cpp" + } + } }, "functional_specifiers_pre_parameters": { "match": "(?\\[\\]=]))", + "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "patterns": [ - { - "include": "#switch_conditional_parentheses" - }, { "name": "meta.head.switch.cpp", - "begin": "\\G| ", - "end": "((?:\\{|(?=;)))", + "begin": "\\G ?", + "end": "((?:\\{|<%|\\?\\?<|(?=;)))", "endCaptures": { "1": { "name": "punctuation.section.block.begin.bracket.curly.switch.cpp" @@ -711,14 +1218,14 @@ "include": "#switch_conditional_parentheses" }, { - "include": "$base" + "include": "#root_context" } ] }, { "name": "meta.body.switch.cpp", - "begin": "(?<=\\{)", - "end": "(\\})", + "begin": "(?<=\\{|<%|\\?\\?<)", + "end": "(\\}|%>|\\?\\?>)", "endCaptures": { "1": { "name": "punctuation.section.block.end.bracket.curly.switch.cpp" @@ -732,31 +1239,34 @@ "include": "#case_statement" }, { - "include": "$base" + "include": "#root_context" + }, + { + "include": "#block_innards" } ] }, { "name": "meta.tail.switch.cpp", - "begin": "(?<=})[\\s\\n]*", + "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", "end": "[\\s\\n]*(?=;)", "patterns": [ { - "include": "$base" + "include": "#root_context" } ] } ] }, - "attributes": { + "cpp_attributes": { "name": "support.other.attribute.cpp", - "begin": "((?:\\[\\[|__attribute\\(\\(|__attribute__\\(\\(|__declspec\\())", + "begin": "(\\[\\[)", "beginCaptures": { "1": { "name": "punctuation.section.attribute.begin.cpp" } }, - "end": "((?:\\]\\]|\\)\\)|\\)))", + "end": "(\\]\\])", "endCaptures": { "1": { "name": "punctuation.section.attribute.end.cpp" @@ -764,14 +1274,14 @@ }, "patterns": [ { - "include": "#attributes" + "include": "#attributes_context" }, { "begin": "\\(", "end": "\\)", "patterns": [ { - "include": "#attributes" + "include": "#attributes_context" }, { "include": "#string_context_c" @@ -779,13 +1289,13 @@ ] }, { - "match": "(using)\\s+((?:,\\w])*>\\s*", + "match": "(?]*|[^>]*+<[^>]*+>)++>\\s*", "captures": { "0": { "name": "meta.template.call.cpp", "patterns": [ { - "include": "#template_call_context" + "include": "#template_call_range" } ] } @@ -849,7 +1548,7 @@ ] }, "template_isolated_definition": { - "match": "(?\\s*$)", + "match": "(?\\s*$)", "captures": { "1": { "name": "storage.type.template.cpp" @@ -913,7 +1612,7 @@ ] }, "template_argument_defaulted": { - "match": "(?<=<|,)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s+)*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*([=])", + "match": "(?<=<|,)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s+)*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*([=])", "captures": { "1": { "name": "storage.type.template.cpp" @@ -927,13 +1626,18 @@ } }, "template_definition_argument": { - "match": "(?:(?:\\s*((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)|((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s+)+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*))|((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(\\.\\.\\.)\\s*((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*))\\s*(?:(,)|(?=>|$))", + "match": "(?:(?:\\s*((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)|((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s+)+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))|((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*(\\.\\.\\.)\\s*((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*(?:(,)|(?=>|$))", "captures": { "1": { "name": "storage.type.template.argument.$1.cpp" }, "2": { - "name": "storage.type.template.argument.$2.cpp" + "patterns": [ + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "storage.type.template.argument.$0.cpp" + } + ] }, "3": { "name": "entity.name.type.template.cpp" @@ -953,34 +1657,875 @@ } }, "scope_resolution": { - "match": "((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s*(?:(?-mix:(?:<(?:[\\s<>:,\\w])*>\\s*)))?::)*\\s*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:(<(?:[\\s<>:,\\w])*>\\s*))?(::)", + "match": "(?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+", + "captures": { + "0": { + "patterns": [ + { + "include": "#scope_resolution_inner_generated" + } + ] + } + } + }, + "scope_resolution_inner_generated": { + "match": "((?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+((?]*|[^>]*+<[^>]*+>)++>\\s*)?(::)", "captures": { "1": { "patterns": [ { - "include": "#scope_resolution" + "include": "#scope_resolution_inner_generated" } ] }, "2": { - "name": "entity.name.type.namespace.scope-resolution.cpp" + "name": "entity.name.scope-resolution.cpp" }, "3": { "name": "meta.template.call.cpp", "patterns": [ { - "include": "#template_call_context" + "include": "#template_call_range" } ] }, "4": { - "name": "punctuation.separator.namespace.access.cpp" + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" + } + } + }, + "scope_resolution_template_call": { + "match": "(?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+", + "captures": { + "0": { + "patterns": [ + { + "include": "#scope_resolution_template_call_inner_generated" + } + ] + } + } + }, + "scope_resolution_template_call_inner_generated": { + "match": "((?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+((?]*|[^>]*+<[^>]*+>)++>\\s*)?(::)", + "captures": { + "1": { + "patterns": [ + { + "include": "#scope_resolution_template_call_inner_generated" + } + ] + }, + "2": { + "name": "entity.name.scope-resolution.template.call.cpp" + }, + "3": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_range" + } + ] + }, + "4": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp" + } + } + }, + "scope_resolution_template_definition": { + "match": "(?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+", + "captures": { + "0": { + "patterns": [ + { + "include": "#scope_resolution_template_definition_inner_generated" + } + ] + } + } + }, + "scope_resolution_template_definition_inner_generated": { + "match": "((?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+((?]*|[^>]*+<[^>]*+>)++>\\s*)?(::)", + "captures": { + "1": { + "patterns": [ + { + "include": "#scope_resolution_template_definition_inner_generated" + } + ] + }, + "2": { + "name": "entity.name.scope-resolution.template.definition.cpp" + }, + "3": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_range" + } + ] + }, + "4": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.definition.cpp" + } + } + }, + "scope_resolution_function_call": { + "match": "(?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+", + "captures": { + "0": { + "patterns": [ + { + "include": "#scope_resolution_function_call_inner_generated" + } + ] + } + } + }, + "scope_resolution_function_call_inner_generated": { + "match": "((?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+((?]*|[^>]*+<[^>]*+>)++>\\s*)?(::)", + "captures": { + "1": { + "patterns": [ + { + "include": "#scope_resolution_function_call_inner_generated" + } + ] + }, + "2": { + "name": "entity.name.scope-resolution.function.call.cpp" + }, + "3": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_range" + } + ] + }, + "4": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.call.cpp" + } + } + }, + "scope_resolution_function_definition": { + "match": "(?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+", + "captures": { + "0": { + "patterns": [ + { + "include": "#scope_resolution_function_definition_inner_generated" + } + ] + } + } + }, + "scope_resolution_function_definition_inner_generated": { + "match": "((?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+((?]*|[^>]*+<[^>]*+>)++>\\s*)?(::)", + "captures": { + "1": { + "patterns": [ + { + "include": "#scope_resolution_function_definition_inner_generated" + } + ] + }, + "2": { + "name": "entity.name.scope-resolution.function.definition.cpp" + }, + "3": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_range" + } + ] + }, + "4": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.cpp" + } + } + }, + "scope_resolution_namespace_alias": { + "match": "(?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+", + "captures": { + "0": { + "patterns": [ + { + "include": "#scope_resolution_namespace_alias_inner_generated" + } + ] + } + } + }, + "scope_resolution_namespace_alias_inner_generated": { + "match": "((?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+((?]*|[^>]*+<[^>]*+>)++>\\s*)?(::)", + "captures": { + "1": { + "patterns": [ + { + "include": "#scope_resolution_namespace_alias_inner_generated" + } + ] + }, + "2": { + "name": "entity.name.scope-resolution.namespace.alias.cpp" + }, + "3": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_range" + } + ] + }, + "4": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.alias.cpp" + } + } + }, + "scope_resolution_namespace_using": { + "match": "(?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+", + "captures": { + "0": { + "patterns": [ + { + "include": "#scope_resolution_namespace_using_inner_generated" + } + ] + } + } + }, + "scope_resolution_namespace_using_inner_generated": { + "match": "((?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+((?]*|[^>]*+<[^>]*+>)++>\\s*)?(::)", + "captures": { + "1": { + "patterns": [ + { + "include": "#scope_resolution_namespace_using_inner_generated" + } + ] + }, + "2": { + "name": "entity.name.scope-resolution.namespace.using.cpp" + }, + "3": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_range" + } + ] + }, + "4": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.using.cpp" + } + } + }, + "scope_resolution_namespace_block": { + "match": "(?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+", + "captures": { + "0": { + "patterns": [ + { + "include": "#scope_resolution_namespace_block_inner_generated" + } + ] + } + } + }, + "scope_resolution_namespace_block_inner_generated": { + "match": "((?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+((?]*|[^>]*+<[^>]*+>)++>\\s*)?(::)", + "captures": { + "1": { + "patterns": [ + { + "include": "#scope_resolution_namespace_block_inner_generated" + } + ] + }, + "2": { + "name": "entity.name.scope-resolution.namespace.block.cpp" + }, + "3": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_range" + } + ] + }, + "4": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.namespace.block.cpp" + } + } + }, + "scope_resolution_function_definition_operator_overload": { + "match": "(?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+", + "captures": { + "0": { + "patterns": [ + { + "include": "#scope_resolution_function_definition_operator_overload_inner_generated" + } + ] + } + } + }, + "scope_resolution_function_definition_operator_overload_inner_generated": { + "match": "((?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+((?]*|[^>]*+<[^>]*+>)++>\\s*)?(::)", + "captures": { + "1": { + "patterns": [ + { + "include": "#scope_resolution_function_definition_operator_overload_inner_generated" + } + ] + }, + "2": { + "name": "entity.name.scope-resolution.function.definition.operator-overload.cpp" + }, + "3": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_range" + } + ] + }, + "4": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.operator-overload.cpp" + } + } + }, + "qualified_type": { + "match": "((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:short|signed|unsigned|long)|(?:class|struct|union|enum))((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+((?]*|[^>]*+<[^>]*+>)++>\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?]*|[^>]*+<[^>]*+>)++>\\s*)?(?![\\w<:.])", + "captures": { + "0": { + "name": "meta.qualified_type.cpp", + "patterns": [ + { + "match": "(?:class|struct|union|enum)", + "name": "storage.type.$0.cpp" + }, + { + "include": "#attributes_context" + }, + { + "include": "#function_type" + }, + { + "include": "#storage_types" + }, + { + "include": "#number_literal" + }, + { + "include": "#string_context_c" + }, + { + "include": "#comma" + }, + { + "include": "#scope_resolution_inner_generated" + }, + { + "include": "#template_call_range" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.type.cpp" + } + ] + }, + "1": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "5": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "8": { + "name": "comment.block.cpp" + }, + "9": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "10": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "11": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "12": { + "name": "comment.block.cpp" + }, + "13": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "15": { + "patterns": [ + { + "include": "#scope_resolution_inner_generated" + } + ] + }, + "16": { + "name": "entity.name.scope-resolution.cpp" + }, + "17": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_range" + } + ] + }, + "18": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" + }, + "19": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "20": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "21": { + "name": "comment.block.cpp" + }, + "22": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "23": { + "name": "entity.name.type.cpp" + } + } + }, + "type_alias": { + "match": "(using)\\s*(?!namespace)(((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:short|signed|unsigned|long)|(?:class|struct|union|enum))((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+((?]*|[^>]*+<[^>]*+>)++>\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?]*|[^>]*+<[^>]*+>)++>\\s*)?(?![\\w<:.]))\\s*(\\=)\\s*((?:typename)?)\\s*((?:(?:(?\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:short|signed|unsigned|long)|(?:class|struct|union|enum))((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+((?]*|[^>]*+<[^>]*+>)++>\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?]*|[^>]*+<[^>]*+>)++>\\s*)?(?![\\w<:.]))|(.+(?\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:\\*((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*)((?:&((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){0,2})((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?)(?:(\\[)(\\w*)(\\])\\s*)?\\s*(?:(;)|\\n)", + "captures": { + "1": { + "name": "keyword.other.using.directive.cpp" + }, + "2": { + "name": "meta.qualified_type.cpp", + "patterns": [ + { + "match": "(?:class|struct|union|enum)", + "name": "storage.type.$0.cpp" + }, + { + "include": "#attributes_context" + }, + { + "include": "#function_type" + }, + { + "include": "#storage_types" + }, + { + "include": "#number_literal" + }, + { + "include": "#string_context_c" + }, + { + "include": "#comma" + }, + { + "include": "#scope_resolution_inner_generated" + }, + { + "include": "#template_call_range" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.type.cpp" + } + ] + }, + "3": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "4": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "5": { + "name": "comment.block.cpp" + }, + "6": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "7": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "10": { + "name": "comment.block.cpp" + }, + "11": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "12": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "13": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "14": { + "name": "comment.block.cpp" + }, + "15": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "17": { + "patterns": [ + { + "include": "#scope_resolution_inner_generated" + } + ] + }, + "18": { + "name": "entity.name.scope-resolution.cpp" + }, + "19": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_range" + } + ] + }, + "20": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" + }, + "21": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "22": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "23": { + "name": "comment.block.cpp" + }, + "24": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "25": { + "name": "entity.name.type.cpp" + }, + "26": { + "name": "keyword.operator.assignment.cpp" + }, + "27": { + "name": "keyword.other.typename.cpp" + }, + "28": { + "patterns": [ + { + "include": "#storage_specifiers" + } + ] + }, + "29": { + "name": "meta.qualified_type.cpp", + "patterns": [ + { + "match": "(?:class|struct|union|enum)", + "name": "storage.type.$0.cpp" + }, + { + "include": "#attributes_context" + }, + { + "include": "#function_type" + }, + { + "include": "#storage_types" + }, + { + "include": "#number_literal" + }, + { + "include": "#string_context_c" + }, + { + "include": "#comma" + }, + { + "include": "#scope_resolution_inner_generated" + }, + { + "include": "#template_call_range" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.type.cpp" + } + ] + }, + "30": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "31": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "32": { + "name": "comment.block.cpp" + }, + "33": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "34": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, + "35": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "36": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "37": { + "name": "comment.block.cpp" + }, + "38": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "39": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "40": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "41": { + "name": "comment.block.cpp" + }, + "42": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "44": { + "patterns": [ + { + "include": "#scope_resolution_inner_generated" + } + ] + }, + "45": { + "name": "entity.name.scope-resolution.cpp" + }, + "46": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_range" + } + ] + }, + "47": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" + }, + "48": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "49": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "50": { + "name": "comment.block.cpp" + }, + "51": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "52": { + "name": "entity.name.type.cpp" + }, + "53": { + "name": "meta.declaration.type.alias.value.unknown.cpp", + "patterns": [ + { + "include": "#evaluation_context" + } + ] + }, + "55": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "56": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "57": { + "name": "comment.block.cpp" + }, + "58": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "59": { + "name": "storage.modifier.pointer.cpp" + }, + "60": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "61": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "62": { + "name": "comment.block.cpp" + }, + "63": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "64": { + "name": "storage.modifier.reference.cpp" + }, + "65": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "66": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "67": { + "name": "comment.block.cpp" + }, + "68": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "69": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "70": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "71": { + "name": "comment.block.cpp" + }, + "72": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "73": { + "name": "punctuation.definition.begin.bracket.square.cpp" + }, + "74": { + "patterns": [ + { + "include": "#evaluation_context" + } + ] + }, + "75": { + "name": "punctuation.definition.end.bracket.square.cpp" + }, + "76": { + "name": "punctuation.terminator.statement.cpp" } }, - "name": "meta.scope-resolution.cpp" + "name": "meta.declaration.type.alias.cpp" }, "struct_declare": { - "match": "(struct)\\s+((?\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:\\*((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*)((?:&((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){0,2})((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?)|\\s+)((?\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:\\*((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*)((?:&((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){0,2})((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?)|\\s+)((?:(?:,\\w])*>\\s*)))?::)*\\s*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:(<(?:[\\s<>:,\\w])*>\\s*))?(\\()", + "name": "meta.function.definition.cpp", + "begin": "((((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:short|signed|unsigned|long)|(?:class|struct|union|enum))((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+((?]*|[^>]*+<[^>]*+>)++>\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?]*|[^>]*+<[^>]*+>)++>\\s*)?(?![\\w<:.]))((?:((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:\\*((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*)((?:&((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){0,2})((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?)((?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?=\\())", "beginCaptures": { "1": { + "name": "meta.head.function.definition.cpp" + }, + "2": { + "name": "meta.qualified_type.cpp", "patterns": [ { - "include": "#scope_resolution" + "match": "(?:class|struct|union|enum)", + "name": "storage.type.$0.cpp" + }, + { + "include": "#attributes_context" + }, + { + "include": "#function_type" + }, + { + "include": "#storage_types" + }, + { + "include": "#number_literal" + }, + { + "include": "#string_context_c" + }, + { + "include": "#comma" + }, + { + "include": "#scope_resolution_inner_generated" + }, + { + "include": "#template_call_range" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.type.cpp" } ] }, - "2": { - "name": "entity.name.function.call.cpp" - }, "3": { - "name": "meta.template.call.cpp", "patterns": [ { - "include": "#template_call_context" + "include": "#inline_comment" } ] }, "4": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "5": { + "name": "comment.block.cpp" + }, + "6": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "7": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, + "8": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "9": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "10": { + "name": "comment.block.cpp" + }, + "11": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "12": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "13": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "14": { + "name": "comment.block.cpp" + }, + "15": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "17": { + "patterns": [ + { + "include": "#scope_resolution_inner_generated" + } + ] + }, + "18": { + "name": "entity.name.scope-resolution.cpp" + }, + "19": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_range" + } + ] + }, + "20": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" + }, + "21": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "22": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "23": { + "name": "comment.block.cpp" + }, + "24": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "25": { + "name": "entity.name.type.cpp" + }, + "27": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "28": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "29": { + "name": "comment.block.cpp" + }, + "30": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "31": { + "name": "storage.modifier.pointer.cpp" + }, + "32": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "33": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "34": { + "name": "comment.block.cpp" + }, + "35": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "36": { + "name": "storage.modifier.reference.cpp" + }, + "37": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "38": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "39": { + "name": "comment.block.cpp" + }, + "40": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "41": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "42": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "43": { + "name": "comment.block.cpp" + }, + "44": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "45": { + "patterns": [ + { + "include": "#scope_resolution_function_definition_inner_generated" + } + ] + }, + "46": { + "name": "entity.name.function.definition.cpp" + }, + "47": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "48": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "49": { + "name": "comment.block.cpp" + }, + "50": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + } + }, + "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", + "patterns": [ + { + "name": "meta.head.function.definition.cpp", + "begin": "\\G ?", + "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "endCaptures": { + "1": { + "name": "punctuation.section.block.begin.bracket.curly.function.definition.cpp" + } + }, + "patterns": [ + { + "contentName": "meta.function.definition.parameters.cpp", + "begin": "(\\()", + "beginCaptures": { + "1": { + "name": "punctuation.section.parameters.begin.bracket.round.cpp" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.section.parameters.end.bracket.round.cpp" + } + }, + "patterns": [ + { + "include": "#function_parameter_context" + }, + { + "include": "#function_call_context" + } + ] + }, + { + "include": "#comments_context" + }, + { + "include": "#root_context" + } + ] + }, + { + "name": "meta.body.function.definition.cpp", + "begin": "(?<=\\{|<%|\\?\\?<)", + "end": "(\\}|%>|\\?\\?>)", + "endCaptures": { + "1": { + "name": "punctuation.section.block.end.bracket.curly.function.definition.cpp" + } + }, + "patterns": [ + { + "include": "#function_body_context" + } + ] + }, + { + "name": "meta.tail.function.definition.cpp", + "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", + "end": "[\\s\\n]*(?=;)", + "patterns": [ + { + "include": "#root_context" + } + ] + } + ] + }, + "static_assert": { + "begin": "(static_assert|_Static_assert)\\s*(\\()", + "beginCaptures": { + "1": { + "name": "keyword.other.static_assert.cpp" + }, + "2": { "name": "punctuation.section.arguments.begin.bracket.round.cpp" } }, @@ -1077,7 +3019,78 @@ }, "patterns": [ { - "include": "#function_call_context_c" + "name": "meta.static_assert.message.cpp", + "begin": "(,)\\s*(?=(?:L|u8|u|U\\s*\\\")?)", + "beginCaptures": { + "1": { + "name": "comma.cpp punctuation.separator.delimiter.cpp" + } + }, + "end": "(?=\\))", + "patterns": [ + { + "include": "#string_context" + }, + { + "include": "#string_context_c" + } + ] + }, + { + "include": "#function_call_context" + } + ] + }, + "function_call": { + "begin": "((?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?]*|[^>]*+<[^>]*+>)++>\\s*)?(\\()", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#scope_resolution_function_call_inner_generated" + } + ] + }, + "2": { + "name": "entity.name.function.call.cpp" + }, + "3": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "4": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "5": { + "name": "comment.block.cpp" + }, + "6": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "7": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_range" + } + ] + }, + "8": { + "name": "punctuation.section.arguments.begin.bracket.round.function.call.cpp" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.section.arguments.end.bracket.round.function.call.cpp" + } + }, + "patterns": [ + { + "include": "#function_call_context" } ] }, @@ -1095,13 +3108,6 @@ { "include": "#typeid_operator" }, - { - "include": "#decltype_specifier" - }, - { - "match": "(?\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:short|signed|unsigned|long)|(?:class|struct|union|enum))((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+((?]*|[^>]*+<[^>]*+>)++>\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?]*|[^>]*+<[^>]*+>)++>\\s*)?(?![\\w<:.]))\\s*((?:((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:\\*((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*)((?:&((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){0,2})((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?)(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", + "beginCaptures": { + "1": { + "name": "meta.qualified_type.cpp", + "patterns": [ + { + "match": "(?:class|struct|union|enum)", + "name": "storage.type.$0.cpp" + }, + { + "include": "#attributes_context" + }, + { + "include": "#function_type" + }, + { + "include": "#storage_types" + }, + { + "include": "#number_literal" + }, + { + "include": "#string_context_c" + }, + { + "include": "#comma" + }, + { + "include": "#scope_resolution_inner_generated" + }, + { + "include": "#template_call_range" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.type.cpp" + } + ] + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "6": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, + "7": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "11": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "12": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "13": { + "name": "comment.block.cpp" + }, + "14": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "16": { + "patterns": [ + { + "include": "#scope_resolution_inner_generated" + } + ] + }, + "17": { + "name": "entity.name.scope-resolution.cpp" + }, + "18": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_range" + } + ] + }, + "19": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" + }, + "20": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "21": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "22": { + "name": "comment.block.cpp" + }, + "23": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "24": { + "name": "entity.name.type.cpp" + }, + "26": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "27": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "28": { + "name": "comment.block.cpp" + }, + "29": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "30": { + "name": "storage.modifier.pointer.cpp" + }, + "31": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "32": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "33": { + "name": "comment.block.cpp" + }, + "34": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "35": { + "name": "storage.modifier.reference.cpp" + }, + "36": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "37": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "38": { + "name": "comment.block.cpp" + }, + "39": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "40": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "41": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "42": { + "name": "comment.block.cpp" + }, + "43": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "44": { + "name": "punctuation.section.parens.begin.bracket.round.function.pointer.cpp" + }, + "45": { + "name": "punctuation.definition.function.pointer.dereference.cpp" + }, + "46": { + "name": "variable.other.definition.pointer.function.cpp" + }, + "47": { + "name": "punctuation.definition.begin.bracket.square.cpp" + }, + "48": { + "patterns": [ + { + "include": "#evaluation_context" + } + ] + }, + "49": { + "name": "punctuation.definition.end.bracket.square.cpp" + }, + "50": { + "name": "punctuation.section.parens.end.bracket.round.function.pointer.cpp" + }, + "51": { + "name": "punctuation.section.parameters.begin.bracket.round.function.pointer.cpp" + } + }, + "end": "(\\))\\s*(?=[{=,);]|\\n)(?!\\()", + "endCaptures": { + "1": { + "name": "punctuation.section.parameters.end.bracket.round.function.pointer.cpp" + } + }, + "patterns": [ + { + "include": "#function_parameter_context" + } + ] + }, + "function_pointer_parameter": { + "begin": "(((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:short|signed|unsigned|long)|(?:class|struct|union|enum))((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(((?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+((?]*|[^>]*+<[^>]*+>)++>\\s*)?(::))?((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?]*|[^>]*+<[^>]*+>)++>\\s*)?(?![\\w<:.]))\\s*((?:((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:\\*((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*)((?:&((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){0,2})((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?)(\\()(\\*)\\s*((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)\\s*(?:(\\[)(\\w*)(\\])\\s*)*(\\))\\s*(\\()", + "beginCaptures": { + "1": { + "name": "meta.qualified_type.cpp", + "patterns": [ + { + "match": "(?:class|struct|union|enum)", + "name": "storage.type.$0.cpp" + }, + { + "include": "#attributes_context" + }, + { + "include": "#function_type" + }, + { + "include": "#storage_types" + }, + { + "include": "#number_literal" + }, + { + "include": "#string_context_c" + }, + { + "include": "#comma" + }, + { + "include": "#scope_resolution_inner_generated" + }, + { + "include": "#template_call_range" + }, + { + "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", + "name": "entity.name.type.cpp" + } + ] + }, + "2": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "6": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, + "7": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "11": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "12": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "13": { + "name": "comment.block.cpp" + }, + "14": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "16": { + "patterns": [ + { + "include": "#scope_resolution_inner_generated" + } + ] + }, + "17": { + "name": "entity.name.scope-resolution.cpp" + }, + "18": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_range" + } + ] + }, + "19": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" + }, + "20": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "21": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "22": { + "name": "comment.block.cpp" + }, + "23": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "24": { + "name": "entity.name.type.cpp" + }, + "26": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "27": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "28": { + "name": "comment.block.cpp" + }, + "29": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "30": { + "name": "storage.modifier.pointer.cpp" + }, + "31": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "32": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "33": { + "name": "comment.block.cpp" + }, + "34": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "35": { + "name": "storage.modifier.reference.cpp" + }, + "36": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "37": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "38": { + "name": "comment.block.cpp" + }, + "39": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "40": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "41": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "42": { + "name": "comment.block.cpp" + }, + "43": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "44": { + "name": "punctuation.section.parens.begin.bracket.round.function.pointer.cpp" + }, + "45": { + "name": "punctuation.definition.function.pointer.dereference.cpp" + }, + "46": { + "name": "variable.parameter.pointer.function.cpp" + }, + "47": { + "name": "punctuation.definition.begin.bracket.square.cpp" + }, + "48": { + "patterns": [ + { + "include": "#evaluation_context" + } + ] + }, + "49": { + "name": "punctuation.definition.end.bracket.square.cpp" + }, + "50": { + "name": "punctuation.section.parens.end.bracket.round.function.pointer.cpp" + }, + "51": { + "name": "punctuation.section.parameters.begin.bracket.round.function.pointer.cpp" + } + }, + "end": "(\\))\\s*(?=[{=,);]|\\n)(?!\\()", + "endCaptures": { + "1": { + "name": "punctuation.section.parameters.end.bracket.round.function.pointer.cpp" + } + }, + "patterns": [ + { + "include": "#function_parameter_context" + } + ] + }, "probably_a_parameter": { - "match": "(?:((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s*(?==))|((?<=\\w |\\*\\/|[&*>\\]\\)]|\\.\\.\\.)\\s*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s*(?=(?:\\[\\]\\s*)?(?:,|\\)))))", + "match": "(?:((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*(?==))|((?<=\\w |\\*\\/|[&*>\\]\\)]|\\.\\.\\.)\\s*(?!(?:auto|void|char|short|int|signed|unsigned|long|float|double|bool|wchar_t|u_char|u_short|u_int|u_long|ushort|uint|u_quad_t|quad_t|qaddr_t|caddr_t|daddr_t|div_t|dev_t|fixpt_t|blkcnt_t|blksize_t|gid_t|in_addr_t|in_port_t|ino_t|key_t|mode_t|nlink_t|id_t|pid_t|off_t|segsz_t|swblk_t|uid_t|id_t|clock_t|size_t|ssize_t|time_t|useconds_t|suseconds_t|int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|int_least8_t|int_least16_t|int_least32_t|int_least64_t|uint_least8_t|uint_least16_t|uint_least32_t|uint_least64_t|int_fast8_t|int_fast16_t|int_fast32_t|int_fast64_t|uint_fast8_t|uint_fast16_t|uint_fast32_t|uint_fast64_t|intptr_t|uintptr_t|intmax_t|intmax_t|uintmax_t|uintmax_t))(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*(?=(?:\\[\\]\\s*)?(?:,|\\)))))", "captures": { "1": { "name": "variable.parameter.defaulted.cpp" @@ -1199,7 +3702,7 @@ }, "operator_overload": { "name": "meta.function.definition.parameters.operator-overload.cpp", - "begin": "(operator)((?:\\s*(?:\\+\\+|\\-\\-|\\(\\)|\\[\\]|\\->|\\+\\+|\\-\\-|\\+|\\-|!|~|\\*|&|\\->\\*|\\*|\\/|%|\\+|\\-|<<|>>|<=>|<|<=|>|>=|==|!=|&|\\^|\\||&&|\\|\\||=|\\+=|\\-=|\\*=|\\/=|%=|<<=|>>=|&=|\\^=|\\|=|,)|\\s+(?:(?:new|new\\[\\]|delete|delete\\[\\])|(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s*(?:(?-mix:(?:<(?:[\\s<>:,\\w])*>\\s*)))?::)*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s*(?:&)?)))\\s*(\\()", + "begin": "(operator)((?:\\s*(?:\\+\\+|\\-\\-|\\(\\)|\\[\\]|\\->|\\+\\+|\\-\\-|\\+|\\-|!|~|\\*|&|\\->\\*|\\*|\\/|%|\\+|\\-|<<|>>|<=>|<|<=|>|>=|==|!=|&|\\^|\\||&&|\\|\\||=|\\+=|\\-=|\\*=|\\/=|%=|<<=|>>=|&=|\\^=|\\|=|,)|\\s+(?:(?:new|new\\[\\]|delete|delete\\[\\])|(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*&?)))\\s*(\\()", "beginCaptures": { "1": { "name": "keyword.other.operator.overload.cpp" @@ -1208,7 +3711,7 @@ "name": "entity.name.operator.overloadee.cpp", "patterns": [ { - "include": "#scope_resolution" + "include": "#scope_resolution_function_definition_operator_overload_inner_generated" } ] }, @@ -1224,15 +3727,12 @@ }, "patterns": [ { - "include": "#probably_a_parameter" - }, - { - "include": "#function_context_c" + "include": "#function_parameter_context" } ] }, "member_access": { - "match": "(?:((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s*(?-mix:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(\\b(?!auto[^(?-mix:\\w)]|void[^(?-mix:\\w)]|char[^(?-mix:\\w)]|short[^(?-mix:\\w)]|int[^(?-mix:\\w)]|signed[^(?-mix:\\w)]|unsigned[^(?-mix:\\w)]|long[^(?-mix:\\w)]|float[^(?-mix:\\w)]|double[^(?-mix:\\w)]|bool[^(?-mix:\\w)]|wchar_t[^(?-mix:\\w)]|u_char[^(?-mix:\\w)]|u_short[^(?-mix:\\w)]|u_int[^(?-mix:\\w)]|u_long[^(?-mix:\\w)]|ushort[^(?-mix:\\w)]|uint[^(?-mix:\\w)]|u_quad_t[^(?-mix:\\w)]|quad_t[^(?-mix:\\w)]|qaddr_t[^(?-mix:\\w)]|caddr_t[^(?-mix:\\w)]|daddr_t[^(?-mix:\\w)]|div_t[^(?-mix:\\w)]|dev_t[^(?-mix:\\w)]|fixpt_t[^(?-mix:\\w)]|blkcnt_t[^(?-mix:\\w)]|blksize_t[^(?-mix:\\w)]|gid_t[^(?-mix:\\w)]|in_addr_t[^(?-mix:\\w)]|in_port_t[^(?-mix:\\w)]|ino_t[^(?-mix:\\w)]|key_t[^(?-mix:\\w)]|mode_t[^(?-mix:\\w)]|nlink_t[^(?-mix:\\w)]|id_t[^(?-mix:\\w)]|pid_t[^(?-mix:\\w)]|off_t[^(?-mix:\\w)]|segsz_t[^(?-mix:\\w)]|swblk_t[^(?-mix:\\w)]|uid_t[^(?-mix:\\w)]|id_t[^(?-mix:\\w)]|clock_t[^(?-mix:\\w)]|size_t[^(?-mix:\\w)]|ssize_t[^(?-mix:\\w)]|time_t[^(?-mix:\\w)]|useconds_t[^(?-mix:\\w)]|suseconds_t[^(?-mix:\\w)]|int8_t[^(?-mix:\\w)]|int16_t[^(?-mix:\\w)]|int32_t[^(?-mix:\\w)]|int64_t[^(?-mix:\\w)]|uint8_t[^(?-mix:\\w)]|uint16_t[^(?-mix:\\w)]|uint32_t[^(?-mix:\\w)]|uint64_t[^(?-mix:\\w)]|int_least8_t[^(?-mix:\\w)]|int_least16_t[^(?-mix:\\w)]|int_least32_t[^(?-mix:\\w)]|int_least64_t[^(?-mix:\\w)]|uint_least8_t[^(?-mix:\\w)]|uint_least16_t[^(?-mix:\\w)]|uint_least32_t[^(?-mix:\\w)]|uint_least64_t[^(?-mix:\\w)]|int_fast8_t[^(?-mix:\\w)]|int_fast16_t[^(?-mix:\\w)]|int_fast32_t[^(?-mix:\\w)]|int_fast64_t[^(?-mix:\\w)]|uint_fast8_t[^(?-mix:\\w)]|uint_fast16_t[^(?-mix:\\w)]|uint_fast32_t[^(?-mix:\\w)]|uint_fast64_t[^(?-mix:\\w)]|intptr_t[^(?-mix:\\w)]|uintptr_t[^(?-mix:\\w)]|intmax_t[^(?-mix:\\w)]|intmax_t[^(?-mix:\\w)]|uintmax_t[^(?-mix:\\w)]|uintmax_t[^(?-mix:\\w)])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\b(?!\\())", + "match": "(?:((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*(?:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*(\\b(?!auto[^(?-mix:\\w)]|void[^(?-mix:\\w)]|char[^(?-mix:\\w)]|short[^(?-mix:\\w)]|int[^(?-mix:\\w)]|signed[^(?-mix:\\w)]|unsigned[^(?-mix:\\w)]|long[^(?-mix:\\w)]|float[^(?-mix:\\w)]|double[^(?-mix:\\w)]|bool[^(?-mix:\\w)]|wchar_t[^(?-mix:\\w)]|u_char[^(?-mix:\\w)]|u_short[^(?-mix:\\w)]|u_int[^(?-mix:\\w)]|u_long[^(?-mix:\\w)]|ushort[^(?-mix:\\w)]|uint[^(?-mix:\\w)]|u_quad_t[^(?-mix:\\w)]|quad_t[^(?-mix:\\w)]|qaddr_t[^(?-mix:\\w)]|caddr_t[^(?-mix:\\w)]|daddr_t[^(?-mix:\\w)]|div_t[^(?-mix:\\w)]|dev_t[^(?-mix:\\w)]|fixpt_t[^(?-mix:\\w)]|blkcnt_t[^(?-mix:\\w)]|blksize_t[^(?-mix:\\w)]|gid_t[^(?-mix:\\w)]|in_addr_t[^(?-mix:\\w)]|in_port_t[^(?-mix:\\w)]|ino_t[^(?-mix:\\w)]|key_t[^(?-mix:\\w)]|mode_t[^(?-mix:\\w)]|nlink_t[^(?-mix:\\w)]|id_t[^(?-mix:\\w)]|pid_t[^(?-mix:\\w)]|off_t[^(?-mix:\\w)]|segsz_t[^(?-mix:\\w)]|swblk_t[^(?-mix:\\w)]|uid_t[^(?-mix:\\w)]|id_t[^(?-mix:\\w)]|clock_t[^(?-mix:\\w)]|size_t[^(?-mix:\\w)]|ssize_t[^(?-mix:\\w)]|time_t[^(?-mix:\\w)]|useconds_t[^(?-mix:\\w)]|suseconds_t[^(?-mix:\\w)]|int8_t[^(?-mix:\\w)]|int16_t[^(?-mix:\\w)]|int32_t[^(?-mix:\\w)]|int64_t[^(?-mix:\\w)]|uint8_t[^(?-mix:\\w)]|uint16_t[^(?-mix:\\w)]|uint32_t[^(?-mix:\\w)]|uint64_t[^(?-mix:\\w)]|int_least8_t[^(?-mix:\\w)]|int_least16_t[^(?-mix:\\w)]|int_least32_t[^(?-mix:\\w)]|int_least64_t[^(?-mix:\\w)]|uint_least8_t[^(?-mix:\\w)]|uint_least16_t[^(?-mix:\\w)]|uint_least32_t[^(?-mix:\\w)]|uint_least64_t[^(?-mix:\\w)]|int_fast8_t[^(?-mix:\\w)]|int_fast16_t[^(?-mix:\\w)]|int_fast32_t[^(?-mix:\\w)]|int_fast64_t[^(?-mix:\\w)]|uint_fast8_t[^(?-mix:\\w)]|uint_fast16_t[^(?-mix:\\w)]|uint_fast32_t[^(?-mix:\\w)]|uint_fast64_t[^(?-mix:\\w)]|intptr_t[^(?-mix:\\w)]|uintptr_t[^(?-mix:\\w)]|intmax_t[^(?-mix:\\w)]|intmax_t[^(?-mix:\\w)]|uintmax_t[^(?-mix:\\w)]|uintmax_t[^(?-mix:\\w)])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b(?!\\())", "captures": { "1": { "name": "variable.language.this.cpp" @@ -1249,11 +3749,24 @@ "5": { "patterns": [ { - "match": "(?<=(?:\\.\\*|\\.|->|->\\*))\\s*(?-mix:(?:(?:(?\\*|->))))", - "name": "variable.other.object.property.cpp" + "match": "(?<=(?:\\.\\*|\\.|->|->\\*))\\s*(?:((?\\*|->)))", + "captures": { + "1": { + "name": "variable.language.this.cpp" + }, + "2": { + "name": "variable.other.object.property.cpp" + }, + "3": { + "name": "punctuation.separator.dot-access.cpp" + }, + "4": { + "name": "punctuation.separator.pointer-access.cpp" + } + } }, { - "match": "(?:((?\\*|->)))", + "match": "(?:((?\\*|->)))", "captures": { "1": { "name": "variable.language.this.cpp" @@ -1283,8 +3796,7 @@ } }, "method_access": { - "contentName": "meta.function-call.member", - "begin": "(?:((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*\\s*(?-mix:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)(\\()", + "begin": "(?:((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*(?:(?:(?:\\.\\*|\\.))|(?:(?:->\\*|->)))\\s*)*)\\s*((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*(\\()", "beginCaptures": { "1": { "name": "variable.language.this.cpp" @@ -1301,11 +3813,24 @@ "5": { "patterns": [ { - "match": "(?<=(?:\\.\\*|\\.|->|->\\*))\\s*(?-mix:(?:(?:(?\\*|->))))", - "name": "variable.other.object.property.cpp" + "match": "(?<=(?:\\.\\*|\\.|->|->\\*))\\s*(?:((?\\*|->)))", + "captures": { + "1": { + "name": "variable.language.this.cpp" + }, + "2": { + "name": "variable.other.object.property.cpp" + }, + "3": { + "name": "punctuation.separator.dot-access.cpp" + }, + "4": { + "name": "punctuation.separator.pointer-access.cpp" + } + } }, { - "match": "(?:((?\\*|->)))", + "match": "(?:((?\\*|->)))", "captures": { "1": { "name": "variable.language.this.cpp" @@ -1344,13 +3869,13 @@ }, "patterns": [ { - "include": "#function_call_context_c" + "include": "#function_call_context" } ] }, "using_namespace": { "name": "meta.using-namespace.cpp", - "begin": "(?:,\\w])*>\\s*)))?::)*\\s*))?((?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)?((?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)\\s*((?:,\\w])*>\\s*)))?::)*\\s*)\\s*(?:((?\\[\\]=]))", + "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "patterns": [ { "name": "meta.head.namespace.cpp", - "begin": "\\G| ", - "end": "((?:\\{|(?=;)))", + "begin": "\\G ?", + "end": "((?:\\{|<%|\\?\\?<|(?=;)))", "endCaptures": { "1": { "name": "punctuation.section.block.begin.bracket.curly.namespace.cpp" } - } + }, + "patterns": [ + { + "include": "#attributes_context" + }, + { + "match": "((?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)\\s*((?|\\?\\?>)", "endCaptures": { "1": { "name": "punctuation.section.block.end.bracket.curly.namespace.cpp" @@ -1427,28 +3992,28 @@ }, "patterns": [ { - "include": "$base" + "include": "#root_context" } ] }, { "name": "meta.tail.namespace.cpp", - "begin": "(?<=})[\\s\\n]*", + "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", "end": "[\\s\\n]*(?=;)", "patterns": [ { - "include": "$base" + "include": "#root_context" } ] } ] }, "macro_argument": { - "match": "##(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*(?!\\w)", + "match": "##(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?!\\w)", "name": "variable.other.macro.argument.cpp" }, "lambdas": { - "begin": "((?:(?<=[^\\s]|^)(?])|(?<=\\Wreturn|^return))\\s*(\\[(?!\\[))((?:.*\\[.*?\\].*?)*.*?)(\\]))", "beginCaptures": { "2": { "name": "punctuation.definition.capture.begin.lambda.cpp" @@ -1457,10 +4022,7 @@ "name": "meta.lambda.capture.cpp", "patterns": [ { - "include": "#probably_a_parameter" - }, - { - "include": "#function_context_c" + "include": "#function_parameter_context" } ] }, @@ -1486,10 +4048,7 @@ }, "patterns": [ { - "include": "#probably_a_parameter" - }, - { - "include": "#function_context_c" + "include": "#function_parameter_context" } ] }, @@ -1498,7 +4057,7 @@ "name": "storage.modifier.lambda.$0.cpp" }, { - "match": "(->)(.+?(?=\\{|$))?", + "match": "(->)((?:.+?(?=\\{|$))?)", "captures": { "1": { "name": "punctuation.definition.lambda.return-type.cpp" @@ -1524,7 +4083,7 @@ }, "patterns": [ { - "include": "$base" + "include": "#root_context" } ] } @@ -1532,15 +4091,1074 @@ }, "pthread_types": { "match": "(?:,\\w])*>\\s*)))?::)*\\s*)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*)\\s*(?:(<(?:[\\s<>:,\\w])*>\\s*))?(::)))?\\s*((?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+((?]*|[^>]*+<[^>]*+>)++>\\s*)?(::))?\\s*((?\\[\\]=]))", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", "endCaptures": { "1": { "name": "punctuation.terminator.statement.cpp" @@ -1611,8 +5222,8 @@ "patterns": [ { "name": "meta.head.enum.cpp", - "begin": "\\G| ", - "end": "((?:\\{|(?=;)))", + "begin": "\\G ?", + "end": "((?:\\{|<%|\\?\\?<|(?=;)))", "endCaptures": { "1": { "name": "punctuation.section.block.begin.bracket.curly.enum.cpp" @@ -1620,14 +5231,14 @@ }, "patterns": [ { - "include": "$base" + "include": "#root_context" } ] }, { "name": "meta.body.enum.cpp", - "begin": "(?<=\\{)", - "end": "(\\})", + "begin": "(?<=\\{|<%|\\?\\?<)", + "end": "(\\}|%>|\\?\\?>)", "endCaptures": { "1": { "name": "punctuation.section.block.end.bracket.curly.enum.cpp" @@ -1635,34 +5246,47 @@ }, "patterns": [ { - "include": "$base" + "include": "#enumerator_list" + }, + { + "include": "#comments_context" + }, + { + "include": "#comma" + }, + { + "include": "#semicolon" } ] }, { "name": "meta.tail.enum.cpp", - "begin": "(?<=})[\\s\\n]*", + "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", "end": "[\\s\\n]*(?=;)", "patterns": [ { - "include": "$base" + "include": "#root_context" } ] } ] }, - "inhertance_context": { + "inheritance_context": { "patterns": [ { "match": ",", - "name": "comma.cpp punctuation.separator.delimiter.inhertance.cpp" + "name": "comma.cpp punctuation.separator.delimiter.inheritance.cpp" }, { "match": "(?\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:short|signed|unsigned|long)|(?:class|struct|union|enum))(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:(?:(?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?(?:::))?(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?]*|[^>]*+<[^>]*+>)++>\\s*)?(?![\\w<:.])))", "captures": { "1": { "name": "entity.name.type.inherited.cpp" @@ -1673,7 +5297,7 @@ }, "class_block": { "name": "meta.block.class.cpp", - "begin": "((((?\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:short|signed|unsigned|long)|(?:class|struct|union|enum))(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:(?:(?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?(?:::))?(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?]*|[^>]*+<[^>]*+>)++>\\s*)?(?![\\w<:.])))+)*))?))", "beginCaptures": { "1": { "name": "meta.head.class.cpp" @@ -1684,14 +5308,20 @@ "4": { "patterns": [ { - "include": "#attributes" + "include": "#attributes_context" + }, + { + "include": "#number_literal" } ] }, "5": { "patterns": [ { - "include": "#attributes" + "include": "#attributes_context" + }, + { + "include": "#number_literal" } ] }, @@ -1702,17 +5332,17 @@ "name": "storage.type.modifier.final.cpp" }, "8": { - "name": "colon.cpp punctuation.separator.inhertance.cpp" + "name": "colon.cpp punctuation.separator.inheritance.cpp" }, "9": { "patterns": [ { - "include": "#inhertance_context" + "include": "#inheritance_context" } ] } }, - "end": "(?:(?:(?<=})\\s*(;)|(;))|(?=[;>\\[\\]=]))", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", "endCaptures": { "1": { "name": "punctuation.terminator.statement.cpp" @@ -1724,8 +5354,8 @@ "patterns": [ { "name": "meta.head.class.cpp", - "begin": "\\G| ", - "end": "((?:\\{|(?=;)))", + "begin": "\\G ?", + "end": "((?:\\{|<%|\\?\\?<|(?=;)))", "endCaptures": { "1": { "name": "punctuation.section.block.begin.bracket.curly.class.cpp" @@ -1736,7 +5366,7 @@ "include": "#preprocessor_context" }, { - "include": "#inhertance_context" + "include": "#inheritance_context" }, { "include": "#template_call_range" @@ -1748,29 +5378,32 @@ }, { "name": "meta.body.class.cpp", - "begin": "(?<=\\{)", - "end": "(\\})", + "begin": "(?<=\\{|<%|\\?\\?<)", + "end": "(\\}|%>|\\?\\?>)", "endCaptures": { "1": { "name": "punctuation.section.block.end.bracket.curly.class.cpp" } }, "patterns": [ + { + "include": "#function_pointer" + }, { "include": "#constructor_context" }, { - "include": "$base" + "include": "#root_context" } ] }, { "name": "meta.tail.class.cpp", - "begin": "(?<=})[\\s\\n]*", + "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", "end": "[\\s\\n]*(?=;)", "patterns": [ { - "include": "$base" + "include": "#root_context" } ] } @@ -1778,7 +5411,7 @@ }, "struct_block": { "name": "meta.block.struct.cpp", - "begin": "((((?\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:short|signed|unsigned|long)|(?:class|struct|union|enum))(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:(?:(?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?(?:::))?(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?]*|[^>]*+<[^>]*+>)++>\\s*)?(?![\\w<:.])))+)*))?))", "beginCaptures": { "1": { "name": "meta.head.struct.cpp" @@ -1789,14 +5422,20 @@ "4": { "patterns": [ { - "include": "#attributes" + "include": "#attributes_context" + }, + { + "include": "#number_literal" } ] }, "5": { "patterns": [ { - "include": "#attributes" + "include": "#attributes_context" + }, + { + "include": "#number_literal" } ] }, @@ -1807,17 +5446,17 @@ "name": "storage.type.modifier.final.cpp" }, "8": { - "name": "colon.cpp punctuation.separator.inhertance.cpp" + "name": "colon.cpp punctuation.separator.inheritance.cpp" }, "9": { "patterns": [ { - "include": "#inhertance_context" + "include": "#inheritance_context" } ] } }, - "end": "(?:(?:(?<=})\\s*(;)|(;))|(?=[;>\\[\\]=]))", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", "endCaptures": { "1": { "name": "punctuation.terminator.statement.cpp" @@ -1829,8 +5468,8 @@ "patterns": [ { "name": "meta.head.struct.cpp", - "begin": "\\G| ", - "end": "((?:\\{|(?=;)))", + "begin": "\\G ?", + "end": "((?:\\{|<%|\\?\\?<|(?=;)))", "endCaptures": { "1": { "name": "punctuation.section.block.begin.bracket.curly.struct.cpp" @@ -1841,7 +5480,7 @@ "include": "#preprocessor_context" }, { - "include": "#inhertance_context" + "include": "#inheritance_context" }, { "include": "#template_call_range" @@ -1853,29 +5492,32 @@ }, { "name": "meta.body.struct.cpp", - "begin": "(?<=\\{)", - "end": "(\\})", + "begin": "(?<=\\{|<%|\\?\\?<)", + "end": "(\\}|%>|\\?\\?>)", "endCaptures": { "1": { "name": "punctuation.section.block.end.bracket.curly.struct.cpp" } }, "patterns": [ + { + "include": "#function_pointer" + }, { "include": "#constructor_context" }, { - "include": "$base" + "include": "#root_context" } ] }, { "name": "meta.tail.struct.cpp", - "begin": "(?<=})[\\s\\n]*", + "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", "end": "[\\s\\n]*(?=;)", "patterns": [ { - "include": "$base" + "include": "#root_context" } ] } @@ -1883,7 +5525,7 @@ }, "union_block": { "name": "meta.block.union.cpp", - "begin": "((((?\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:short|signed|unsigned|long)|(?:class|struct|union|enum))(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:(?:(?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?(?:::))?(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?]*|[^>]*+<[^>]*+>)++>\\s*)?(?![\\w<:.])))+)*))?))", "beginCaptures": { "1": { "name": "meta.head.union.cpp" @@ -1894,14 +5536,20 @@ "4": { "patterns": [ { - "include": "#attributes" + "include": "#attributes_context" + }, + { + "include": "#number_literal" } ] }, "5": { "patterns": [ { - "include": "#attributes" + "include": "#attributes_context" + }, + { + "include": "#number_literal" } ] }, @@ -1912,17 +5560,17 @@ "name": "storage.type.modifier.final.cpp" }, "8": { - "name": "colon.cpp punctuation.separator.inhertance.cpp" + "name": "colon.cpp punctuation.separator.inheritance.cpp" }, "9": { "patterns": [ { - "include": "#inhertance_context" + "include": "#inheritance_context" } ] } }, - "end": "(?:(?:(?<=})\\s*(;)|(;))|(?=[;>\\[\\]=]))", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", "endCaptures": { "1": { "name": "punctuation.terminator.statement.cpp" @@ -1934,8 +5582,8 @@ "patterns": [ { "name": "meta.head.union.cpp", - "begin": "\\G| ", - "end": "((?:\\{|(?=;)))", + "begin": "\\G ?", + "end": "((?:\\{|<%|\\?\\?<|(?=;)))", "endCaptures": { "1": { "name": "punctuation.section.block.begin.bracket.curly.union.cpp" @@ -1946,7 +5594,7 @@ "include": "#preprocessor_context" }, { - "include": "#inhertance_context" + "include": "#inheritance_context" }, { "include": "#template_call_range" @@ -1958,29 +5606,32 @@ }, { "name": "meta.body.union.cpp", - "begin": "(?<=\\{)", - "end": "(\\})", + "begin": "(?<=\\{|<%|\\?\\?<)", + "end": "(\\}|%>|\\?\\?>)", "endCaptures": { "1": { "name": "punctuation.section.block.end.bracket.curly.union.cpp" } }, "patterns": [ + { + "include": "#function_pointer" + }, { "include": "#constructor_context" }, { - "include": "$base" + "include": "#root_context" } ] }, { "name": "meta.tail.union.cpp", - "begin": "(?<=})[\\s\\n]*", + "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", "end": "[\\s\\n]*(?=;)", "patterns": [ { - "include": "$base" + "include": "#root_context" } ] } @@ -1997,7 +5648,7 @@ "name": "storage.type.extern.cpp" } }, - "end": "(?:(?:(?<=})\\s*(;)|(;))|(?=[;>\\[\\]=]))", + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", "endCaptures": { "1": { "name": "punctuation.terminator.statement.cpp" @@ -2009,8 +5660,8 @@ "patterns": [ { "name": "meta.head.extern.cpp", - "begin": "\\G| ", - "end": "((?:\\{|(?=;)))", + "begin": "\\G ?", + "end": "((?:\\{|<%|\\?\\?<|(?=;)))", "endCaptures": { "1": { "name": "punctuation.section.block.begin.bracket.curly.extern.cpp" @@ -2018,14 +5669,14 @@ }, "patterns": [ { - "include": "$base" + "include": "#root_context" } ] }, { "name": "meta.body.extern.cpp", - "begin": "(?<=\\{)", - "end": "(\\})", + "begin": "(?<=\\{|<%|\\?\\?<)", + "end": "(\\}|%>|\\?\\?>)", "endCaptures": { "1": { "name": "punctuation.section.block.end.bracket.curly.extern.cpp" @@ -2033,22 +5684,631 @@ }, "patterns": [ { - "include": "$base" + "include": "#root_context" } ] }, { "name": "meta.tail.extern.cpp", - "begin": "(?<=})[\\s\\n]*", + "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", "end": "[\\s\\n]*(?=;)", "patterns": [ { - "include": "$base" + "include": "#root_context" } ] }, { - "include": "$base" + "include": "#root_context" + } + ] + }, + "typedef_class": { + "begin": "((?\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:short|signed|unsigned|long)|(?:class|struct|union|enum))(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:(?:(?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?(?:::))?(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?]*|[^>]*+<[^>]*+>)++>\\s*)?(?![\\w<:.])))+)*))?))", + "beginCaptures": { + "1": { + "name": "meta.head.class.cpp" + }, + "3": { + "name": "storage.type.$3.cpp" + }, + "4": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, + "5": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, + "6": { + "name": "entity.name.type.$3.cpp" + }, + "7": { + "name": "storage.type.modifier.final.cpp" + }, + "8": { + "name": "colon.cpp punctuation.separator.inheritance.cpp" + }, + "9": { + "patterns": [ + { + "include": "#inheritance_context" + } + ] + } + }, + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", + "endCaptures": { + "1": { + "name": "punctuation.terminator.statement.cpp" + }, + "2": { + "name": "punctuation.terminator.statement.cpp" + } + }, + "patterns": [ + { + "name": "meta.head.class.cpp", + "begin": "\\G ?", + "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "endCaptures": { + "1": { + "name": "punctuation.section.block.begin.bracket.curly.class.cpp" + } + }, + "patterns": [ + { + "include": "#preprocessor_context" + }, + { + "include": "#inheritance_context" + }, + { + "include": "#template_call_range" + }, + { + "include": "#comments_context" + } + ] + }, + { + "name": "meta.body.class.cpp", + "begin": "(?<=\\{|<%|\\?\\?<)", + "end": "(\\}|%>|\\?\\?>)", + "endCaptures": { + "1": { + "name": "punctuation.section.block.end.bracket.curly.class.cpp" + } + }, + "patterns": [ + { + "include": "#function_pointer" + }, + { + "include": "#constructor_context" + }, + { + "include": "#root_context" + } + ] + }, + { + "name": "meta.tail.class.cpp", + "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", + "end": "[\\s\\n]*(?=;)", + "patterns": [ + { + "match": "((?:((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:\\*((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*)((?:&((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){0,2})((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?)((?\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:short|signed|unsigned|long)|(?:class|struct|union|enum))(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:(?:(?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?(?:::))?(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?]*|[^>]*+<[^>]*+>)++>\\s*)?(?![\\w<:.])))+)*))?))", + "beginCaptures": { + "1": { + "name": "meta.head.struct.cpp" + }, + "3": { + "name": "storage.type.$3.cpp" + }, + "4": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, + "5": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, + "6": { + "name": "entity.name.type.$3.cpp" + }, + "7": { + "name": "storage.type.modifier.final.cpp" + }, + "8": { + "name": "colon.cpp punctuation.separator.inheritance.cpp" + }, + "9": { + "patterns": [ + { + "include": "#inheritance_context" + } + ] + } + }, + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", + "endCaptures": { + "1": { + "name": "punctuation.terminator.statement.cpp" + }, + "2": { + "name": "punctuation.terminator.statement.cpp" + } + }, + "patterns": [ + { + "name": "meta.head.struct.cpp", + "begin": "\\G ?", + "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "endCaptures": { + "1": { + "name": "punctuation.section.block.begin.bracket.curly.struct.cpp" + } + }, + "patterns": [ + { + "include": "#preprocessor_context" + }, + { + "include": "#inheritance_context" + }, + { + "include": "#template_call_range" + }, + { + "include": "#comments_context" + } + ] + }, + { + "name": "meta.body.struct.cpp", + "begin": "(?<=\\{|<%|\\?\\?<)", + "end": "(\\}|%>|\\?\\?>)", + "endCaptures": { + "1": { + "name": "punctuation.section.block.end.bracket.curly.struct.cpp" + } + }, + "patterns": [ + { + "include": "#function_pointer" + }, + { + "include": "#constructor_context" + }, + { + "include": "#root_context" + } + ] + }, + { + "name": "meta.tail.struct.cpp", + "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", + "end": "[\\s\\n]*(?=;)", + "patterns": [ + { + "match": "((?:((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:\\*((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*)((?:&((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){0,2})((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?)((?\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\(\\(.*?\\)\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:(?:short|signed|unsigned|long)|(?:class|struct|union|enum))(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*(?:(?:(?:::)?(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?::)*\\s*+)(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s*+(?:(?]*|[^>]*+<[^>]*+>)++>\\s*)?(?:::))?(?:(?:(?:(?>\\s+)|(?:\\/\\*)(?:.+?)(?:\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))(?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?]*|[^>]*+<[^>]*+>)++>\\s*)?(?![\\w<:.])))+)*))?))", + "beginCaptures": { + "1": { + "name": "meta.head.union.cpp" + }, + "3": { + "name": "storage.type.$3.cpp" + }, + "4": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, + "5": { + "patterns": [ + { + "include": "#attributes_context" + }, + { + "include": "#number_literal" + } + ] + }, + "6": { + "name": "entity.name.type.$3.cpp" + }, + "7": { + "name": "storage.type.modifier.final.cpp" + }, + "8": { + "name": "colon.cpp punctuation.separator.inheritance.cpp" + }, + "9": { + "patterns": [ + { + "include": "#inheritance_context" + } + ] + } + }, + "end": "(?:(?:(?<=\\}|%>|\\?\\?>)\\s*(;)|(;))|(?=[;>\\[\\]=]))", + "endCaptures": { + "1": { + "name": "punctuation.terminator.statement.cpp" + }, + "2": { + "name": "punctuation.terminator.statement.cpp" + } + }, + "patterns": [ + { + "name": "meta.head.union.cpp", + "begin": "\\G ?", + "end": "((?:\\{|<%|\\?\\?<|(?=;)))", + "endCaptures": { + "1": { + "name": "punctuation.section.block.begin.bracket.curly.union.cpp" + } + }, + "patterns": [ + { + "include": "#preprocessor_context" + }, + { + "include": "#inheritance_context" + }, + { + "include": "#template_call_range" + }, + { + "include": "#comments_context" + } + ] + }, + { + "name": "meta.body.union.cpp", + "begin": "(?<=\\{|<%|\\?\\?<)", + "end": "(\\}|%>|\\?\\?>)", + "endCaptures": { + "1": { + "name": "punctuation.section.block.end.bracket.curly.union.cpp" + } + }, + "patterns": [ + { + "include": "#function_pointer" + }, + { + "include": "#constructor_context" + }, + { + "include": "#root_context" + } + ] + }, + { + "name": "meta.tail.union.cpp", + "begin": "(?<=\\}|%>|\\?\\?>)[\\s\\n]*", + "end": "[\\s\\n]*(?=;)", + "patterns": [ + { + "match": "((?:((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))((?:\\*((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))*)((?:&((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z)))){0,2})((?:(?:(?>\\s+)|(\\/\\*)(.+?)(\\*\\/))+?|(?:(?:(?:(?:\\b|(?<=\\W))|(?=\\W))|\\A)|\\Z))))?)((?(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))\\s*::\\s*~\\2|~(?>(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)))\\s*(\\()\\s*(\\))", + "captures": { "1": { - "name": "entity.name.function.destructor.cpp" + "name": "entity.name.function.destructor.cpp entity.name.function.special.destructor.cpp" }, - "2": { + "3": { "name": "punctuation.definition.parameters.begin.destructor.cpp" - } - }, - "end": "(?-mix:\\))", - "endCaptures": { - "0": { + }, + "4": { "name": "punctuation.definition.parameters.end.destructor.cpp" } }, - "patterns": [ - { - "include": "$base" - } - ] - }, - "destructor_prototype": { - "name": "meta.function.destructor.prototype.cpp", - "begin": "(?x)\n(?:\n ^ | # beginning of line\n (?:(?:,\\w])*>\\s*)))?)\\( # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\\s*\\(\n)", - "end": "(?<=\\))(?!\\w)", - "name": "meta.function-call.cpp", - "patterns": [ - { - "include": "#function_call_context_c" + "include": "#root_context" } ] }, @@ -2703,7 +7014,10 @@ }, "patterns": [ { - "include": "$base" + "include": "#parameter_struct" + }, + { + "include": "#root_context" } ] }, @@ -2802,8 +7116,7 @@ "string_escapes_context_c": { "patterns": [ { - "match": "(?x)\\\\ (\n\\\\\t\t\t |\n[abefnprtv'\"?] |\n[0-3]\\d{,2}\t |\n[4-7]\\d?\t\t|\nx[a-fA-F0-9]{,2} |\nu[a-fA-F0-9]{,4} |\nU[a-fA-F0-9]{,8} )", - "name": "constant.character.escape.cpp" + "include": "#backslash_escapes" }, { "match": "\\\\.", @@ -2883,7 +7196,7 @@ ] }, { - "include": "$base" + "include": "#root_context" } ] }, @@ -3101,12 +7414,12 @@ ] }, { - "include": "$base" + "include": "#root_context" } ] }, { - "contentName": "comment.block.preprocessor.if-branch", + "contentName": "comment.block.preprocessor.if-branch.cpp", "begin": "\\n", "end": "(?=^\\s*((#)\\s*(?:else|elif|endif)\\b))", "patterns": [ @@ -3199,7 +7512,7 @@ ] }, { - "contentName": "comment.block.preprocessor.if-branch.in-block", + "contentName": "comment.block.preprocessor.if-branch.in-block.cpp", "begin": "\\n", "end": "(?=^\\s*((#)\\s*(?:else|elif|endif)\\b))", "patterns": [ @@ -3242,7 +7555,7 @@ "include": "#comments_context" }, { - "contentName": "comment.block.preprocessor.elif-branch", + "contentName": "comment.block.preprocessor.elif-branch.cpp", "begin": "\\n", "end": "(?=^\\s*((#)\\s*(?:else|elif|endif)\\b))", "patterns": [ @@ -3299,7 +7612,7 @@ "include": "#comments_context" }, { - "contentName": "comment.block.preprocessor.else-branch", + "contentName": "comment.block.preprocessor.else-branch.cpp", "begin": "^\\s*((#)\\s*else\\b)", "beginCaptures": { "0": { @@ -3323,7 +7636,7 @@ ] }, { - "contentName": "comment.block.preprocessor.if-branch", + "contentName": "comment.block.preprocessor.if-branch.cpp", "begin": "^\\s*((#)\\s*elif\\b)", "beginCaptures": { "0": { @@ -3351,7 +7664,7 @@ "end": "(?=^\\s*((#)\\s*(?:else|elif|endif)\\b))", "patterns": [ { - "include": "$base" + "include": "#root_context" } ] } @@ -3397,7 +7710,7 @@ "include": "#comments_context" }, { - "contentName": "comment.block.preprocessor.else-branch.in-block", + "contentName": "comment.block.preprocessor.else-branch.in-block.cpp", "begin": "^\\s*((#)\\s*else\\b)", "beginCaptures": { "0": { @@ -3421,7 +7734,7 @@ ] }, { - "contentName": "comment.block.preprocessor.if-branch.in-block", + "contentName": "comment.block.preprocessor.if-branch.in-block.cpp", "begin": "^\\s*((#)\\s*elif\\b)", "beginCaptures": { "0": { @@ -3488,7 +7801,7 @@ "end": "(?=^\\s*((#)\\s*(?:endif)\\b))", "patterns": [ { - "contentName": "comment.block.preprocessor.elif-branch", + "contentName": "comment.block.preprocessor.elif-branch.cpp", "begin": "^\\s*((#)\\s*(else)\\b)", "beginCaptures": { "0": { @@ -3512,7 +7825,7 @@ ] }, { - "contentName": "comment.block.preprocessor.elif-branch", + "contentName": "comment.block.preprocessor.elif-branch.cpp", "begin": "^\\s*((#)\\s*(elif)\\b)", "beginCaptures": { "0": { @@ -3536,7 +7849,7 @@ ] }, { - "include": "$base" + "include": "#root_context" } ] } @@ -3575,7 +7888,7 @@ "end": "(?=^\\s*((#)\\s*(?:endif)\\b))", "patterns": [ { - "contentName": "comment.block.preprocessor.elif-branch.in-block", + "contentName": "comment.block.preprocessor.elif-branch.in-block.cpp", "begin": "^\\s*((#)\\s*(else)\\b)", "beginCaptures": { "0": { @@ -3599,7 +7912,7 @@ ] }, { - "contentName": "comment.block.preprocessor.elif-branch", + "contentName": "comment.block.preprocessor.elif-branch.cpp", "begin": "^\\s*((#)\\s*(elif)\\b)", "beginCaptures": { "0": { @@ -3645,7 +7958,7 @@ "end": "(?=^\\s*((#)\\s*endif\\b))", "patterns": [ { - "include": "$base" + "include": "#root_context" } ] }, @@ -3675,7 +7988,7 @@ "include": "#vararg_ellipses" }, { - "match": "(?-mix:##?(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F]))(?:(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U000[0-9a-fA-F])))*(?!\\w))", + "match": "(?-mix:##?(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?!\\w))", "name": "variable.other.macro.argument.cpp" }, { @@ -3707,7 +8020,7 @@ "name": "punctuation.section.parens.end.bracket.round.cpp" }, { - "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|return|typeid|alignof|alignas|sizeof|and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq|alignof|alignas|asm|__asm__|auto|bool|_Bool|char|_Complex|double|enum|float|_Imaginary|int|long|short|signed|struct|typedef|union|unsigned|void)\\s*\\()\n(?=\n (?:[A-Za-z_][A-Za-z0-9_]*+|::)++\\s*\\( # actual name\n |\n (?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\\s*\\(\n)", + "begin": "(?x)\n(?=+!]+|\\(\\)|\\[\\]))\\s*\\(\n)", "end": "(?<=\\))(?!\\w)|(?=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", - "beginCaptures": { - "1": { - "name": "entity.name.function.cpp" - }, - "2": { - "name": "punctuation.section.parameters.begin.bracket.round.cpp" - } - }, - "end": "(?-mix:\\)|:)", - "endCaptures": { - "0": { - "name": "punctuation.section.parameters.end.bracket.round.cpp" - } - }, - "patterns": [ - { - "include": "#probably_a_parameter" - }, - { - "include": "#function_context_c" - } - ] + "include": "#parentheses" }, { - "begin": "\\(", - "beginCaptures": { - "0": { - "name": "punctuation.section.parens.begin.bracket.round.cpp" - } - }, - "end": "\\)", - "endCaptures": { - "0": { - "name": "punctuation.section.parens.end.bracket.round.cpp" - } - }, - "patterns": [ - { - "include": "#function_context_c" - } - ] - }, - { - "include": "$base" - } - ] - }, - "function_call_context_c": { - "patterns": [ - { - "include": "#attributes" - }, - { - "include": "#comments_context" - }, - { - "include": "#storage_types" - }, - { - "include": "#method_access" - }, - { - "include": "#member_access" - }, - { - "include": "#operators" - }, - { - "begin": "(?x)\n(?!(?:while|for|do|if|else|switch|catch|return|typeid|alignof|alignas|sizeof|and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|typeid|xor|xor_eq|alignof|alignas)\\s*\\()\n(\n(?:new)\\s*((?-mix:(?:(?-mix:(?:<(?:[\\s<>:,\\w])*>\\s*)))?)) # actual name\n|\n(?:(?<=operator)(?:[-*&<>=+!]+|\\(\\)|\\[\\]))\n)\n\\s*(\\()", - "beginCaptures": { - "1": { - "name": "keyword.operator.wordlike.cpp memory.cpp keyword.operator.new.cpp" - }, - "2": { - "patterns": [ - { - "include": "#template_call_innards" - } - ] - }, - "3": { - "name": "punctuation.section.arguments.begin.bracket.round.cpp" - } - }, - "end": "\\)", - "endCaptures": { - "0": { - "name": "punctuation.section.arguments.end.bracket.round.cpp" - } - }, - "patterns": [ - { - "include": "#function_call_context_c" - } - ] + "include": "#type_casting_operators" }, { "include": "#function_call" }, { - "begin": "\\(", - "beginCaptures": { - "0": { - "name": "punctuation.section.parens.begin.bracket.round.cpp" - } - }, - "end": "\\)", - "endCaptures": { - "0": { - "name": "punctuation.section.parens.end.bracket.round.cpp" - } - }, - "patterns": [ - { - "include": "#function_call_context_c" - } - ] + "include": "#scope_resolution_inner_generated" }, { - "include": "#block_context" + "include": "#storage_types" + }, + { + "include": "#line_continuation_character" + }, + { + "include": "#square_brackets" + }, + { + "include": "#empty_square_brackets" + }, + { + "include": "#semicolon" + }, + { + "include": "#comma" } ] } diff --git a/extensions/cpp/test/colorize-results/test-23630_cpp.json b/extensions/cpp/test/colorize-results/test-23630_cpp.json index a58961ae945..55aec19d2cb 100644 --- a/extensions/cpp/test/colorize-results/test-23630_cpp.json +++ b/extensions/cpp/test/colorize-results/test-23630_cpp.json @@ -1,7 +1,7 @@ [ { "c": "#", - "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", + "t": "source.cpp source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -12,7 +12,7 @@ }, { "c": "ifndef", - "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", + "t": "source.cpp source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -23,7 +23,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.cpp", + "t": "source.cpp source.cpp meta.preprocessor.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -34,7 +34,7 @@ }, { "c": "_UCRT", - "t": "source.cpp meta.preprocessor.cpp entity.name.function.preprocessor.cpp", + "t": "source.cpp source.cpp meta.preprocessor.cpp entity.name.function.preprocessor.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -45,7 +45,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.macro.cpp", + "t": "source.cpp source.cpp meta.preprocessor.macro.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -56,7 +56,7 @@ }, { "c": "#", - "t": "source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp punctuation.definition.directive.cpp", + "t": "source.cpp source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -67,7 +67,7 @@ }, { "c": "define", - "t": "source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp", + "t": "source.cpp source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -78,7 +78,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.macro.cpp", + "t": "source.cpp source.cpp meta.preprocessor.macro.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -89,7 +89,7 @@ }, { "c": "_UCRT", - "t": "source.cpp meta.preprocessor.macro.cpp entity.name.function.preprocessor.cpp", + "t": "source.cpp source.cpp meta.preprocessor.macro.cpp entity.name.function.preprocessor.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -100,7 +100,7 @@ }, { "c": "#", - "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", + "t": "source.cpp source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -111,7 +111,7 @@ }, { "c": "endif", - "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", + "t": "source.cpp source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", diff --git a/extensions/cpp/test/colorize-results/test-23850_cpp.json b/extensions/cpp/test/colorize-results/test-23850_cpp.json index 924bbc78243..dfcdfd6787a 100644 --- a/extensions/cpp/test/colorize-results/test-23850_cpp.json +++ b/extensions/cpp/test/colorize-results/test-23850_cpp.json @@ -1,7 +1,7 @@ [ { "c": "#", - "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", + "t": "source.cpp source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -12,7 +12,7 @@ }, { "c": "ifndef", - "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", + "t": "source.cpp source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -23,7 +23,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.cpp", + "t": "source.cpp source.cpp meta.preprocessor.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -34,7 +34,7 @@ }, { "c": "_UCRT", - "t": "source.cpp meta.preprocessor.cpp entity.name.function.preprocessor.cpp", + "t": "source.cpp source.cpp meta.preprocessor.cpp entity.name.function.preprocessor.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -45,7 +45,7 @@ }, { "c": "#", - "t": "source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp punctuation.definition.directive.cpp", + "t": "source.cpp source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -56,7 +56,7 @@ }, { "c": "define", - "t": "source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp", + "t": "source.cpp source.cpp meta.preprocessor.macro.cpp keyword.control.directive.define.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -67,7 +67,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.macro.cpp", + "t": "source.cpp source.cpp meta.preprocessor.macro.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -78,7 +78,7 @@ }, { "c": "_UCRT", - "t": "source.cpp meta.preprocessor.macro.cpp entity.name.function.preprocessor.cpp", + "t": "source.cpp source.cpp meta.preprocessor.macro.cpp entity.name.function.preprocessor.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -89,7 +89,7 @@ }, { "c": "#", - "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", + "t": "source.cpp source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -100,7 +100,7 @@ }, { "c": "endif", - "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", + "t": "source.cpp source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", diff --git a/extensions/cpp/test/colorize-results/test_c.json b/extensions/cpp/test/colorize-results/test_c.json index 5a83a3ac616..74be734c5ca 100644 --- a/extensions/cpp/test/colorize-results/test_c.json +++ b/extensions/cpp/test/colorize-results/test_c.json @@ -848,7 +848,7 @@ }, { "c": "4", - "t": "source.c meta.block.c constant.numeric.c", + "t": "source.c meta.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -980,7 +980,7 @@ }, { "c": "0", - "t": "source.c meta.block.c meta.parens.block.c constant.numeric.c", + "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1178,7 +1178,7 @@ }, { "c": "2", - "t": "source.c meta.block.c meta.parens.block.c constant.numeric.c", + "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1387,7 +1387,7 @@ }, { "c": "2", - "t": "source.c meta.block.c meta.parens.block.c constant.numeric.c", + "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1717,7 +1717,7 @@ }, { "c": "0", - "t": "source.c meta.block.c meta.parens.block.c constant.numeric.c", + "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1860,7 +1860,7 @@ }, { "c": "2", - "t": "source.c meta.block.c meta.parens.block.c constant.numeric.c", + "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -2223,7 +2223,7 @@ }, { "c": "2", - "t": "source.c meta.block.c meta.parens.block.c constant.numeric.c", + "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -2388,7 +2388,7 @@ }, { "c": "2", - "t": "source.c meta.block.c meta.parens.block.c constant.numeric.c", + "t": "source.c meta.block.c meta.parens.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -2762,7 +2762,7 @@ }, { "c": "0", - "t": "source.c meta.block.c constant.numeric.c", + "t": "source.c meta.block.c constant.numeric.decimal.c", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", diff --git a/extensions/cpp/test/colorize-results/test_cc.json b/extensions/cpp/test/colorize-results/test_cc.json index e53e3ede6e4..81cecfffcd2 100644 --- a/extensions/cpp/test/colorize-results/test_cc.json +++ b/extensions/cpp/test/colorize-results/test_cc.json @@ -1,7 +1,7 @@ [ { "c": "#", - "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", + "t": "source.cpp source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -12,7 +12,7 @@ }, { "c": "if", - "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", + "t": "source.cpp source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -23,7 +23,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.cpp", + "t": "source.cpp source.cpp meta.preprocessor.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -34,7 +34,7 @@ }, { "c": "B4G_DEBUG_CHECK", - "t": "source.cpp meta.preprocessor.cpp entity.name.function.preprocessor.cpp", + "t": "source.cpp source.cpp meta.preprocessor.cpp entity.name.function.preprocessor.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -45,7 +45,7 @@ }, { "c": " ", - "t": "source.cpp", + "t": "source.cpp source.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -56,7 +56,7 @@ }, { "c": "fprintf", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", + "t": "source.cpp source.cpp entity.name.function.call.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -67,7 +67,7 @@ }, { "c": "(", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", + "t": "source.cpp source.cpp punctuation.section.arguments.begin.bracket.round.function.call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -78,7 +78,7 @@ }, { "c": "stderr", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp", + "t": "source.cpp source.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -89,7 +89,7 @@ }, { "c": ",", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp comma.cpp punctuation.separator.delimiter.cpp", + "t": "source.cpp source.cpp comma.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -100,7 +100,7 @@ }, { "c": "\"", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp source.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -111,7 +111,7 @@ }, { "c": "num_candidate_ret=", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp", + "t": "source.cpp source.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -122,7 +122,7 @@ }, { "c": "%d", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp constant.other.placeholder.cpp", + "t": "source.cpp source.cpp string.quoted.double.cpp constant.other.placeholder.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -133,7 +133,7 @@ }, { "c": ":", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp", + "t": "source.cpp source.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -144,7 +144,7 @@ }, { "c": "\"", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp source.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -155,7 +155,7 @@ }, { "c": ",", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp comma.cpp punctuation.separator.delimiter.cpp", + "t": "source.cpp source.cpp comma.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -166,7 +166,7 @@ }, { "c": " num_candidate", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp", + "t": "source.cpp source.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -177,7 +177,7 @@ }, { "c": ")", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", + "t": "source.cpp source.cpp punctuation.section.arguments.end.bracket.round.function.call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -188,7 +188,7 @@ }, { "c": ";", - "t": "source.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -199,7 +199,7 @@ }, { "c": " ", - "t": "source.cpp", + "t": "source.cpp source.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -210,7 +210,7 @@ }, { "c": "for", - "t": "source.cpp keyword.control.for.cpp", + "t": "source.cpp source.cpp keyword.control.for.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -221,7 +221,7 @@ }, { "c": "(", - "t": "source.cpp meta.parens.cpp punctuation.section.parens.begin.bracket.round.cpp", + "t": "source.cpp source.cpp meta.parens.cpp punctuation.section.parens.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -232,7 +232,7 @@ }, { "c": "int", - "t": "source.cpp meta.parens.cpp storage.type.primitive.cpp", + "t": "source.cpp source.cpp meta.parens.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -243,7 +243,7 @@ }, { "c": " i", - "t": "source.cpp meta.parens.cpp", + "t": "source.cpp source.cpp meta.parens.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -254,7 +254,7 @@ }, { "c": "=", - "t": "source.cpp meta.parens.cpp keyword.operator.assignment.cpp", + "t": "source.cpp source.cpp meta.parens.cpp keyword.operator.assignment.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -265,7 +265,7 @@ }, { "c": "0", - "t": "source.cpp meta.parens.cpp constant.numeric.decimal.cpp", + "t": "source.cpp source.cpp meta.parens.cpp constant.numeric.decimal.cpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -276,7 +276,7 @@ }, { "c": ";", - "t": "source.cpp meta.parens.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp meta.parens.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -287,7 +287,7 @@ }, { "c": "i", - "t": "source.cpp meta.parens.cpp", + "t": "source.cpp source.cpp meta.parens.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -298,7 +298,7 @@ }, { "c": "<", - "t": "source.cpp meta.parens.cpp keyword.operator.comparison.cpp", + "t": "source.cpp source.cpp meta.parens.cpp keyword.operator.comparison.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -309,7 +309,7 @@ }, { "c": "num_candidate", - "t": "source.cpp meta.parens.cpp", + "t": "source.cpp source.cpp meta.parens.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -320,7 +320,7 @@ }, { "c": ";", - "t": "source.cpp meta.parens.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp meta.parens.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -331,7 +331,7 @@ }, { "c": "++", - "t": "source.cpp meta.parens.cpp keyword.operator.increment.cpp", + "t": "source.cpp source.cpp meta.parens.cpp keyword.operator.increment.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -342,7 +342,7 @@ }, { "c": "i", - "t": "source.cpp meta.parens.cpp", + "t": "source.cpp source.cpp meta.parens.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -353,7 +353,7 @@ }, { "c": ")", - "t": "source.cpp meta.parens.cpp punctuation.section.parens.end.bracket.round.cpp", + "t": "source.cpp source.cpp meta.parens.cpp punctuation.section.parens.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -364,7 +364,7 @@ }, { "c": " ", - "t": "source.cpp", + "t": "source.cpp source.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -375,7 +375,7 @@ }, { "c": "fprintf", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", + "t": "source.cpp source.cpp entity.name.function.call.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -386,7 +386,7 @@ }, { "c": "(", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", + "t": "source.cpp source.cpp punctuation.section.arguments.begin.bracket.round.function.call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -397,7 +397,7 @@ }, { "c": "stderr", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp", + "t": "source.cpp source.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -408,7 +408,7 @@ }, { "c": ",", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp comma.cpp punctuation.separator.delimiter.cpp", + "t": "source.cpp source.cpp comma.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -419,7 +419,7 @@ }, { "c": "\"", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp source.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -430,7 +430,7 @@ }, { "c": "%d", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp constant.other.placeholder.cpp", + "t": "source.cpp source.cpp string.quoted.double.cpp constant.other.placeholder.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -441,7 +441,7 @@ }, { "c": ",", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp", + "t": "source.cpp source.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -452,7 +452,7 @@ }, { "c": "\"", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp source.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -463,7 +463,7 @@ }, { "c": ",", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp comma.cpp punctuation.separator.delimiter.cpp", + "t": "source.cpp source.cpp comma.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -474,7 +474,7 @@ }, { "c": "user_candidate", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp meta.bracket.square.access.cpp variable.other.object.cpp", + "t": "source.cpp source.cpp meta.bracket.square.access.cpp variable.other.object.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -485,7 +485,7 @@ }, { "c": "[", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp meta.bracket.square.access.cpp punctuation.definition.begin.bracket.square.cpp", + "t": "source.cpp source.cpp meta.bracket.square.access.cpp punctuation.definition.begin.bracket.square.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -496,7 +496,7 @@ }, { "c": "i", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp meta.bracket.square.access.cpp", + "t": "source.cpp source.cpp meta.bracket.square.access.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -507,7 +507,7 @@ }, { "c": "]", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp meta.bracket.square.access.cpp punctuation.definition.end.bracket.square.cpp", + "t": "source.cpp source.cpp meta.bracket.square.access.cpp punctuation.definition.end.bracket.square.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -518,7 +518,7 @@ }, { "c": ")", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", + "t": "source.cpp source.cpp punctuation.section.arguments.end.bracket.round.function.call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -529,7 +529,7 @@ }, { "c": ";", - "t": "source.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -540,7 +540,7 @@ }, { "c": " ", - "t": "source.cpp", + "t": "source.cpp source.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -551,7 +551,7 @@ }, { "c": "fprintf", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", + "t": "source.cpp source.cpp entity.name.function.call.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -562,7 +562,7 @@ }, { "c": "(", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", + "t": "source.cpp source.cpp punctuation.section.arguments.begin.bracket.round.function.call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -573,7 +573,7 @@ }, { "c": "stderr", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp", + "t": "source.cpp source.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -584,7 +584,7 @@ }, { "c": ",", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp comma.cpp punctuation.separator.delimiter.cpp", + "t": "source.cpp source.cpp comma.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -595,7 +595,7 @@ }, { "c": "\"", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp source.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -606,7 +606,7 @@ }, { "c": ";", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp", + "t": "source.cpp source.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -617,7 +617,7 @@ }, { "c": "\"", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp source.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -628,7 +628,7 @@ }, { "c": ")", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", + "t": "source.cpp source.cpp punctuation.section.arguments.end.bracket.round.function.call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -639,7 +639,7 @@ }, { "c": ";", - "t": "source.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -650,7 +650,7 @@ }, { "c": "#", - "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", + "t": "source.cpp source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -661,7 +661,7 @@ }, { "c": "endif", - "t": "source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", + "t": "source.cpp source.cpp meta.preprocessor.cpp keyword.control.directive.conditional.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -672,7 +672,7 @@ }, { "c": " ", - "t": "source.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.qualified_type.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -683,7 +683,7 @@ }, { "c": "void", - "t": "source.cpp storage.type.primitive.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.qualified_type.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -694,7 +694,7 @@ }, { "c": " ", - "t": "source.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -705,7 +705,7 @@ }, { "c": "main", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp entity.name.function.definition.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -716,7 +716,7 @@ }, { "c": "(", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -727,7 +727,7 @@ }, { "c": "O ", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -738,7 +738,7 @@ }, { "c": "obj", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp variable.parameter.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp variable.parameter.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -749,7 +749,7 @@ }, { "c": ")", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -760,7 +760,7 @@ }, { "c": " ", - "t": "source.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -771,7 +771,7 @@ }, { "c": "{", - "t": "source.cpp meta.block.cpp punctuation.section.block.begin.bracket.curly.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.block.begin.bracket.curly.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -782,7 +782,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -793,7 +793,7 @@ }, { "c": "LOG_INFO", - "t": "source.cpp meta.block.cpp meta.function-call.cpp entity.name.function.call.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp entity.name.function.call.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -804,7 +804,7 @@ }, { "c": "(", - "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.begin.bracket.round.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.arguments.begin.bracket.round.function.call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -815,7 +815,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -826,7 +826,7 @@ }, { "c": "not hilighted as string", - "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -837,7 +837,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -848,7 +848,7 @@ }, { "c": ")", - "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.end.bracket.round.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.arguments.end.bracket.round.function.call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -859,7 +859,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -870,7 +870,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -881,7 +881,7 @@ }, { "c": "LOG_INFO", - "t": "source.cpp meta.block.cpp meta.function-call.cpp entity.name.function.call.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp entity.name.function.call.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -892,7 +892,7 @@ }, { "c": "(", - "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.begin.bracket.round.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.arguments.begin.bracket.round.function.call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -903,7 +903,7 @@ }, { "c": "obj ", - "t": "source.cpp meta.block.cpp meta.function-call.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -914,7 +914,7 @@ }, { "c": "<<", - "t": "source.cpp meta.block.cpp meta.function-call.cpp keyword.operator.bitwise.shift.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp keyword.operator.bitwise.shift.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -925,7 +925,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp meta.function-call.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -936,7 +936,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -947,7 +947,7 @@ }, { "c": ", even worse; ", - "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -958,7 +958,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -969,7 +969,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp meta.function-call.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -980,7 +980,7 @@ }, { "c": "<<", - "t": "source.cpp meta.block.cpp meta.function-call.cpp keyword.operator.bitwise.shift.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp keyword.operator.bitwise.shift.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -991,7 +991,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp meta.function-call.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1002,7 +1002,7 @@ }, { "c": "obj", - "t": "source.cpp meta.block.cpp meta.function-call.cpp variable.other.object.access.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp variable.other.object.access.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1013,7 +1013,7 @@ }, { "c": ".", - "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.separator.dot-access.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.separator.dot-access.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1024,7 +1024,7 @@ }, { "c": "x", - "t": "source.cpp meta.block.cpp meta.function-call.cpp variable.other.property.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp variable.other.property.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1035,7 +1035,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp meta.function-call.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1046,7 +1046,7 @@ }, { "c": "<<", - "t": "source.cpp meta.block.cpp meta.function-call.cpp keyword.operator.bitwise.shift.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp keyword.operator.bitwise.shift.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1057,7 +1057,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp meta.function-call.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1068,7 +1068,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1079,7 +1079,7 @@ }, { "c": " check this out.", - "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1090,7 +1090,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1101,7 +1101,7 @@ }, { "c": ")", - "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.end.bracket.round.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.arguments.end.bracket.round.function.call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1112,7 +1112,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1123,7 +1123,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp punctuation.whitespace.comment.leading.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.whitespace.comment.leading.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1134,7 +1134,7 @@ }, { "c": "//", - "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp punctuation.definition.comment.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp comment.line.double-slash.cpp punctuation.definition.comment.cpp", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -1145,7 +1145,7 @@ }, { "c": " everything from this point on is interpeted as a string literal...", - "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp comment.line.double-slash.cpp", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -1156,7 +1156,7 @@ }, { "c": " O x", - "t": "source.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1167,7 +1167,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1178,7 +1178,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp meta.scope-resolution.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1189,18 +1189,18 @@ }, { "c": "std", - "t": "source.cpp meta.block.cpp meta.scope-resolution.cpp entity.name.type.namespace.scope-resolution.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp entity.name.scope-resolution.cpp", "r": { - "dark_plus": "entity.name.type: #4EC9B0", - "light_plus": "entity.name.type: #267F99", + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "entity.name.type: #4EC9B0" + "hc_black": "entity.name.scope-resolution: #4EC9B0" } }, { "c": "::", - "t": "source.cpp meta.block.cpp meta.scope-resolution.cpp punctuation.separator.namespace.access.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1211,7 +1211,7 @@ }, { "c": "unique_ptr", - "t": "source.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1222,7 +1222,7 @@ }, { "c": "<", - "t": "source.cpp meta.block.cpp keyword.operator.comparison.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp keyword.operator.comparison.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1233,7 +1233,7 @@ }, { "c": "O", - "t": "source.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1244,7 +1244,7 @@ }, { "c": ">", - "t": "source.cpp meta.block.cpp keyword.operator.comparison.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp keyword.operator.comparison.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1255,7 +1255,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1266,7 +1266,7 @@ }, { "c": "o", - "t": "source.cpp meta.block.cpp meta.function-call.cpp entity.name.function.call.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp entity.name.function.call.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -1277,7 +1277,7 @@ }, { "c": "(", - "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.begin.bracket.round.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.arguments.begin.bracket.round.function.call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1288,18 +1288,18 @@ }, { "c": "new", - "t": "source.cpp meta.block.cpp meta.function-call.cpp keyword.operator.wordlike.cpp alias.cpp keyword.operator.new.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp keyword.operator.wordlike.cpp memory.cpp keyword.operator.new.cpp", "r": { - "dark_plus": "keyword.operator.new.cpp: #C586C0", - "light_plus": "keyword.operator.new.cpp: #AF00DB", + "dark_plus": "source.cpp keyword.operator.new: #C586C0", + "light_plus": "source.cpp keyword.operator.new: #AF00DB", "dark_vs": "keyword.operator.new: #569CD6", "light_vs": "keyword.operator.new: #0000FF", - "hc_black": "keyword.operator.new.cpp: #C586C0" + "hc_black": "source.cpp keyword.operator.new: #C586C0" } }, { "c": " O", - "t": "source.cpp meta.block.cpp meta.function-call.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1310,7 +1310,7 @@ }, { "c": ")", - "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.end.bracket.round.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.arguments.end.bracket.round.function.call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1321,7 +1321,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1332,7 +1332,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp punctuation.whitespace.comment.leading.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.whitespace.comment.leading.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1343,7 +1343,7 @@ }, { "c": "//", - "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp punctuation.definition.comment.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp comment.line.double-slash.cpp punctuation.definition.comment.cpp", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -1354,7 +1354,7 @@ }, { "c": " sadness.", - "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp comment.line.double-slash.cpp", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -1365,7 +1365,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1376,7 +1376,7 @@ }, { "c": "sprintf", - "t": "source.cpp meta.block.cpp meta.function-call.cpp entity.name.function.call.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp entity.name.function.call.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -1387,7 +1387,7 @@ }, { "c": "(", - "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.begin.bracket.round.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.arguments.begin.bracket.round.function.call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1398,7 +1398,7 @@ }, { "c": "options", - "t": "source.cpp meta.block.cpp meta.function-call.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1409,7 +1409,7 @@ }, { "c": ",", - "t": "source.cpp meta.block.cpp meta.function-call.cpp comma.cpp punctuation.separator.delimiter.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp comma.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1420,7 +1420,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp meta.function-call.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1431,7 +1431,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1442,7 +1442,7 @@ }, { "c": "STYLE=Keramik;TITLE=", - "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1453,7 +1453,7 @@ }, { "c": "%s", - "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp constant.other.placeholder.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp constant.other.placeholder.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1464,7 +1464,7 @@ }, { "c": ";THEME=", - "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1475,7 +1475,7 @@ }, { "c": "%s", - "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp constant.other.placeholder.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp constant.other.placeholder.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1486,7 +1486,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1497,7 +1497,7 @@ }, { "c": ",", - "t": "source.cpp meta.block.cpp meta.function-call.cpp comma.cpp punctuation.separator.delimiter.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp comma.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1508,7 +1508,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp meta.function-call.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1519,7 +1519,7 @@ }, { "c": "...", - "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.vararg-ellipses.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.vararg-ellipses.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1530,7 +1530,7 @@ }, { "c": ")", - "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.end.bracket.round.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.arguments.end.bracket.round.function.call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1541,7 +1541,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1552,7 +1552,7 @@ }, { "c": "}", - "t": "source.cpp meta.block.cpp punctuation.section.block.end.bracket.curly.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.block.end.bracket.curly.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1563,7 +1563,7 @@ }, { "c": "int", - "t": "source.cpp storage.type.primitive.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.qualified_type.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -1574,7 +1574,7 @@ }, { "c": " ", - "t": "source.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1585,7 +1585,7 @@ }, { "c": "main2", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp entity.name.function.definition.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -1596,7 +1596,7 @@ }, { "c": "(", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1607,7 +1607,7 @@ }, { "c": ")", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1618,7 +1618,7 @@ }, { "c": " ", - "t": "source.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1629,7 +1629,7 @@ }, { "c": "{", - "t": "source.cpp meta.block.cpp punctuation.section.block.begin.bracket.curly.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.block.begin.bracket.curly.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1640,7 +1640,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1651,7 +1651,7 @@ }, { "c": "printf", - "t": "source.cpp meta.block.cpp meta.function-call.cpp entity.name.function.call.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp entity.name.function.call.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -1662,7 +1662,7 @@ }, { "c": "(", - "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.begin.bracket.round.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.arguments.begin.bracket.round.function.call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1673,7 +1673,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1684,7 +1684,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1695,7 +1695,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1706,7 +1706,7 @@ }, { "c": ")", - "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.end.bracket.round.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.arguments.end.bracket.round.function.call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1717,7 +1717,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1728,7 +1728,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp punctuation.whitespace.comment.leading.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.whitespace.comment.leading.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1739,7 +1739,7 @@ }, { "c": "//", - "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp punctuation.definition.comment.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp comment.line.double-slash.cpp punctuation.definition.comment.cpp", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -1750,7 +1750,7 @@ }, { "c": " the rest of", - "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp comment.line.double-slash.cpp", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -1761,7 +1761,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1771,8 +1771,74 @@ } }, { - "c": "asm(\"movw $0x38, %ax; ltr %ax\");", - "t": "source.cpp meta.block.cpp meta.function-call.cpp", + "c": "asm", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp meta.asm.cpp storage.type.asm.cpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": "(", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp meta.asm.cpp punctuation.section.parens.begin.bracket.round.assembly.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp meta.asm.cpp string.quoted.double.cpp punctuation.definition.string.begin.assembly.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "movw $0x38, %ax; ltr %ax", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp meta.asm.cpp string.quoted.double.cpp meta.embedded.assembly.cpp", + "r": { + "dark_plus": "meta.embedded: #D4D4D4", + "light_plus": "meta.embedded: #000000", + "dark_vs": "meta.embedded: #D4D4D4", + "light_vs": "meta.embedded: #000000", + "hc_black": "meta.embedded: #FFFFFF" + } + }, + { + "c": "\"", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp meta.asm.cpp string.quoted.double.cpp punctuation.definition.string.end.assembly.cpp", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp meta.asm.cpp punctuation.section.parens.end.bracket.round.assembly.cpp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ";", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1783,7 +1849,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp meta.function-call.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1794,7 +1860,7 @@ }, { "c": "fn", - "t": "source.cpp meta.block.cpp meta.function-call.cpp entity.name.function.call.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp entity.name.function.call.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -1805,7 +1871,7 @@ }, { "c": "(", - "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.begin.bracket.round.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.arguments.begin.bracket.round.function.call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1816,7 +1882,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1827,7 +1893,7 @@ }, { "c": "{};", - "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1838,7 +1904,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.cpp meta.function-call.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1849,7 +1915,7 @@ }, { "c": ")", - "t": "source.cpp meta.block.cpp meta.function-call.cpp punctuation.section.arguments.end.bracket.round.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.arguments.end.bracket.round.function.call.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1860,7 +1926,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1871,7 +1937,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp punctuation.whitespace.comment.leading.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.whitespace.comment.leading.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1882,7 +1948,7 @@ }, { "c": "//", - "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp punctuation.definition.comment.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp comment.line.double-slash.cpp punctuation.definition.comment.cpp", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -1893,7 +1959,7 @@ }, { "c": " the rest of", - "t": "source.cpp meta.block.cpp comment.line.double-slash.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp comment.line.double-slash.cpp", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -1904,7 +1970,7 @@ }, { "c": "}", - "t": "source.cpp meta.block.cpp punctuation.section.block.end.bracket.curly.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.block.end.bracket.curly.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/cpp/test/colorize-results/test_cpp.json b/extensions/cpp/test/colorize-results/test_cpp.json index d82030e7d00..6254e50c6a6 100644 --- a/extensions/cpp/test/colorize-results/test_cpp.json +++ b/extensions/cpp/test/colorize-results/test_cpp.json @@ -1,7 +1,7 @@ [ { "c": "//", - "t": "source.cpp comment.line.double-slash.cpp punctuation.definition.comment.cpp", + "t": "source.cpp source.cpp comment.line.double-slash.cpp punctuation.definition.comment.cpp", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -12,7 +12,7 @@ }, { "c": " classes example", - "t": "source.cpp comment.line.double-slash.cpp", + "t": "source.cpp source.cpp comment.line.double-slash.cpp", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -23,7 +23,7 @@ }, { "c": "#", - "t": "source.cpp meta.preprocessor.include.cpp keyword.control.directive.include.cpp punctuation.definition.directive.cpp", + "t": "source.cpp source.cpp meta.preprocessor.include.cpp keyword.control.directive.include.cpp punctuation.definition.directive.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -34,7 +34,7 @@ }, { "c": "include", - "t": "source.cpp meta.preprocessor.include.cpp keyword.control.directive.include.cpp", + "t": "source.cpp source.cpp meta.preprocessor.include.cpp keyword.control.directive.include.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -45,7 +45,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.include.cpp", + "t": "source.cpp source.cpp meta.preprocessor.include.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -56,7 +56,7 @@ }, { "c": "<", - "t": "source.cpp meta.preprocessor.include.cpp string.quoted.other.lt-gt.include.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp source.cpp meta.preprocessor.include.cpp string.quoted.other.lt-gt.include.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -67,7 +67,7 @@ }, { "c": "iostream", - "t": "source.cpp meta.preprocessor.include.cpp string.quoted.other.lt-gt.include.cpp", + "t": "source.cpp source.cpp meta.preprocessor.include.cpp string.quoted.other.lt-gt.include.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -78,7 +78,7 @@ }, { "c": ">", - "t": "source.cpp meta.preprocessor.include.cpp string.quoted.other.lt-gt.include.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp source.cpp meta.preprocessor.include.cpp string.quoted.other.lt-gt.include.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -89,7 +89,7 @@ }, { "c": "using", - "t": "source.cpp meta.using-namespace.cpp keyword.other.using.directive.cpp", + "t": "source.cpp source.cpp meta.using-namespace.cpp keyword.other.using.directive.cpp", "r": { "dark_plus": "keyword.other.using: #C586C0", "light_plus": "keyword.other.using: #AF00DB", @@ -100,7 +100,7 @@ }, { "c": " ", - "t": "source.cpp meta.using-namespace.cpp", + "t": "source.cpp source.cpp meta.using-namespace.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -111,7 +111,7 @@ }, { "c": "namespace", - "t": "source.cpp meta.using-namespace.cpp keyword.other.namespace.directive.cpp storage.type.namespace.directive.cpp", + "t": "source.cpp source.cpp meta.using-namespace.cpp keyword.other.namespace.directive.cpp storage.type.namespace.directive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -122,7 +122,7 @@ }, { "c": " ", - "t": "source.cpp meta.using-namespace.cpp", + "t": "source.cpp source.cpp meta.using-namespace.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -133,18 +133,18 @@ }, { "c": "std", - "t": "source.cpp meta.using-namespace.cpp entity.name.type.namespace.cpp", + "t": "source.cpp source.cpp meta.using-namespace.cpp entity.name.namespace.cpp", "r": { - "dark_plus": "entity.name.type: #4EC9B0", - "light_plus": "entity.name.type: #267F99", + "dark_plus": "entity.name.namespace: #4EC9B0", + "light_plus": "entity.name.namespace: #267F99", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "entity.name.type: #4EC9B0" + "hc_black": "entity.name.namespace: #4EC9B0" } }, { "c": ";", - "t": "source.cpp meta.using-namespace.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp meta.using-namespace.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -155,7 +155,7 @@ }, { "c": "class", - "t": "source.cpp meta.block.class.cpp meta.head.class.cpp storage.type.class.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.head.class.cpp storage.type.class.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -166,7 +166,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.class.cpp meta.head.class.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.head.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -177,7 +177,7 @@ }, { "c": "Rectangle", - "t": "source.cpp meta.block.class.cpp meta.head.class.cpp entity.name.type.class.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.head.class.cpp entity.name.type.class.cpp", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -188,7 +188,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.class.cpp meta.head.class.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.head.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -199,7 +199,7 @@ }, { "c": "{", - "t": "source.cpp meta.block.class.cpp meta.head.class.cpp punctuation.section.block.begin.bracket.curly.class.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.head.class.cpp punctuation.section.block.begin.bracket.curly.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -210,7 +210,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -221,7 +221,7 @@ }, { "c": "int", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp storage.type.primitive.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -232,7 +232,7 @@ }, { "c": " width", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -243,7 +243,7 @@ }, { "c": ",", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp comma.cpp punctuation.separator.delimiter.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp comma.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -254,7 +254,7 @@ }, { "c": " height", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -265,7 +265,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -276,7 +276,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -286,8 +286,19 @@ } }, { - "c": "public:", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp storage.type.modifier.access.control.public:.cpp", + "c": "public", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp storage.type.modifier.access.control.public.cpp", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6" + } + }, + { + "c": ":", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp storage.type.modifier.access.control.public.cpp colon.cpp punctuation.separator.delimiter.colon.access.control.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -298,7 +309,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.qualified_type.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -309,7 +320,7 @@ }, { "c": "void", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp storage.type.primitive.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.qualified_type.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -320,7 +331,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -331,7 +342,7 @@ }, { "c": "set_values", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp entity.name.function.definition.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -342,7 +353,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -353,7 +364,7 @@ }, { "c": "(", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -364,7 +375,7 @@ }, { "c": "int", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp storage.type.primitive.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -375,7 +386,7 @@ }, { "c": ",", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp comma.cpp punctuation.separator.delimiter.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp comma.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -386,7 +397,7 @@ }, { "c": "int", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp storage.type.primitive.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -397,7 +408,7 @@ }, { "c": ")", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -408,7 +419,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -419,7 +430,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.qualified_type.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -430,7 +441,7 @@ }, { "c": "int", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp storage.type.primitive.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.qualified_type.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -441,7 +452,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -452,7 +463,7 @@ }, { "c": "area", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp entity.name.function.definition.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -463,7 +474,7 @@ }, { "c": "(", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -474,7 +485,7 @@ }, { "c": ")", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -485,7 +496,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -496,7 +507,7 @@ }, { "c": "{", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.block.cpp punctuation.section.block.begin.bracket.curly.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.block.begin.bracket.curly.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -507,7 +518,7 @@ }, { "c": "return", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.block.cpp keyword.control.return.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.body.function.definition.cpp keyword.control.return.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -518,7 +529,7 @@ }, { "c": " width", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -529,7 +540,7 @@ }, { "c": "*", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.block.cpp keyword.operator.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.body.function.definition.cpp keyword.operator.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -540,7 +551,7 @@ }, { "c": "height", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -551,7 +562,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.block.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -562,7 +573,7 @@ }, { "c": "}", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp meta.block.cpp punctuation.section.block.end.bracket.curly.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.block.end.bracket.curly.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -573,7 +584,7 @@ }, { "c": "}", - "t": "source.cpp meta.block.class.cpp meta.body.class.cpp punctuation.section.block.end.bracket.curly.class.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp meta.body.class.cpp punctuation.section.block.end.bracket.curly.class.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -584,7 +595,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.class.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp meta.block.class.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -595,7 +606,7 @@ }, { "c": "void", - "t": "source.cpp storage.type.primitive.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.qualified_type.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -606,7 +617,7 @@ }, { "c": " ", - "t": "source.cpp meta.scope-resolution.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -617,18 +628,18 @@ }, { "c": "Rectangle", - "t": "source.cpp meta.scope-resolution.cpp entity.name.type.namespace.scope-resolution.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp entity.name.scope-resolution.function.definition.cpp", "r": { - "dark_plus": "entity.name.type: #4EC9B0", - "light_plus": "entity.name.type: #267F99", + "dark_plus": "entity.name.scope-resolution: #4EC9B0", + "light_plus": "entity.name.scope-resolution: #267F99", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "entity.name.type: #4EC9B0" + "hc_black": "entity.name.scope-resolution: #4EC9B0" } }, { "c": "::", - "t": "source.cpp meta.scope-resolution.cpp punctuation.separator.namespace.access.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -639,7 +650,7 @@ }, { "c": "set_values", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp entity.name.function.definition.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -650,7 +661,7 @@ }, { "c": " ", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -661,7 +672,7 @@ }, { "c": "(", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -672,7 +683,7 @@ }, { "c": "int", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp storage.type.primitive.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -683,7 +694,7 @@ }, { "c": " ", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -694,7 +705,7 @@ }, { "c": "x", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp variable.parameter.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp variable.parameter.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -705,7 +716,7 @@ }, { "c": ",", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp comma.cpp punctuation.separator.delimiter.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp comma.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -716,7 +727,7 @@ }, { "c": " ", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -727,7 +738,7 @@ }, { "c": "int", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp storage.type.primitive.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -738,7 +749,7 @@ }, { "c": " ", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -749,7 +760,7 @@ }, { "c": "y", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp variable.parameter.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp meta.function.definition.parameters.cpp variable.parameter.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -760,7 +771,7 @@ }, { "c": ")", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -771,7 +782,7 @@ }, { "c": " ", - "t": "source.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -782,7 +793,7 @@ }, { "c": "{", - "t": "source.cpp meta.block.cpp punctuation.section.block.begin.bracket.curly.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.block.begin.bracket.curly.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -793,7 +804,7 @@ }, { "c": " width ", - "t": "source.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -804,7 +815,7 @@ }, { "c": "=", - "t": "source.cpp meta.block.cpp keyword.operator.assignment.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp keyword.operator.assignment.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -815,7 +826,7 @@ }, { "c": " x", - "t": "source.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -826,7 +837,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -837,7 +848,7 @@ }, { "c": " height ", - "t": "source.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -848,7 +859,7 @@ }, { "c": "=", - "t": "source.cpp meta.block.cpp keyword.operator.assignment.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp keyword.operator.assignment.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -859,7 +870,7 @@ }, { "c": " y", - "t": "source.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -870,7 +881,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -881,7 +892,7 @@ }, { "c": "}", - "t": "source.cpp meta.block.cpp punctuation.section.block.end.bracket.curly.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.block.end.bracket.curly.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -892,7 +903,7 @@ }, { "c": "int", - "t": "source.cpp storage.type.primitive.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.qualified_type.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -903,7 +914,7 @@ }, { "c": " ", - "t": "source.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -914,7 +925,7 @@ }, { "c": "main", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp entity.name.function.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp entity.name.function.definition.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -925,7 +936,7 @@ }, { "c": " ", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -936,7 +947,7 @@ }, { "c": "(", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.begin.bracket.round.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -947,7 +958,7 @@ }, { "c": ")", - "t": "source.cpp meta.function.definition.parameters.cpp meta.function.definition.parameters.cpp punctuation.section.parameters.end.bracket.round.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -958,7 +969,7 @@ }, { "c": " ", - "t": "source.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -969,7 +980,7 @@ }, { "c": "{", - "t": "source.cpp meta.block.cpp punctuation.section.block.begin.bracket.curly.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.block.begin.bracket.curly.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -980,7 +991,7 @@ }, { "c": " Rectangle rect", - "t": "source.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -991,7 +1002,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1002,7 +1013,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1013,7 +1024,7 @@ }, { "c": "rect", - "t": "source.cpp meta.block.cpp variable.other.object.access.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp variable.other.object.access.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1024,7 +1035,7 @@ }, { "c": ".", - "t": "source.cpp meta.block.cpp punctuation.separator.dot-access.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.separator.dot-access.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1035,18 +1046,18 @@ }, { "c": "set_values", - "t": "source.cpp meta.block.cpp variable.other.property.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp entity.name.function.member.cpp", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE" + "hc_black": "entity.name.function: #DCDCAA" } }, { "c": " ", - "t": "source.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1057,7 +1068,7 @@ }, { "c": "(", - "t": "source.cpp meta.block.cpp meta.parens.block.cpp punctuation.section.parens.begin.bracket.round.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.arguments.begin.bracket.round.function.member.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1068,7 +1079,7 @@ }, { "c": "3", - "t": "source.cpp meta.block.cpp meta.parens.block.cpp constant.numeric.decimal.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.numeric.decimal.cpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1079,7 +1090,7 @@ }, { "c": ",", - "t": "source.cpp meta.block.cpp meta.parens.block.cpp comma.cpp punctuation.separator.delimiter.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp comma.cpp punctuation.separator.delimiter.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1090,7 +1101,7 @@ }, { "c": "4", - "t": "source.cpp meta.block.cpp meta.parens.block.cpp constant.numeric.decimal.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.numeric.decimal.cpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1101,7 +1112,7 @@ }, { "c": ")", - "t": "source.cpp meta.block.cpp meta.parens.block.cpp punctuation.section.parens.end.bracket.round.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.arguments.end.bracket.round.function.member.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1112,7 +1123,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1123,7 +1134,7 @@ }, { "c": " cout ", - "t": "source.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1134,7 +1145,7 @@ }, { "c": "<<", - "t": "source.cpp meta.block.cpp keyword.operator.bitwise.shift.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp keyword.operator.bitwise.shift.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1145,7 +1156,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1156,7 +1167,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp punctuation.definition.string.begin.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1167,7 +1178,7 @@ }, { "c": "area: ", - "t": "source.cpp meta.block.cpp string.quoted.double.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1178,7 +1189,7 @@ }, { "c": "\"", - "t": "source.cpp meta.block.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp string.quoted.double.cpp punctuation.definition.string.end.cpp", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1189,7 +1200,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1200,7 +1211,7 @@ }, { "c": "<<", - "t": "source.cpp meta.block.cpp keyword.operator.bitwise.shift.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp keyword.operator.bitwise.shift.cpp", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1211,7 +1222,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1222,7 +1233,7 @@ }, { "c": "rect", - "t": "source.cpp meta.block.cpp variable.other.object.access.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp variable.other.object.access.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1233,7 +1244,7 @@ }, { "c": ".", - "t": "source.cpp meta.block.cpp punctuation.separator.dot-access.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.separator.dot-access.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1244,7 +1255,7 @@ }, { "c": "area", - "t": "source.cpp meta.block.cpp entity.name.function.member.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp entity.name.function.member.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -1255,7 +1266,7 @@ }, { "c": "(", - "t": "source.cpp meta.block.cpp punctuation.section.arguments.begin.bracket.round.function.member.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.arguments.begin.bracket.round.function.member.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1266,7 +1277,7 @@ }, { "c": ")", - "t": "source.cpp meta.block.cpp punctuation.section.arguments.end.bracket.round.function.member.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.arguments.end.bracket.round.function.member.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1277,7 +1288,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1288,7 +1299,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1299,7 +1310,7 @@ }, { "c": "return", - "t": "source.cpp meta.block.cpp keyword.control.return.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp keyword.control.return.cpp", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -1310,7 +1321,7 @@ }, { "c": " ", - "t": "source.cpp meta.block.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1321,7 +1332,7 @@ }, { "c": "0", - "t": "source.cpp meta.block.cpp constant.numeric.decimal.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.numeric.decimal.cpp", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1332,7 +1343,7 @@ }, { "c": ";", - "t": "source.cpp meta.block.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.terminator.statement.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1343,7 +1354,7 @@ }, { "c": "}", - "t": "source.cpp meta.block.cpp punctuation.section.block.end.bracket.curly.cpp", + "t": "source.cpp source.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.block.end.bracket.curly.function.definition.cpp", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/csharp/package.json b/extensions/csharp/package.json index 3e2925282e2..3841eabd132 100644 --- a/extensions/csharp/package.json +++ b/extensions/csharp/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "0.10.x" }, diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index 0b2e83c1d8d..a435a4e16b8 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "^1.29.0" }, @@ -732,11 +733,11 @@ ] }, "dependencies": { - "vscode-languageclient": "^5.2.1", - "vscode-nls": "^4.0.0" + "vscode-languageclient": "^5.3.0-next.6", + "vscode-nls": "^4.1.1" }, "devDependencies": { - "@types/node": "^10.12.21", + "@types/node": "^10.14.8", "mocha": "^5.2.0" } } diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index e3fd51ae5ec..6f2e734a109 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -9,15 +9,15 @@ }, "main": "./out/cssServerMain", "dependencies": { - "vscode-css-languageservice": "^4.0.2-next.3", - "vscode-languageserver": "^5.3.0-next.2" + "vscode-css-languageservice": "^4.0.2", + "vscode-languageserver": "^5.3.0-next.8" }, "devDependencies": { "@types/mocha": "2.2.33", - "@types/node": "^10.12.21", - "glob": "^7.1.2", - "mocha": "^5.2.0", - "mocha-junit-reporter": "^1.17.0", + "@types/node": "^10.14.8", + "glob": "^7.1.4", + "mocha": "^6.1.4", + "mocha-junit-reporter": "^1.23.0", "mocha-multi-reporters": "^1.1.7" }, "scripts": { diff --git a/extensions/css-language-features/server/src/cssServerMain.ts b/extensions/css-language-features/server/src/cssServerMain.ts index e1c78b158c1..3eee352d7f2 100644 --- a/extensions/css-language-features/server/src/cssServerMain.ts +++ b/extensions/css-language-features/server/src/cssServerMain.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { - createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities, ConfigurationRequest, WorkspaceFolder + createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities, ConfigurationRequest, WorkspaceFolder, TextDocumentSyncKind } from 'vscode-languageserver'; import URI from 'vscode-uri'; import { TextDocument, CompletionList, Position } from 'vscode-languageserver-types'; @@ -32,9 +32,8 @@ process.on('unhandledRejection', (e: any) => { connection.console.error(formatError(`Unhandled exception`, e)); }); -// Create a simple text document manager. The text document manager -// supports full document sync only -const documents: TextDocuments = new TextDocuments(); +// Create a text document manager. +const documents: TextDocuments = new TextDocuments(TextDocumentSyncKind.Incremental); // Make the text document manager listen on the connection // for open, change and close text document events documents.listen(connection); diff --git a/extensions/css-language-features/server/src/test/links.test.ts b/extensions/css-language-features/server/src/test/links.test.ts new file mode 100644 index 00000000000..bce7a11bc8a --- /dev/null +++ b/extensions/css-language-features/server/src/test/links.test.ts @@ -0,0 +1,79 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import 'mocha'; +import * as assert from 'assert'; +import Uri from 'vscode-uri'; +import { resolve } from 'path'; +import { TextDocument, DocumentLink } from 'vscode-languageserver-types'; +import { WorkspaceFolder } from 'vscode-languageserver-protocol'; +import { getCSSLanguageService } from 'vscode-css-languageservice'; +import { getDocumentContext } from '../utils/documentContext'; + +export interface ItemDescription { + offset: number; + value: string; + target: string; +} + +suite('Links', () => { + const cssLanguageService = getCSSLanguageService(); + + let assertLink = function (links: DocumentLink[], expected: ItemDescription, document: TextDocument) { + let matches = links.filter(link => { + return document.offsetAt(link.range.start) === expected.offset; + }); + + assert.equal(matches.length, 1, `${expected.offset} should only existing once: Actual: ${links.map(l => document.offsetAt(l.range.start)).join(', ')}`); + let match = matches[0]; + assert.equal(document.getText(match.range), expected.value); + assert.equal(match.target, expected.target); + }; + + function assertLinks(value: string, expected: ItemDescription[], testUri: string, workspaceFolders?: WorkspaceFolder[], lang: string = 'css'): void { + const offset = value.indexOf('|'); + value = value.substr(0, offset) + value.substr(offset + 1); + + const document = TextDocument.create(testUri, lang, 0, value); + + if (!workspaceFolders) { + workspaceFolders = [{ name: 'x', uri: testUri.substr(0, testUri.lastIndexOf('/')) }]; + } + + const context = getDocumentContext(testUri, workspaceFolders); + + const stylesheet = cssLanguageService.parseStylesheet(document); + let links = cssLanguageService.findDocumentLinks(document, stylesheet, context)!; + + assert.equal(links.length, expected.length); + + for (let item of expected) { + assertLink(links, item, document); + } + } + + function getTestResource(path: string) { + return Uri.file(resolve(__dirname, '../../test/linksTestFixtures', path)).toString(); + } + + test('url links', function () { + + let testUri = getTestResource('about.css'); + let folders = [{ name: 'x', uri: getTestResource('') }]; + + assertLinks('html { background-image: url("hello.html|")', + [{ offset: 29, value: '"hello.html"', target: getTestResource('hello.html') }], testUri, folders + ); + }); + + test('node module resolving', function () { + + let testUri = getTestResource('about.css'); + let folders = [{ name: 'x', uri: getTestResource('') }]; + + assertLinks('html { background-image: url("~foo/hello.html|")', + [{ offset: 29, value: '"~foo/hello.html"', target: getTestResource('node_modules/foo/hello.html') }], testUri, folders + ); + }); +}); \ No newline at end of file diff --git a/extensions/css-language-features/server/src/utils/documentContext.ts b/extensions/css-language-features/server/src/utils/documentContext.ts index 9858ca84367..494ff395e9d 100644 --- a/extensions/css-language-features/server/src/utils/documentContext.ts +++ b/extensions/css-language-features/server/src/utils/documentContext.ts @@ -7,6 +7,27 @@ import { DocumentContext } from 'vscode-css-languageservice'; import { endsWith, startsWith } from '../utils/strings'; import * as url from 'url'; import { WorkspaceFolder } from 'vscode-languageserver'; +import URI from 'vscode-uri'; +import { join, dirname } from 'path'; +import { existsSync } from 'fs'; + +function getModuleNameFromPath(path: string) { + // If a scoped module (starts with @) then get up until second instance of '/', otherwise get until first isntance of '/' + if (path[0] === '@') { + return path.substring(0, path.indexOf('/', path.indexOf('/') + 1)); + } + return path.substring(0, path.indexOf('/')); +} + +function resolvePathToModule(_moduleName: string, _relativeTo: string): string | undefined { + // resolve the module relative to the document. We can't use `require` here as the code is webpacked. + const documentFolder = dirname(URI.parse(_relativeTo).fsPath); + const packPath = join(documentFolder, 'node_modules', _moduleName, 'package.json'); + if (existsSync(packPath)) { + return URI.file(packPath).toString(); + } + return undefined; +} export function getDocumentContext(documentUri: string, workspaceFolders: WorkspaceFolder[]): DocumentContext { function getRootFolder(): string | undefined { @@ -32,6 +53,22 @@ export function getDocumentContext(documentUri: string, workspaceFolders: Worksp } } } + // Following [css-loader](https://github.com/webpack-contrib/css-loader#url) + // and [sass-loader's](https://github.com/webpack-contrib/sass-loader#imports) + // convention, if an import path starts with ~ then use node module resolution + // *unless* it starts with "~/" as this refers to the user's home directory. + if (ref[0] === '~' && ref[1] !== '/') { + ref = ref.substring(1); + if (startsWith(base, 'file://')) { + const moduleName = getModuleNameFromPath(ref); + const modulePath = resolvePathToModule(moduleName, base); + if (modulePath) { + const pathWithinModule = ref.substring(moduleName.length + 1); + return url.resolve(modulePath, pathWithinModule); + } + } + + } return url.resolve(base, ref); }, }; diff --git a/extensions/css-language-features/server/test/linksTestFixtures/.gitignore b/extensions/css-language-features/server/test/linksTestFixtures/.gitignore new file mode 100644 index 00000000000..d591cdfd50a --- /dev/null +++ b/extensions/css-language-features/server/test/linksTestFixtures/.gitignore @@ -0,0 +1 @@ +!/node_modules \ No newline at end of file diff --git a/extensions/css-language-features/server/test/linksTestFixtures/node_modules/foo/package.json b/extensions/css-language-features/server/test/linksTestFixtures/node_modules/foo/package.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index dbe0fa51fd9..c5bfa63c869 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -7,16 +7,45 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.33.tgz#d79a0061ec270379f4d9e225f4096fb436669def" integrity sha1-15oAYewnA3n02eIl9AlvtDZmne8= -"@types/node@^10.12.21": - version "10.12.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" - integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== +"@types/node@^10.14.8": + version "10.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" + integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== + +ansi-colors@3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" + integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= ansi-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -35,32 +64,78 @@ browser-stdout@1.3.1: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +chalk@^2.0.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + charenc@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= -commander@2.15.1: - version "2.15.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" - integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== +cliui@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" + integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== + dependencies: + string-width "^2.1.1" + strip-ansi "^4.0.0" + wrap-ansi "^2.0.0" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +cross-spawn@^6.0.0: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + crypt@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= -debug@3.1.0, debug@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== +debug@3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== dependencies: - ms "2.0.0" + ms "^2.1.1" debug@^2.2.0: version "2.6.9" @@ -69,25 +144,143 @@ debug@^2.2.0: dependencies: ms "2.0.0" +debug@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +define-properties@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + diff@3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== -escape-string-regexp@1.0.5: +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +end-of-stream@^1.1.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" + integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== + dependencies: + once "^1.4.0" + +es-abstract@^1.5.1: + version "1.13.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" + integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg== + dependencies: + es-to-primitive "^1.2.0" + function-bind "^1.1.1" + has "^1.0.3" + is-callable "^1.1.4" + is-regex "^1.0.4" + object-keys "^1.0.12" + +es-to-primitive@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" + integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.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" + +find-up@3.0.0, find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +flat@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.0.tgz#090bec8b05e39cba309747f1d588f04dbaf98db2" + integrity sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw== + dependencies: + is-buffer "~2.0.3" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -glob@7.1.2, glob@^7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" - integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +get-caller-file@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +glob@7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.4: + version "7.1.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" + integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -106,10 +299,22 @@ has-flag@^3.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= -he@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" - integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= +has-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" + integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= + +has@^1.0.1, has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== inflight@^1.0.4: version "1.0.6" @@ -124,16 +329,114 @@ inherits@2: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= +invert-kv@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" + integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== + is-buffer@~1.1.1: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== +is-buffer@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" + integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw== + +is-callable@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" + integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== + +is-date-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= + +is-fullwidth-code-point@^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" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + 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" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-regex@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= + dependencies: + has "^1.0.1" + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-symbol@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" + integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== + dependencies: + has-symbols "^1.0.0" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +js-yaml@3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +lcid@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" + integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== + dependencies: + invert-kv "^2.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + lodash@^4.16.4: version "4.17.10" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" integrity sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg== +lodash@^4.17.11: + version "4.17.11" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" + integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== + +log-symbols@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== + dependencies: + chalk "^2.0.1" + +map-age-cleaner@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" + integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== + dependencies: + p-defer "^1.0.0" + md5@^2.1.0: version "2.2.1" resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" @@ -143,6 +446,20 @@ md5@^2.1.0: crypt "~0.0.1" is-buffer "~1.1.1" +mem@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" + integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== + dependencies: + map-age-cleaner "^0.1.1" + mimic-fn "^2.0.0" + p-is-promise "^2.0.0" + +mimic-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + minimatch@3.0.4, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -162,10 +479,10 @@ mkdirp@0.5.1, mkdirp@~0.5.1: dependencies: minimist "0.0.8" -mocha-junit-reporter@^1.17.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.17.0.tgz#2e5149ed40fc5d2e3ca71e42db5ab1fec9c6d85c" - integrity sha1-LlFJ7UD8XS48px5C21qx/snG2Fw= +mocha-junit-reporter@^1.23.0: + version "1.23.0" + resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.23.0.tgz#c5ad7f10b5aa9a7cc6e169b6bf15baf2700266ca" + integrity sha512-pmpnEO4iDTmLfrT2RKqPsc5relG4crnDSGmXPuGogdda27A7kLujDNJV4EbTbXlVBCZXggN9rQYPEWMkOv4AAA== dependencies: debug "^2.2.0" md5 "^2.1.0" @@ -181,40 +498,251 @@ mocha-multi-reporters@^1.1.7: debug "^3.1.0" lodash "^4.16.4" -mocha@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" - integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ== +mocha@^6.1.4: + version "6.1.4" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.1.4.tgz#e35fada242d5434a7e163d555c705f6875951640" + integrity sha512-PN8CIy4RXsIoxoFJzS4QNnCH4psUCPWc4/rPrst/ecSJJbLBkubMiyGCP2Kj/9YnWbotFqAoeXyXMucj7gwCFg== dependencies: + ansi-colors "3.2.3" browser-stdout "1.3.1" - commander "2.15.1" - debug "3.1.0" + debug "3.2.6" diff "3.5.0" escape-string-regexp "1.0.5" - glob "7.1.2" + find-up "3.0.0" + glob "7.1.3" growl "1.10.5" - he "1.1.1" + he "1.2.0" + js-yaml "3.13.1" + log-symbols "2.2.0" minimatch "3.0.4" mkdirp "0.5.1" - supports-color "5.4.0" + ms "2.1.1" + node-environment-flags "1.0.5" + object.assign "4.1.0" + strip-json-comments "2.0.1" + supports-color "6.0.0" + which "1.3.1" + wide-align "1.1.3" + yargs "13.2.2" + yargs-parser "13.0.0" + yargs-unparser "1.5.0" ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= -once@^1.3.0: +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +node-environment-flags@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" + integrity sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ== + dependencies: + object.getownpropertydescriptors "^2.0.3" + semver "^5.7.0" + +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" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +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" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +object-keys@^1.0.11, object-keys@^1.0.12: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.getownpropertydescriptors@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" + integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY= + dependencies: + define-properties "^1.1.2" + es-abstract "^1.5.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" +os-locale@^3.0.0, os-locale@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" + integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== + dependencies: + execa "^1.0.0" + lcid "^2.0.0" + mem "^4.0.0" + +p-defer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-is-promise@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" + integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== + +p-limit@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.0.tgz#417c9941e6027a9abcba5092dd2904e255b5fbc2" + integrity sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ== + dependencies: + p-try "^2.0.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +semver@^5.5.0, semver@^5.7.0: + version "5.7.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" + integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA== + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + 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" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +signal-exit@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +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" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + 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" @@ -222,57 +750,114 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -supports-color@5.4.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" - integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w== +strip-ansi@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +strip-json-comments@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +supports-color@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" + integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg== dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^4.0.2-next.3: - version "4.0.2-next.3" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.2-next.3.tgz#f81925d6037f05724d1c1fe00e8fb7fcbece72ff" - integrity sha512-Th6ESBGTdNo4CbZEeKNVBKi4DwGjafS1+05kuoH3hO5mFCKr6ttdvu4GxMHca7nGN1efv5tiZ6slO8PCN1bLNA== +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: - vscode-languageserver-types "^3.14.0" - vscode-nls "^4.0.0" + has-flag "^3.0.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.15.0-next.1: - version "3.15.0-next.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.1.tgz#1e45e224d7eef8c79b4bed75b9dcb1930d2ab8ed" - integrity sha512-LXF0d9s3vxFBxVQ4aKl/XghdEMAncGt3dh4urIYa9Is43g3MfIQL9fC44YZtP+XXOrI2rpZU8lRNN01U1V6CDg== +vscode-css-languageservice@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-4.0.2.tgz#7496e538b0c151feac16d5888cc0b1b104f4c736" + integrity sha512-pTnfXbsME3pl+yDfhUp/mtvPyIJk0Le4zqJxDn56s9GY9LqY0RmkSEh0oHH6D0HXR3Ni6wKosIaqu8a2G0+jdw== dependencies: - vscode-jsonrpc "^4.0.0" - vscode-languageserver-types "3.14.0" + vscode-languageserver-types "^3.15.0-next.2" + vscode-nls "^4.1.1" -vscode-languageserver-types@3.14.0, vscode-languageserver-types@^3.14.0: - version "3.14.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743" - integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A== +vscode-jsonrpc@^4.1.0-next.2: + version "4.1.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.1.0-next.2.tgz#3bd318910a48e631742b290975386e3dae685be3" + integrity sha512-GsBLjP9DxQ42yl1mW9GEIlnSc0+R8mfzhaebwmmTPEJjezD5SPoAo3DFrIAFZha9yvQ1nzZfZlhtVpGQmgxtXg== -vscode-languageserver@^5.3.0-next.2: - version "5.3.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.3.0-next.2.tgz#31ce4c34d68b517b400ca9e211e43f8d868b8dcc" - integrity sha512-n5onRw9naMrRHp2jnOn+ZwN1n+tTfzftWLPonjp1FWf/iCZWIlnw2TyF/Hn+SDGhLoVtoghmxhwEQaxEAfLHvw== +vscode-languageserver-protocol@^3.15.0-next.6: + version "3.15.0-next.6" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.6.tgz#a8aeb7e7dd65da8216b386db59494cdfd3215d92" + integrity sha512-/yDpYlWyNs26mM23mT73xmOFsh1iRfgZfBdHmfAxwDKwpQKLoOSqVidtYfxlK/pD3IEKGcAVnT4WXTsguxxAMQ== dependencies: - vscode-languageserver-protocol "3.15.0-next.1" + vscode-jsonrpc "^4.1.0-next.2" + vscode-languageserver-types "^3.15.0-next.2" + +vscode-languageserver-types@^3.15.0-next.2: + version "3.15.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.2.tgz#a0601332cdaafac21931f497bb080cfb8d73f254" + integrity sha512-2JkrMWWUi2rlVLSo9OFR2PIGUzdiowEM8NgNYiwLKnXTjpwpjjIrJbNNxDik7Rv4oo9KtikcFQZKXbrKilL/MQ== + +vscode-languageserver@^5.3.0-next.8: + version "5.3.0-next.8" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.3.0-next.8.tgz#12a4adf60374dbb93e153e08bdca5525f9b2029f" + integrity sha512-6vUb96wsRfrFqndril3gct/FBCSc24OxFZ2iz7kuEuXvLaIcEVOcSZIqQK8oFN7PdbAIaa9nnIpKSy4Yd15cIw== + dependencies: + vscode-languageserver-protocol "^3.15.0-next.6" + vscode-textbuffer "^1.0.0" vscode-uri "^1.0.6" -vscode-nls@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002" - integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw== +vscode-nls@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" + integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== + +vscode-textbuffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/vscode-textbuffer/-/vscode-textbuffer-1.0.0.tgz#1faee638c8e0e4131c8d5c353993a1874acda086" + integrity sha512-zPaHo4urgpwsm+PrJWfNakolRpryNja18SUip/qIIsfhuEqEIPEXMxHOlFPjvDC4JgTaimkncNW7UMXRJTY6ow== vscode-uri@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.6.tgz#6b8f141b0bbc44ad7b07e94f82f168ac7608ad4d" integrity sha512-sLI2L0uGov3wKVb9EB+vIQBl9tVP90nqRvxSoJ35vI3NjxE8jfsE5DSOhWgSunHSZmKS4OCi2jrtfxK7uyp2ww== +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@1.3.1, which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +wide-align@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= + 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" @@ -282,3 +867,76 @@ xml@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU= + +"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + +yargs-parser@13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.0.0.tgz#3fc44f3e76a8bdb1cc3602e860108602e5ccde8b" + integrity sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" + integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^13.0.0: + version "13.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" + integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-unparser@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.5.0.tgz#f2bb2a7e83cbc87bb95c8e572828a06c9add6e0d" + integrity sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw== + dependencies: + flat "^4.1.0" + lodash "^4.17.11" + yargs "^12.0.5" + +yargs@13.2.2: + version "13.2.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.2.tgz#0c101f580ae95cea7f39d927e7770e3fdc97f993" + integrity sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA== + dependencies: + cliui "^4.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + os-locale "^3.1.0" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.0.0" + +yargs@^12.0.5: + version "12.0.5" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" + integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw== + dependencies: + cliui "^4.0.0" + decamelize "^1.2.0" + find-up "^3.0.0" + get-caller-file "^1.0.1" + os-locale "^3.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1 || ^4.0.0" + yargs-parser "^11.1.1" diff --git a/extensions/css-language-features/yarn.lock b/extensions/css-language-features/yarn.lock index 385d59738dd..48f16a42fe5 100644 --- a/extensions/css-language-features/yarn.lock +++ b/extensions/css-language-features/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@^10.12.21": - version "10.12.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" - integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== +"@types/node@^10.14.8": + version "10.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" + integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== balanced-match@^1.0.0: version "1.0.0" @@ -162,36 +162,36 @@ supports-color@5.4.0: dependencies: has-flag "^3.0.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-jsonrpc@^4.1.0-next.2: + version "4.1.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.1.0-next.2.tgz#3bd318910a48e631742b290975386e3dae685be3" + integrity sha512-GsBLjP9DxQ42yl1mW9GEIlnSc0+R8mfzhaebwmmTPEJjezD5SPoAo3DFrIAFZha9yvQ1nzZfZlhtVpGQmgxtXg== -vscode-languageclient@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-5.2.1.tgz#7cfc83a294c409f58cfa2b910a8cfeaad0397193" - integrity sha512-7jrS/9WnV0ruqPamN1nE7qCxn0phkH5LjSgSp9h6qoJGoeAKzwKz/PF6M+iGA/aklx4GLZg1prddhEPQtuXI1Q== +vscode-languageclient@^5.3.0-next.6: + version "5.3.0-next.6" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-5.3.0-next.6.tgz#35e74882781158e8b111911c0953869d3df08777" + integrity sha512-DxT8+gkenjCjJV6ArcP75/AQfx6HP6m6kHIbacPCpffMeoE1YMLKj6ZixA9J87yr0fMtBmqumLmDeGe7MIF2bw== dependencies: semver "^5.5.0" - vscode-languageserver-protocol "3.14.1" + vscode-languageserver-protocol "^3.15.0-next.6" -vscode-languageserver-protocol@3.14.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz#b8aab6afae2849c84a8983d39a1cf742417afe2f" - integrity sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g== +vscode-languageserver-protocol@^3.15.0-next.6: + version "3.15.0-next.6" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.6.tgz#a8aeb7e7dd65da8216b386db59494cdfd3215d92" + integrity sha512-/yDpYlWyNs26mM23mT73xmOFsh1iRfgZfBdHmfAxwDKwpQKLoOSqVidtYfxlK/pD3IEKGcAVnT4WXTsguxxAMQ== dependencies: - vscode-jsonrpc "^4.0.0" - vscode-languageserver-types "3.14.0" + vscode-jsonrpc "^4.1.0-next.2" + vscode-languageserver-types "^3.15.0-next.2" -vscode-languageserver-types@3.14.0: - version "3.14.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743" - integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A== +vscode-languageserver-types@^3.15.0-next.2: + version "3.15.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.2.tgz#a0601332cdaafac21931f497bb080cfb8d73f254" + integrity sha512-2JkrMWWUi2rlVLSo9OFR2PIGUzdiowEM8NgNYiwLKnXTjpwpjjIrJbNNxDik7Rv4oo9KtikcFQZKXbrKilL/MQ== -vscode-nls@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002" - integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw== +vscode-nls@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" + integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== wrappy@1: version "1.0.2" diff --git a/extensions/css/cgmanifest.json b/extensions/css/cgmanifest.json index 7b7a8d5e8e8..a47fef967ec 100644 --- a/extensions/css/cgmanifest.json +++ b/extensions/css/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "octref/language-css", "repositoryUrl": "https://github.com/octref/language-css", - "commitHash": "6d3a2d01dd67ef062030f4520dd42a5424330a3b" + "commitHash": "377734aad976be88a425aab5667784f3f71ea7e5" } }, "license": "MIT", diff --git a/extensions/css/package.json b/extensions/css/package.json index 13f737e92fc..117f759dd9d 100644 --- a/extensions/css/package.json +++ b/extensions/css/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "0.10.x" }, diff --git a/extensions/css/syntaxes/css.tmLanguage.json b/extensions/css/syntaxes/css.tmLanguage.json index a45463c8b9b..8dbdf1f7413 100644 --- a/extensions/css/syntaxes/css.tmLanguage.json +++ b/extensions/css/syntaxes/css.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/octref/language-css/commit/6d3a2d01dd67ef062030f4520dd42a5424330a3b", + "version": "https://github.com/octref/language-css/commit/377734aad976be88a425aab5667784f3f71ea7e5", "name": "CSS", "scopeName": "source.css", "patterns": [ @@ -606,8 +606,32 @@ ] }, { - "begin": "(?i)(?=@[\\w-]+(\\s|\\(|{|;|/\\*|$))", - "end": "(?<=}|;)(?!\\G)", + "begin": "(?i)(?=@[\\w-]+[^;]+;s*$)", + "end": "(?<=;)(?!\\G)", + "patterns": [ + { + "begin": "(?i)\\G(@)[\\w-]+", + "beginCaptures": { + "0": { + "name": "keyword.control.at-rule.css" + }, + "1": { + "name": "punctuation.definition.keyword.css" + } + }, + "end": ";", + "endCaptures": { + "0": { + "name": "punctuation.terminator.rule.css" + } + }, + "name": "meta.at-rule.header.css" + } + ] + }, + { + "begin": "(?i)(?=@[\\w-]+(\\s|\\(|{|/\\*|$))", + "end": "(?<=})(?!\\G)", "patterns": [ { "begin": "(?i)\\G(@)[\\w-]+", diff --git a/extensions/debug-auto-launch/package.json b/extensions/debug-auto-launch/package.json index 7b676cfbe60..b1444f044b8 100644 --- a/extensions/debug-auto-launch/package.json +++ b/extensions/debug-auto-launch/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "^1.5.0" }, @@ -49,6 +50,6 @@ "vscode-nls": "^4.0.0" }, "devDependencies": { - "@types/node": "8.0.33" + "@types/node": "^10.14.8" } } diff --git a/extensions/debug-auto-launch/yarn.lock b/extensions/debug-auto-launch/yarn.lock index 6767cb8d8c2..e6247e29255 100644 --- a/extensions/debug-auto-launch/yarn.lock +++ b/extensions/debug-auto-launch/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@8.0.33": - version "8.0.33" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.33.tgz#1126e94374014e54478092830704f6ea89df04cd" - integrity sha512-vmCdO8Bm1ExT+FWfC9sd9r4jwqM7o97gGy2WBshkkXbf/2nLAJQUrZfIhw27yVOtLUev6kSZc4cav/46KbDd8A== +"@types/node@^10.14.8": + version "10.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" + integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/debug-server-ready/package.json b/extensions/debug-server-ready/package.json index ee66be8012d..cd006ce71e6 100644 --- a/extensions/debug-server-ready/package.json +++ b/extensions/debug-server-ready/package.json @@ -105,6 +105,6 @@ "vscode-nls": "^4.0.0" }, "devDependencies": { - "@types/node": "8.0.33" + "@types/node": "^10.14.8" } } diff --git a/extensions/debug-server-ready/src/extension.ts b/extensions/debug-server-ready/src/extension.ts index 57863fd995c..d6e122f9624 100644 --- a/extensions/debug-server-ready/src/extension.ts +++ b/extensions/debug-server-ready/src/extension.ts @@ -147,7 +147,7 @@ class ServerReadyDetector extends vscode.Disposable { webRoot: args.webRoot || WEB_ROOT }, session); } else { - const errMsg = localize('server.ready.chrome.not.installed', "The action 'debugWithChrome' requires the '{0}'", 'Debugger for Chrome'); + const errMsg = localize('server.ready.chrome.not.installed', "The action '{0}' requires the '{1}' extension.", 'debugWithChrome', 'Debugger for Chrome'); vscode.window.showErrorMessage(errMsg, { modal: true }).then(_ => undefined); } break; diff --git a/extensions/debug-server-ready/yarn.lock b/extensions/debug-server-ready/yarn.lock index 6767cb8d8c2..e6247e29255 100644 --- a/extensions/debug-server-ready/yarn.lock +++ b/extensions/debug-server-ready/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@8.0.33": - version "8.0.33" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.33.tgz#1126e94374014e54478092830704f6ea89df04cd" - integrity sha512-vmCdO8Bm1ExT+FWfC9sd9r4jwqM7o97gGy2WBshkkXbf/2nLAJQUrZfIhw27yVOtLUev6kSZc4cav/46KbDd8A== +"@types/node@^10.14.8": + version "10.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" + integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/docker/package.json b/extensions/docker/package.json index 41df24723fa..f5f2f795fc4 100644 --- a/extensions/docker/package.json +++ b/extensions/docker/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, "scripts": { "update-grammar": "node ../../build/npm/update-grammar.js moby/moby contrib/syntax/textmate/Docker.tmbundle/Syntaxes/Dockerfile.tmLanguage ./syntaxes/docker.tmLanguage.json" diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index 9805a694c83..fd347852231 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -3,7 +3,8 @@ "displayName": "Emmet", "description": "%description%", "version": "1.0.0", - "publisher": "vscode", + "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "^1.13.0" }, @@ -439,7 +440,7 @@ "deps": "yarn add vscode-emmet-helper" }, "devDependencies": { - "@types/node": "8.0.33", + "@types/node": "^10.14.8", "mocha-junit-reporter": "^1.17.0", "mocha-multi-reporters": "^1.1.7", "vscode": "1.0.1" diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index f332aff2e00..e548023487f 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -491,7 +491,7 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen } let valid = true; - let foundSpace = false; // If < is found before finding whitespace, then its valid abbreviation. Eg: )", + "end": "((?|\\))", "endComment": "The group (? when using SRTP synthax", "beginCaptures": { "1": { @@ -792,6 +857,9 @@ } ] }, + { + "include": "#anonymous_record_declaration" + }, { "begin": "({)", "end": "(})", @@ -814,6 +882,14 @@ { "include": "#definition" }, + { + "match": "(?<=>)\\s*(``([[:alpha:]0-9'^._ ]+)``|[[:alpha:]0-9'`^._]+)", + "captures": { + "1": { + "name": "entity.name.type.fsharp" + } + } + }, { "include": "#variables" }, @@ -826,7 +902,7 @@ "patterns": [ { "name": "binding.fsharp", - "begin": "\\b(let mutable|static let mutable|let inline|let|member val|static member inline|static member|default|member|override|let!)(\\s+rec|mutable)?(\\s+\\[\\<.*\\>\\])?\\s*(private|internal|public)?\\s+(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9,\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9,\\._`\\s]+|(?<=,)\\s)*)?", + "begin": "\\b(let mutable|static let mutable|let inline|let|member val|static member inline|static member|default|member|override|let!)(\\s+rec|mutable)?(\\s+\\[\\<.*\\>\\])?\\s*(private|internal|public)?\\s+(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9\\._`\\s]+|(?<=,)\\s)*)?", "end": "\\s*(with\\b|=|\\n+=|(?<=\\=))", "beginCaptures": { "1": { @@ -856,6 +932,26 @@ } ] }, + { + "name": "binding.fsharp", + "begin": "\\b((get|set)\\s*(?=\\())(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9\\._`\\s]+|(?<=,)\\s)*)?", + "end": "\\s*(=|\\n+=|(?<=\\=))", + "beginCaptures": { + "3": { + "name": "variable.fsharp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.fsharp" + } + }, + "patterns": [ + { + "include": "#common_binding_definition" + } + ] + }, { "name": "binding.fsharp", "begin": "\\b(static val mutable|val mutable|val)(\\s+rec|mutable)?(\\s+\\[\\<.*\\>\\])?\\s*(private|internal|public)?\\s+(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9,\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9,\\._`\\s]+|(?<=,)\\s)*)?", @@ -920,13 +1016,16 @@ } }, { - "match": "([[:alpha:]0-9'`^._]+)|``([[:alpha:]0-9'^._ ]+)``", + "match": "(``([[:alpha:]0-9'^._ ]+)``|[[:alpha:]0-9'`^._]+)", "captures": { "1": { "name": "entity.name.type.fsharp" } } }, + { + "include": "#anonymous_record_declaration" + }, { "include": "#keywords" } @@ -1007,6 +1106,9 @@ "name": "entity.name.section.fsharp" } } + }, + { + "include": "#comments" } ] }, @@ -1142,7 +1244,7 @@ "match": "\\(\\)" }, { - "match": "(\\?{0,1})(``[[:alpha:]0-9'`^:,._ ]+``|[[:alpha:]0-9'`<>^._ ]\\w*)", + "match": "(\\?{0,1})(``[[:alpha:]0-9'`^:,._ ]+``|(?!private\\b)\\b[\\w[:alpha:]0-9'`<>^._ ]+)", "captures": { "1": { "name": "keyword.symbol.fsharp" @@ -1200,6 +1302,9 @@ } } }, + { + "include": "#anonymous_record_declaration" + }, { "begin": "(\\?{0,1})([[:alpha:]0-9'`^._ ]+)\\s*(:)(\\s*([?[:alpha:]0-9'`^._ ]+)(<))", "end": "(>)", diff --git a/extensions/fsharp/test/colorize-results/test_fs.json b/extensions/fsharp/test/colorize-results/test_fs.json index 37c0b61c14a..e5736fc10aa 100644 --- a/extensions/fsharp/test/colorize-results/test_fs.json +++ b/extensions/fsharp/test/colorize-results/test_fs.json @@ -604,28 +604,6 @@ "hc_black": "keyword: #569CD6" } }, - { - "c": " get", - "t": "source.fsharp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "()", - "t": "source.fsharp constant.language.unit.fsharp", - "r": { - "dark_plus": "constant.language: #569CD6", - "light_plus": "constant.language: #0000FF", - "dark_vs": "constant.language: #569CD6", - "light_vs": "constant.language: #0000FF", - "hc_black": "constant.language: #569CD6" - } - }, { "c": " ", "t": "source.fsharp", @@ -637,9 +615,42 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "get", + "t": "source.fsharp binding.fsharp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "()", + "t": "source.fsharp binding.fsharp constant.language.unit.fsharp", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6" + } + }, + { + "c": " ", + "t": "source.fsharp binding.fsharp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": "=", - "t": "source.fsharp keyword.symbol.fsharp", + "t": "source.fsharp binding.fsharp keyword.fsharp", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -681,50 +692,6 @@ "hc_black": "keyword: #569CD6" } }, - { - "c": " set", - "t": "source.fsharp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "(", - "t": "source.fsharp keyword.symbol.fsharp", - "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" - } - }, - { - "c": "value", - "t": "source.fsharp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": ")", - "t": "source.fsharp keyword.symbol.fsharp", - "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6" - } - }, { "c": " ", "t": "source.fsharp", @@ -736,9 +703,64 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "set", + "t": "source.fsharp binding.fsharp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "(", + "t": "source.fsharp binding.fsharp keyword.symbol.fsharp", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": "value", + "t": "source.fsharp binding.fsharp variable.parameter.fsharp", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.fsharp binding.fsharp keyword.symbol.fsharp", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6" + } + }, + { + "c": " ", + "t": "source.fsharp binding.fsharp", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": "=", - "t": "source.fsharp keyword.symbol.fsharp", + "t": "source.fsharp binding.fsharp keyword.fsharp", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -979,7 +1001,7 @@ } }, { - "c": " targetAge", + "c": " targetAge ", "t": "source.fsharp binding.fsharp variable.parameter.fsharp", "r": { "dark_plus": "variable: #9CDCFE", @@ -989,17 +1011,6 @@ "hc_black": "variable: #9CDCFE" } }, - { - "c": " ", - "t": "source.fsharp binding.fsharp", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, { "c": "=", "t": "source.fsharp binding.fsharp keyword.fsharp", diff --git a/extensions/git-ui/.vscodeignore b/extensions/git-ui/.vscodeignore new file mode 100644 index 00000000000..7462f7448d3 --- /dev/null +++ b/extensions/git-ui/.vscodeignore @@ -0,0 +1,8 @@ +src/** +test/** +out/** +tsconfig.json +build/** +extension.webpack.config.js +cgmanifest.json +yarn.lock diff --git a/extensions/git-ui/README.md b/extensions/git-ui/README.md new file mode 100644 index 00000000000..d418425acfe --- /dev/null +++ b/extensions/git-ui/README.md @@ -0,0 +1,7 @@ +# Git UI integration for Visual Studio Code + +**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled. + +## Features + +See [Git support in VS Code](https://code.visualstudio.com/docs/editor/versioncontrol#_git-support) to learn about the features of this extension. diff --git a/extensions/git-ui/cgmanifest.json b/extensions/git-ui/cgmanifest.json new file mode 100644 index 00000000000..f3071eb691a --- /dev/null +++ b/extensions/git-ui/cgmanifest.json @@ -0,0 +1,4 @@ +{ + "registrations": [], + "version": 1 +} \ No newline at end of file diff --git a/extensions/git-ui/extension.webpack.config.js b/extensions/git-ui/extension.webpack.config.js new file mode 100644 index 00000000000..19c0ea3042d --- /dev/null +++ b/extensions/git-ui/extension.webpack.config.js @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withDefaults = require('../shared.webpack.config'); + +module.exports = withDefaults({ + context: __dirname, + entry: { + main: './src/main.ts' + } +}); diff --git a/extensions/git-ui/package.json b/extensions/git-ui/package.json new file mode 100644 index 00000000000..2f1ab43f892 --- /dev/null +++ b/extensions/git-ui/package.json @@ -0,0 +1,28 @@ +{ + "name": "git-ui", + "displayName": "%displayName%", + "description": "%description%", + "publisher": "vscode", + "version": "1.0.0", + "engines": { + "vscode": "^1.5.0" + }, + "extensionKind": "ui", + "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", + "enableProposedApi": true, + "categories": [ + "Other" + ], + "activationEvents": [ + "onCommand:git.credential" + ], + "main": "./out/main", + "icon": "resources/icons/git.png", + "scripts": { + "compile": "gulp compile-extension:git-ui", + "watch": "gulp watch-extension:git-ui" + }, + "devDependencies": { + "@types/node": "^10.14.8" + } +} \ No newline at end of file diff --git a/extensions/git-ui/package.nls.json b/extensions/git-ui/package.nls.json new file mode 100644 index 00000000000..5303e91f4cd --- /dev/null +++ b/extensions/git-ui/package.nls.json @@ -0,0 +1,4 @@ +{ + "displayName": "Git UI", + "description": "Git SCM UI Integration" +} \ No newline at end of file diff --git a/extensions/git-ui/resources/icons/git.png b/extensions/git-ui/resources/icons/git.png new file mode 100644 index 00000000000..51f4ae5404f Binary files /dev/null and b/extensions/git-ui/resources/icons/git.png differ diff --git a/extensions/git-ui/src/main.ts b/extensions/git-ui/src/main.ts new file mode 100644 index 00000000000..d233b753be2 --- /dev/null +++ b/extensions/git-ui/src/main.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 { ExtensionContext, commands } from 'vscode'; + +import * as cp from 'child_process'; + +export async function deactivate(): Promise { +} + +export async function activate(context: ExtensionContext): Promise { + context.subscriptions.push(commands.registerCommand('git.credential', async (data: any) => { + try { + const { stdout, stderr } = await exec(`git credential ${data.command}`, { + stdin: data.stdin, + env: Object.assign(process.env, { GIT_TERMINAL_PROMPT: '0' }) + }); + return { stdout, stderr, code: 0 }; + } catch ({ stdout, stderr, error }) { + const code = error.code || 0; + if (stderr.indexOf('terminal prompts disabled') !== -1) { + stderr = ''; + } + return { stdout, stderr, code }; + } + })); +} + +export interface ExecResult { + error: Error | null; + stdout: string; + stderr: string; +} + + +export function exec(command: string, options: cp.ExecOptions & { stdin?: string } = {}) { + return new Promise((resolve, reject) => { + const child = cp.exec(command, options, (error, stdout, stderr) => { + (error ? reject : resolve)({ error, stdout, stderr }); + }); + if (options.stdin) { + child.stdin.write(options.stdin, (err: any) => { + if (err) { + reject(err); + return; + } + child.stdin.end((err: any) => { + if (err) { + reject(err); + } + }); + }); + } + }); +} diff --git a/extensions/git-ui/src/typings/refs.d.ts b/extensions/git-ui/src/typings/refs.d.ts new file mode 100644 index 00000000000..a933431220b --- /dev/null +++ b/extensions/git-ui/src/typings/refs.d.ts @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// +/// +/// \ No newline at end of file diff --git a/extensions/git-ui/tsconfig.json b/extensions/git-ui/tsconfig.json new file mode 100644 index 00000000000..27e9268b39b --- /dev/null +++ b/extensions/git-ui/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../shared.tsconfig.json", + "compilerOptions": { + "outDir": "./out", + "experimentalDecorators": true, + "typeRoots": [ + "./node_modules/@types" + ] + }, + "include": [ + "src/**/*" + ] +} \ No newline at end of file diff --git a/extensions/git-ui/yarn.lock b/extensions/git-ui/yarn.lock new file mode 100644 index 00000000000..b23b0ac0392 --- /dev/null +++ b/extensions/git-ui/yarn.lock @@ -0,0 +1,8 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/node@^10.14.8": + version "10.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" + integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== diff --git a/extensions/git/package.json b/extensions/git/package.json index 4395d306bb5..f85fd49c1d7 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -3,6 +3,7 @@ "displayName": "%displayName%", "description": "%description%", "publisher": "vscode", + "license": "MIT", "version": "1.0.0", "engines": { "vscode": "^1.5.0" @@ -20,7 +21,8 @@ "scripts": { "compile": "gulp compile-extension:git", "watch": "gulp watch-extension:git", - "update-grammar": "node ./build/update-grammars.js" + "update-grammar": "node ./build/update-grammars.js", + "test": "mocha" }, "contributes": { "commands": [ @@ -317,12 +319,12 @@ }, { "command": "git.pushWithTags", - "title": "%command.pushWithTags%", + "title": "%command.pushFollowTags%", "category": "Git" }, { "command": "git.pushWithTagsForce", - "title": "%command.pushWithTagsForce%", + "title": "%command.pushFollowTagsForce%", "category": "Git" }, { @@ -1272,6 +1274,12 @@ "default": false, "description": "%config.fetchOnPull%" }, + "git.pullTags": { + "type": "boolean", + "scope": "resource", + "default": true, + "description": "%config.pullTags%" + }, "git.autoStash": { "type": "boolean", "scope": "resource", @@ -1452,13 +1460,14 @@ "jschardet": "^1.6.0", "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.0.0", + "vscode-uri": "^2.0.0", "which": "^1.3.0" }, "devDependencies": { "@types/byline": "4.2.31", "@types/file-type": "^5.2.1", "@types/mocha": "2.2.43", - "@types/node": "^10.12.21", + "@types/node": "^10.14.8", "@types/which": "^1.0.28", "mocha": "^3.2.0" } diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 892e3519cd1..adff0134ed9 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -47,8 +47,8 @@ "command.pushForce": "Push (Force)", "command.pushTo": "Push to...", "command.pushToForce": "Push to... (Force)", - "command.pushWithTags": "Push With Tags", - "command.pushWithTagsForce": "Push With Tags (Force)", + "command.pushFollowTags": "Push (Follow Tags)", + "command.pushFollowTagsForce": "Push (Follow Tags, Force)", "command.addRemote": "Add Remote", "command.removeRemote": "Remove Remote", "command.sync": "Sync", @@ -112,6 +112,7 @@ "config.rebaseWhenSync": "Force git to use rebase when running the sync command.", "config.confirmEmptyCommits": "Always confirm the creation of empty commits.", "config.fetchOnPull": "Fetch all branches when pulling or just the current one.", + "config.pullTags": "Fetch all tags when pulling.", "config.autoStash": "Stash any changes before pulling and restore them after successful pull.", "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.", diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index ae8eb5315bc..9444ea1fada 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -235,4 +235,4 @@ export const enum GitErrorCodes { CantRebaseMultipleBranches = 'CantRebaseMultipleBranches', PatchDoesNotApply = 'PatchDoesNotApply', NoPathFound = 'NoPathFound' -} \ No newline at end of file +} diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index f6e00e9abdd..85ec8ce866a 100755 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -56,7 +56,13 @@ class CheckoutRemoteHeadItem extends CheckoutItem { return; } - await repository.checkoutTracking(this.ref.name); + const branches = await repository.findTrackingBranches(this.ref.name); + + if (branches.length > 0) { + await repository.checkout(branches[0].name!); + } else { + await repository.checkoutTracking(this.ref.name); + } } } @@ -196,7 +202,7 @@ function createCheckoutItems(repository: Repository): CheckoutItem[] { enum PushType { Push, PushTo, - PushTags, + PushFollowTags, } interface PushOptions { @@ -481,10 +487,10 @@ export class CommandCenter { (_, token) => this.git.clone(url!, parentPath, token) ); - const choices = []; let message = localize('proposeopen', "Would you like to open the cloned repository?"); - const open = localize('openrepo', "Open Repository"); - choices.push(open); + const open = localize('openrepo', "Open"); + const openNewWindow = localize('openreponew', "Open in New Window"); + const choices = [open, openNewWindow]; const addToWorkspace = localize('add', "Add to Workspace"); if (workspace.workspaceFolders) { @@ -509,6 +515,8 @@ export class CommandCenter { commands.executeCommand('vscode.openFolder', uri); } else if (result === addToWorkspace) { workspace.updateWorkspaceFolders(workspace.workspaceFolders!.length, 0, { uri }); + } else if (result === openNewWindow) { + commands.executeCommand('vscode.openFolder', uri, true); } } catch (err) { if (/already exists and is not an empty directory/.test(err && err.stderr || '')) { @@ -593,10 +601,10 @@ export class CommandCenter { await this.git.init(repositoryPath); - const choices = []; let message = localize('proposeopen init', "Would you like to open the initialized repository?"); - const open = localize('openrepo', "Open Repository"); - choices.push(open); + const open = localize('openrepo', "Open"); + const openNewWindow = localize('openreponew', "Open in New Window"); + const choices = [open, openNewWindow]; if (!askToOpen) { return; @@ -615,6 +623,8 @@ export class CommandCenter { commands.executeCommand('vscode.openFolder', uri); } else if (result === addToWorkspace) { workspace.updateWorkspaceFolders(workspace.workspaceFolders!.length, 0, { uri }); + } else if (result === openNewWindow) { + commands.executeCommand('vscode.openFolder', uri, true); } else { await this.model.openRepository(repositoryPath); } @@ -1753,10 +1763,8 @@ export class CommandCenter { } } - if (pushOptions.pushType === PushType.PushTags) { - await repository.pushTags(undefined, forcePushMode); - - window.showInformationMessage(localize('push with tags success', "Successfully pushed with tags.")); + if (pushOptions.pushType === PushType.PushFollowTags) { + await repository.pushFollowTags(undefined, forcePushMode); return; } @@ -1813,13 +1821,13 @@ export class CommandCenter { } @command('git.pushWithTags', { repository: true }) - async pushWithTags(repository: Repository): Promise { - await this._push(repository, { pushType: PushType.PushTags }); + async pushFollowTags(repository: Repository): Promise { + await this._push(repository, { pushType: PushType.PushFollowTags }); } @command('git.pushWithTagsForce', { repository: true }) - async pushWithTagsForce(repository: Repository): Promise { - await this._push(repository, { pushType: PushType.PushTags, forcePush: true }); + async pushFollowTagsForce(repository: Repository): Promise { + await this._push(repository, { pushType: PushType.PushFollowTags, forcePush: true }); } @command('git.pushTo', { repository: true }) diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 3132b8cdf86..30348608dd0 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -12,7 +12,8 @@ import { EventEmitter } from 'events'; import iconv = require('iconv-lite'); import * as filetype from 'file-type'; import { assign, groupBy, denodeify, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent } from './util'; -import { CancellationToken, Uri } from 'vscode'; +import { CancellationToken } from 'vscode'; +import { URI } from 'vscode-uri'; import { detectEncoding } from './encoding'; import { Ref, RefType, Branch, Remote, GitErrorCodes, LogOptions, Change, Status } from './api/git'; @@ -306,6 +307,8 @@ function getGitErrorCode(stderr: string): string | undefined { return GitErrorCodes.BranchAlreadyExists; } else if (/'.+' is not a valid branch name/.test(stderr)) { return GitErrorCodes.InvalidBranchName; + } else if (/Please,? commit your changes or stash them/.test(stderr)) { + return GitErrorCodes.DirtyWorkTree; } return undefined; @@ -326,8 +329,8 @@ export class Git { this.env = options.env || {}; } - open(repository: string): Repository { - return new Repository(this, repository); + open(repository: string, dotGit: string): Repository { + return new Repository(this, repository, dotGit); } async init(repository: string): Promise { @@ -336,7 +339,7 @@ export class Git { } async clone(url: string, parentPath: string, cancellationToken?: CancellationToken): Promise { - let baseFolderName = decodeURI(url).replace(/^.*\//, '').replace(/\.git$/, '') || 'repository'; + let baseFolderName = decodeURI(url).replace(/[\/]+$/, '').replace(/^.*\//, '').replace(/\.git$/, '') || 'repository'; let folderName = baseFolderName; let folderPath = path.join(parentPath, folderName); let count = 1; @@ -367,6 +370,17 @@ export class Git { return path.normalize(result.stdout.trim()); } + async getRepositoryDotGit(repositoryPath: string): Promise { + const result = await this.exec(repositoryPath, ['rev-parse', '--git-dir']); + let dotGitPath = result.stdout.trim(); + + if (!path.isAbsolute(dotGitPath)) { + dotGitPath = path.join(repositoryPath, dotGitPath); + } + + return path.normalize(dotGitPath); + } + async exec(cwd: string, args: string[], options: SpawnOptions = {}): Promise> { options = assign({ cwd }, options || {}); return await this._exec(args, options); @@ -555,7 +569,7 @@ export function parseGitmodules(raw: string): Submodule[] { return; } - const propertyMatch = /^\s*(\w+) = (.*)$/.exec(line); + const propertyMatch = /^\s*(\w+)\s+=\s+(.*)$/.exec(line); if (!propertyMatch) { return; @@ -634,6 +648,7 @@ export interface CommitOptions { export interface PullOptions { unshallow?: boolean; + tags?: boolean; } export enum ForcePushMode { @@ -645,7 +660,8 @@ export class Repository { constructor( private _git: Git, - private repositoryRoot: string + private repositoryRoot: string, + readonly dotGit: string ) { } get git(): Git { @@ -993,7 +1009,7 @@ export class Repository { break; } - const originalUri = Uri.file(path.isAbsolute(resourcePath) ? resourcePath : path.join(this.repositoryRoot, resourcePath)); + const originalUri = URI.file(path.isAbsolute(resourcePath) ? resourcePath : path.join(this.repositoryRoot, resourcePath)); let status: Status = Status.UNTRACKED; // Copy or Rename status comes with a number, e.g. 'R100'. We don't need the number, so we use only first character of the status. @@ -1021,7 +1037,7 @@ export class Repository { break; } - const uri = Uri.file(path.isAbsolute(newPath) ? newPath : path.join(this.repositoryRoot, newPath)); + const uri = URI.file(path.isAbsolute(newPath) ? newPath : path.join(this.repositoryRoot, newPath)); result.push({ uri, renameUri: uri, @@ -1360,7 +1376,11 @@ export class Repository { } async pull(rebase?: boolean, remote?: string, branch?: string, options: PullOptions = {}): Promise { - const args = ['pull', '--tags']; + const args = ['pull']; + + if (options.tags) { + args.push('--tags'); + } if (options.unshallow) { args.push('--unshallow'); @@ -1411,7 +1431,7 @@ export class Repository { } if (tags) { - args.push('--tags'); + args.push('--follow-tags'); } if (remote) { @@ -1571,6 +1591,14 @@ export class Repository { } } + async findTrackingBranches(upstreamBranch: string): Promise { + const result = await this.run(['for-each-ref', '--format', '%(refname:short)%00%(upstream:short)', 'refs/heads']); + return result.stdout.trim().split('\n') + .map(line => line.trim().split('\0')) + .filter(([_, upstream]) => upstream === upstreamBranch) + .map(([ref]) => ({ name: ref, type: RefType.Head } as Branch)); + } + async getRefs(): Promise { const result = await this.run(['for-each-ref', '--format', '%(refname) %(objectname)', '--sort', '-committerdate']); diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index 22c690a5dfd..a04dc33c8be 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -78,7 +78,7 @@ export class Model { this.disposables.push(fsWatcher); const onWorkspaceChange = anyEvent(fsWatcher.onDidChange, fsWatcher.onDidCreate, fsWatcher.onDidDelete); - const onGitRepositoryChange = filterEvent(onWorkspaceChange, uri => /\/\.git\//.test(uri.path)); + const onGitRepositoryChange = filterEvent(onWorkspaceChange, uri => /\/\.git/.test(uri.path)); const onPossibleGitRepositoryChange = filterEvent(onGitRepositoryChange, uri => !this.getRepository(uri)); onPossibleGitRepositoryChange(this.onPossibleGitRepositoryChange, this, this.disposables); @@ -232,7 +232,8 @@ export class Model { return; } - const repository = new Repository(this.git.open(repositoryRoot), this.globalState); + const dotGit = await this.git.getRepositoryDotGit(repositoryRoot); + const repository = new Repository(this.git.open(repositoryRoot, dotGit), this.globalState, this.outputChannel); this.open(repository); } catch (err) { diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 2fac9e2a39f..a581f9f5fd4 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { commands, Uri, Command, EventEmitter, Event, scm, SourceControl, SourceControlInputBox, SourceControlResourceGroup, SourceControlResourceState, SourceControlResourceDecorations, SourceControlInputBoxValidation, Disposable, ProgressLocation, window, workspace, WorkspaceEdit, ThemeColor, DecorationData, Memento, SourceControlInputBoxValidationType } from 'vscode'; +import { commands, Uri, Command, EventEmitter, Event, scm, SourceControl, SourceControlInputBox, SourceControlResourceGroup, SourceControlResourceState, SourceControlResourceDecorations, SourceControlInputBoxValidation, Disposable, ProgressLocation, window, workspace, WorkspaceEdit, ThemeColor, DecorationData, Memento, SourceControlInputBoxValidationType, OutputChannel, LogLevel, env } from 'vscode'; import { Repository as BaseRepository, Commit, Stash, GitError, Submodule, CommitOptions, ForcePushMode } from './git'; -import { anyEvent, filterEvent, eventToPromise, dispose, find, isDescendant, IDisposable, onceEvent, EmptyDisposable, debounceEvent } from './util'; +import { anyEvent, filterEvent, eventToPromise, dispose, find, isDescendant, IDisposable, onceEvent, EmptyDisposable, debounceEvent, combinedDisposable, watch, IFileWatcher } from './util'; import { memoize, throttle, debounce } from './decorators'; import { toGitUri } from './uri'; import { AutoFetcher } from './autofetch'; @@ -299,6 +299,7 @@ export const enum Operation { GetObjectDetails = 'GetObjectDetails', SubmoduleUpdate = 'SubmoduleUpdate', RebaseContinue = 'RebaseContinue', + FindTrackingBranches = 'GetTracking', Apply = 'Apply', Blame = 'Blame', Log = 'Log', @@ -446,6 +447,91 @@ class ProgressManager { } } +class FileEventLogger { + + private eventDisposable: IDisposable = EmptyDisposable; + private logLevelDisposable: IDisposable = EmptyDisposable; + + constructor( + private onWorkspaceWorkingTreeFileChange: Event, + private onDotGitFileChange: Event, + private outputChannel: OutputChannel + ) { + this.logLevelDisposable = env.onDidChangeLogLevel(this.onDidChangeLogLevel, this); + this.onDidChangeLogLevel(env.logLevel); + } + + private onDidChangeLogLevel(level: LogLevel): void { + this.eventDisposable.dispose(); + + if (level > LogLevel.Debug) { + return; + } + + this.eventDisposable = combinedDisposable([ + this.onWorkspaceWorkingTreeFileChange(uri => this.outputChannel.appendLine(`[debug] [wt] Change: ${uri.fsPath}`)), + this.onDotGitFileChange(uri => this.outputChannel.appendLine(`[debug] [.git] Change: ${uri.fsPath}`)) + ]); + } + + dispose(): void { + this.eventDisposable.dispose(); + this.logLevelDisposable.dispose(); + } +} + +class DotGitWatcher implements IFileWatcher { + + readonly event: Event; + + private emitter = new EventEmitter(); + private transientDisposables: IDisposable[] = []; + private disposables: IDisposable[] = []; + + constructor( + private repository: Repository, + private outputChannel: OutputChannel + ) { + const rootWatcher = watch(repository.dotGit); + this.disposables.push(rootWatcher); + + const filteredRootWatcher = filterEvent(rootWatcher.event, uri => !/\/\.git(\/index\.lock)?$/.test(uri.path)); + this.event = anyEvent(filteredRootWatcher, this.emitter.event); + + repository.onDidRunGitStatus(this.updateTransientWatchers, this, this.disposables); + this.updateTransientWatchers(); + } + + private updateTransientWatchers() { + this.transientDisposables = dispose(this.transientDisposables); + + if (!this.repository.HEAD || !this.repository.HEAD.upstream) { + return; + } + + this.transientDisposables = dispose(this.transientDisposables); + + const { name, remote } = this.repository.HEAD.upstream; + const upstreamPath = path.join(this.repository.dotGit, 'refs', 'remotes', remote, name); + + try { + const upstreamWatcher = watch(upstreamPath); + this.transientDisposables.push(upstreamWatcher); + upstreamWatcher.event(this.emitter.fire, this.emitter, this.transientDisposables); + } catch (err) { + if (env.logLevel <= LogLevel.Info) { + this.outputChannel.appendLine(`Failed to watch ref '${upstreamPath}'. Ref is most likely packed.`); + } + } + } + + dispose() { + this.emitter.dispose(); + this.transientDisposables = dispose(this.transientDisposables); + this.disposables = dispose(this.disposables); + } +} + export class Repository implements Disposable { private _onDidChangeRepository = new EventEmitter(); @@ -543,36 +629,41 @@ export class Repository implements Disposable { return this.repository.root; } + get dotGit(): string { + return this.repository.dotGit; + } + private isRepositoryHuge = false; private didWarnAboutLimit = false; private isFreshRepository: boolean | undefined = undefined; + private disposables: Disposable[] = []; constructor( private readonly repository: BaseRepository, - globalState: Memento + globalState: Memento, + outputChannel: OutputChannel ) { - const fsWatcher = workspace.createFileSystemWatcher('**'); - this.disposables.push(fsWatcher); + const workspaceWatcher = workspace.createFileSystemWatcher('**'); + this.disposables.push(workspaceWatcher); - const workspaceFilter = (uri: Uri) => isDescendant(repository.root, uri.fsPath); - const onWorkspaceDelete = filterEvent(fsWatcher.onDidDelete, workspaceFilter); - const onWorkspaceChange = filterEvent(anyEvent(fsWatcher.onDidChange, fsWatcher.onDidCreate), workspaceFilter); - const onRepositoryDotGitDelete = filterEvent(onWorkspaceDelete, uri => /\/\.git$/.test(uri.path)); - const onRepositoryChange = anyEvent(onWorkspaceDelete, onWorkspaceChange); + const onWorkspaceFileChange = anyEvent(workspaceWatcher.onDidChange, workspaceWatcher.onDidCreate, workspaceWatcher.onDidDelete); + const onWorkspaceRepositoryFileChange = filterEvent(onWorkspaceFileChange, uri => isDescendant(repository.root, uri.fsPath)); + const onWorkspaceWorkingTreeFileChange = filterEvent(onWorkspaceRepositoryFileChange, uri => !/\/\.git($|\/)/.test(uri.path)); - // relevant repository changes are: - // - DELETE .git folder - // - ANY CHANGE within .git folder except .git itself and .git/index.lock - const onRelevantRepositoryChange = anyEvent( - onRepositoryDotGitDelete, - filterEvent(onRepositoryChange, uri => !/\/\.git(\/index\.lock)?$/.test(uri.path)) - ); + const dotGitFileWatcher = new DotGitWatcher(this, outputChannel); + this.disposables.push(dotGitFileWatcher); - onRelevantRepositoryChange(this.onFSChange, this, this.disposables); + // FS changes should trigger `git status`: + // - any change inside the repository working tree + // - any change whithin the first level of the `.git` folder, except the folder itself and `index.lock` + const onFileChange = anyEvent(onWorkspaceWorkingTreeFileChange, dotGitFileWatcher.event); + onFileChange(this.onFileChange, this, this.disposables); - const onRelevantGitChange = filterEvent(onRelevantRepositoryChange, uri => /\/\.git\//.test(uri.path)); - onRelevantGitChange(this._onDidChangeRepository.fire, this._onDidChangeRepository, this.disposables); + // Relevate repository changes should trigger virtual document change events + dotGitFileWatcher.event(this._onDidChangeRepository.fire, this._onDidChangeRepository, this.disposables); + + this.disposables.push(new FileEventLogger(onWorkspaceWorkingTreeFileChange, dotGitFileWatcher.event, outputChannel)); const root = Uri.file(repository.root); this._sourceControl = scm.createSourceControl('git', 'Git', root); @@ -582,9 +673,9 @@ export class Repository implements Disposable { this._sourceControl.inputBox.validateInput = this.validateInput.bind(this); this.disposables.push(this._sourceControl); - this._mergeGroup = this._sourceControl.createResourceGroup('merge', localize('merge changes', "Merge Changes")); - this._indexGroup = this._sourceControl.createResourceGroup('index', localize('staged changes', "Staged Changes")); - this._workingTreeGroup = this._sourceControl.createResourceGroup('workingTree', localize('changes', "Changes")); + this._mergeGroup = this._sourceControl.createResourceGroup('merge', localize('merge changes', "MERGE CHANGES")); + this._indexGroup = this._sourceControl.createResourceGroup('index', localize('staged changes', "STAGED CHANGES")); + this._workingTreeGroup = this._sourceControl.createResourceGroup('workingTree', localize('changes', "CHANGES")); const updateIndexGroupVisibility = () => { const config = workspace.getConfiguration('git', root); @@ -909,6 +1000,10 @@ export class Repository implements Disposable { await this.run(Operation.CheckoutTracking, () => this.repository.checkout(treeish, [], { track: true })); } + async findTrackingBranches(upstreamRef: string): Promise { + return await this.run(Operation.FindTrackingBranches, () => this.repository.findTrackingBranches(upstreamRef)); + } + async getCommit(ref: string): Promise { return await this.repository.getCommit(ref); } @@ -979,11 +1074,12 @@ export class Repository implements Disposable { await this.maybeAutoStash(async () => { const config = workspace.getConfiguration('git', Uri.file(this.root)); const fetchOnPull = config.get('fetchOnPull'); + const tags = config.get('pullTags'); if (fetchOnPull) { - await this.repository.pull(rebase, undefined, undefined, { unshallow }); + await this.repository.pull(rebase, undefined, undefined, { unshallow, tags }); } else { - await this.repository.pull(rebase, remote, branch, { unshallow }); + await this.repository.pull(rebase, remote, branch, { unshallow, tags }); } }); }); @@ -1006,7 +1102,7 @@ export class Repository implements Disposable { await this.run(Operation.Push, () => this.repository.push(remote, name, setUpstream, undefined, forcePushMode)); } - async pushTags(remote?: string, forcePushMode?: ForcePushMode): Promise { + async pushFollowTags(remote?: string, forcePushMode?: ForcePushMode): Promise { await this.run(Operation.Push, () => this.repository.push(remote, undefined, false, true, forcePushMode)); } @@ -1039,11 +1135,12 @@ export class Repository implements Disposable { await this.maybeAutoStash(async () => { const config = workspace.getConfiguration('git', Uri.file(this.root)); const fetchOnPull = config.get('fetchOnPull'); + const tags = config.get('pullTags'); if (fetchOnPull) { - await this.repository.pull(rebase); + await this.repository.pull(rebase, undefined, undefined, { tags }); } else { - await this.repository.pull(rebase, remoteName, pullBranch); + await this.repository.pull(rebase, remoteName, pullBranch, { tags }); } const remote = this.remotes.find(r => r.name === remoteName); @@ -1156,7 +1253,7 @@ export class Repository implements Disposable { } // https://git-scm.com/docs/git-check-ignore#git-check-ignore--z - const child = this.repository.stream(['check-ignore', '-z', '--stdin'], { stdio: [null, null, null] }); + const child = this.repository.stream(['check-ignore', '-v', '-z', '--stdin'], { stdio: [null, null, null] }); child.stdin.end(filePaths.join('\0'), 'utf8'); const onExit = (exitCode: number) => { @@ -1164,8 +1261,7 @@ export class Repository implements Disposable { // nothing ignored resolve(new Set()); } else if (exitCode === 0) { - // paths are separated by the null-character - resolve(new Set(data.split('\0'))); + resolve(new Set(this.parseIgnoreCheck(data))); } else { if (/ is in submodule /.test(stderr)) { reject(new GitError({ stdout: data, stderr, exitCode, gitErrorCode: GitErrorCodes.IsInSubmodule })); @@ -1193,6 +1289,23 @@ export class Repository implements Disposable { }); } + // Parses output of `git check-ignore -v -z` and returns only those paths + // that are actually ignored by git. + // Matches to a negative pattern (starting with '!') are filtered out. + // See also https://git-scm.com/docs/git-check-ignore#_output. + private parseIgnoreCheck(raw: string): string[] { + const ignored = []; + const elements = raw.split('\0'); + for (let i = 0; i < elements.length; i += 4) { + const pattern = elements[i + 2]; + const path = elements[i + 3]; + if (pattern && !pattern.startsWith('!')) { + ignored.push(path); + } + } + return ignored; + } + private async run(operation: Operation, runOperation: () => Promise = () => Promise.resolve(null)): Promise { if (this.state !== RepositoryState.Idle) { throw new Error('Repository not initialized'); @@ -1430,7 +1543,7 @@ export class Repository implements Disposable { return result; } - private onFSChange(_uri: Uri): void { + private onFileChange(_uri: Uri): void { const config = workspace.getConfiguration('git'); const autorefresh = config.get('autorefresh'); diff --git a/extensions/git/src/test/git.test.ts b/extensions/git/src/test/git.test.ts index e28cf10d192..f0444ce5795 100644 --- a/extensions/git/src/test/git.test.ts +++ b/extensions/git/src/test/git.test.ts @@ -172,6 +172,17 @@ suite('git', () => { { name: 'deps/spdlog4', path: 'deps/spdlog4', url: 'https://github.com/gabime/spdlog4.git' } ]); }); + + test('whitespace #74844', () => { + const sample = `[submodule "deps/spdlog"] + path = deps/spdlog + url = https://github.com/gabime/spdlog.git +`; + + assert.deepEqual(parseGitmodules(sample), [ + { name: 'deps/spdlog', path: 'deps/spdlog', url: 'https://github.com/gabime/spdlog.git' } + ]); + }); }); suite('parseGitCommit', () => { diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index 7bf81adccd9..c4e93850619 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event } from 'vscode'; -import { dirname, sep } from 'path'; +import { Event, EventEmitter, Uri } from 'vscode'; +import { dirname, sep, join } from 'path'; import { Readable } from 'stream'; import * as fs from 'fs'; import * as byline from 'byline'; @@ -343,4 +343,20 @@ export function pathEquals(a: string, b: string): boolean { } return a === b; -} \ No newline at end of file +} + +export interface IFileWatcher extends IDisposable { + readonly event: Event; +} + +export function watch(location: string): IFileWatcher { + const dotGitWatcher = fs.watch(location); + const onDotGitFileChangeEmitter = new EventEmitter(); + dotGitWatcher.on('change', (_, e) => onDotGitFileChangeEmitter.fire(Uri.file(join(location, e as string)))); + dotGitWatcher.on('error', err => console.error(err)); + + return new class implements IFileWatcher { + event = onDotGitFileChangeEmitter.event; + dispose() { dotGitWatcher.close(); } + }; +} diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index f13ef93c34f..1f2ea8aed42 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -26,10 +26,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.51.tgz#b31d716fb8d58eeb95c068a039b9b6292817d5fb" integrity sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ== -"@types/node@^10.12.21": - version "10.12.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" - integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== +"@types/node@^10.14.8": + version "10.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" + integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== "@types/which@^1.0.28": version "1.0.28" @@ -325,6 +325,11 @@ vscode-nls@^4.0.0: resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002" integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw== +vscode-uri@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.0.tgz#2df704222f72b8a71ff266ba0830ed6c51ac1542" + integrity sha512-lWXWofDSYD8r/TIyu64MdwB4FaSirQ608PP/TzUyslyOeHGwQ0eTHUZeJrK1ILOmwUHaJtV693m2JoUYroUDpw== + which@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" diff --git a/extensions/go/language-configuration.json b/extensions/go/language-configuration.json index cf20e02f307..a5e06a56bad 100644 --- a/extensions/go/language-configuration.json +++ b/extensions/go/language-configuration.json @@ -27,5 +27,11 @@ "indentationRules": { "increaseIndentPattern": "^.*(\\bcase\\b.*:|\\bdefault\\b:|(\\b(func|if|else|switch|select|for|struct)\\b.*)?{[^}\"'`]*|\\([^)\"'`]*)$", "decreaseIndentPattern": "^\\s*(\\bcase\\b.*:|\\bdefault\\b:|}[)}]*[),]?|\\)[,]?)$" + }, + "folding": { + "markers": { + "start": "^\\s*//\\s*#?region\\b", + "end": "^\\s*//\\s*#?endregion\\b" + } } } \ No newline at end of file diff --git a/extensions/go/package.json b/extensions/go/package.json index 97aced567ae..a91d729adaf 100644 --- a/extensions/go/package.json +++ b/extensions/go/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, "scripts": { "update-grammar": "node ../../build/npm/update-grammar.js atom/language-go grammars/go.cson ./syntaxes/go.tmLanguage.json" diff --git a/extensions/groovy/package.json b/extensions/groovy/package.json index 9cb93f29956..9bed5780419 100644 --- a/extensions/groovy/package.json +++ b/extensions/groovy/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, "scripts": { "update-grammar": "node ../../build/npm/update-grammar.js textmate/groovy.tmbundle Syntaxes/Groovy.tmLanguage ./syntaxes/groovy.tmLanguage.json" diff --git a/extensions/grunt/package.json b/extensions/grunt/package.json index 74fa452028a..f65cc6e62b0 100644 --- a/extensions/grunt/package.json +++ b/extensions/grunt/package.json @@ -5,6 +5,7 @@ "displayName": "Grunt support for VS Code", "version": "1.0.0", "icon": "images/grunt.png", + "license": "MIT", "engines": { "vscode": "*" }, @@ -19,7 +20,7 @@ "vscode-nls": "^4.0.0" }, "devDependencies": { - "@types/node": "^10.12.21" + "@types/node": "^10.14.8" }, "main": "./out/main", "activationEvents": [ diff --git a/extensions/grunt/src/main.ts b/extensions/grunt/src/main.ts index 09a812ac06f..60dd60839a9 100644 --- a/extensions/grunt/src/main.ts +++ b/extensions/grunt/src/main.ts @@ -230,7 +230,7 @@ class TaskDetector { this.detectors.clear(); } - private updateWorkspaceFolders(added: vscode.WorkspaceFolder[], removed: vscode.WorkspaceFolder[]): void { + private updateWorkspaceFolders(added: readonly vscode.WorkspaceFolder[], removed: readonly vscode.WorkspaceFolder[]): void { for (let remove of removed) { let detector = this.detectors.get(remove.uri.toString()); if (detector) { diff --git a/extensions/grunt/yarn.lock b/extensions/grunt/yarn.lock index 1bcd757b8a1..e6247e29255 100644 --- a/extensions/grunt/yarn.lock +++ b/extensions/grunt/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@^10.12.21": - version "10.12.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" - integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== +"@types/node@^10.14.8": + version "10.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" + integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/gulp/package.json b/extensions/gulp/package.json index a9139780f7a..b602bf30c7d 100644 --- a/extensions/gulp/package.json +++ b/extensions/gulp/package.json @@ -5,6 +5,7 @@ "displayName": "%displayName%", "version": "1.0.0", "icon": "images/gulp.png", + "license": "MIT", "engines": { "vscode": "*" }, @@ -19,7 +20,7 @@ "vscode-nls": "^4.0.0" }, "devDependencies": { - "@types/node": "^10.12.21" + "@types/node": "^10.14.8" }, "main": "./out/main", "activationEvents": [ diff --git a/extensions/gulp/src/main.ts b/extensions/gulp/src/main.ts index 653e34bd27f..49011bf7d68 100644 --- a/extensions/gulp/src/main.ts +++ b/extensions/gulp/src/main.ts @@ -151,7 +151,7 @@ class FolderDetector { task: line }; let options: vscode.ShellExecutionOptions = { cwd: this.workspaceFolder.uri.fsPath }; - let task = new vscode.Task(kind, this.workspaceFolder, line, 'gulp', new vscode.ShellExecution(`${gulpCommand} ${line}`, options)); + let task = new vscode.Task(kind, this.workspaceFolder, line, 'gulp', new vscode.ShellExecution(gulpCommand, [line], options)); result.push(task); let lowerCaseLine = line.toLowerCase(); if (isBuildTask(lowerCaseLine)) { @@ -209,7 +209,7 @@ class TaskDetector { this.detectors.clear(); } - private updateWorkspaceFolders(added: vscode.WorkspaceFolder[], removed: vscode.WorkspaceFolder[]): void { + private updateWorkspaceFolders(added: readonly vscode.WorkspaceFolder[], removed: readonly vscode.WorkspaceFolder[]): void { for (let remove of removed) { let detector = this.detectors.get(remove.uri.toString()); if (detector) { diff --git a/extensions/gulp/yarn.lock b/extensions/gulp/yarn.lock index 1bcd757b8a1..e6247e29255 100644 --- a/extensions/gulp/yarn.lock +++ b/extensions/gulp/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@types/node@^10.12.21": - version "10.12.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e" - integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ== +"@types/node@^10.14.8": + version "10.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" + integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/handlebars/cgmanifest.json b/extensions/handlebars/cgmanifest.json index 4d30387e915..39f8efc676d 100644 --- a/extensions/handlebars/cgmanifest.json +++ b/extensions/handlebars/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "daaain/Handlebars", "repositoryUrl": "https://github.com/daaain/Handlebars", - "commitHash": "790f2b0222098a3a236bd9e91bb9a039eeca4d8e" + "commitHash": "85a153a6f759df4e8da7533e1b3651f007867c51" } }, "licenseDetail": [ @@ -29,7 +29,7 @@ "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": "1.7.1" + "version": "1.8.0" } ], "version": 1 diff --git a/extensions/handlebars/package.json b/extensions/handlebars/package.json index fdb8d4fc734..37b63ee54ce 100644 --- a/extensions/handlebars/package.json +++ b/extensions/handlebars/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "0.10.x" }, diff --git a/extensions/handlebars/syntaxes/Handlebars.tmLanguage.json b/extensions/handlebars/syntaxes/Handlebars.tmLanguage.json index 957f16ae035..be8b06fa085 100644 --- a/extensions/handlebars/syntaxes/Handlebars.tmLanguage.json +++ b/extensions/handlebars/syntaxes/Handlebars.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/daaain/Handlebars/commit/790f2b0222098a3a236bd9e91bb9a039eeca4d8e", + "version": "https://github.com/daaain/Handlebars/commit/85a153a6f759df4e8da7533e1b3651f007867c51", "name": "Handlebars", "scopeName": "text.html.handlebars", "patterns": [ @@ -653,7 +653,7 @@ ] }, "else_token": { - "begin": "(\\{\\{)(~?else)(@?\\s(if)\\s([-a-zA-Z0-9_\\./]+))?", + "begin": "(\\{\\{)(~?else)(@?\\s(if)\\s([-a-zA-Z0-9_\\.\\(\\s\\)/]+))?", "end": "(~?\\}\\}\\}*)", "name": "meta.function.inline.else.handlebars", "beginCaptures": { diff --git a/extensions/hlsl/package.json b/extensions/hlsl/package.json index f68ce50ee72..b2f635b4aa0 100644 --- a/extensions/hlsl/package.json +++ b/extensions/hlsl/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "engines": { "vscode": "*" }, "scripts": { "update-grammar": "node ../../build/npm/update-grammar.js tgjones/shaders-tmLanguage grammars/hlsl.json ./syntaxes/hlsl.tmLanguage.json" diff --git a/extensions/html-language-features/client/src/tagClosing.ts b/extensions/html-language-features/client/src/tagClosing.ts index 35511e63f43..298edcdaa0a 100644 --- a/extensions/html-language-features/client/src/tagClosing.ts +++ b/extensions/html-language-features/client/src/tagClosing.ts @@ -32,7 +32,7 @@ export function activateTagClosing(tagProvider: (document: TextDocument, positio isEnabled = true; } - function onDidChangeTextDocument(document: TextDocument, changes: TextDocumentContentChangeEvent[]) { + function onDidChangeTextDocument(document: TextDocument, changes: readonly TextDocumentContentChangeEvent[]) { if (!isEnabled) { return; } diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index e44dc9f7102..e2d06c20860 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -5,6 +5,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "license": "MIT", "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", "engines": { "vscode": "0.10.x" @@ -176,10 +177,10 @@ }, "dependencies": { "vscode-extension-telemetry": "0.1.1", - "vscode-languageclient": "^5.2.1", - "vscode-nls": "^4.0.0" + "vscode-languageclient": "^5.3.0-next.6", + "vscode-nls": "^4.1.1" }, "devDependencies": { - "@types/node": "^10.12.21" + "@types/node": "^10.14.8" } } diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index 74aa5e694a1..e97a42ab0a6 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -9,16 +9,16 @@ }, "main": "./out/htmlServerMain", "dependencies": { - "vscode-css-languageservice": "^4.0.2-next.3", - "vscode-html-languageservice": "^3.0.0-next.6", - "vscode-languageserver": "^5.3.0-next.2", - "vscode-languageserver-types": "^3.14.0", - "vscode-nls": "^4.0.0", - "vscode-uri": "^1.0.6" + "vscode-css-languageservice": "^4.0.2", + "vscode-html-languageservice": "^3.0.0", + "vscode-languageserver": "^5.3.0-next.8", + "vscode-languageserver-types": "3.15.0-next.2", + "vscode-nls": "^4.1.1", + "vscode-uri": "^2.0.1" }, "devDependencies": { "@types/mocha": "2.2.33", - "@types/node": "^10.12.21", + "@types/node": "^10.14.8", "glob": "^7.1.2", "mocha": "^5.2.0", "mocha-junit-reporter": "^1.17.0", diff --git a/extensions/html-language-features/server/src/htmlServerMain.ts b/extensions/html-language-features/server/src/htmlServerMain.ts index 7974284bd77..46bd6e8d090 100644 --- a/extensions/html-language-features/server/src/htmlServerMain.ts +++ b/extensions/html-language-features/server/src/htmlServerMain.ts @@ -7,7 +7,7 @@ import { createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, RequestType, DocumentRangeFormattingRequest, Disposable, DocumentSelector, TextDocumentPositionParams, ServerCapabilities, Position, ConfigurationRequest, ConfigurationParams, DidChangeWorkspaceFoldersNotification, - WorkspaceFolder, DocumentColorRequest, ColorInformation, ColorPresentationRequest + WorkspaceFolder, DocumentColorRequest, ColorInformation, ColorPresentationRequest, TextDocumentSyncKind } from 'vscode-languageserver'; import { TextDocument, Diagnostic, DocumentLink, SymbolInformation } from 'vscode-languageserver-types'; import { getLanguageModes, LanguageModes, Settings } from './modes/languageModes'; @@ -15,7 +15,7 @@ import { getLanguageModes, LanguageModes, Settings } from './modes/languageModes import { format } from './modes/formatting'; import { pushAll } from './utils/arrays'; import { getDocumentContext } from './utils/documentContext'; -import uri from 'vscode-uri'; +import { URI } from 'vscode-uri'; import { formatError, runSafe, runSafeAsync } from './utils/runner'; import { getFoldingRanges } from './modes/htmlFolding'; @@ -38,9 +38,8 @@ process.on('uncaughtException', (e: any) => { console.error(formatError(`Unhandled exception`, e)); }); -// Create a simple text document manager. The text document manager -// supports full document sync only -const documents: TextDocuments = new TextDocuments(); +// Create a text document manager. +const documents: TextDocuments = new TextDocuments(TextDocumentSyncKind.Incremental); // Make the text document manager listen on the connection // for open, change and close text document events documents.listen(connection); @@ -85,7 +84,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { if (!Array.isArray(workspaceFolders)) { workspaceFolders = []; if (params.rootPath) { - workspaceFolders.push({ name: '', uri: uri.file(params.rootPath).toString() }); + workspaceFolders.push({ name: '', uri: URI.file(params.rootPath).toString() }); } } diff --git a/extensions/html-language-features/server/src/modes/htmlMode.ts b/extensions/html-language-features/server/src/modes/htmlMode.ts index 09efb996f6e..57249a65b8a 100644 --- a/extensions/html-language-features/server/src/modes/htmlMode.ts +++ b/extensions/html-language-features/server/src/modes/htmlMode.ts @@ -15,7 +15,7 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: getId() { return 'html'; }, - getSelectionRanges(document: TextDocument, positions: Position[]): SelectionRange[][] { + getSelectionRanges(document: TextDocument, positions: Position[]): SelectionRange[] { return htmlLanguageService.getSelectionRanges(document, positions); }, doComplete(document: TextDocument, position: Position, settings = workspace.settings) { diff --git a/extensions/html-language-features/server/src/modes/languageModes.ts b/extensions/html-language-features/server/src/modes/languageModes.ts index 94c0b04a293..b44b2442420 100644 --- a/extensions/html-language-features/server/src/modes/languageModes.ts +++ b/extensions/html-language-features/server/src/modes/languageModes.ts @@ -31,7 +31,7 @@ export interface Workspace { export interface LanguageMode { getId(): string; - getSelectionRanges?: (document: TextDocument, positions: Position[]) => SelectionRange[][]; + getSelectionRanges?: (document: TextDocument, positions: Position[]) => SelectionRange[]; doValidation?: (document: TextDocument, settings?: Settings) => Diagnostic[]; doComplete?: (document: TextDocument, position: Position, settings?: Settings) => CompletionList; doResolve?: (document: TextDocument, item: CompletionItem) => CompletionItem; diff --git a/extensions/html-language-features/server/src/modes/pathCompletion.ts b/extensions/html-language-features/server/src/modes/pathCompletion.ts index cb3a20f8e4c..7451fee1d93 100644 --- a/extensions/html-language-features/server/src/modes/pathCompletion.ts +++ b/extensions/html-language-features/server/src/modes/pathCompletion.ts @@ -7,7 +7,7 @@ import { TextDocument, CompletionItemKind, CompletionItem, TextEdit, Range, Posi import { WorkspaceFolder } from 'vscode-languageserver'; import * as path from 'path'; import * as fs from 'fs'; -import URI from 'vscode-uri'; +import { URI } from 'vscode-uri'; import { ICompletionParticipant } from 'vscode-html-languageservice'; import { startsWith } from '../utils/strings'; import { contains } from '../utils/arrays'; @@ -160,6 +160,7 @@ function shiftRange(range: Range, startOffset: number, endOffset: number): Range const PATH_TAG_AND_ATTR: { [tag: string]: string | string[] } = { // HTML 4 a: 'href', + area: 'href', body: 'background', del: 'cite', form: 'action', @@ -176,7 +177,7 @@ const PATH_TAG_AND_ATTR: { [tag: string]: string | string[] } = { command: 'icon', embed: 'src', html: 'manifest', - input: 'formaction', + input: ['src', 'formaction'], source: 'src', track: 'src', video: ['src', 'poster'] diff --git a/extensions/html-language-features/server/src/test/completions.test.ts b/extensions/html-language-features/server/src/test/completions.test.ts index c28daf9f0c2..de056ed3c1e 100644 --- a/extensions/html-language-features/server/src/test/completions.test.ts +++ b/extensions/html-language-features/server/src/test/completions.test.ts @@ -5,7 +5,7 @@ import 'mocha'; import * as assert from 'assert'; import * as path from 'path'; -import Uri from 'vscode-uri'; +import { URI } from 'vscode-uri'; import { TextDocument, CompletionList, CompletionItemKind } from 'vscode-languageserver-types'; import { getLanguageModes } from '../modes/languageModes'; import { WorkspaceFolder } from 'vscode-languageserver'; @@ -58,8 +58,8 @@ export function testCompletionFor(value: string, expected: { count?: number, ite let document = TextDocument.create(uri, 'html', 0, value); let position = document.positionAt(offset); - var languageModes = getLanguageModes({ css: true, javascript: true }, workspace); - var mode = languageModes.getModeAtPosition(document, position)!; + const languageModes = getLanguageModes({ css: true, javascript: true }, workspace); + const mode = languageModes.getModeAtPosition(document, position)!; let list = mode.doComplete!(document, position); @@ -95,9 +95,9 @@ suite('HTML Path Completion', () => { }; const fixtureRoot = path.resolve(__dirname, '../../src/test/pathCompletionFixtures'); - const fixtureWorkspace = { name: 'fixture', uri: Uri.file(fixtureRoot).toString() }; - const indexHtmlUri = Uri.file(path.resolve(fixtureRoot, 'index.html')).toString(); - const aboutHtmlUri = Uri.file(path.resolve(fixtureRoot, 'about/about.html')).toString(); + const fixtureWorkspace = { name: 'fixture', uri: URI.file(fixtureRoot).toString() }; + const indexHtmlUri = URI.file(path.resolve(fixtureRoot, 'index.html')).toString(); + const aboutHtmlUri = URI.file(path.resolve(fixtureRoot, 'about/about.html')).toString(); test('Basics - Correct label/kind/result/command', () => { testCompletionFor(' - ${this.getStyles(sourceUri, nonce, config, state)} - + data-settings="${escapeAttribute(JSON.stringify(initialData))}" + data-strings="${escapeAttribute(JSON.stringify(previewStrings))}" + data-state="${escapeAttribute(JSON.stringify(state || {}))}"> + + ${this.getStyles(webviewResourceRoot, sourceUri, nonce, config, state)} + ${body}
- ${this.getScripts(nonce)} + ${this.getScripts(webviewResourceRoot, nonce)} `; } @@ -103,13 +110,12 @@ export class MarkdownContentProvider { `; } - private extensionResourcePath(mediaFile: string): string { - return vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile))) - .with({ scheme: 'vscode-resource' }) + private extensionResourcePath(webviewResourceRoot: string, mediaFile: string): string { + return toResoruceUri(webviewResourceRoot, vscode.Uri.file(this.context.asAbsolutePath(path.join('media', mediaFile)))) .toString(); } - private fixHref(resource: vscode.Uri, href: string): string { + private fixHref(webviewResourceRoot: string, resource: vscode.Uri, href: string): string { if (!href) { return href; } @@ -120,29 +126,23 @@ export class MarkdownContentProvider { // Assume it must be a local file if (path.isAbsolute(href)) { - return vscode.Uri.file(href) - .with({ scheme: 'vscode-resource' }) - .toString(); + return toResoruceUri(webviewResourceRoot, vscode.Uri.file(href)).toString(); } // Use a workspace relative path if there is a workspace const root = vscode.workspace.getWorkspaceFolder(resource); if (root) { - return vscode.Uri.file(path.join(root.uri.fsPath, href)) - .with({ scheme: 'vscode-resource' }) - .toString(); + return toResoruceUri(webviewResourceRoot, vscode.Uri.file(path.join(root.uri.fsPath, href))).toString(); } // Otherwise look relative to the markdown file - return vscode.Uri.file(path.join(path.dirname(resource.fsPath), href)) - .with({ scheme: 'vscode-resource' }) - .toString(); + return toResoruceUri(webviewResourceRoot, vscode.Uri.file(path.join(path.dirname(resource.fsPath), href))).toString(); } - private computeCustomStyleSheetIncludes(resource: vscode.Uri, config: MarkdownPreviewConfiguration): string { + private computeCustomStyleSheetIncludes(webviewResourceRoot: string, resource: vscode.Uri, config: MarkdownPreviewConfiguration): string { if (Array.isArray(config.styles)) { return config.styles.map(style => { - return ``; + return ``; }).join('\n'); } return ''; @@ -150,7 +150,7 @@ export class MarkdownContentProvider { private getSettingsOverrideStyles(nonce: string, config: MarkdownPreviewConfiguration): string { return ` @@ -95,7 +95,13 @@ export class ReleaseNotesManager { 'releaseNotes', title, { group: ACTIVE_GROUP, preserveFocus: false }, - { tryRestoreScrollPosition: true, enableFindWidget: true }, + { + tryRestoreScrollPosition: true, + enableFindWidget: true, + localResourceRoots: [ + URI.parse(require.toUrl('./media')) + ] + }, undefined, { onDidClickLink: uri => this.onDidClickLink(uri), onDispose: () => { this._currentReleaseNotes = undefined; } diff --git a/src/vs/workbench/contrib/update/electron-browser/update.contribution.ts b/src/vs/workbench/contrib/update/electron-browser/update.contribution.ts index efa834b0e6d..e39fa57979e 100644 --- a/src/vs/workbench/contrib/update/electron-browser/update.contribution.ts +++ b/src/vs/workbench/contrib/update/electron-browser/update.contribution.ts @@ -3,15 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/update.contribution'; import 'vs/platform/update/node/update.config.contribution'; import * as platform from 'vs/base/common/platform'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { IGlobalActivityRegistry, GlobalActivityExtensions } from 'vs/workbench/common/activity'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { ShowCurrentReleaseNotesAction, ProductContribution, UpdateContribution, Win3264BitContribution, Linux32BitContribution } from './update'; +import { ShowCurrentReleaseNotesAction, ProductContribution, UpdateContribution, Win3264BitContribution } from './update'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; const workbench = Registry.as(WorkbenchExtensions.Workbench); @@ -24,15 +22,7 @@ if (platform.isWindows) { } } -// TODO@ben remove me after a while -if (platform.isLinux) { - if (process.arch === 'ia32') { - workbench.registerWorkbenchContribution(Linux32BitContribution, LifecyclePhase.Restored); - } -} - -Registry.as(GlobalActivityExtensions) - .registerActivity(UpdateContribution); +workbench.registerWorkbenchContribution(UpdateContribution, LifecyclePhase.Restored); // Editor Registry.as(ActionExtensions.WorkbenchActions) diff --git a/src/vs/workbench/contrib/update/electron-browser/update.ts b/src/vs/workbench/contrib/update/electron-browser/update.ts index 785e702c130..7078a1bb2fd 100644 --- a/src/vs/workbench/contrib/update/electron-browser/update.ts +++ b/src/vs/workbench/contrib/update/electron-browser/update.ts @@ -5,17 +5,15 @@ import * as nls from 'vs/nls'; import severity from 'vs/base/common/severity'; -import { IAction, Action } from 'vs/base/common/actions'; -import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; -import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Action } from 'vs/base/common/actions'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import pkg from 'vs/platform/product/node/package'; import product from 'vs/platform/product/node/product'; import { URI } from 'vs/base/common/uri'; import { IActivityService, NumberBadge, IBadge, ProgressBadge } from 'vs/workbench/services/activity/common/activity'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IGlobalActivity } from 'vs/workbench/common/activity'; +import { GLOBAL_ACTIVITY_ID } from 'vs/workbench/common/activity'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { ICommandService } from 'vs/platform/commands/common/commands'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IUpdateService, State as UpdateState, StateType, IUpdate } from 'vs/platform/update/common/update'; @@ -27,6 +25,12 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { ReleaseNotesManager } from './releaseNotesEditor'; import { isWindows } from 'vs/base/common/platform'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { FalseContext } from 'vs/platform/contextkey/common/contextkeys'; + +const CONTEXT_UPDATE_STATE = new RawContextKey('updateState', StateType.Uninitialized); let releaseNotesManager: ReleaseNotesManager | undefined = undefined; @@ -212,95 +216,27 @@ export class Win3264BitContribution implements IWorkbenchContribution { } } -export class Linux32BitContribution implements IWorkbenchContribution { - - private static readonly KEY = 'update/linux32-64bits'; - private static readonly URL = 'https://code.visualstudio.com/updates/v1_32#_linux-32-bit-support-ends-soon'; - private static readonly INSIDER_URL = 'https://github.com/Microsoft/vscode-docs/blob/vnext/release-notes/v1_32.md#linux-32-bit-support-ends-soon'; - - constructor( - @IStorageService storageService: IStorageService, - @INotificationService notificationService: INotificationService, - @IEnvironmentService environmentService: IEnvironmentService - ) { - if (environmentService.disableUpdates) { - return; - } - - const neverShowAgain = new NeverShowAgain(Linux32BitContribution.KEY, storageService); - - if (!neverShowAgain.shouldShow()) { - return; - } - - const url = product.quality === 'insider' - ? Linux32BitContribution.INSIDER_URL - : Linux32BitContribution.URL; - - const handle = notificationService.prompt( - severity.Info, - nls.localize('linux64bits', "{0} for 32-bit Linux will soon be discontinued. Please update to the 64-bit version.", product.nameShort, url), - [{ - label: nls.localize('learnmore', "Learn More"), - run: () => { - window.open(url); - } - }, - { - label: nls.localize('neveragain', "Don't Show Again"), - isSecondary: true, - run: () => { - neverShowAgain.action.run(handle); - neverShowAgain.action.dispose(); - } - }], - { sticky: true } - ); - } -} - -class CommandAction extends Action { - - constructor( - commandId: string, - label: string, - @ICommandService commandService: ICommandService - ) { - super(`command-action:${commandId}`, label, undefined, true, () => commandService.executeCommand(commandId)); - } -} - -export class UpdateContribution implements IGlobalActivity { - - private static readonly showCommandsId = 'workbench.action.showCommands'; - private static readonly openSettingsId = 'workbench.action.openSettings'; - private static readonly openKeybindingsId = 'workbench.action.openGlobalKeybindings'; - private static readonly openUserSnippets = 'workbench.action.openSnippets'; - private static readonly selectColorThemeId = 'workbench.action.selectTheme'; - private static readonly selectIconThemeId = 'workbench.action.selectIconTheme'; - private static readonly showExtensionsId = 'workbench.view.extensions'; - - get id() { return 'vs.update'; } - get name() { return nls.localize('manage', "Manage"); } - get cssClass() { return 'update-activity'; } +export class UpdateContribution extends Disposable implements IWorkbenchContribution { private state: UpdateState; private badgeDisposable: IDisposable = Disposable.None; - private disposables: IDisposable[] = []; + private updateStateContextKey: IContextKey; constructor( @IStorageService private readonly storageService: IStorageService, - @ICommandService private readonly commandService: ICommandService, @IInstantiationService private readonly instantiationService: IInstantiationService, @INotificationService private readonly notificationService: INotificationService, @IDialogService private readonly dialogService: IDialogService, @IUpdateService private readonly updateService: IUpdateService, @IActivityService private readonly activityService: IActivityService, - @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, + @IContextKeyService private readonly contextKeyService: IContextKeyService ) { + super(); this.state = updateService.state; + this.updateStateContextKey = CONTEXT_UPDATE_STATE.bindTo(this.contextKeyService); - updateService.onStateChange(this.onUpdateStateChange, this, this.disposables); + this._register(updateService.onStateChange(this.onUpdateStateChange, this)); this.onUpdateStateChange(this.updateService.state); /* @@ -319,9 +255,13 @@ export class UpdateContribution implements IGlobalActivity { this.storageService.remove('update/lastKnownVersion', StorageScope.GLOBAL); this.storageService.remove('update/updateNotificationTime', StorageScope.GLOBAL); } + + this.registerGlobalActivityActions(); } private onUpdateStateChange(state: UpdateState): void { + this.updateStateContextKey.set(state.type); + switch (state.type) { case StateType.Idle: if (state.error) { @@ -361,7 +301,7 @@ export class UpdateContribution implements IGlobalActivity { this.badgeDisposable.dispose(); if (badge) { - this.badgeDisposable = this.activityService.showActivity(this.id, badge, clazz); + this.badgeDisposable = this.activityService.showActivity(GLOBAL_ACTIVITY_ID, badge, clazz); } this.state = state; @@ -395,7 +335,7 @@ export class UpdateContribution implements IGlobalActivity { severity.Info, nls.localize('thereIsUpdateAvailable', "There is an available update."), [{ - label: nls.localize('download now', "Download Now"), + label: nls.localize('download update', "Download Update"), run: () => this.updateService.downloadUpdate() }, { label: nls.localize('later', "Later"), @@ -518,65 +458,78 @@ export class UpdateContribution implements IGlobalActivity { return diffDays > 5; } - getActions(): IAction[] { - const result: IAction[] = [ - new CommandAction(UpdateContribution.showCommandsId, nls.localize('commandPalette', "Command Palette..."), this.commandService), - new Separator(), - new CommandAction(UpdateContribution.openSettingsId, nls.localize('settings', "Settings"), this.commandService), - new CommandAction(UpdateContribution.showExtensionsId, nls.localize('showExtensions', "Extensions"), this.commandService), - new CommandAction(UpdateContribution.openKeybindingsId, nls.localize('keyboardShortcuts', "Keyboard Shortcuts"), this.commandService), - new Separator(), - new CommandAction(UpdateContribution.openUserSnippets, nls.localize('userSnippets', "User Snippets"), this.commandService), - new Separator(), - new CommandAction(UpdateContribution.selectColorThemeId, nls.localize('selectTheme.label', "Color Theme"), this.commandService), - new CommandAction(UpdateContribution.selectIconThemeId, nls.localize('themes.selectIconTheme.label', "File Icon Theme"), this.commandService) - ]; + private registerGlobalActivityActions(): void { + CommandsRegistry.registerCommand('update.check', () => this.updateService.checkForUpdates({ windowId: this.environmentService.configuration.windowId })); + MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { + group: '5_update', + command: { + id: 'update.check', + title: nls.localize('checkForUpdates', "Check for Updates...") + }, + when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Idle) + }); - const updateAction = this.getUpdateAction(); + CommandsRegistry.registerCommand('update.checking', () => { }); + MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { + group: '5_update', + command: { + id: 'update.checking', + title: nls.localize('checkingForUpdates', "Checking For Updates..."), + precondition: FalseContext + }, + when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.CheckingForUpdates) + }); - if (updateAction) { - result.push(new Separator(), updateAction); - } + CommandsRegistry.registerCommand('update.downloadNow', () => this.updateService.downloadUpdate()); + MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { + group: '5_update', + command: { + id: 'update.downloadNow', + title: nls.localize('download update', "Download Update") + }, + when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.AvailableForDownload) + }); - return result; - } + CommandsRegistry.registerCommand('update.downloading', () => { }); + MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { + group: '5_update', + command: { + id: 'update.downloading', + title: nls.localize('DownloadingUpdate', "Downloading Update..."), + precondition: FalseContext + }, + when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Downloading) + }); - private getUpdateAction(): IAction | null { - const state = this.updateService.state; + CommandsRegistry.registerCommand('update.install', () => this.updateService.applyUpdate()); + MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { + group: '5_update', + command: { + id: 'update.install', + title: nls.localize('installUpdate...', "Install Update...") + }, + when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Downloaded) + }); - switch (state.type) { - case StateType.Uninitialized: - return null; + CommandsRegistry.registerCommand('update.updating', () => { }); + MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { + group: '5_update', + command: { + id: 'update.updating', + title: nls.localize('installingUpdate', "Installing Update..."), + precondition: FalseContext + }, + when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Updating) + }); - case StateType.Idle: - const windowId = this.environmentService.configuration.windowId; - return new Action('update.check', nls.localize('checkForUpdates', "Check for Updates..."), undefined, true, () => - this.updateService.checkForUpdates({ windowId })); - - case StateType.CheckingForUpdates: - return new Action('update.checking', nls.localize('checkingForUpdates', "Checking For Updates..."), undefined, false); - - case StateType.AvailableForDownload: - return new Action('update.downloadNow', nls.localize('download now', "Download Now"), undefined, true, () => - this.updateService.downloadUpdate()); - - case StateType.Downloading: - return new Action('update.downloading', nls.localize('DownloadingUpdate', "Downloading Update..."), undefined, false); - - case StateType.Downloaded: - return new Action('update.install', nls.localize('installUpdate...', "Install Update..."), undefined, true, () => - this.updateService.applyUpdate()); - - case StateType.Updating: - return new Action('update.updating', nls.localize('installingUpdate', "Installing Update..."), undefined, false); - - case StateType.Ready: - return new Action('update.restart', nls.localize('restartToUpdate', "Restart to Update"), undefined, true, () => - this.updateService.quitAndInstall()); - } - } - - dispose(): void { - this.disposables = dispose(this.disposables); + CommandsRegistry.registerCommand('update.restart', () => this.updateService.quitAndInstall()); + MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { + group: '5_update', + command: { + id: 'update.restart', + title: nls.localize('restartToUpdate', "Restart to Update") + }, + when: CONTEXT_UPDATE_STATE.isEqualTo(StateType.Ready) + }); } } diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/common/url.contribution.ts index 63e352b2003..cbed9bef6a3 100644 --- a/src/vs/workbench/contrib/url/common/url.contribution.ts +++ b/src/vs/workbench/contrib/url/common/url.contribution.ts @@ -35,4 +35,4 @@ export class OpenUrlAction extends Action { } Registry.as(ActionExtensions.WorkbenchActions) - .registerWorkbenchAction(new SyncActionDescriptor(OpenUrlAction, OpenUrlAction.ID, OpenUrlAction.LABEL), 'OpenUrl', localize('developer', "Developer")); \ No newline at end of file + .registerWorkbenchAction(new SyncActionDescriptor(OpenUrlAction, OpenUrlAction.ID, OpenUrlAction.LABEL), 'Open URL', localize('developer', "Developer")); \ No newline at end of file diff --git a/src/vs/workbench/contrib/watermark/browser/watermark.ts b/src/vs/workbench/contrib/watermark/browser/watermark.ts index b63e923abc6..7788f6e36a0 100644 --- a/src/vs/workbench/contrib/watermark/browser/watermark.ts +++ b/src/vs/workbench/contrib/watermark/browser/watermark.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./watermark'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { assign } from 'vs/base/common/objects'; import { isMacintosh, OS } from 'vs/base/common/platform'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -102,9 +102,8 @@ const folderEntries = [ const WORKBENCH_TIPS_ENABLED_KEY = 'workbench.tips.enabled'; -export class WatermarkContribution implements IWorkbenchContribution { +export class WatermarkContribution extends Disposable implements IWorkbenchContribution { - private toDispose: IDisposable[] = []; private watermark: HTMLElement; private enabled: boolean; private workbenchState: WorkbenchState; @@ -117,6 +116,7 @@ export class WatermarkContribution implements IWorkbenchContribution { @IConfigurationService private readonly configurationService: IConfigurationService, @IEditorGroupsService private readonly editorGroupsService: IEditorGroupsService ) { + super(); this.workbenchState = contextService.getWorkbenchState(); lifecycleService.onShutdown(this.dispose, this); @@ -124,7 +124,7 @@ export class WatermarkContribution implements IWorkbenchContribution { if (this.enabled) { this.create(); } - this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => { + this._register(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(WORKBENCH_TIPS_ENABLED_KEY)) { const enabled = this.configurationService.getValue(WORKBENCH_TIPS_ENABLED_KEY); if (enabled !== this.enabled) { @@ -137,7 +137,7 @@ export class WatermarkContribution implements IWorkbenchContribution { } } })); - this.toDispose.push(this.contextService.onDidChangeWorkbenchState(e => { + this._register(this.contextService.onDidChangeWorkbenchState(e => { const previousWorkbenchState = this.workbenchState; this.workbenchState = this.contextService.getWorkbenchState(); @@ -171,8 +171,8 @@ export class WatermarkContribution implements IWorkbenchContribution { }; update(); dom.prepend(container.firstElementChild as HTMLElement, this.watermark); - this.toDispose.push(this.keybindingService.onDidUpdateKeybindings(update)); - this.toDispose.push(this.editorGroupsService.onDidLayout(dimension => this.handleEditorPartSize(container, dimension))); + this._register(this.keybindingService.onDidUpdateKeybindings(update)); + this._register(this.editorGroupsService.onDidLayout(dimension => this.handleEditorPartSize(container, dimension))); this.handleEditorPartSize(container, this.editorGroupsService.dimension); } @@ -197,10 +197,6 @@ export class WatermarkContribution implements IWorkbenchContribution { this.destroy(); this.create(); } - - public dispose(): void { - this.toDispose = dispose(this.toDispose); - } } Registry.as(WorkbenchExtensions.Workbench) diff --git a/src/vs/workbench/contrib/webview/browser/pre/fake.html b/src/vs/workbench/contrib/webview/browser/pre/fake.html new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/vs/workbench/contrib/webview/browser/pre/host.js b/src/vs/workbench/contrib/webview/browser/pre/host.js new file mode 100644 index 00000000000..3ffd33bdb72 --- /dev/null +++ b/src/vs/workbench/contrib/webview/browser/pre/host.js @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// @ts-check +(function () { + const id = document.location.search.match(/\bid=([\w-]+)/)[1]; + + const hostMessaging = new class HostMessaging { + constructor() { + this.handlers = new Map(); + window.addEventListener('message', (e) => { + if (e.data && (e.data.command === 'onmessage' || e.data.command === 'do-update-state')) { + // Came from inner iframe + this.postMessage(e.data.command, e.data.data); + return; + } + + const channel = e.data.channel; + const handler = this.handlers.get(channel); + if (handler) { + handler(e, e.data.args); + } else { + console.log('no handler for ', e); + } + }); + } + + postMessage(channel, data) { + window.parent.postMessage({ target: id, channel, data }, '*'); + } + + onMessage(channel, handler) { + this.handlers.set(channel, handler); + } + }(); + + const workerReady = new Promise(async (resolveWorkerReady) => { + if (!navigator.serviceWorker) { + return resolveWorkerReady(); + } + + const expectedWorkerVersion = 1; + + navigator.serviceWorker.register('service-worker.js').then(async registration => { + await navigator.serviceWorker.ready; + + const versionHandler = (event) => { + if (event.data.channel !== 'version') { + return; + } + + navigator.serviceWorker.removeEventListener('message', versionHandler); + if (event.data.version === expectedWorkerVersion) { + return resolveWorkerReady(); + } else { + // If we have the wrong version, try once to unregister and re-register + return registration.update() + .then(() => navigator.serviceWorker.ready) + .finally(resolveWorkerReady); + } + }; + navigator.serviceWorker.addEventListener('message', versionHandler); + registration.active.postMessage({ channel: 'version' }); + }); + + const forwardFromHostToWorker = (channel) => { + hostMessaging.onMessage(channel, event => { + navigator.serviceWorker.ready.then(registration => { + registration.active.postMessage({ channel: channel, data: event.data.args }); + }); + }); + }; + forwardFromHostToWorker('did-load-resource'); + forwardFromHostToWorker('did-load-localhost'); + + navigator.serviceWorker.addEventListener('message', event => { + if (['load-resource', 'load-localhost'].includes(event.data.channel)) { + hostMessaging.postMessage(event.data.channel, event.data); + } + }); + }); + + window.createWebviewManager({ + postMessage: hostMessaging.postMessage.bind(hostMessaging), + onMessage: hostMessaging.onMessage.bind(hostMessaging), + ready: workerReady, + }); +}()); \ No newline at end of file diff --git a/src/vs/workbench/contrib/webview/browser/pre/index.html b/src/vs/workbench/contrib/webview/browser/pre/index.html new file mode 100644 index 00000000000..03337a5f6af --- /dev/null +++ b/src/vs/workbench/contrib/webview/browser/pre/index.html @@ -0,0 +1,19 @@ + + + + + + + + + + Virtual Document + + + + + + + + \ No newline at end of file diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index 133b3d28edd..a6be033e074 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -3,41 +3,42 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ // @ts-check -'use strict'; +(function () { + 'use strict'; -/** - * Use polling to track focus of main webview and iframes within the webview - * - * @param {Object} handlers - * @param {() => void} handlers.onFocus - * @param {() => void} handlers.onBlur - */ -const trackFocus = ({ onFocus, onBlur }) => { - const interval = 50; - let isFocused = document.hasFocus(); - setInterval(() => { - const isCurrentlyFocused = document.hasFocus(); - if (isCurrentlyFocused === isFocused) { - return; - } - isFocused = isCurrentlyFocused; - if (isCurrentlyFocused) { - onFocus(); - } else { - onBlur(); - } - }, interval); -}; + /** + * Use polling to track focus of main webview and iframes within the webview + * + * @param {Object} handlers + * @param {() => void} handlers.onFocus + * @param {() => void} handlers.onBlur + */ + const trackFocus = ({ onFocus, onBlur }) => { + const interval = 50; + let isFocused = document.hasFocus(); + setInterval(() => { + const isCurrentlyFocused = document.hasFocus(); + if (isCurrentlyFocused === isFocused) { + return; + } + isFocused = isCurrentlyFocused; + if (isCurrentlyFocused) { + onFocus(); + } else { + onBlur(); + } + }, interval); + }; -const getActiveFrame = () => { - return /** @type {HTMLIFrameElement} */ (document.getElementById('active-frame')); -}; + const getActiveFrame = () => { + return /** @type {HTMLIFrameElement} */ (document.getElementById('active-frame')); + }; -const getPendingFrame = () => { - return /** @type {HTMLIFrameElement} */ (document.getElementById('pending-frame')); -}; + const getPendingFrame = () => { + return /** @type {HTMLIFrameElement} */ (document.getElementById('pending-frame')); + }; -const defaultCssRules = ` + const defaultCssRules = ` body { background-color: var(--vscode-editor-background); color: var(--vscode-editor-foreground); @@ -93,166 +94,182 @@ const defaultCssRules = ` background-color: var(--vscode-scrollbarSlider-activeBackground); }`; -/** - * @typedef {{ postMessage: (channel: string, data?: any) => void, onMessage: (channel: string, handler: any) => void }} HostCommunications - */ - -/** - * @param {HostCommunications} host - */ -module.exports = function createWebviewManager(host) { - // state - let firstLoad = true; - let loadTimeout; - let pendingMessages = []; - let isInDevelopmentMode = false; - - const initData = { - initialScrollProgress: undefined - }; + /** + * @typedef {{ + * postMessage: (channel: string, data?: any) => void, + * onMessage: (channel: string, handler: any) => void, + * injectHtml?: (document: HTMLDocument) => void, + * focusIframeOnCreate?: boolean, + * ready?: Promise + * }} HostCommunications + */ /** - * @param {HTMLDocument?} document - * @param {HTMLElement?} body + * @param {HostCommunications} host */ - const applyStyles = (document, body) => { - if (!document) { - return; - } + function createWebviewManager(host) { + // state + let firstLoad = true; + let loadTimeout; + let pendingMessages = []; + let isInDevelopmentMode = false; - if (body) { - body.classList.remove('vscode-light', 'vscode-dark', 'vscode-high-contrast'); - body.classList.add(initData.activeTheme); - } + const initData = { + initialScrollProgress: undefined + }; - if (initData.styles) { - for (const variable of Object.keys(initData.styles)) { - document.documentElement.style.setProperty(`--${variable}`, initData.styles[variable]); - } - } - }; + // Service worker for resource loading + const FAKE_LOAD = !!navigator.serviceWorker; - /** - * @param {MouseEvent} event - */ - const handleInnerClick = (event) => { - if (!event || !event.view || !event.view.document) { - return; - } - - let baseElement = event.view.document.getElementsByTagName('base')[0]; - /** @type {any} */ - let node = event.target; - while (node) { - if (node.tagName && node.tagName.toLowerCase() === 'a' && node.href) { - if (node.getAttribute('href') === '#') { - event.view.scrollTo(0, 0); - } else if (node.hash && (node.getAttribute('href') === node.hash || (baseElement && node.href.indexOf(baseElement.href) >= 0))) { - let scrollTarget = event.view.document.getElementById(node.hash.substr(1, node.hash.length - 1)); - if (scrollTarget) { - scrollTarget.scrollIntoView(); - } - } else { - host.postMessage('did-click-link', node.href); - } - event.preventDefault(); - break; - } - node = node.parentNode; - } - }; - - /** - * @param {KeyboardEvent} e - */ - const handleInnerKeydown = (e) => { - host.postMessage('did-keydown', { - key: e.key, - keyCode: e.keyCode, - code: e.code, - shiftKey: e.shiftKey, - altKey: e.altKey, - ctrlKey: e.ctrlKey, - metaKey: e.metaKey, - repeat: e.repeat - }); - }; - - const onMessage = (message) => { - host.postMessage(message.data.command, message.data.data); - }; - - let isHandlingScroll = false; - const handleInnerScroll = (event) => { - if (!event.target || !event.target.body) { - return; - } - if (isHandlingScroll) { - return; - } - - const progress = event.currentTarget.scrollY / event.target.body.clientHeight; - if (isNaN(progress)) { - return; - } - - isHandlingScroll = true; - window.requestAnimationFrame(() => { - try { - host.postMessage('did-scroll', progress); - } catch (e) { - // noop - } - isHandlingScroll = false; - }); - }; - - document.addEventListener('DOMContentLoaded', () => { - if (!document.body) { - return; - } - - host.onMessage('styles', (_event, variables, activeTheme) => { - initData.styles = variables; - initData.activeTheme = activeTheme; - - const target = getActiveFrame(); - if (!target) { + /** + * @param {HTMLDocument?} document + * @param {HTMLElement?} body + */ + const applyStyles = (document, body) => { + if (!document) { return; } - if (target.contentDocument) { - applyStyles(target.contentDocument, target.contentDocument.body); + if (body) { + body.classList.remove('vscode-light', 'vscode-dark', 'vscode-high-contrast'); + body.classList.add(initData.activeTheme); } - }); - // propagate focus - host.onMessage('focus', () => { - const target = getActiveFrame(); - if (target) { - target.contentWindow.focus(); + if (initData.styles) { + for (const variable of Object.keys(initData.styles)) { + document.documentElement.style.setProperty(`--${variable}`, initData.styles[variable]); + } } - }); + }; - // update iframe-contents - host.onMessage('content', (_event, data) => { - const options = data.options; + /** + * @param {MouseEvent} event + */ + const handleInnerClick = (event) => { + if (!event || !event.view || !event.view.document) { + return; + } - const text = data.contents; - const newDocument = new DOMParser().parseFromString(text, 'text/html'); + let baseElement = event.view.document.getElementsByTagName('base')[0]; + /** @type {any} */ + let node = event.target; + while (node) { + if (node.tagName && node.tagName.toLowerCase() === 'a' && node.href) { + if (node.getAttribute('href') === '#') { + event.view.scrollTo(0, 0); + } else if (node.hash && (node.getAttribute('href') === node.hash || (baseElement && node.href.indexOf(baseElement.href) >= 0))) { + let scrollTarget = event.view.document.getElementById(node.hash.substr(1, node.hash.length - 1)); + if (scrollTarget) { + scrollTarget.scrollIntoView(); + } + } else { + host.postMessage('did-click-link', node.href.baseVal || node.href); + } + event.preventDefault(); + break; + } + node = node.parentNode; + } + }; - newDocument.querySelectorAll('a').forEach(a => { - if (!a.title) { - a.title = a.getAttribute('href'); + /** + * @param {KeyboardEvent} e + */ + const handleInnerKeydown = (e) => { + host.postMessage('did-keydown', { + key: e.key, + keyCode: e.keyCode, + code: e.code, + shiftKey: e.shiftKey, + altKey: e.altKey, + ctrlKey: e.ctrlKey, + metaKey: e.metaKey, + repeat: e.repeat + }); + }; + + let isHandlingScroll = false; + const handleInnerScroll = (event) => { + if (!event.target || !event.target.body) { + return; + } + if (isHandlingScroll) { + return; + } + + const progress = event.currentTarget.scrollY / event.target.body.clientHeight; + if (isNaN(progress)) { + return; + } + + isHandlingScroll = true; + window.requestAnimationFrame(() => { + try { + host.postMessage('did-scroll', progress); + } catch (e) { + // noop + } + isHandlingScroll = false; + }); + }; + + document.addEventListener('DOMContentLoaded', () => { + const idMatch = document.location.search.match(/\bid=([\w-]+)/); + const ID = idMatch ? idMatch[1] : undefined; + if (!document.body) { + return; + } + + host.onMessage('styles', (_event, data) => { + initData.styles = data.styles; + initData.activeTheme = data.activeTheme; + + const target = getActiveFrame(); + if (!target) { + return; + } + + if (target.contentDocument) { + applyStyles(target.contentDocument, target.contentDocument.body); } }); - // apply default script - if (options.allowScripts) { - const defaultScript = newDocument.createElement('script'); - defaultScript.textContent = ` + // propagate focus + host.onMessage('focus', () => { + const target = getActiveFrame(); + if (target) { + target.contentWindow.focus(); + } + }); + + + // update iframe-contents + let updateId = 0; + host.onMessage('content', async (_event, data) => { + const currentUpdateId = ++updateId; + await host.ready; + if (currentUpdateId !== updateId) { + return; + } + + const options = data.options; + + const text = data.contents; + const newDocument = new DOMParser().parseFromString(text, 'text/html'); + + newDocument.querySelectorAll('a').forEach(a => { + if (!a.title) { + a.title = a.getAttribute('href'); + } + }); + + // apply default script + if (options.allowScripts) { + const defaultScript = newDocument.createElement('script'); + defaultScript.textContent = ` const acquireVsCodeApi = (function() { const originalPostMessage = window.parent.postMessage.bind(window.parent); + const targetOrigin = '*'; let acquired = false; let state = ${data.state ? `JSON.parse(${JSON.stringify(data.state)})` : undefined}; @@ -264,11 +281,11 @@ module.exports = function createWebviewManager(host) { acquired = true; return Object.freeze({ postMessage: function(msg) { - return originalPostMessage({ command: 'onmessage', data: msg }, '*'); + return originalPostMessage({ command: 'onmessage', data: msg }, targetOrigin); }, setState: function(newState) { state = newState; - originalPostMessage({ command: 'do-update-state', data: JSON.stringify(newState) }, '*'); + originalPostMessage({ command: 'do-update-state', data: JSON.stringify(newState) }, targetOrigin); return newState; }, getState: function() { @@ -282,165 +299,226 @@ module.exports = function createWebviewManager(host) { delete window.frameElement; `; - newDocument.head.prepend(defaultScript); - } + newDocument.head.prepend(defaultScript); + } - // apply default styles - const defaultStyles = newDocument.createElement('style'); - defaultStyles.id = '_defaultStyles'; - defaultStyles.innerHTML = defaultCssRules; - newDocument.head.prepend(defaultStyles); + // apply default styles + const defaultStyles = newDocument.createElement('style'); + defaultStyles.id = '_defaultStyles'; + defaultStyles.innerHTML = defaultCssRules; + newDocument.head.prepend(defaultStyles); - applyStyles(newDocument, newDocument.body); + applyStyles(newDocument, newDocument.body); - const frame = getActiveFrame(); - const wasFirstLoad = firstLoad; - // keep current scrollY around and use later - let setInitialScrollPosition; - if (firstLoad) { - firstLoad = false; - setInitialScrollPosition = (body, window) => { - if (!isNaN(initData.initialScrollProgress)) { - if (window.scrollY === 0) { - window.scroll(0, body.clientHeight * initData.initialScrollProgress); + if (host.injectHtml) { + host.injectHtml(newDocument); + } + + const frame = getActiveFrame(); + const wasFirstLoad = firstLoad; + // keep current scrollY around and use later + let setInitialScrollPosition; + if (firstLoad) { + firstLoad = false; + setInitialScrollPosition = (body, window) => { + if (!isNaN(initData.initialScrollProgress)) { + if (window.scrollY === 0) { + window.scroll(0, body.clientHeight * initData.initialScrollProgress); + } } - } - }; - } else { - const scrollY = frame && frame.contentDocument && frame.contentDocument.body ? frame.contentWindow.scrollY : 0; - setInitialScrollPosition = (body, window) => { - if (window.scrollY === 0) { - window.scroll(0, scrollY); - } - }; - } - - // Clean up old pending frames and set current one as new one - const previousPendingFrame = getPendingFrame(); - if (previousPendingFrame) { - previousPendingFrame.setAttribute('id', ''); - document.body.removeChild(previousPendingFrame); - } - if (!wasFirstLoad) { - pendingMessages = []; - } - - const newFrame = document.createElement('iframe'); - newFrame.setAttribute('id', 'pending-frame'); - newFrame.setAttribute('frameborder', '0'); - newFrame.setAttribute('sandbox', options.allowScripts ? 'allow-scripts allow-forms allow-same-origin' : 'allow-same-origin'); - newFrame.style.cssText = 'display: block; margin: 0; overflow: hidden; position: absolute; width: 100%; height: 100%; visibility: hidden'; - document.body.appendChild(newFrame); - - // write new content onto iframe - newFrame.contentDocument.open('text/html', 'replace'); - - newFrame.contentWindow.addEventListener('keydown', handleInnerKeydown); - - newFrame.contentWindow.addEventListener('DOMContentLoaded', e => { - const contentDocument = e.target ? (/** @type {HTMLDocument} */ (e.target)) : undefined; - if (contentDocument) { - applyStyles(contentDocument, contentDocument.body); - } - }); - - newFrame.contentWindow.onbeforeunload = () => { - if (isInDevelopmentMode) { // Allow reloads while developing a webview - host.postMessage('do-reload'); - return false; + }; + } else { + const scrollY = frame && frame.contentDocument && frame.contentDocument.body ? frame.contentWindow.scrollY : 0; + setInitialScrollPosition = (body, window) => { + if (window.scrollY === 0) { + window.scroll(0, scrollY); + } + }; } - // Block navigation when not in development mode - console.log('prevented webview navigation'); - return false; - }; - - const onLoad = (contentDocument, contentWindow) => { - if (contentDocument && contentDocument.body) { - // Workaround for https://github.com/Microsoft/vscode/issues/12865 - // check new scrollY and reset if neccessary - setInitialScrollPosition(contentDocument.body, contentWindow); + // Clean up old pending frames and set current one as new one + const previousPendingFrame = getPendingFrame(); + if (previousPendingFrame) { + previousPendingFrame.setAttribute('id', ''); + document.body.removeChild(previousPendingFrame); } - - const newFrame = getPendingFrame(); - if (newFrame && newFrame.contentDocument && newFrame.contentDocument === contentDocument) { - const oldActiveFrame = getActiveFrame(); - if (oldActiveFrame) { - document.body.removeChild(oldActiveFrame); - } - // Styles may have changed since we created the element. Make sure we re-style - applyStyles(newFrame.contentDocument, newFrame.contentDocument.body); - newFrame.setAttribute('id', 'active-frame'); - newFrame.style.visibility = 'visible'; - newFrame.contentWindow.focus(); - - contentWindow.addEventListener('scroll', handleInnerScroll); - - pendingMessages.forEach((data) => { - contentWindow.postMessage(data, '*'); - }); + if (!wasFirstLoad) { pendingMessages = []; } - }; - clearTimeout(loadTimeout); - loadTimeout = undefined; - loadTimeout = setTimeout(() => { - clearTimeout(loadTimeout); - loadTimeout = undefined; - onLoad(newFrame.contentDocument, newFrame.contentWindow); - }, 200); + const newFrame = document.createElement('iframe'); + newFrame.setAttribute('id', 'pending-frame'); + newFrame.setAttribute('frameborder', '0'); + newFrame.setAttribute('sandbox', options.allowScripts ? 'allow-scripts allow-forms allow-same-origin' : 'allow-same-origin'); + if (FAKE_LOAD) { + // We should just be able to use srcdoc, but I wasn't + // seeing the service worker applying properly. + // Fake load an empty on the correct origin and then write real html + // into it to get around this. + newFrame.src = `/fake.html?id=${ID}`; + } + newFrame.style.cssText = 'display: block; margin: 0; overflow: hidden; position: absolute; width: 100%; height: 100%; visibility: hidden'; + document.body.appendChild(newFrame); - newFrame.contentWindow.addEventListener('load', function (e) { - if (loadTimeout) { + if (!FAKE_LOAD) { + // write new content onto iframe + newFrame.contentDocument.open(); + } + + newFrame.contentWindow.addEventListener('keydown', handleInnerKeydown); + + newFrame.contentWindow.addEventListener('DOMContentLoaded', e => { + if (FAKE_LOAD) { + newFrame.contentDocument.open(); + newFrame.contentDocument.write('\n' + newDocument.documentElement.outerHTML); + newFrame.contentDocument.close(); + hookupOnLoadHandlers(newFrame); + } + const contentDocument = e.target ? (/** @type {HTMLDocument} */ (e.target)) : undefined; + if (contentDocument) { + applyStyles(contentDocument, contentDocument.body); + } + }); + + const onLoad = (contentDocument, contentWindow) => { + if (contentDocument && contentDocument.body) { + // Workaround for https://github.com/Microsoft/vscode/issues/12865 + // check new scrollY and reset if neccessary + setInitialScrollPosition(contentDocument.body, contentWindow); + } + + const newFrame = getPendingFrame(); + if (newFrame && newFrame.contentDocument && newFrame.contentDocument === contentDocument) { + const oldActiveFrame = getActiveFrame(); + if (oldActiveFrame) { + document.body.removeChild(oldActiveFrame); + } + // Styles may have changed since we created the element. Make sure we re-style + applyStyles(newFrame.contentDocument, newFrame.contentDocument.body); + newFrame.setAttribute('id', 'active-frame'); + newFrame.style.visibility = 'visible'; + if (host.focusIframeOnCreate) { + newFrame.contentWindow.focus(); + } + + contentWindow.addEventListener('scroll', handleInnerScroll); + + pendingMessages.forEach((data) => { + contentWindow.postMessage(data, '*'); + }); + pendingMessages = []; + } + }; + + /** + * @param {HTMLIFrameElement} newFrame + */ + function hookupOnLoadHandlers(newFrame) { clearTimeout(loadTimeout); loadTimeout = undefined; - onLoad(e.target, this); + loadTimeout = setTimeout(() => { + clearTimeout(loadTimeout); + loadTimeout = undefined; + onLoad(newFrame.contentDocument, newFrame.contentWindow); + }, 200); + + newFrame.contentWindow.addEventListener('load', function (e) { + if (loadTimeout) { + clearTimeout(loadTimeout); + loadTimeout = undefined; + onLoad(e.target, this); + } + }); + + if (!FAKE_LOAD) { + newFrame.contentWindow.onbeforeunload = () => { + if (isInDevelopmentMode) { // Allow reloads while developing a webview + host.postMessage('do-reload'); + return false; + } + + // Block navigation when not in development mode + console.log('prevented webview navigation'); + return false; + }; + } + + // Bubble out link clicks + newFrame.contentWindow.addEventListener('click', handleInnerClick); + + // Electron 4 eats mouseup events from inside webviews + // https://github.com/microsoft/vscode/issues/75090 + // Try to fix this by rebroadcasting mouse moves and mouseups so that we can + // emulate these on the main window + if (!FAKE_LOAD) { + let isMouseDown = false; + + newFrame.contentWindow.addEventListener('mousedown', () => { + isMouseDown = true; + }); + + const tryDispatchSyntheticMouseEvent = (e) => { + if (!isMouseDown) { + host.postMessage('synthetic-mouse-event', { type: e.type, screenX: e.screenX, screenY: e.screenY, clientX: e.clientX, clientY: e.clientY }); + } + }; + newFrame.contentWindow.addEventListener('mouseup', e => { + tryDispatchSyntheticMouseEvent(e); + isMouseDown = false; + }); + newFrame.contentWindow.addEventListener('mousemove', tryDispatchSyntheticMouseEvent); + } } + + if (!FAKE_LOAD) { + hookupOnLoadHandlers(newFrame); + } + + // set DOCTYPE for newDocument explicitly as DOMParser.parseFromString strips it off + // and DOCTYPE is needed in the iframe to ensure that the user agent stylesheet is correctly overridden + if (!FAKE_LOAD) { + newFrame.contentDocument.write(''); + newFrame.contentDocument.write(newDocument.documentElement.innerHTML); + newFrame.contentDocument.close(); + } + + host.postMessage('did-set-content', undefined); }); - // Bubble out link clicks - newFrame.contentWindow.addEventListener('click', handleInnerClick); - - // set DOCTYPE for newDocument explicitly as DOMParser.parseFromString strips it off - // and DOCTYPE is needed in the iframe to ensure that the user agent stylesheet is correctly overridden - newFrame.contentDocument.write(''); - newFrame.contentDocument.write(newDocument.documentElement.innerHTML); - newFrame.contentDocument.close(); - - host.postMessage('did-set-content', undefined); - }); - - // Forward message to the embedded iframe - host.onMessage('message', (_event, data) => { - const pending = getPendingFrame(); - if (!pending) { - const target = getActiveFrame(); - if (target) { - target.contentWindow.postMessage(data, '*'); - return; + // Forward message to the embedded iframe + host.onMessage('message', (_event, data) => { + const pending = getPendingFrame(); + if (!pending) { + const target = getActiveFrame(); + if (target) { + target.contentWindow.postMessage(data, '*'); + return; + } } - } - pendingMessages.push(data); + pendingMessages.push(data); + }); + + host.onMessage('initial-scroll-position', (_event, progress) => { + initData.initialScrollProgress = progress; + }); + + host.onMessage('devtools-opened', () => { + isInDevelopmentMode = true; + }); + + trackFocus({ + onFocus: () => host.postMessage('did-focus'), + onBlur: () => host.postMessage('did-blur') + }); + + // signal ready + host.postMessage('webview-ready', {}); }); + } - host.onMessage('initial-scroll-position', (_event, progress) => { - initData.initialScrollProgress = progress; - }); - - host.onMessage('devtools-opened', () => { - isInDevelopmentMode = true; - }); - - trackFocus({ - onFocus: () => host.postMessage('did-focus'), - onBlur: () => host.postMessage('did-blur') - }); - - // Forward messages from the embedded iframe - window.onmessage = onMessage; - - // signal ready - host.postMessage('webview-ready', process.pid); - }); -}; + if (typeof module !== 'undefined') { + module.exports = createWebviewManager; + } else { + window.createWebviewManager = createWebviewManager; + } +}()); diff --git a/src/vs/workbench/contrib/webview/browser/pre/service-worker.js b/src/vs/workbench/contrib/webview/browser/pre/service-worker.js new file mode 100644 index 00000000000..23f08be0281 --- /dev/null +++ b/src/vs/workbench/contrib/webview/browser/pre/service-worker.js @@ -0,0 +1,274 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +const VERSION = 1; + +/** + * Root path for resources + */ +const resourceRoot = '/vscode-resource'; + +const resolveTimeout = 30000; + +/** + * @template T + * @typedef {{ + * resolve: (x: T) => void, + * promise: Promise + * }} RequestStoreEntry + */ + +/** + * @template T + */ +class RequestStore { + constructor() { + /** @type {Map>} */ + this.map = new Map(); + } + + /** + * @param {string} webviewId + * @param {string} path + * @return {Promise | undefined} + */ + get(webviewId, path) { + const entry = this.map.get(this._key(webviewId, path)); + return entry && entry.promise; + } + + /** + * @param {string} webviewId + * @param {string} path + * @returns {Promise} + */ + create(webviewId, path) { + const existing = this.get(webviewId, path); + if (existing) { + return existing; + } + let resolve; + const promise = new Promise(r => resolve = r); + const entry = { resolve, promise }; + const key = this._key(webviewId, path); + this.map.set(key, entry); + + const dispose = () => { + clearTimeout(timeout); + const existingEntry = this.map.get(key); + if (existingEntry === entry) { + return this.map.delete(key); + } + }; + const timeout = setTimeout(dispose, resolveTimeout); + return promise; + } + + /** + * @param {string} webviewId + * @param {string} path + * @param {T} result + * @return {boolean} + */ + resolve(webviewId, path, result) { + const entry = this.map.get(this._key(webviewId, path)); + if (!entry) { + return false; + } + entry.resolve(result); + return true; + } + + /** + * @param {string} webviewId + * @param {string} path + * @return {string} + */ + _key(webviewId, path) { + return `${webviewId}@@@${path}`; + } +} + +/** + * Map of requested paths to responses. + * + * @type {RequestStore<{ body: any, mime: string } | undefined>} + */ +const resourceRequestStore = new RequestStore(); + +/** + * Map of requested localhost origins to optional redirects. + * + * @type {RequestStore} + */ +const localhostRequestStore = new RequestStore(); + +const notFound = () => + new Response('Not Found', { status: 404, }); + +self.addEventListener('message', async (event) => { + switch (event.data.channel) { + case 'version': + { + self.clients.get(event.source.id).then(client => { + if (client) { + client.postMessage({ + channel: 'version', + version: VERSION + }); + } + }); + return; + } + case 'did-load-resource': + { + const webviewId = getWebviewIdForClient(event.source); + const data = event.data.data; + const response = data.status === 200 + ? { body: data.data, mime: data.mime } + : undefined; + + if (!resourceRequestStore.resolve(webviewId, data.path, response)) { + console.log('Could not resolve unknown resource', data.path); + } + return; + } + + case 'did-load-localhost': + { + const webviewId = getWebviewIdForClient(event.source); + const data = event.data.data; + if (!localhostRequestStore.resolve(webviewId, data.origin, data.location)) { + console.log('Could not resolve unknown localhost', data.origin); + } + return; + } + } + + console.log('Unknown message'); +}); + +self.addEventListener('fetch', (event) => { + const requestUrl = new URL(event.request.url); + + // See if it's a resource request + if (requestUrl.origin === self.origin && requestUrl.pathname.startsWith(resourceRoot + '/')) { + return event.respondWith(processResourceRequest(event, requestUrl)); + } + + // See if it's a localhost request + if (requestUrl.origin !== self.origin && requestUrl.host.match(/^localhost:(\d+)$/)) { + return event.respondWith(processLocalhostRequest(event, requestUrl)); + } +}); + +self.addEventListener('install', (event) => { + event.waitUntil(self.skipWaiting()); // Activate worker immediately +}); + +self.addEventListener('activate', (event) => { + event.waitUntil(self.clients.claim()); // Become available to all pages +}); + +async function processResourceRequest(event, requestUrl) { + const client = await self.clients.get(event.clientId); + if (!client) { + console.log('Could not find inner client for request'); + return notFound(); + } + + const webviewId = getWebviewIdForClient(client); + const resourcePath = requestUrl.pathname.replace(resourceRoot, ''); + + function resolveResourceEntry(entry) { + if (!entry) { + return notFound(); + } + return new Response(entry.body, { + status: 200, + headers: { 'Content-Type': entry.mime } + }); + } + + const parentClient = await getOuterIframeClient(webviewId); + if (!parentClient) { + console.log('Could not find parent client for request'); + return notFound(); + } + + // Check if we've already resolved this request + const existing = resourceRequestStore.get(webviewId, resourcePath); + if (existing) { + return existing.then(resolveResourceEntry); + } + + parentClient.postMessage({ + channel: 'load-resource', + path: resourcePath + }); + + return resourceRequestStore.create(webviewId, resourcePath) + .then(resolveResourceEntry); +} + +/** + * @param {*} event + * @param {URL} requestUrl + */ +async function processLocalhostRequest(event, requestUrl) { + const client = await self.clients.get(event.clientId); + if (!client) { + // This is expected when requesting resources on other localhost ports + // that are not spawned by vs code + return undefined; + } + const webviewId = getWebviewIdForClient(client); + const origin = requestUrl.origin; + + const resolveRedirect = redirectOrigin => { + if (!redirectOrigin) { + return fetch(event.request); + } + const location = event.request.url.replace(new RegExp(`^${requestUrl.origin}(/|$)`), `${redirectOrigin}$1`); + return new Response(null, { + status: 302, + headers: { + Location: location + } + }); + }; + + const parentClient = await getOuterIframeClient(webviewId); + if (!parentClient) { + console.log('Could not find parent client for request'); + return notFound(); + } + + // Check if we've already resolved this request + const existing = localhostRequestStore.get(webviewId, origin); + if (existing) { + return existing.then(resolveRedirect); + } + + parentClient.postMessage({ + channel: 'load-localhost', + origin: origin + }); + + return localhostRequestStore.create(webviewId, origin) + .then(resolveRedirect); +} + +function getWebviewIdForClient(client) { + const requesterClientUrl = new URL(client.url); + return requesterClientUrl.search.match(/\bid=([a-z0-9-]+)/i)[1]; +} + +async function getOuterIframeClient(webviewId) { + const allClients = await self.clients.matchAll({ includeUncontrolled: true }); + return allClients.find(client => { + const clientUrl = new URL(client.url); + return clientUrl.pathname === '/' && clientUrl.search.match(new RegExp('\\bid=' + webviewId)); + }); +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts index f05591890db..32f91cd0ec9 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts @@ -17,8 +17,8 @@ import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } fro import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; import { WebviewEditorInputFactory } from 'vs/workbench/contrib/webview/browser/webviewEditorInputFactory'; -import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE } from 'vs/workbench/contrib/webview/common/webview'; -import { CopyWebviewEditorCommand, CutWebviewEditorCommand, HideWebViewEditorFindCommand, OpenWebviewDeveloperToolsAction, PasteWebviewEditorCommand, RedoWebviewEditorCommand, ReloadWebviewAction, SelectAllWebviewEditorCommand, ShowWebViewEditorFindWidgetCommand, UndoWebviewEditorCommand } from '../browser/webviewCommands'; +import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, webviewDeveloperCategory } from 'vs/workbench/contrib/webview/common/webview'; +import { CopyWebviewEditorCommand, CutWebviewEditorCommand, HideWebViewEditorFindCommand, PasteWebviewEditorCommand, RedoWebviewEditorCommand, ReloadWebviewAction, SelectAllWebviewEditorCommand, ShowWebViewEditorFindWidgetCommand, UndoWebviewEditorCommand } from '../browser/webviewCommands'; import { WebviewEditor } from '../browser/webviewEditor'; import { WebviewEditorInput } from '../browser/webviewEditorInput'; import { IWebviewEditorService, WebviewEditorService } from '../browser/webviewEditorService'; @@ -35,9 +35,6 @@ Registry.as(EditorInputExtensions.EditorInputFactor registerSingleton(IWebviewEditorService, WebviewEditorService, true); - -const webviewDeveloperCategory = localize('developer', "Developer"); - const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); export function registerWebViewCommands(editorId: string): void { @@ -127,12 +124,7 @@ export function registerWebViewCommands(editorId: string): void { registerWebViewCommands(WebviewEditor.ID); -actionRegistry.registerWorkbenchAction( - new SyncActionDescriptor(OpenWebviewDeveloperToolsAction, OpenWebviewDeveloperToolsAction.ID, OpenWebviewDeveloperToolsAction.LABEL), - 'Webview Tools', - webviewDeveloperCategory); - actionRegistry.registerWorkbenchAction( new SyncActionDescriptor(ReloadWebviewAction, ReloadWebviewAction.ID, ReloadWebviewAction.LABEL), - 'Reload Webview', + 'Reload Webviews', webviewDeveloperCategory); diff --git a/src/vs/workbench/contrib/webview/browser/webviewCommands.ts b/src/vs/workbench/contrib/webview/browser/webviewCommands.ts index 91b4ad454b9..dbf38d80b79 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewCommands.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewCommands.ts @@ -98,30 +98,6 @@ export class RedoWebviewEditorCommand extends Command { } } -export class OpenWebviewDeveloperToolsAction extends Action { - static readonly ID = 'workbench.action.webview.openDeveloperTools'; - static readonly LABEL = nls.localize('openToolsLabel', "Open Webview Developer Tools"); - - public constructor( - id: string, - label: string - ) { - super(id, label); - } - - public run(): Promise { - const elements = document.querySelectorAll('webview.ready'); - for (let i = 0; i < elements.length; i++) { - try { - (elements.item(i) as Electron.WebviewTag).openDevTools(); - } catch (e) { - console.error(e); - } - } - return Promise.resolve(true); - } -} - export class ReloadWebviewAction extends Action { static readonly ID = 'workbench.action.webview.reloadWebviewAction'; static readonly LABEL = nls.localize('refreshWebviewLabel', "Reload Webviews"); diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts index 079afa85d16..7c5730e4457 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts @@ -6,7 +6,7 @@ import * as DOM from 'vs/base/browser/dom'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -33,8 +33,8 @@ export class WebviewEditor extends BaseEditor { private _content?: HTMLElement; private _webviewContent: HTMLElement | undefined; - private _webviewFocusTrackerDisposables: IDisposable[] = []; - private _onFocusWindowHandler?: IDisposable; + private readonly _webviewFocusTrackerDisposables = this._register(new DisposableStore()); + private readonly _onFocusWindowHandler = this._register(new MutableDisposable()); private readonly _onDidFocusWebview = this._register(new Emitter()); public get onDidFocus(): Event { return this._onDidFocusWebview.event; } @@ -89,12 +89,6 @@ export class WebviewEditor extends BaseEditor { this._content = undefined; } - this._webviewFocusTrackerDisposables = dispose(this._webviewFocusTrackerDisposables); - - if (this._onFocusWindowHandler) { - this._onFocusWindowHandler.dispose(); - } - super.dispose(); } @@ -136,10 +130,10 @@ export class WebviewEditor extends BaseEditor { public focus(): void { super.focus(); - if (!this._onFocusWindowHandler) { + if (!this._onFocusWindowHandler.value) { // Make sure we restore focus when switching back to a VS Code window - this._onFocusWindowHandler = this._windowService.onDidChangeFocus(focused => { + this._onFocusWindowHandler.value = this._windowService.onDidChangeFocus(focused => { if (focused && this._editorService.activeControl === this) { this.focus(); } @@ -304,14 +298,14 @@ export class WebviewEditor extends BaseEditor { } private trackFocus() { - this._webviewFocusTrackerDisposables = dispose(this._webviewFocusTrackerDisposables); + this._webviewFocusTrackerDisposables.clear(); // Track focus in webview content const webviewContentFocusTracker = DOM.trackFocus(this._webviewContent!); - this._webviewFocusTrackerDisposables.push(webviewContentFocusTracker); - this._webviewFocusTrackerDisposables.push(webviewContentFocusTracker.onDidFocus(() => this._onDidFocusWebview.fire())); + this._webviewFocusTrackerDisposables.add(webviewContentFocusTracker); + this._webviewFocusTrackerDisposables.add(webviewContentFocusTracker.onDidFocus(() => this._onDidFocusWebview.fire())); // Track focus in webview element - this._webviewFocusTrackerDisposables.push(this._webview!.onDidFocus(() => this._onDidFocusWebview.fire())); + this._webviewFocusTrackerDisposables.add(this._webview!.onDidFocus(() => this._onDidFocusWebview.fire())); } } diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts index 0df01ea4d6f..6d4d096a9c7 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; import { Emitter } from 'vs/base/common/event'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IEditorModel } from 'vs/platform/editor/common/editor'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; @@ -59,7 +59,7 @@ export class WebviewEditorInput extends EditorInput { private _container?: HTMLElement; private _webview?: Webview; private _webviewOwner: any; - private _webviewDisposables: IDisposable[] = []; + private readonly _webviewDisposables = this._register(new DisposableStore()); private _group?: GroupIdentifier; private _scrollYPercentage: number = 0; private _state: any; @@ -170,7 +170,7 @@ export class WebviewEditorInput extends EditorInput { this._html = value; if (this._webview) { - this._webview.contents = value; + this._webview.html = value; this._currentWebviewHtml = value; } } @@ -229,7 +229,7 @@ export class WebviewEditorInput extends EditorInput { } public set webview(value: Webview | undefined) { - this._webviewDisposables = dispose(this._webviewDisposables); + this._webviewDisposables.clear(); this._webview = value; if (!this._webview) { @@ -284,8 +284,7 @@ export class WebviewEditorInput extends EditorInput { this._webview = undefined; } - this._webviewDisposables = dispose(this._webviewDisposables); - + this._webviewDisposables.clear(); this._webviewOwner = undefined; if (this._container) { @@ -327,4 +326,4 @@ export class RevivedWebviewEditorInput extends WebviewEditorInput { } return super.resolve(); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts index b12720c63ff..187214dbe60 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts @@ -92,7 +92,7 @@ export function areWebviewInputOptionsEqual(a: WebviewInputOptions, b: WebviewIn && a.retainContextWhenHidden === b.retainContextWhenHidden && a.tryRestoreScrollPosition === b.tryRestoreScrollPosition && (a.localResourceRoots === b.localResourceRoots || (Array.isArray(a.localResourceRoots) && Array.isArray(b.localResourceRoots) && equals(a.localResourceRoots, b.localResourceRoots, (a, b) => a.toString() === b.toString()))) - && (a.portMapping === b.portMapping || (Array.isArray(a.portMapping) && Array.isArray(b.portMapping) && equals(a.portMapping, b.portMapping, (a, b) => a.from === b.from && a.to === b.to))); + && (a.portMapping === b.portMapping || (Array.isArray(a.portMapping) && Array.isArray(b.portMapping) && equals(a.portMapping, b.portMapping, (a, b) => a.extensionHostPort === b.extensionHostPort && a.webviewPort === b.webviewPort))); } function canRevive(reviver: WebviewReviver, webview: WebviewEditorInput): boolean { diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts new file mode 100644 index 00000000000..0c57649c8b6 --- /dev/null +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -0,0 +1,335 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter } from 'vs/base/common/event'; +import { URI } from 'vs/base/common/uri'; +import { Webview, WebviewContentOptions, WebviewOptions } from 'vs/workbench/contrib/webview/common/webview'; +import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IFileService } from 'vs/platform/files/common/files'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { areWebviewInputOptionsEqual } from 'vs/workbench/contrib/webview/browser/webviewEditorService'; +import { addDisposableListener, addClass } from 'vs/base/browser/dom'; +import { getWebviewThemeData } from 'vs/workbench/contrib/webview/common/themeing'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { loadLocalResource } from 'vs/workbench/contrib/webview/common/resourceLoader'; +import { WebviewPortMappingManager } from 'vs/workbench/contrib/webview/common/portMapping'; +import { ITunnelService } from 'vs/platform/remote/common/tunnel'; + +interface WebviewContent { + readonly html: string; + readonly options: WebviewContentOptions; + readonly state: string | undefined; +} + +export class IFrameWebview extends Disposable implements Webview { + private element?: HTMLIFrameElement; + + private readonly _ready: Promise; + + private content: WebviewContent; + private _focused = false; + + private readonly id: string; + + private readonly _portMappingManager: WebviewPortMappingManager; + + constructor( + private _options: WebviewOptions, + contentOptions: WebviewContentOptions, + @IThemeService themeService: IThemeService, + @ITunnelService tunnelService: ITunnelService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IFileService private readonly fileService: IFileService, + @IConfigurationService private readonly _configurationService: IConfigurationService, + ) { + super(); + if (typeof environmentService.webviewEndpoint !== 'string') { + throw new Error('To use iframe based webviews, you must configure `environmentService.webviewEndpoint`'); + } + + this._portMappingManager = this._register(new WebviewPortMappingManager( + this._options.extension ? this._options.extension.location : undefined, + () => this.content.options.portMappings || [], + tunnelService + )); + + this.content = { + html: '', + options: contentOptions, + state: undefined + }; + + this.id = `webview-${Date.now()}`; + + this.element = document.createElement('iframe'); + this.element.sandbox.add('allow-scripts', 'allow-same-origin'); + this.element.setAttribute('src', `${environmentService.webviewEndpoint}?id=${this.id}`); + this.element.style.border = 'none'; + this.element.style.width = '100%'; + this.element.style.height = '100%'; + + this._register(addDisposableListener(window, 'message', e => { + if (!e || !e.data || e.data.target !== this.id) { + return; + } + + switch (e.data.channel) { + case 'onmessage': + if (e.data.data) { + this._onMessage.fire(e.data.data); + } + return; + + case 'did-click-link': + const uri = e.data.data; + this._onDidClickLink.fire(URI.parse(uri)); + return; + + case 'did-scroll': + // if (e.args && typeof e.args[0] === 'number') { + // this._onDidScroll.fire({ scrollYPercentage: e.args[0] }); + // } + return; + + case 'do-reload': + this.reload(); + return; + + case 'do-update-state': + const state = e.data.data; + this.state = state; + this._onDidUpdateState.fire(state); + return; + + case 'did-focus': + this.handleFocusChange(true); + return; + + case 'did-blur': + this.handleFocusChange(false); + return; + + case 'load-resource': + { + const requestPath = e.data.data.path; + const uri = URI.file(decodeURIComponent(requestPath)); + this.loadResource(requestPath, uri); + return; + } + + case 'load-localhost': + { + this.localLocalhost(e.data.data.origin); + return; + } + } + })); + + this._ready = new Promise(resolve => { + const subscription = this._register(addDisposableListener(window, 'message', (e) => { + if (e.data && e.data.target === this.id && e.data.channel === 'webview-ready') { + if (this.element) { + addClass(this.element, 'ready'); + } + subscription.dispose(); + resolve(); + } + })); + }); + + this.style(themeService.getTheme()); + this._register(themeService.onThemeChange(this.style, this)); + } + + public mountTo(parent: HTMLElement) { + if (this.element) { + parent.appendChild(this.element); + } + } + + public set options(options: WebviewContentOptions) { + if (areWebviewInputOptionsEqual(options, this.content.options)) { + return; + } + + this.content = { + html: this.content.html, + options: options, + state: this.content.state, + }; + this.doUpdateContent(); + } + + public set html(value: string) { + this.content = { + html: this.preprocessHtml(value), + options: this.content.options, + state: this.content.state, + }; + this.doUpdateContent(); + } + + private preprocessHtml(value: string): string { + return value.replace(/(["'])vscode-resource:([^\s'"]+?)(["'])/gi, (_, startQuote, path, endQuote) => + `${startQuote}${this.environmentService.webviewEndpoint}/vscode-resource${path}${endQuote}`); + } + + public update(html: string, options: WebviewContentOptions, retainContextWhenHidden: boolean) { + if (retainContextWhenHidden && html === this.content.html && areWebviewInputOptionsEqual(options, this.content.options)) { + return; + } + this.content = { + html: this.preprocessHtml(html), + options: options, + state: this.content.state, + }; + this.doUpdateContent(); + } + + private doUpdateContent() { + this._send('content', { + contents: this.content.html, + options: this.content.options, + state: this.content.state + }); + } + + private handleFocusChange(isFocused: boolean): void { + this._focused = isFocused; + if (this._focused) { + this._onDidFocus.fire(); + } + } + + initialScrollProgress: number; + + private readonly _onDidFocus = this._register(new Emitter()); + public readonly onDidFocus = this._onDidFocus.event; + + private readonly _onDidClickLink = this._register(new Emitter()); + public readonly onDidClickLink = this._onDidClickLink.event; + + private readonly _onDidScroll = this._register(new Emitter<{ scrollYPercentage: number }>()); + public readonly onDidScroll = this._onDidScroll.event; + + private readonly _onDidUpdateState = this._register(new Emitter()); + public readonly onDidUpdateState = this._onDidUpdateState.event; + + private readonly _onMessage = this._register(new Emitter()); + public readonly onMessage = this._onMessage.event; + + sendMessage(data: any): void { + this._send('message', data); + } + + layout(): void { + // noop + } + + focus(): void { + if (this.element) { + this.element.focus(); + } + } + + dispose(): void { + if (this.element) { + if (this.element.parentElement) { + this.element.parentElement.removeChild(this.element); + } + } + + this.element = undefined!; + super.dispose(); + } + + reload(): void { + this.doUpdateContent(); + } + + selectAll(): void { + throw new Error('Method not implemented.'); + } + copy(): void { + throw new Error('Method not implemented.'); + } + paste(): void { + throw new Error('Method not implemented.'); + } + cut(): void { + throw new Error('Method not implemented.'); + } + undo(): void { + throw new Error('Method not implemented.'); + } + redo(): void { + throw new Error('Method not implemented.'); + } + showFind(): void { + throw new Error('Method not implemented.'); + } + hideFind(): void { + throw new Error('Method not implemented.'); + } + + public set state(state: string | undefined) { + this.content = { + html: this.content.html, + options: this.content.options, + state, + }; + } + + private _send(channel: string, data: any): void { + this._ready + .then(() => { + if (!this.element) { + return; + } + this.element.contentWindow!.postMessage({ + channel: channel, + args: data + }, '*'); + }) + .catch(err => console.error(err)); + } + + private style(theme: ITheme): void { + const { styles, activeTheme } = getWebviewThemeData(theme, this._configurationService); + this._send('styles', { styles, activeTheme }); + } + + private async loadResource(requestPath: string, uri: URI) { + try { + const result = await loadLocalResource(uri, this.fileService, this._options.extension ? this._options.extension.location : undefined, + () => (this.content.options.localResourceRoots || [])); + + if (result.type === 'success') { + return this._send('did-load-resource', { + status: 200, + path: requestPath, + mime: result.mimeType, + data: result.data.buffer + }); + } + } catch { + // noop + } + + return this._send('did-load-resource', { + status: 404, + path: uri.path + }); + } + + private async localLocalhost(origin: string) { + const redirect = await this._portMappingManager.getRedirect(origin); + return this._send('did-load-localhost', { + origin, + location: redirect + }); + } +} diff --git a/src/vs/workbench/contrib/webview/browser/webviewService.ts b/src/vs/workbench/contrib/webview/browser/webviewService.ts new file mode 100644 index 00000000000..58448a143cb --- /dev/null +++ b/src/vs/workbench/contrib/webview/browser/webviewService.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IFrameWebview as WebviewElement } from 'vs/workbench/contrib/webview/browser/webviewElement'; +import { IWebviewService, WebviewOptions, WebviewContentOptions, Webview } from 'vs/workbench/contrib/webview/common/webview'; + +export class WebviewService implements IWebviewService { + _serviceBrand: any; + + constructor( + @IInstantiationService private readonly _instantiationService: IInstantiationService, + ) { } + + createWebview( + options: WebviewOptions, + contentOptions: WebviewContentOptions + ): Webview { + return this._instantiationService.createInstance(WebviewElement, + options, + contentOptions); + } +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/webview/common/mimeTypes.ts b/src/vs/workbench/contrib/webview/common/mimeTypes.ts new file mode 100644 index 00000000000..a34d61f10b3 --- /dev/null +++ b/src/vs/workbench/contrib/webview/common/mimeTypes.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 { getMediaMime, MIME_UNKNOWN } from 'vs/base/common/mime'; +import { extname } from 'vs/base/common/path'; +import { URI } from 'vs/base/common/uri'; + +const webviewMimeTypes = { + '.svg': 'image/svg+xml', + '.txt': 'text/plain', + '.css': 'text/css', + '.js': 'application/javascript', + '.json': 'application/json', + '.html': 'text/html', + '.htm': 'text/html', + '.xhtml': 'application/xhtml+xml', + '.oft': 'font/otf', + '.xml': 'application/xml', +}; + +export function getWebviewContentMimeType(normalizedPath: URI): string { + const ext = extname(normalizedPath.fsPath).toLowerCase(); + return webviewMimeTypes[ext] || getMediaMime(normalizedPath.fsPath) || MIME_UNKNOWN; +} diff --git a/src/vs/workbench/contrib/webview/common/portMapping.ts b/src/vs/workbench/contrib/webview/common/portMapping.ts new file mode 100644 index 00000000000..964c0ac6da0 --- /dev/null +++ b/src/vs/workbench/contrib/webview/common/portMapping.ts @@ -0,0 +1,87 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import * as modes from 'vs/editor/common/modes'; +import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; +import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; + +export function extractLocalHostUriMetaDataForPortMapping(uri: URI): { address: string, port: number } | undefined { + if (uri.scheme !== 'http' && uri.scheme !== 'https') { + return undefined; + } + const localhostMatch = /^(localhost|127\.0\.0\.1):(\d+)$/.exec(uri.authority); + if (!localhostMatch) { + return undefined; + } + return { + address: localhostMatch[1], + port: +localhostMatch[2], + }; +} + +export class WebviewPortMappingManager extends Disposable { + + private readonly _tunnels = new Map>(); + + constructor( + private readonly extensionLocation: URI | undefined, + private readonly mappings: () => ReadonlyArray, + private readonly tunnelService: ITunnelService + ) { + super(); + } + + public async getRedirect(url: string): Promise { + const uri = URI.parse(url); + const requestLocalHostInfo = extractLocalHostUriMetaDataForPortMapping(uri); + if (!requestLocalHostInfo) { + return undefined; + } + + for (const mapping of this.mappings()) { + if (mapping.webviewPort === requestLocalHostInfo.port) { + if (this.extensionLocation && this.extensionLocation.scheme === REMOTE_HOST_SCHEME) { + const tunnel = await this.getOrCreateTunnel(mapping.extensionHostPort); + if (tunnel) { + return uri.with({ + authority: `127.0.0.1:${tunnel.tunnelLocalPort}`, + }).toString(); + } + } + + if (mapping.webviewPort !== mapping.extensionHostPort) { + return uri.with({ + authority: `${requestLocalHostInfo.address}:${mapping.extensionHostPort}` + }).toString(); + } + } + } + + return undefined; + } + + dispose() { + super.dispose(); + + for (const tunnel of this._tunnels.values()) { + tunnel.then(tunnel => tunnel.dispose()); + } + this._tunnels.clear(); + } + + private getOrCreateTunnel(remotePort: number): Promise | undefined { + const existing = this._tunnels.get(remotePort); + if (existing) { + return existing; + } + const tunnel = this.tunnelService.openTunnel(remotePort); + if (tunnel) { + this._tunnels.set(remotePort, tunnel); + } + return tunnel; + } +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/webview/common/resourceLoader.ts b/src/vs/workbench/contrib/webview/common/resourceLoader.ts new file mode 100644 index 00000000000..2d9971f7e4e --- /dev/null +++ b/src/vs/workbench/contrib/webview/common/resourceLoader.ts @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { VSBuffer } from 'vs/base/common/buffer'; +import { sep } from 'vs/base/common/path'; +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'; +import { getWebviewContentMimeType } from 'vs/workbench/contrib/webview/common/mimeTypes'; + +class Success { + readonly type = 'success'; + + constructor( + public readonly data: VSBuffer, + public readonly mimeType: string + ) { } +} + +const Failed = new class { readonly type = 'failed'; }; +const AccessDenied = new class { readonly type = 'access-denied'; }; + +type LocalResourceResponse = Success | typeof Failed | typeof AccessDenied; + +async function resolveContent( + fileService: IFileService, + resource: URI, + mime: string +): Promise { + try { + const contents = await fileService.readFile(resource); + return new Success(contents.value, mime); + } catch (err) { + console.log(err); + return Failed; + } +} + +export async function loadLocalResource( + requestUri: URI, + fileService: IFileService, + extensionLocation: URI | undefined, + getRoots: () => ReadonlyArray +): Promise { + const requestPath = requestUri.path; + const normalizedPath = URI.file(requestPath); + for (const root of getRoots()) { + if (!startsWith(normalizedPath.fsPath, root.fsPath + sep)) { + continue; + } + + if (extensionLocation && extensionLocation.scheme === REMOTE_HOST_SCHEME) { + const redirectedUri = URI.from({ + scheme: REMOTE_HOST_SCHEME, + authority: extensionLocation.authority, + path: '/vscode-resource', + query: JSON.stringify({ + requestResourcePath: requestUri.path + }) + }); + return resolveContent(fileService, redirectedUri, getWebviewContentMimeType(requestUri)); + } else { + return resolveContent(fileService, normalizedPath, getWebviewContentMimeType(normalizedPath)); + } + } + + return AccessDenied; +} diff --git a/src/vs/workbench/contrib/webview/common/themeing.ts b/src/vs/workbench/contrib/webview/common/themeing.ts new file mode 100644 index 00000000000..5f0649247a8 --- /dev/null +++ b/src/vs/workbench/contrib/webview/common/themeing.ts @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import * as colorRegistry from 'vs/platform/theme/common/colorRegistry'; +import { ITheme, LIGHT, DARK } from 'vs/platform/theme/common/themeService'; + +interface WebviewThemeData { + readonly activeTheme: string; + readonly styles: { readonly [key: string]: string | number }; +} + +export function getWebviewThemeData( + theme: ITheme, + configurationService: IConfigurationService +): WebviewThemeData { + const configuration = configurationService.getValue('editor'); + const editorFontFamily = configuration.fontFamily || EDITOR_FONT_DEFAULTS.fontFamily; + const editorFontWeight = configuration.fontWeight || EDITOR_FONT_DEFAULTS.fontWeight; + const editorFontSize = configuration.fontSize || EDITOR_FONT_DEFAULTS.fontSize; + + const exportedColors = colorRegistry.getColorRegistry().getColors().reduce((colors, entry) => { + const color = theme.getColor(entry.id); + if (color) { + colors['vscode-' + entry.id.replace('.', '-')] = color.toString(); + } + return colors; + }, {} as { [key: string]: string }); + + const styles = { + 'vscode-font-family': '-apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", ans-serif', + 'vscode-font-weight': 'normal', + 'vscode-font-size': '13px', + 'vscode-editor-font-family': editorFontFamily, + 'vscode-editor-font-weight': editorFontWeight, + 'vscode-editor-font-size': editorFontSize, + ...exportedColors + }; + + const activeTheme = ApiThemeClassName.fromTheme(theme); + return { styles, activeTheme }; +} + +enum ApiThemeClassName { + light = 'vscode-light', + dark = 'vscode-dark', + highContrast = 'vscode-high-contrast' +} + +namespace ApiThemeClassName { + export function fromTheme(theme: ITheme): ApiThemeClassName { + if (theme.type === LIGHT) { + return ApiThemeClassName.light; + } else if (theme.type === DARK) { + return ApiThemeClassName.dark; + } else { + return ApiThemeClassName.highContrast; + } + } +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/webview/common/webview.ts b/src/vs/workbench/contrib/webview/common/webview.ts index 3358c27dc92..ca427d11f57 100644 --- a/src/vs/workbench/contrib/webview/common/webview.ts +++ b/src/vs/workbench/contrib/webview/common/webview.ts @@ -4,11 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; +import * as modes from 'vs/editor/common/modes'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import * as modes from 'vs/editor/common/modes'; +import * as nls from 'vs/nls'; /** * Set when the find widget in a webview is visible. @@ -29,6 +31,8 @@ export interface IWebviewService { ): Webview; } +export const WebviewResourceScheme = 'vscode-resource'; + export interface WebviewOptions { readonly allowSvgs?: boolean; readonly extension?: { @@ -45,9 +49,9 @@ export interface WebviewContentOptions { readonly portMappings?: ReadonlyArray; } -export interface Webview { +export interface Webview extends IDisposable { - contents: string; + html: string; options: WebviewContentOptions; initialScrollProgress: number; state: string | undefined; @@ -68,8 +72,6 @@ export interface Webview { layout(): void; mountTo(parent: HTMLElement): void; focus(): void; - dispose(): void; - reload(): void; selectAll(): void; @@ -82,3 +84,5 @@ export interface Webview { showFind(): void; hideFind(): void; } + +export const webviewDeveloperCategory = nls.localize('developer', "Developer"); diff --git a/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js b/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js index 21e15e0eedd..a5f4d3b1809 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js +++ b/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js @@ -34,10 +34,16 @@ }, onMessage: (channel, handler) => { ipcRenderer.on(channel, handler); - } + }, + focusIframeOnCreate: true }); document.addEventListener('DOMContentLoaded', () => { registerVscodeResourceScheme(); + + // Forward messages from the embedded iframe + window.onmessage = (message) => { + ipcRenderer.sendToHost(message.data.command, message.data.data); + }; }); }()); \ No newline at end of file diff --git a/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts b/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts index 7b7b2c2dc76..cd912708989 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts @@ -3,8 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IWebviewService } from 'vs/workbench/contrib/webview/common/webview'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; +import { IWebviewService, webviewDeveloperCategory } from 'vs/workbench/contrib/webview/common/webview'; import { WebviewService } from 'vs/workbench/contrib/webview/electron-browser/webviewService'; +import { OpenWebviewDeveloperToolsAction } from 'vs/workbench/contrib/webview/electron-browser/webviewCommands'; registerSingleton(IWebviewService, WebviewService, true); + +const actionRegistry = Registry.as(ActionExtensions.WorkbenchActions); + +actionRegistry.registerWorkbenchAction( + new SyncActionDescriptor(OpenWebviewDeveloperToolsAction, OpenWebviewDeveloperToolsAction.ID, OpenWebviewDeveloperToolsAction.LABEL), + OpenWebviewDeveloperToolsAction.ALIAS, + webviewDeveloperCategory); diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts new file mode 100644 index 00000000000..e239b767855 --- /dev/null +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewCommands.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Action } from 'vs/base/common/actions'; +import * as nls from 'vs/nls'; + +export class OpenWebviewDeveloperToolsAction extends Action { + static readonly ID = 'workbench.action.webview.openDeveloperTools'; + static readonly ALIAS = 'Open Webview Developer Tools'; + static readonly LABEL = nls.localize('openToolsLabel', "Open Webview Developer Tools"); + + public constructor(id: string, label: string) { + super(id, label); + } + + public run(): Promise { + const elements = document.querySelectorAll('webview.ready'); + for (let i = 0; i < elements.length; i++) { + try { + (elements.item(i) as Electron.WebviewTag).openDevTools(); + } catch (e) { + console.error(e); + } + } + return Promise.resolve(true); + } +} diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts index 1477b88a213..67ca21ee160 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewElement.ts @@ -11,20 +11,17 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { isMacintosh } from 'vs/base/common/platform'; import { endsWith } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import * as modes from 'vs/editor/common/modes'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; -import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import * as colorRegistry from 'vs/platform/theme/common/colorRegistry'; -import { DARK, ITheme, IThemeService, LIGHT } from 'vs/platform/theme/common/themeService'; -import { Webview, WebviewContentOptions, WebviewOptions } from 'vs/workbench/contrib/webview/common/webview'; -import { registerFileProtocol, WebviewProtocol } from 'vs/workbench/contrib/webview/electron-browser/webviewProtocols'; +import { ITunnelService } from 'vs/platform/remote/common/tunnel'; +import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; +import { WebviewPortMappingManager } from 'vs/workbench/contrib/webview/common/portMapping'; +import { getWebviewThemeData } from 'vs/workbench/contrib/webview/common/themeing'; +import { Webview, WebviewContentOptions, WebviewOptions, WebviewResourceScheme } from 'vs/workbench/contrib/webview/common/webview'; +import { registerFileProtocol } from 'vs/workbench/contrib/webview/electron-browser/webviewProtocols'; import { areWebviewInputOptionsEqual } from '../browser/webviewEditorService'; import { WebviewFindWidget } from '../browser/webviewFindWidget'; @@ -117,8 +114,7 @@ class WebviewProtocolProvider extends Disposable { webview: Electron.WebviewTag, private readonly _extensionLocation: URI | undefined, private readonly _getLocalResourceRoots: () => ReadonlyArray, - private readonly _environmentService: IEnvironmentService, - private readonly _textFileService: ITextFileService, + private readonly _fileService: IFileService, ) { super(); @@ -135,13 +131,7 @@ class WebviewProtocolProvider extends Disposable { return; } - const appRootUri = URI.file(this._environmentService.appRoot); - - registerFileProtocol(contents, WebviewProtocol.CoreResource, this._textFileService, undefined, () => [ - appRootUri - ]); - - registerFileProtocol(contents, WebviewProtocol.VsCodeResource, this._textFileService, this._extensionLocation, () => + registerFileProtocol(contents, WebviewResourceScheme, this._fileService, this._extensionLocation, () => this._getLocalResourceRoots() ); } @@ -149,88 +139,22 @@ class WebviewProtocolProvider extends Disposable { class WebviewPortMappingProvider extends Disposable { - private readonly _tunnels = new Map>(); + private readonly _manager: WebviewPortMappingManager; constructor( session: WebviewSession, extensionLocation: URI | undefined, mappings: () => ReadonlyArray, - private readonly tunnelService: ITunnelService, - extensionId: ExtensionIdentifier | undefined, - @ITelemetryService telemetryService: ITelemetryService + tunnelService: ITunnelService, ) { super(); + this._manager = this._register(new WebviewPortMappingManager(extensionLocation, mappings, tunnelService)); - let hasLogged = false; - - session.onBeforeRequest(async (details) => { - const uri = URI.parse(details.url); - if (uri.scheme !== 'http' && uri.scheme !== 'https') { - return undefined; - } - - const localhostMatch = /^localhost:(\d+)$/.exec(uri.authority); - if (localhostMatch) { - if (!hasLogged && extensionId) { - hasLogged = true; - - /* __GDPR__ - "webview.accessLocalhost" : { - "extension" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - telemetryService.publicLog('webview.accessLocalhost', { extension: extensionId.value }); - } - - const port = +localhostMatch[1]; - for (const mapping of mappings()) { - if (mapping.webviewPort === port) { - if (extensionLocation && extensionLocation.scheme === REMOTE_HOST_SCHEME) { - const tunnel = await this.getOrCreateTunnel(mapping.extensionHostPort); - if (tunnel) { - return { - redirectURL: details.url.replace( - new RegExp(`^${uri.scheme}://localhost:${mapping.webviewPort}/`), - `${uri.scheme}://localhost:${tunnel.tunnelLocalPort}/`) - }; - } - } - - if (mapping.webviewPort !== mapping.extensionHostPort) { - return { - redirectURL: details.url.replace( - new RegExp(`^${uri.scheme}://localhost:${mapping.webviewPort}/`), - `${uri.scheme}://localhost:${mapping.extensionHostPort}/`) - }; - } - } - } - } - - return undefined; + session.onBeforeRequest(async details => { + const redirect = await this._manager.getRedirect(details.url); + return redirect ? { redirectURL: redirect } : undefined; }); } - - dispose() { - super.dispose(); - - for (const tunnel of this._tunnels.values()) { - tunnel.then(tunnel => tunnel.dispose()); - } - this._tunnels.clear(); - } - - private getOrCreateTunnel(remotePort: number): Promise | undefined { - const existing = this._tunnels.get(remotePort); - if (existing) { - return existing; - } - const tunnel = this.tunnelService.openTunnel(remotePort); - if (tunnel) { - this._tunnels.set(remotePort, tunnel); - } - return tunnel; - } } class SvgBlocker extends Disposable { @@ -354,15 +278,20 @@ class WebviewKeyboardHandler extends Disposable { } } +interface WebviewContent { + readonly html: string; + readonly options: WebviewContentOptions; + readonly state: string | undefined; +} export class WebviewElement extends Disposable implements Webview { - private _webview: Electron.WebviewTag; + private _webview: Electron.WebviewTag | undefined; private _ready: Promise; - private _webviewFindWidget: WebviewFindWidget; + private _webviewFindWidget: WebviewFindWidget | undefined; private _findStarted: boolean = false; - private _contents: string = ''; - private _state: string | undefined = undefined; + private content: WebviewContent; + private _focused = false; private readonly _onDidFocus = this._register(new Emitter()); @@ -370,19 +299,22 @@ export class WebviewElement extends Disposable implements Webview { constructor( private readonly _options: WebviewOptions, - private _contentOptions: WebviewContentOptions, + contentOptions: WebviewContentOptions, @IInstantiationService instantiationService: IInstantiationService, @IThemeService themeService: IThemeService, - @IEnvironmentService environmentService: IEnvironmentService, - @ITextFileService textFileService: ITextFileService, + @IFileService fileService: IFileService, @ITunnelService tunnelService: ITunnelService, - @ITelemetryService telemetryService: ITelemetryService, @IConfigurationService private readonly _configurationService: IConfigurationService, ) { super(); + this.content = { + html: '', + options: contentOptions, + state: undefined + }; + this._webview = document.createElement('webview'); this._webview.setAttribute('partition', `webview${Date.now()}`); - this._webview.setAttribute('webpreferences', 'contextIsolation=yes'); this._webview.style.flex = '0 1'; @@ -394,8 +326,8 @@ export class WebviewElement extends Disposable implements Webview { this._webview.src = 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%09%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E'; this._ready = new Promise(resolve => { - const subscription = this._register(addDisposableListener(this._webview, 'ipc-message', (event) => { - if (event.channel === 'webview-ready') { + const subscription = this._register(addDisposableListener(this._webview!, 'ipc-message', (event) => { + if (this._webview && event.channel === 'webview-ready') { // console.info('[PID Webview] ' event.args[0]); addClass(this._webview, 'ready'); // can be found by debug command @@ -410,21 +342,18 @@ export class WebviewElement extends Disposable implements Webview { this._register(new WebviewProtocolProvider( this._webview, this._options.extension ? this._options.extension.location : undefined, - () => (this._contentOptions.localResourceRoots || []), - environmentService, - textFileService)); + () => (this.content.options.localResourceRoots || []), + fileService)); this._register(new WebviewPortMappingProvider( session, _options.extension ? _options.extension.location : undefined, - () => (this._contentOptions.portMappings || []), + () => (this.content.options.portMappings || []), tunnelService, - _options.extension ? _options.extension.id : undefined, - telemetryService )); if (!this._options.allowSvgs) { - const svgBlocker = this._register(new SvgBlocker(session, this._contentOptions)); + const svgBlocker = this._register(new SvgBlocker(session, this.content.options)); svgBlocker.onDidBlockSvg(() => this.onDidBlockSvg()); } @@ -437,7 +366,7 @@ export class WebviewElement extends Disposable implements Webview { this.layout(); // Workaround for https://github.com/electron/electron/issues/14474 - if (this._focused || document.activeElement === this._webview) { + if (this._webview && (this._focused || document.activeElement === this._webview)) { this._webview.blur(); this._webview.focus(); } @@ -446,6 +375,10 @@ export class WebviewElement extends Disposable implements Webview { console.error('embedded page crashed'); })); this._register(addDisposableListener(this._webview, 'ipc-message', (event) => { + if (!this._webview) { + return; + } + switch (event.channel) { case 'onmessage': if (event.args && event.args.length) { @@ -458,6 +391,18 @@ export class WebviewElement extends Disposable implements Webview { this._onDidClickLink.fire(URI.parse(uri)); return; + case 'synthetic-mouse-event': + { + const rawEvent = event.args[0]; + const bounds = this._webview.getBoundingClientRect(); + window.dispatchEvent(new MouseEvent(rawEvent.type, { + ...rawEvent, + clientX: rawEvent.clientX + bounds.left, + clientY: rawEvent.clientY + bounds.top, + })); + return; + } + case 'did-set-content': this._webview.style.flex = ''; this._webview.style.width = '100%'; @@ -476,8 +421,9 @@ export class WebviewElement extends Disposable implements Webview { return; case 'do-update-state': - this._state = event.args[0]; - this._onDidUpdateState.fire(this._state); + const state = event.args[0]; + this.state = state; + this._onDidUpdateState.fire(state); return; case 'did-focus': @@ -498,10 +444,14 @@ export class WebviewElement extends Disposable implements Webview { } this.style(themeService.getTheme()); - themeService.onThemeChange(this.style, this, this._toDispose); + this._register(themeService.onThemeChange(this.style, this)); } public mountTo(parent: HTMLElement) { + if (!this._webview) { + return; + } + if (this._webviewFindWidget) { parent.appendChild(this._webviewFindWidget.getDomNode()!); } @@ -513,10 +463,13 @@ export class WebviewElement extends Disposable implements Webview { if (this._webview.parentElement) { this._webview.parentElement.removeChild(this._webview); } + this._webview = undefined; } - this._webview = undefined!; - this._webviewFindWidget = undefined!; + if (this._webviewFindWidget) { + this._webviewFindWidget.dispose(); + this._webviewFindWidget = undefined; + } super.dispose(); } @@ -532,9 +485,13 @@ export class WebviewElement extends Disposable implements Webview { private readonly _onMessage = this._register(new Emitter()); public readonly onMessage = this._onMessage.event; - private _send(channel: string, ...args: any[]): void { + private _send(channel: string, data?: any): void { this._ready - .then(() => this._webview.send(channel, ...args)) + .then(() => { + if (this._webview) { + this._webview.send(channel, data); + } + }) .catch(err => console.error(err)); } @@ -542,46 +499,60 @@ export class WebviewElement extends Disposable implements Webview { this._send('initial-scroll-position', value); } - public set state(value: string | undefined) { - this._state = value; + public set state(state: string | undefined) { + this.content = { + html: this.content.html, + options: this.content.options, + state, + }; } - public set options(value: WebviewContentOptions) { - if (this._contentOptions && areWebviewInputOptionsEqual(value, this._contentOptions)) { + public set options(options: WebviewContentOptions) { + if (areWebviewInputOptionsEqual(options, this.content.options)) { return; } - this._contentOptions = value; - this._send('content', { - contents: this._contents, - options: this._contentOptions, - state: this._state - }); + this.content = { + html: this.content.html, + options: options, + state: this.content.state, + }; + this.doUpdateContent(); } - public set contents(value: string) { - this._contents = value; - this._send('content', { - contents: value, - options: this._contentOptions, - state: this._state - }); + public set html(value: string) { + this.content = { + html: value, + options: this.content.options, + state: this.content.state, + }; + this.doUpdateContent(); } - public update(value: string, options: WebviewContentOptions, retainContextWhenHidden: boolean) { - if (retainContextWhenHidden && value === this._contents && this._contentOptions && areWebviewInputOptionsEqual(options, this._contentOptions)) { + public update(html: string, options: WebviewContentOptions, retainContextWhenHidden: boolean) { + if (retainContextWhenHidden && html === this.content.html && areWebviewInputOptionsEqual(options, this.content.options)) { return; } - this._contents = value; - this._contentOptions = options; + this.content = { + html: html, + options: options, + state: this.content.state, + }; + this.doUpdateContent(); + } + + private doUpdateContent() { this._send('content', { - contents: this._contents, - options: this._contentOptions, - state: this._state + contents: this.content.html, + options: this.content.options, + state: this.content.state }); } public focus(): void { + if (!this._webview) { + return; + } this._webview.focus(); this._send('focus'); @@ -607,32 +578,8 @@ export class WebviewElement extends Disposable implements Webview { } private style(theme: ITheme): void { - const configuration = this._configurationService.getValue('editor'); - const editorFontFamily = configuration.fontFamily || EDITOR_FONT_DEFAULTS.fontFamily; - const editorFontWeight = configuration.fontWeight || EDITOR_FONT_DEFAULTS.fontWeight; - const editorFontSize = configuration.fontSize || EDITOR_FONT_DEFAULTS.fontSize; - - const exportedColors = colorRegistry.getColorRegistry().getColors().reduce((colors, entry) => { - const color = theme.getColor(entry.id); - if (color) { - colors['vscode-' + entry.id.replace('.', '-')] = color.toString(); - } - return colors; - }, {} as { [key: string]: string }); - - - const styles = { - 'vscode-font-family': '-apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif', - 'vscode-font-weight': 'normal', - 'vscode-font-size': '13px', - 'vscode-editor-font-family': editorFontFamily, - 'vscode-editor-font-weight': editorFontWeight, - 'vscode-editor-font-size': editorFontSize, - ...exportedColors - }; - - const activeTheme = ApiThemeClassName.fromTheme(theme); - this._send('styles', styles, activeTheme); + const { styles, activeTheme } = getWebviewThemeData(theme, this._configurationService); + this._send('styles', { styles, activeTheme }); if (this._webviewFindWidget) { this._webviewFindWidget.updateTheme(theme); @@ -640,6 +587,9 @@ export class WebviewElement extends Disposable implements Webview { } public layout(): void { + if (!this._webview) { + return; + } const contents = this._webview.getWebContents(); if (!contents || contents.isDestroyed()) { return; @@ -658,7 +608,7 @@ export class WebviewElement extends Disposable implements Webview { } public startFind(value: string, options?: Electron.FindInPageOptions) { - if (!value) { + if (!value || !this._webview) { return; } @@ -685,6 +635,10 @@ export class WebviewElement extends Disposable implements Webview { * @param value The string to search for. Empty strings are ignored. */ public find(value: string, previous: boolean): void { + if (!this._webview) { + return; + } + // Searching with an empty value will throw an exception if (!value) { return; @@ -700,6 +654,9 @@ export class WebviewElement extends Disposable implements Webview { } public stopFind(keepSelection?: boolean): void { + if (!this._webview) { + return; + } this._findStarted = false; this._webview.stopFindInPage(keepSelection ? 'keepSelection' : 'clearSelection'); } @@ -717,49 +674,42 @@ export class WebviewElement extends Disposable implements Webview { } public reload() { - this.contents = this._contents; + this.doUpdateContent(); } public selectAll() { - this._webview.selectAll(); + if (this._webview) { + this._webview.selectAll(); + } } public copy() { - this._webview.copy(); + if (this._webview) { + this._webview.copy(); + } } public paste() { - this._webview.paste(); + if (this._webview) { + this._webview.paste(); + } } public cut() { - this._webview.cut(); + if (this._webview) { + this._webview.cut(); + } } public undo() { - this._webview.undo(); + if (this._webview) { + this._webview.undo(); + } } public redo() { - this._webview.redo(); - } -} - - -enum ApiThemeClassName { - light = 'vscode-light', - dark = 'vscode-dark', - highContrast = 'vscode-high-contrast' -} - -namespace ApiThemeClassName { - export function fromTheme(theme: ITheme): ApiThemeClassName { - if (theme.type === LIGHT) { - return ApiThemeClassName.light; - } else if (theme.type === DARK) { - return ApiThemeClassName.dark; - } else { - return ApiThemeClassName.highContrast; + if (this._webview) { + this._webview.redo(); } } } diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts index 37ea9bbf6d0..95d41d147cc 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewProtocols.ts @@ -2,64 +2,36 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getMediaMime, MIME_UNKNOWN } from 'vs/base/common/mime'; -import { extname, sep } from 'vs/base/common/path'; -import { startsWith } from 'vs/base/common/strings'; +import * as electron from 'electron'; import { URI } from 'vs/base/common/uri'; -import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; - -export const enum WebviewProtocol { - CoreResource = 'vscode-core-resource', - VsCodeResource = 'vscode-resource', -} - -function resolveContent(textFileService: ITextFileService, resource: URI, mime: string, callback: any): void { - textFileService.read(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 */ }); - }); -} +import { IFileService } from 'vs/platform/files/common/files'; +import { loadLocalResource } from 'vs/workbench/contrib/webview/common/resourceLoader'; export function registerFileProtocol( - contents: Electron.WebContents, - protocol: WebviewProtocol, - textFileService: ITextFileService, + contents: electron.WebContents, + protocol: string, + fileService: IFileService, extensionLocation: URI | undefined, getRoots: () => ReadonlyArray ) { - contents.session.protocol.registerBufferProtocol(protocol, (request, callback: any) => { - const requestPath = URI.parse(request.url).path; - const normalizedPath = URI.file(requestPath); - for (const root of getRoots()) { - if (!startsWith(normalizedPath.fsPath, root.fsPath + sep)) { - continue; - } - - 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 - }) + contents.session.protocol.registerBufferProtocol(protocol, async (request, callback: any) => { + try { + const result = await loadLocalResource(URI.parse(request.url), fileService, extensionLocation, getRoots); + if (result.type === 'success') { + return callback({ + data: Buffer.from(result.data.buffer), + mimeType: result.mimeType }); - resolveContent(textFileService, redirectedUri, getMimeType(requestUri), callback); - return; - } else { - resolveContent(textFileService, normalizedPath, getMimeType(normalizedPath), callback); - return; } + if (result.type === 'access-denied') { + console.error('Webview: Cannot load resource outside of protocol root'); + return callback({ error: -10 /* ACCESS_DENIED: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ }); + } + } catch { + // noop } - console.error('Webview: Cannot load resource outside of protocol root'); - callback({ error: -10 /* ACCESS_DENIED: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ }); + + return callback({ error: -2 /* FAILED: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ }); }, (error) => { if (error) { console.error(`Failed to register '${protocol}' protocol`); @@ -67,20 +39,3 @@ export function registerFileProtocol( }); } -const webviewMimeTypes = { - '.svg': 'image/svg+xml', - '.txt': 'text/plain', - '.css': 'text/css', - '.js': 'application/javascript', - '.json': 'application/json', - '.html': 'text/html', - '.htm': 'text/html', - '.xhtml': 'application/xhtml+xml', - '.oft': 'font/otf', - '.xml': 'application/xml', -}; - -function getMimeType(normalizedPath: URI): string { - const ext = extname(normalizedPath.fsPath).toLowerCase(); - return webviewMimeTypes[ext] || getMediaMime(normalizedPath.fsPath) || MIME_UNKNOWN; -} diff --git a/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts b/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts index e341b1864ea..3d2c83af712 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts +++ b/src/vs/workbench/contrib/webview/electron-browser/webviewService.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IWebviewService, Webview, WebviewContentOptions, WebviewOptions } from 'vs/workbench/contrib/webview/common/webview'; import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement'; +import { IWebviewService, WebviewOptions, WebviewContentOptions, Webview } from 'vs/workbench/contrib/webview/common/webview'; export class WebviewService implements IWebviewService { _serviceBrand: any; @@ -18,10 +18,8 @@ export class WebviewService implements IWebviewService { options: WebviewOptions, contentOptions: WebviewContentOptions ): Webview { - const element = this._instantiationService.createInstance(WebviewElement, + return this._instantiationService.createInstance(WebviewElement, options, contentOptions); - - return element; } } \ No newline at end of file diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/telemetryOptOut.ts b/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/telemetryOptOut.ts index 2c31578abdc..67edffb9c5f 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/telemetryOptOut.ts +++ b/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/telemetryOptOut.ts @@ -13,7 +13,7 @@ import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; -import { IExperimentService, ExperimentState } from 'vs/workbench/contrib/experiments/node/experimentService'; +import { IExperimentService, ExperimentState } from 'vs/workbench/contrib/experiments/electron-browser/experimentService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { language, locale } from 'vs/base/common/platform'; import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; diff --git a/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts b/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts index 84b1219c443..a56cff41c02 100644 --- a/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts +++ b/src/vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay.ts @@ -15,7 +15,7 @@ import { Action } from 'vs/base/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -150,9 +150,8 @@ export class HideWelcomeOverlayAction extends Action { } } -class WelcomeOverlay { +class WelcomeOverlay extends Disposable { - private _toDispose: IDisposable[] = []; private _overlayVisible: IContextKey; private _overlay: HTMLElement; @@ -163,6 +162,7 @@ class WelcomeOverlay { @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IKeybindingService private readonly keybindingService: IKeybindingService ) { + super(); this._overlayVisible = OVERLAY_VISIBLE.bindTo(this._contextKeyService); this.create(); } @@ -177,7 +177,7 @@ class WelcomeOverlay { this._overlay.style.display = 'none'; this._overlay.tabIndex = -1; - this._toDispose.push(dom.addStandardDisposableListener(this._overlay, 'click', () => this.hide())); + this._register(dom.addStandardDisposableListener(this._overlay, 'click', () => this.hide())); this.commandService.onWillExecuteCommand(() => this.hide()); dom.append(this._overlay, $('.commandPalettePlaceholder')); @@ -214,7 +214,7 @@ class WelcomeOverlay { } private updateProblemsKey() { - const problems = document.querySelector('.task-statusbar-item'); + const problems = document.querySelector('div[id="workbench.parts.statusbar"] .statusbar-item.left .octicon.octicon-warning'); const key = this._overlay.querySelector('.key.problems') as HTMLElement; if (problems instanceof HTMLElement) { const target = problems.getBoundingClientRect(); @@ -237,10 +237,6 @@ class WelcomeOverlay { this._overlayVisible.reset(); } } - - dispose() { - this._toDispose = dispose(this._toDispose); - } } Registry.as(Extensions.WorkbenchActions) diff --git a/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts b/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts index d4cd525812e..be63cb5a888 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts @@ -44,6 +44,7 @@ export default () => `
  • ${escape(localize('welcomePage.productDocumentation', "Product documentation"))}
  • ${escape(localize('welcomePage.gitHubRepository', "GitHub repository"))}
  • ${escape(localize('welcomePage.stackOverflow', "Stack Overflow"))}
  • +
  • ${escape(localize('welcomePage.newsletterSignup', "Join our Newsletter"))}
  • diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index b26ccbb854f..071c367c648 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -25,7 +25,7 @@ import { getInstalledExtensions, IExtensionStatus, onExtensionChanged, isKeymapE import { IExtensionEnablementService, IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService, EnablementState, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { used } from 'vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page'; import { ILifecycleService, StartupKind } from 'vs/platform/lifecycle/common/lifecycle'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { splitName } from 'vs/base/common/labels'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { registerColor, focusBorder, textLinkForeground, textLinkActiveForeground, foreground, descriptionForeground, contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; @@ -151,7 +151,7 @@ const extensionPacks: ExtensionSuggestion[] = [ // { name: localize('welcomePage.go', "Go"), id: 'lukehoban.go' }, { name: localize('welcomePage.php', "PHP"), id: 'felixfbecker.php-pack' }, { name: localize('welcomePage.azure', "Azure"), title: localize('welcomePage.showAzureExtensions', "Show Azure extensions"), id: 'workbench.extensions.action.showAzureExtensions', isCommand: true }, - { name: localize('welcomePage.docker', "Docker"), id: 'peterjausovec.vscode-docker' }, + { name: localize('welcomePage.docker', "Docker"), id: 'ms-azuretools.vscode-docker' }, ]; const keymapExtensions: ExtensionSuggestion[] = [ @@ -245,9 +245,7 @@ const keymapStrings: Strings = { const welcomeInputTypeId = 'workbench.editors.welcomePageInput'; -class WelcomePage { - - private disposables: IDisposable[] = []; +class WelcomePage extends Disposable { readonly editorInput: WalkThroughInput; @@ -267,7 +265,8 @@ class WelcomePage { @ILifecycleService lifecycleService: ILifecycleService, @ITelemetryService private readonly telemetryService: ITelemetryService ) { - this.disposables.push(lifecycleService.onShutdown(() => this.dispose())); + super(); + this._register(lifecycleService.onShutdown(() => this.dispose())); const recentlyOpened = this.windowService.getRecentlyOpened(); const installedExtensions = this.instantiationService.invokeFunction(getInstalledExtensions); @@ -321,14 +320,14 @@ class WelcomePage { ul.append(...listEntries, moreRecent); }; updateEntries(); - this.disposables.push(this.labelService.onDidChangeFormatters(updateEntries)); + this._register(this.labelService.onDidChangeFormatters(updateEntries)); }).then(undefined, onUnexpectedError); this.addExtensionList(container, '.extensionPackList', extensionPacks, extensionPackStrings); this.addExtensionList(container, '.keymapList', keymapExtensions, keymapStrings); this.updateInstalledExtensions(container, installedExtensions); - this.disposables.push(this.instantiationService.invokeFunction(onExtensionChanged)(ids => { + this._register(this.instantiationService.invokeFunction(onExtensionChanged)(ids => { for (const id of ids) { if (container.querySelector(`.installExtension[data-extension="${id.id}"], .enabledExtension[data-extension="${id.id}"]`)) { const installedExtensions = this.instantiationService.invokeFunction(getInstalledExtensions); @@ -593,10 +592,6 @@ class WelcomePage { }); }).then(undefined, onUnexpectedError); } - - dispose(): void { - this.disposables = dispose(this.disposables); - } } export class WelcomeInputFactory implements IEditorInputFactory { diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts index a51b73abb56..2e85c4301fe 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart.ts @@ -8,7 +8,7 @@ import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableEle import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { EditorOptions, IEditorMemento } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -56,7 +56,7 @@ export class WalkThroughPart extends BaseEditor { static readonly ID: string = 'workbench.editor.walkThroughPart'; - private disposables: IDisposable[] = []; + private readonly disposables = new DisposableStore(); private contentDisposables: IDisposable[] = []; private content: HTMLDivElement; private scrollbar: DomScrollableElement; @@ -92,13 +92,13 @@ export class WalkThroughPart extends BaseEditor { horizontal: ScrollbarVisibility.Auto, vertical: ScrollbarVisibility.Auto }); - this.disposables.push(this.scrollbar); + this.disposables.add(this.scrollbar); container.appendChild(this.scrollbar.getDomNode()); this.registerFocusHandlers(); this.registerClickHandler(); - this.disposables.push(this.scrollbar.onScroll(e => this.updatedScrollPosition())); + this.disposables.add(this.scrollbar.onScroll(e => this.updatedScrollPosition())); } private updatedScrollPosition() { @@ -120,16 +120,16 @@ export class WalkThroughPart extends BaseEditor { } private registerFocusHandlers() { - this.disposables.push(this.addEventListener(this.content, 'mousedown', e => { + this.disposables.add(this.addEventListener(this.content, 'mousedown', e => { this.focus(); })); - this.disposables.push(this.addEventListener(this.content, 'focus', e => { + this.disposables.add(this.addEventListener(this.content, 'focus', e => { this.editorFocus.set(true); })); - this.disposables.push(this.addEventListener(this.content, 'blur', e => { + this.disposables.add(this.addEventListener(this.content, 'blur', e => { this.editorFocus.reset(); })); - this.disposables.push(this.addEventListener(this.content, 'focusin', e => { + this.disposables.add(this.addEventListener(this.content, 'focusin', e => { // Work around scrolling as side-effect of setting focus on the offscreen zone widget (#18929) if (e.target instanceof HTMLElement && e.target.classList.contains('zone-widget-container')) { const scrollPosition = this.scrollbar.getScrollPosition(); @@ -514,7 +514,7 @@ export class WalkThroughPart extends BaseEditor { dispose(): void { this.editorFocus.reset(); this.contentDisposables = dispose(this.contentDisposables); - this.disposables = dispose(this.disposables); + this.disposables.dispose(); super.dispose(); } } diff --git a/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughContentProvider.ts b/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughContentProvider.ts index f76817286f6..49584361d46 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughContentProvider.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughContentProvider.ts @@ -39,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.createByFilepathOrFirstLine(resource.fsPath), resource); + codeEditorModel = this.modelService.createModel(content, this.modeService.createByFilepathOrFirstLine(resource), resource); } else { this.modelService.updateModel(codeEditorModel, content); } diff --git a/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput.ts b/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput.ts index 338d372c657..6d69a2758f1 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughInput.ts @@ -6,7 +6,7 @@ import * as strings from 'vs/base/common/strings'; import { EditorInput, EditorModel, ITextEditorModel } from 'vs/workbench/common/editor'; import { URI } from 'vs/base/common/uri'; -import { IReference, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IReference } from 'vs/base/common/lifecycle'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import * as marked from 'vs/base/common/marked/marked'; import { Schemas } from 'vs/base/common/network'; @@ -46,8 +46,6 @@ export interface WalkThroughInputOptions { export class WalkThroughInput extends EditorInput { - private disposables: IDisposable[] = []; - private promise: Promise | null = null; private maxTopScroll = 0; @@ -80,7 +78,7 @@ export class WalkThroughInput extends EditorInput { return this.options.telemetryFrom; } - getTelemetryDescriptor(): object { + getTelemetryDescriptor(): { [key: string]: unknown } { const descriptor = super.getTelemetryDescriptor(); descriptor['target'] = this.getTelemetryFrom(); /* __GDPR__FRAGMENT__ @@ -139,8 +137,6 @@ export class WalkThroughInput extends EditorInput { } dispose(): void { - this.disposables = dispose(this.disposables); - if (this.promise) { this.promise.then(model => model.dispose()); this.promise = null; diff --git a/src/vs/workbench/electron-browser/actions/developerActions.ts b/src/vs/workbench/electron-browser/actions/developerActions.ts index 5199d75473a..4da3b429e48 100644 --- a/src/vs/workbench/electron-browser/actions/developerActions.ts +++ b/src/vs/workbench/electron-browser/actions/developerActions.ts @@ -6,16 +6,6 @@ import { Action } from 'vs/base/common/actions'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; import * as nls from 'vs/nls'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { domEvent } from 'vs/base/browser/event'; -import { Event } from 'vs/base/common/event'; -import { IDisposable, toDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; -import { getDomNodePagePosition, createStyleSheet, createCSSRule, append, $ } from 'vs/base/browser/dom'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { Context } from 'vs/platform/contextkey/browser/contextKeyService'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { timeout } from 'vs/base/common/async'; -import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; export class ToggleDevToolsAction extends Action { @@ -44,176 +34,3 @@ export class ToggleSharedProcessAction extends Action { return this.windowsService.toggleSharedProcess(); } } - -export class InspectContextKeysAction extends Action { - - static readonly ID = 'workbench.action.inspectContextKeys'; - static LABEL = nls.localize('inspect context keys', "Inspect Context Keys"); - - constructor( - id: string, - label: string, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IWindowService private readonly windowService: IWindowService, - ) { - super(id, label); - } - - run(): Promise { - const disposables: IDisposable[] = []; - - const stylesheet = createStyleSheet(); - disposables.push(toDisposable(() => { - if (stylesheet.parentNode) { - stylesheet.parentNode.removeChild(stylesheet); - } - })); - createCSSRule('*', 'cursor: crosshair !important;', stylesheet); - - const hoverFeedback = document.createElement('div'); - document.body.appendChild(hoverFeedback); - disposables.push(toDisposable(() => document.body.removeChild(hoverFeedback))); - - hoverFeedback.style.position = 'absolute'; - hoverFeedback.style.pointerEvents = 'none'; - hoverFeedback.style.backgroundColor = 'rgba(255, 0, 0, 0.5)'; - hoverFeedback.style.zIndex = '1000'; - - const onMouseMove = domEvent(document.body, 'mousemove', true); - disposables.push(onMouseMove(e => { - const target = e.target as HTMLElement; - const position = getDomNodePagePosition(target); - - hoverFeedback.style.top = `${position.top}px`; - hoverFeedback.style.left = `${position.left}px`; - hoverFeedback.style.width = `${position.width}px`; - hoverFeedback.style.height = `${position.height}px`; - })); - - const onMouseDown = Event.once(domEvent(document.body, 'mousedown', true)); - onMouseDown(e => { e.preventDefault(); e.stopPropagation(); }, null, disposables); - - const onMouseUp = Event.once(domEvent(document.body, 'mouseup', true)); - onMouseUp(e => { - e.preventDefault(); - e.stopPropagation(); - - const context = this.contextKeyService.getContext(e.target as HTMLElement) as Context; - console.log(context.collectAllValues()); - this.windowService.openDevTools(); - - dispose(disposables); - }, null, disposables); - - return Promise.resolve(); - } -} - -export class ToggleScreencastModeAction extends Action { - - static readonly ID = 'workbench.action.toggleScreencastMode'; - static LABEL = nls.localize('toggle screencast mode', "Toggle Screencast Mode"); - - static disposable: IDisposable | undefined; - - constructor( - id: string, - label: string, - @IKeybindingService private readonly keybindingService: IKeybindingService, - @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService - ) { - super(id, label); - } - - async run(): Promise { - if (ToggleScreencastModeAction.disposable) { - ToggleScreencastModeAction.disposable.dispose(); - ToggleScreencastModeAction.disposable = undefined; - return; - } - - const container = this.layoutService.getWorkbenchElement(); - - const mouseMarker = append(container, $('div')); - mouseMarker.style.position = 'absolute'; - mouseMarker.style.border = '2px solid red'; - mouseMarker.style.borderRadius = '20px'; - mouseMarker.style.width = '20px'; - mouseMarker.style.height = '20px'; - mouseMarker.style.top = '0'; - mouseMarker.style.left = '0'; - mouseMarker.style.zIndex = '100000'; - mouseMarker.style.content = ' '; - mouseMarker.style.pointerEvents = 'none'; - mouseMarker.style.display = 'none'; - - const onMouseDown = domEvent(container, 'mousedown', true); - const onMouseUp = domEvent(container, 'mouseup', true); - const onMouseMove = domEvent(container, 'mousemove', true); - - const mouseListener = onMouseDown(e => { - mouseMarker.style.top = `${e.clientY - 10}px`; - mouseMarker.style.left = `${e.clientX - 10}px`; - mouseMarker.style.display = 'block'; - - const mouseMoveListener = onMouseMove(e => { - mouseMarker.style.top = `${e.clientY - 10}px`; - mouseMarker.style.left = `${e.clientX - 10}px`; - }); - - Event.once(onMouseUp)(() => { - mouseMarker.style.display = 'none'; - mouseMoveListener.dispose(); - }); - }); - - const keyboardMarker = append(container, $('div')); - keyboardMarker.style.position = 'absolute'; - keyboardMarker.style.backgroundColor = 'rgba(0, 0, 0 ,0.5)'; - keyboardMarker.style.width = '100%'; - keyboardMarker.style.height = '100px'; - keyboardMarker.style.bottom = '20%'; - keyboardMarker.style.left = '0'; - keyboardMarker.style.zIndex = '100000'; - keyboardMarker.style.pointerEvents = 'none'; - keyboardMarker.style.color = 'white'; - keyboardMarker.style.lineHeight = '100px'; - keyboardMarker.style.textAlign = 'center'; - keyboardMarker.style.fontSize = '56px'; - keyboardMarker.style.display = 'none'; - - const onKeyDown = domEvent(container, 'keydown', true); - let keyboardTimeout: IDisposable = Disposable.None; - - const keyboardListener = onKeyDown(e => { - keyboardTimeout.dispose(); - - const event = new StandardKeyboardEvent(e); - const keybinding = this.keybindingService.resolveKeyboardEvent(event); - const label = keybinding.getLabel(); - - if (!event.ctrlKey && !event.altKey && !event.metaKey && !event.shiftKey && this.keybindingService.mightProducePrintableCharacter(event) && label) { - keyboardMarker.textContent += ' ' + label; - } else { - keyboardMarker.textContent = label; - } - - keyboardMarker.style.display = 'block'; - - const promise = timeout(800); - keyboardTimeout = toDisposable(() => promise.cancel()); - - promise.then(() => { - keyboardMarker.textContent = ''; - keyboardMarker.style.display = 'none'; - }); - }); - - ToggleScreencastModeAction.disposable = toDisposable(() => { - mouseListener.dispose(); - keyboardListener.dispose(); - mouseMarker.remove(); - keyboardMarker.remove(); - }); - } -} diff --git a/src/vs/workbench/electron-browser/actions/helpActions.ts b/src/vs/workbench/electron-browser/actions/helpActions.ts index 79e2ba29813..3c5d242f92f 100644 --- a/src/vs/workbench/electron-browser/actions/helpActions.ts +++ b/src/vs/workbench/electron-browser/actions/helpActions.ts @@ -7,6 +7,7 @@ import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; import product from 'vs/platform/product/node/product'; import { isMacintosh, isLinux, language } from 'vs/base/common/platform'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; export class KeybindingsReferenceAction extends Action { @@ -91,11 +92,34 @@ export class OpenTipsAndTricksUrlAction extends Action { run(): Promise { window.open(OpenTipsAndTricksUrlAction.URL); - return Promise.resolve(); } } +export class OpenNewsletterSignupUrlAction extends Action { + + static readonly ID = 'workbench.action.openNewsletterSignupUrl'; + static readonly LABEL = nls.localize('newsletterSignup', "Signup for the VS Code Newsletter"); + private telemetryService: ITelemetryService; + private static readonly URL = product.newsletterSignupUrl; + static readonly AVAILABLE = !!OpenNewsletterSignupUrlAction.URL; + + constructor( + id: string, + label: string, + @ITelemetryService telemetryService: ITelemetryService + ) { + super(id, label); + this.telemetryService = telemetryService; + } + + async run(): Promise { + const info = await this.telemetryService.getTelemetryInfo(); + + window.open(`${OpenNewsletterSignupUrlAction.URL}?machineId=${encodeURIComponent(info.machineId)}`); + } +} + export class OpenTwitterUrlAction extends Action { static readonly ID = 'workbench.action.openTwitterUrl'; diff --git a/src/vs/workbench/electron-browser/actions/windowActions.ts b/src/vs/workbench/electron-browser/actions/windowActions.ts index 223fda7d7a7..58b52edeb51 100644 --- a/src/vs/workbench/electron-browser/actions/windowActions.ts +++ b/src/vs/workbench/electron-browser/actions/windowActions.ts @@ -61,20 +61,6 @@ export class NewWindowAction extends Action { } } -export class ToggleFullScreenAction extends Action { - - static readonly ID = 'workbench.action.toggleFullScreen'; - static LABEL = nls.localize('toggleFullScreen', "Toggle Full Screen"); - - constructor(id: string, label: string, @IWindowService private readonly windowService: IWindowService) { - super(id, label); - } - - run(): Promise { - return this.windowService.toggleFullScreen(); - } -} - export abstract class BaseZoomAction extends Action { private static readonly SETTING_KEY = 'window.zoomLevel'; @@ -86,7 +72,7 @@ export abstract class BaseZoomAction extends Action { super(id, label); } - protected setConfiguredZoomLevel(level: number): void { + protected async setConfiguredZoomLevel(level: number): Promise { level = Math.round(level); // when reaching smallest zoom, prevent fractional zoom levels const applyZoom = () => { @@ -98,7 +84,9 @@ export abstract class BaseZoomAction extends Action { browser.setZoomLevel(webFrame.getZoomLevel(), /*isTrusted*/false); }; - this.configurationService.updateValue(BaseZoomAction.SETTING_KEY, level).then(() => applyZoom()); + await this.configurationService.updateValue(BaseZoomAction.SETTING_KEY, level); + + applyZoom(); } } @@ -162,24 +150,6 @@ export class ZoomResetAction extends BaseZoomAction { } } -export class ReloadWindowAction extends Action { - - static readonly ID = 'workbench.action.reloadWindow'; - static LABEL = nls.localize('reloadWindow', "Reload Window"); - - constructor( - id: string, - label: string, - @IWindowService private readonly windowService: IWindowService - ) { - super(id, label); - } - - run(): Promise { - return this.windowService.reloadWindow().then(() => true); - } -} - export class ReloadWindowWithExtensionsDisabledAction extends Action { static readonly ID = 'workbench.action.reloadWindowWithExtensionsDisabled'; @@ -193,8 +163,10 @@ export class ReloadWindowWithExtensionsDisabledAction extends Action { super(id, label); } - run(): Promise { - return this.windowService.reloadWindow({ _: [], 'disable-extensions': true }).then(() => true); + async run(): Promise { + await this.windowService.reloadWindow({ _: [], 'disable-extensions': true }); + + return true; } } @@ -221,41 +193,38 @@ export abstract class BaseSwitchWindow extends Action { protected abstract isQuickNavigate(): boolean; - run(): Promise { + async run(): Promise { const currentWindowId = this.windowService.windowId; - return this.windowsService.getWindows().then(windows => { - const placeHolder = nls.localize('switchWindowPlaceHolder', "Select a window to switch to"); - const picks = windows.map(win => { - const resource = win.filename ? URI.file(win.filename) : win.folderUri ? win.folderUri : win.workspace ? win.workspace.configPath : undefined; - const fileKind = win.filename ? FileKind.FILE : win.workspace ? FileKind.ROOT_FOLDER : win.folderUri ? FileKind.FOLDER : FileKind.FILE; - return { - payload: win.id, - label: win.title, - iconClasses: getIconClasses(this.modelService, this.modeService, resource, fileKind), - description: (currentWindowId === win.id) ? nls.localize('current', "Current Window") : undefined, - buttons: (!this.isQuickNavigate() && currentWindowId !== win.id) ? [this.closeWindowAction] : undefined - }; - }); + const windows = await this.windowsService.getWindows(); + const placeHolder = nls.localize('switchWindowPlaceHolder', "Select a window to switch to"); + const picks = windows.map(win => { + const resource = win.filename ? URI.file(win.filename) : win.folderUri ? win.folderUri : win.workspace ? win.workspace.configPath : undefined; + const fileKind = win.filename ? FileKind.FILE : win.workspace ? FileKind.ROOT_FOLDER : win.folderUri ? FileKind.FOLDER : FileKind.FILE; + return { + payload: win.id, + label: win.title, + iconClasses: getIconClasses(this.modelService, this.modeService, resource, fileKind), + description: (currentWindowId === win.id) ? nls.localize('current', "Current Window") : undefined, + buttons: (!this.isQuickNavigate() && currentWindowId !== win.id) ? [this.closeWindowAction] : undefined + }; + }); + const autoFocusIndex = (picks.indexOf(picks.filter(pick => pick.payload === currentWindowId)[0]) + 1) % picks.length; - const autoFocusIndex = (picks.indexOf(picks.filter(pick => pick.payload === currentWindowId)[0]) + 1) % picks.length; - - return this.quickInputService.pick(picks, { - contextKey: 'inWindowsPicker', - activeItem: picks[autoFocusIndex], - placeHolder, - quickNavigate: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : undefined, - onDidTriggerItemButton: context => { - this.windowsService.closeWindow(context.item.payload).then(() => { - context.removeItem(); - }); - } - }); - }).then(pick => { - if (pick) { - this.windowsService.focusWindow(pick.payload); + const pick = await this.quickInputService.pick(picks, { + contextKey: 'inWindowsPicker', + activeItem: picks[autoFocusIndex], + placeHolder, + quickNavigate: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : undefined, + onDidTriggerItemButton: async context => { + await this.windowsService.closeWindow(context.item.payload); + context.removeItem(); } }); + + if (pick) { + this.windowsService.focusWindow(pick.payload); + } } } @@ -331,12 +300,13 @@ export abstract class BaseOpenRecentAction extends Action { protected abstract isQuickNavigate(): boolean; - run(): Promise { - return this.windowService.getRecentlyOpened() - .then(({ workspaces, files }) => this.openRecent(workspaces, files)); + async run(): Promise { + const { workspaces, files } = await this.windowService.getRecentlyOpened(); + + this.openRecent(workspaces, files); } - private openRecent(recentWorkspaces: Array, recentFiles: IRecentFile[]): void { + private async openRecent(recentWorkspaces: Array, recentFiles: IRecentFile[]): Promise { const toPick = (recent: IRecent, labelService: ILabelService, buttons: IQuickInputButton[] | undefined) => { let uriToOpen: IURIToOpen | undefined; @@ -376,26 +346,26 @@ export abstract class BaseOpenRecentAction extends Action { const firstEntry = recentWorkspaces[0]; let autoFocusSecondEntry: boolean = firstEntry && this.contextService.isCurrentWorkspace(isRecentWorkspace(firstEntry) ? firstEntry.workspace : firstEntry.folderUri); - let keyMods: IKeyMods; + let keyMods: IKeyMods | undefined; const workspaceSeparator: IQuickPickSeparator = { type: 'separator', label: nls.localize('workspaces', "workspaces") }; const fileSeparator: IQuickPickSeparator = { type: 'separator', label: nls.localize('files', "files") }; const picks = [workspaceSeparator, ...workspacePicks, fileSeparator, ...filePicks]; - this.quickInputService.pick(picks, { + const pick = await this.quickInputService.pick(picks, { contextKey: inRecentFilesPickerContextKey, activeItem: [...workspacePicks, ...filePicks][autoFocusSecondEntry ? 1 : 0], placeHolder: isMacintosh ? nls.localize('openRecentPlaceHolderMac', "Select to open (hold Cmd-key to open in new window)") : nls.localize('openRecentPlaceHolder', "Select to open (hold Ctrl-key to open in new window)"), matchOnDescription: true, onKeyMods: mods => keyMods = mods, quickNavigate: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : undefined, - onDidTriggerItemButton: context => { - this.windowsService.removeFromRecentlyOpened([context.item.resource]).then(() => context.removeItem()); - } - }).then((pick): Promise | void => { - if (pick) { - const forceNewWindow = keyMods.ctrlCmd; - return this.windowService.openWindow([pick.uriToOpen], { forceNewWindow }); + onDidTriggerItemButton: async context => { + await this.windowsService.removeFromRecentlyOpened([context.item.resource]); + context.removeItem(); } }); + + if (pick) { + return this.windowService.openWindow([pick.uriToOpen], { forceNewWindow: keyMods && keyMods.ctrlCmd }); + } } } diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index de7017c8edd..9d2b119c091 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -11,20 +11,21 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; -import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction } from 'vs/workbench/electron-browser/actions/helpActions'; -import { ToggleSharedProcessAction, InspectContextKeysAction, ToggleScreencastModeAction, ToggleDevToolsAction } from 'vs/workbench/electron-browser/actions/developerActions'; -import { ShowAboutDialogAction, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey, OpenRecentAction, ReloadWindowWithExtensionsDisabledAction, NewWindowTabHandler, ReloadWindowAction, ShowPreviousWindowTabHandler, ShowNextWindowTabHandler, MoveWindowTabToNewWindowHandler, MergeWindowTabsHandlerHandler, ToggleWindowTabsBarHandler } from 'vs/workbench/electron-browser/actions/windowActions'; -import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, DuplicateWorkspaceInNewWindowAction, OpenFileFolderAction, OpenFileAction, OpenFolderAction, CloseWorkspaceAction, OpenLocalFileAction, OpenLocalFolderAction, OpenLocalFileFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; +import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction, OpenNewsletterSignupUrlAction } from 'vs/workbench/electron-browser/actions/helpActions'; +import { ToggleSharedProcessAction, ToggleDevToolsAction } from 'vs/workbench/electron-browser/actions/developerActions'; +import { ShowAboutDialogAction, ZoomResetAction, ZoomOutAction, ZoomInAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey, OpenRecentAction, ReloadWindowWithExtensionsDisabledAction, NewWindowTabHandler, ShowPreviousWindowTabHandler, ShowNextWindowTabHandler, MoveWindowTabToNewWindowHandler, MergeWindowTabsHandlerHandler, ToggleWindowTabsBarHandler } from 'vs/workbench/electron-browser/actions/windowActions'; +import { AddRootFolderAction, GlobalRemoveRootFolderAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, DuplicateWorkspaceInNewWindowAction, CloseWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { inQuickOpenContext, getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ADD_ROOT_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; -import { SupportsWorkspacesContext, IsMacContext, HasMacNativeTabsContext, IsDevelopmentContext, WorkbenchStateContext, WorkspaceFolderCountContext, RemoteFileDialogContext } from 'vs/workbench/common/contextkeys'; +import { SupportsWorkspacesContext, IsMacContext, HasMacNativeTabsContext, IsDevelopmentContext, WorkbenchStateContext, WorkspaceFolderCountContext } from 'vs/workbench/browser/contextkeys'; import { NoEditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench/common/editor'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; import { LogStorageAction } from 'vs/platform/storage/node/storageService'; +import product from 'vs/platform/product/node/product'; // Actions (function registerActions(): void { @@ -34,16 +35,6 @@ import { LogStorageAction } from 'vs/platform/storage/node/storageService'; (function registerFileActions(): void { const fileCategory = nls.localize('file', "File"); - if (isMacintosh) { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileFolderAction, OpenFileFolderAction.ID, OpenFileFolderAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open...', fileCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLocalFileFolderAction, OpenLocalFileFolderAction.ID, OpenLocalFileFolderAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }, RemoteFileDialogContext), 'File: Open Local...', fileCategory, RemoteFileDialogContext); - } else { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileAction, OpenFileAction.ID, OpenFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open File...', fileCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFolderAction, OpenFolderAction.ID, OpenFolderAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_O) }), 'File: Open Folder...', fileCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLocalFileAction, OpenLocalFileAction.ID, OpenLocalFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }, RemoteFileDialogContext), 'File: Open Local File...', fileCategory, RemoteFileDialogContext); - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLocalFolderAction, OpenLocalFolderAction.ID, OpenLocalFolderAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_O) }, RemoteFileDialogContext), 'File: Open Local Folder...', fileCategory, RemoteFileDialogContext); - } - registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenRecentAction, QuickOpenRecentAction.ID, QuickOpenRecentAction.LABEL), 'File: Quick Open Recent...', fileCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenRecentAction, OpenRecentAction.ID, OpenRecentAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_R, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_R } }), 'File: Open Recent...', fileCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(CloseWorkspaceAction, CloseWorkspaceAction.ID, CloseWorkspaceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_F) }), 'File: Close Workspace', fileCategory); @@ -78,7 +69,6 @@ import { LogStorageAction } from 'vs/platform/storage/node/storageService'; registry.registerWorkbenchAction(new SyncActionDescriptor(ZoomInAction, ZoomInAction.ID, ZoomInAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_EQUAL, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_EQUAL, KeyMod.CtrlCmd | KeyCode.NUMPAD_ADD] }), 'View: Zoom In', viewCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(ZoomOutAction, ZoomOutAction.ID, ZoomOutAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_MINUS, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_MINUS, KeyMod.CtrlCmd | KeyCode.NUMPAD_SUBTRACT], linux: { primary: KeyMod.CtrlCmd | KeyCode.US_MINUS, secondary: [KeyMod.CtrlCmd | KeyCode.NUMPAD_SUBTRACT] } }), 'View: Zoom Out', viewCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(ZoomResetAction, ZoomResetAction.ID, ZoomResetAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.NUMPAD_0 }), 'View: Reset Zoom', viewCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleFullScreenAction, ToggleFullScreenAction.ID, ToggleFullScreenAction.LABEL, { primary: KeyCode.F11, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_F } }), 'View: Toggle Full Screen', viewCategory); })(); // Actions: Window @@ -118,7 +108,6 @@ import { LogStorageAction } from 'vs/platform/storage/node/storageService'; registry.registerWorkbenchAction(new SyncActionDescriptor(AddRootFolderAction, AddRootFolderAction.ID, AddRootFolderAction.LABEL), 'Workspaces: Add Folder to Workspace...', workspacesCategory, SupportsWorkspacesContext); registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalRemoveRootFolderAction, GlobalRemoveRootFolderAction.ID, GlobalRemoveRootFolderAction.LABEL), 'Workspaces: Remove Folder from Workspace...', workspacesCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenWorkspaceAction, OpenWorkspaceAction.ID, OpenWorkspaceAction.LABEL), 'Workspaces: Open Workspace...', workspacesCategory, SupportsWorkspacesContext); registry.registerWorkbenchAction(new SyncActionDescriptor(SaveWorkspaceAsAction, SaveWorkspaceAsAction.ID, SaveWorkspaceAsAction.LABEL), 'Workspaces: Save Workspace As...', workspacesCategory, SupportsWorkspacesContext); registry.registerWorkbenchAction(new SyncActionDescriptor(DuplicateWorkspaceInNewWindowAction, DuplicateWorkspaceInNewWindowAction.ID, DuplicateWorkspaceInNewWindowAction.LABEL), 'Workspaces: Duplicate Workspace in New Window', workspacesCategory); @@ -160,20 +149,10 @@ import { LogStorageAction } from 'vs/platform/storage/node/storageService'; (function registerDeveloperActions(): void { const developerCategory = nls.localize('developer', "Developer"); registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSharedProcessAction, ToggleSharedProcessAction.ID, ToggleSharedProcessAction.LABEL), 'Developer: Toggle Shared Process', developerCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(InspectContextKeysAction, InspectContextKeysAction.ID, InspectContextKeysAction.LABEL), 'Developer: Inspect Context Keys', developerCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleScreencastModeAction, ToggleScreencastModeAction.ID, ToggleScreencastModeAction.LABEL), 'Developer: Toggle Screencast Mode', developerCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(ReloadWindowWithExtensionsDisabledAction, ReloadWindowWithExtensionsDisabledAction.ID, ReloadWindowWithExtensionsDisabledAction.LABEL), 'Developer: Reload Window Without Extensions', developerCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(LogStorageAction, LogStorageAction.ID, LogStorageAction.LABEL), 'Developer: Log Storage', developerCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL), 'Developer: Reload Window', developerCategory); + registry.registerWorkbenchAction(new SyncActionDescriptor(ReloadWindowWithExtensionsDisabledAction, ReloadWindowWithExtensionsDisabledAction.ID, ReloadWindowWithExtensionsDisabledAction.LABEL), 'Developer: Reload Window With Extensions Disabled', developerCategory); + registry.registerWorkbenchAction(new SyncActionDescriptor(LogStorageAction, LogStorageAction.ID, LogStorageAction.LABEL), 'Developer: Log Storage Database Contents', developerCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleDevToolsAction, ToggleDevToolsAction.ID, ToggleDevToolsAction.LABEL), 'Developer: Toggle Developer Tools', developerCategory); - KeybindingsRegistry.registerKeybindingRule({ - id: ReloadWindowAction.ID, - weight: KeybindingWeight.WorkbenchContrib + 50, - when: IsDevelopmentContext, - primary: KeyMod.CtrlCmd | KeyCode.KEY_R - }); - KeybindingsRegistry.registerKeybindingRule({ id: ToggleDevToolsAction.ID, weight: KeybindingWeight.WorkbenchContrib + 50, @@ -203,11 +182,15 @@ import { LogStorageAction } from 'vs/platform/storage/node/storageService'; registry.registerWorkbenchAction(new SyncActionDescriptor(OpenTipsAndTricksUrlAction, OpenTipsAndTricksUrlAction.ID, OpenTipsAndTricksUrlAction.LABEL), 'Help: Tips and Tricks', helpCategory); } + if (OpenNewsletterSignupUrlAction.AVAILABLE) { + registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNewsletterSignupUrlAction, OpenNewsletterSignupUrlAction.ID, OpenNewsletterSignupUrlAction.LABEL), 'Help: Tips and Tricks', helpCategory); + } + registry.registerWorkbenchAction(new SyncActionDescriptor(OpenTwitterUrlAction, OpenTwitterUrlAction.ID, OpenTwitterUrlAction.LABEL), 'Help: Join Us on Twitter', helpCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenRequestFeatureUrlAction, OpenRequestFeatureUrlAction.ID, OpenRequestFeatureUrlAction.LABEL), 'Help: Search Feature Requests', helpCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLicenseUrlAction, OpenLicenseUrlAction.ID, OpenLicenseUrlAction.LABEL), 'Help: View License', helpCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPrivacyStatementUrlAction, OpenPrivacyStatementUrlAction.ID, OpenPrivacyStatementUrlAction.LABEL), 'Help: Privacy Statement', helpCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAboutDialogAction, ShowAboutDialogAction.ID, ShowAboutDialogAction.LABEL), 'Help: About', helpCategory); + registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAboutDialogAction, ShowAboutDialogAction.ID, ShowAboutDialogAction.LABEL), `Help: About ${product.applicationName}`, helpCategory); })(); })(); @@ -222,45 +205,6 @@ import { LogStorageAction } from 'vs/platform/storage/node/storageService'; order: 2 }); - if (isMacintosh) { - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '2_open', - command: { - id: OpenFileFolderAction.ID, - title: nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...") - }, - order: 1 - }); - } else { - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '2_open', - command: { - id: OpenFileAction.ID, - title: nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '2_open', - command: { - id: OpenFolderAction.ID, - title: nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...") - }, - order: 2 - }); - } - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '2_open', - command: { - id: OpenWorkspaceAction.ID, - title: nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "Open Wor&&kspace...") - }, - order: 3, - when: SupportsWorkspacesContext - }); - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { title: nls.localize({ key: 'miOpenRecent', comment: ['&& denotes a mnemonic'] }, "Open &&Recent"), submenu: MenuId.MenubarRecentMenu, @@ -298,14 +242,6 @@ import { LogStorageAction } from 'vs/platform/storage/node/storageService'; when: SupportsWorkspacesContext }); - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - title: nls.localize({ key: 'miPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences"), - submenu: MenuId.MenubarPreferencesMenu, - group: '5_autosave', - order: 2, - when: IsMacContext.toNegated() - }); - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: '6_close', command: { @@ -346,23 +282,6 @@ import { LogStorageAction } from 'vs/platform/storage/node/storageService'; when: IsMacContext.toNegated() }); - // Appereance menu - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '2_appearance', - title: nls.localize({ key: 'miAppearance', comment: ['&& denotes a mnemonic'] }, "&&Appearance"), - submenu: MenuId.MenubarAppearanceMenu, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '1_toggle_view', - command: { - id: ToggleFullScreenAction.ID, - title: nls.localize({ key: 'miToggleFullScreen', comment: ['&& denotes a mnemonic'] }, "Toggle &&Full Screen") - }, - order: 1 - }); - // Zoom MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { @@ -546,18 +465,6 @@ import { LogStorageAction } from 'vs/platform/storage/node/storageService'; nls.localize('openFilesInNewWindowMac', "Controls whether files should open in a new window. \nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") : nls.localize('openFilesInNewWindow', "Controls whether files should open in a new window.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") }, - 'window.openFoldersInNewWindow': { - 'type': 'string', - 'enum': ['on', 'off', 'default'], - 'enumDescriptions': [ - nls.localize('window.openFoldersInNewWindow.on', "Folders will open in a new window."), - nls.localize('window.openFoldersInNewWindow.off', "Folders will replace the last active window."), - nls.localize('window.openFoldersInNewWindow.default', "Folders will open in a new window unless a folder is picked from within the application (e.g. via the File menu).") - ], - 'default': 'default', - 'scope': ConfigurationScope.APPLICATION, - 'markdownDescription': nls.localize('openFoldersInNewWindow', "Controls whether folders should open in a new window or replace the last active window.\nNote that there can still be cases where this setting is ignored (e.g. when using the `--new-window` or `--reuse-window` command line option).") - }, 'window.openWithoutArgumentsInNewWindow': { 'type': 'string', 'enum': ['on', 'off'], @@ -611,27 +518,6 @@ import { LogStorageAction } from 'vs/platform/storage/node/storageService'; 'default': false, 'description': nls.localize('closeWhenEmpty', "Controls whether closing the last editor should also close the window. This setting only applies for windows that do not show folders.") }, - 'window.menuBarVisibility': { - 'type': 'string', - 'enum': ['default', 'visible', 'toggle', 'hidden'], - 'enumDescriptions': [ - nls.localize('window.menuBarVisibility.default', "Menu is only hidden in full screen mode."), - nls.localize('window.menuBarVisibility.visible', "Menu is always visible even in full screen mode."), - nls.localize('window.menuBarVisibility.toggle', "Menu is hidden but can be displayed via Alt key."), - nls.localize('window.menuBarVisibility.hidden', "Menu is always hidden.") - ], - 'default': 'default', - 'scope': ConfigurationScope.APPLICATION, - 'description': nls.localize('menuBarVisibility', "Control the visibility of the menu bar. A setting of 'toggle' means that the menu bar is hidden and a single press of the Alt key will show it. By default, the menu bar will be visible, unless the window is full screen."), - 'included': isWindows || isLinux - }, - 'window.enableMenuBarMnemonics': { - 'type': 'boolean', - 'default': true, - 'scope': ConfigurationScope.APPLICATION, - 'description': nls.localize('enableMenuBarMnemonics', "If enabled, the main menus can be opened via Alt-key shortcuts. Disabling mnemonics allows to bind these Alt-key shortcuts to editor commands instead."), - 'included': isWindows || isLinux - }, 'window.autoDetectHighContrast': { 'type': 'boolean', 'default': true, @@ -664,7 +550,7 @@ import { LogStorageAction } from 'vs/platform/storage/node/storageService'; '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."), 'scope': ConfigurationScope.APPLICATION, - 'included': isMacintosh + 'included': false /* isMacintosh */ }, 'window.clickThroughInactive': { 'type': 'boolean', diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index 1d13aba375f..87259d771c9 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -19,14 +19,13 @@ import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/n import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { stat } from 'vs/base/node/pfs'; -import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; +import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/nativeKeymapService'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { webFrame } from 'electron'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceInitializationPayload, ISingleFolderWorkspaceInitializationPayload, reviveWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; -import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; import { ConsoleLogService, MultiplexLogService, ILogService } from 'vs/platform/log/common/log'; import { StorageService } from 'vs/platform/storage/node/storageService'; -import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/node/logIpc'; +import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc'; import { Schemas } from 'vs/base/common/network'; import { sanitizeFilePath } from 'vs/base/common/extpath'; import { basename } from 'vs/base/common/path'; @@ -48,7 +47,9 @@ import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { REMOTE_FILE_SYSTEM_CHANNEL_NAME, RemoteExtensionsFileSystemProvider } from 'vs/platform/remote/common/remoteAgentFileSystemChannel'; import { DefaultConfigurationExportHelper } from 'vs/workbench/services/configuration/node/configurationExportHelper'; import { ConfigurationCache } from 'vs/workbench/services/configuration/node/configurationCache'; -import { ConfigurationFileService } from 'vs/workbench/services/configuration/node/configurationFileService'; +import { SpdLogService } from 'vs/platform/log/node/spdlogService'; +import { SignService } from 'vs/platform/sign/node/signService'; +import { ISignService } from 'vs/platform/sign/common/sign'; class CodeRendererMain extends Disposable { @@ -106,42 +107,39 @@ class CodeRendererMain extends Disposable { } } - open(): Promise { - return this.initServices().then(services => { + async open(): Promise { + const services = await this.initServices(); + await domContentLoaded(); + mark('willStartWorkbench'); - return domContentLoaded().then(() => { - mark('willStartWorkbench'); + // Create Workbench + this.workbench = new Workbench(document.body, services.serviceCollection, services.logService); - // Create Workbench - this.workbench = new Workbench(document.body, services.serviceCollection, services.logService); + // Layout + this._register(addDisposableListener(window, EventType.RESIZE, e => this.onWindowResize(e, true))); - // Layout - this._register(addDisposableListener(window, EventType.RESIZE, e => this.onWindowResize(e, true))); + // Workbench Lifecycle + this._register(this.workbench.onShutdown(() => this.dispose())); + this._register(this.workbench.onWillShutdown(event => event.join(services.storageService.close()))); - // Workbench Lifecycle - this._register(this.workbench.onShutdown(() => this.dispose())); - this._register(this.workbench.onWillShutdown(event => event.join(services.storageService.close()))); + // Startup + const instantiationService = this.workbench.startup(); - // Startup - const instantiationService = this.workbench.startup(); + // Window + this._register(instantiationService.createInstance(ElectronWindow)); - // Window - this._register(instantiationService.createInstance(ElectronWindow)); + // Driver + if (this.configuration.driver) { + instantiationService.invokeFunction(async accessor => this._register(await registerWindowDriver(accessor))); + } - // Driver - if (this.configuration.driver) { - instantiationService.invokeFunction(accessor => registerWindowDriver(accessor).then(disposable => this._register(disposable))); - } + // Config Exporter + if (this.configuration['export-default-configuration']) { + instantiationService.createInstance(DefaultConfigurationExportHelper); + } - // Config Exporter - if (this.configuration['export-default-configuration']) { - instantiationService.createInstance(DefaultConfigurationExportHelper); - } - - // Logging - services.logService.trace('workbench configuration', JSON.stringify(this.configuration)); - }); - }); + // Logging + services.logService.trace('workbench configuration', JSON.stringify(this.configuration)); } private onWindowResize(e: Event, retry: boolean): void { @@ -162,7 +160,7 @@ class CodeRendererMain extends Disposable { } } - private initServices(): Promise<{ serviceCollection: ServiceCollection, logService: ILogService, storageService: StorageService }> { + private async initServices(): Promise<{ serviceCollection: ServiceCollection, logService: ILogService, storageService: StorageService }> { const serviceCollection = new ServiceCollection(); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -186,7 +184,11 @@ class CodeRendererMain extends Disposable { const remoteAuthorityResolverService = new RemoteAuthorityResolverService(); serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService); - const remoteAgentService = this._register(new RemoteAgentService(this.configuration, environmentService, remoteAuthorityResolverService)); + // Sign + const signService = new SignService(); + serviceCollection.set(ISignService, signService); + + const remoteAgentService = this._register(new RemoteAgentService(this.configuration, environmentService, remoteAuthorityResolverService, signService)); serviceCollection.set(IRemoteAgentService, remoteAgentService); // Files @@ -203,7 +205,9 @@ class CodeRendererMain extends Disposable { fileService.registerProvider(Schemas.vscodeRemote, remoteFileSystemProvider); } - return this.resolveWorkspaceInitializationPayload(environmentService).then(payload => Promise.all([ + const payload = await this.resolveWorkspaceInitializationPayload(environmentService); + + const services = await Promise.all([ this.createWorkspaceService(payload, environmentService, fileService, remoteAgentService, logService).then(service => { // Workspace @@ -222,47 +226,46 @@ class CodeRendererMain extends Disposable { return service; }) - ]).then(services => ({ serviceCollection, logService, storageService: services[1] }))); + ]); + + return { serviceCollection, logService, storageService: services[1] }; } - private resolveWorkspaceInitializationPayload(environmentService: IWorkbenchEnvironmentService): Promise { + private async resolveWorkspaceInitializationPayload(environmentService: IWorkbenchEnvironmentService): Promise { // Multi-root workspace if (this.configuration.workspace) { - return Promise.resolve(this.configuration.workspace); + return this.configuration.workspace; } // Single-folder workspace - let workspaceInitializationPayload: Promise = Promise.resolve(undefined); + let workspaceInitializationPayload: IWorkspaceInitializationPayload | undefined; if (this.configuration.folderUri) { - workspaceInitializationPayload = this.resolveSingleFolderWorkspaceInitializationPayload(this.configuration.folderUri); + workspaceInitializationPayload = await this.resolveSingleFolderWorkspaceInitializationPayload(this.configuration.folderUri); } - return workspaceInitializationPayload.then(payload => { - - // Fallback to empty workspace if we have no payload yet. - if (!payload) { - let id: string; - if (this.configuration.backupPath) { - id = basename(this.configuration.backupPath); // we know the backupPath must be a unique path so we leverage its name as workspace ID - } else if (environmentService.isExtensionDevelopment) { - id = 'ext-dev'; // extension development window never stores backups and is a singleton - } else { - return Promise.reject(new Error('Unexpected window configuration without backupPath')); - } - - payload = { id }; + // Fallback to empty workspace if we have no payload yet. + if (!workspaceInitializationPayload) { + let id: string; + if (this.configuration.backupPath) { + id = basename(this.configuration.backupPath); // we know the backupPath must be a unique path so we leverage its name as workspace ID + } else if (environmentService.isExtensionDevelopment) { + id = 'ext-dev'; // extension development window never stores backups and is a singleton + } else { + throw new Error('Unexpected window configuration without backupPath'); } - return payload; - }); + workspaceInitializationPayload = { id }; + } + + return workspaceInitializationPayload; } - private resolveSingleFolderWorkspaceInitializationPayload(folderUri: ISingleFolderWorkspaceIdentifier): Promise { + private async resolveSingleFolderWorkspaceInitializationPayload(folderUri: ISingleFolderWorkspaceIdentifier): Promise { // Return early the folder is not local if (folderUri.scheme !== Schemas.file) { - return Promise.resolve({ id: createHash('md5').update(folderUri.toString()).digest('hex'), folder: folderUri }); + return { id: createHash('md5').update(folderUri.toString()).digest('hex'), folder: folderUri }; } function computeLocalDiskFolderId(folder: URI, stat: fs.Stats): string { @@ -285,44 +288,55 @@ class CodeRendererMain extends Disposable { } // For local: ensure path is absolute and exists - const sanitizedFolderPath = sanitizeFilePath(folderUri.fsPath, process.env['VSCODE_CWD'] || process.cwd()); - return stat(sanitizedFolderPath).then(stat => { + try { + const sanitizedFolderPath = sanitizeFilePath(folderUri.fsPath, process.env['VSCODE_CWD'] || process.cwd()); + const fileStat = await stat(sanitizedFolderPath); + const sanitizedFolderUri = URI.file(sanitizedFolderPath); return { - id: computeLocalDiskFolderId(sanitizedFolderUri, stat), + id: computeLocalDiskFolderId(sanitizedFolderUri, fileStat), folder: sanitizedFolderUri }; - }, error => onUnexpectedError(error)); + } catch (error) { + onUnexpectedError(error); + } + + return; } - private createWorkspaceService(payload: IWorkspaceInitializationPayload, environmentService: IWorkbenchEnvironmentService, fileService: FileService, remoteAgentService: IRemoteAgentService, logService: ILogService): Promise { - const configurationFileService = new ConfigurationFileService(); - configurationFileService.fileService = fileService; + private async createWorkspaceService(payload: IWorkspaceInitializationPayload, environmentService: IWorkbenchEnvironmentService, fileService: FileService, remoteAgentService: IRemoteAgentService, logService: ILogService): Promise { + const workspaceService = new WorkspaceService({ userSettingsResource: environmentService.settingsResource, remoteAuthority: this.configuration.remoteAuthority, configurationCache: new ConfigurationCache(environmentService) }, fileService, remoteAgentService); - const workspaceService = new WorkspaceService({ userSettingsResource: URI.file(environmentService.appSettingsPath), remoteAuthority: this.configuration.remoteAuthority, configurationCache: new ConfigurationCache(environmentService) }, configurationFileService, remoteAgentService); + try { + await workspaceService.initialize(payload); - return workspaceService.initialize(payload).then(() => workspaceService, error => { + return workspaceService; + } catch (error) { onUnexpectedError(error); logService.error(error); return workspaceService; - }); + } } - private createStorageService(payload: IWorkspaceInitializationPayload, environmentService: IWorkbenchEnvironmentService, logService: ILogService, mainProcessService: IMainProcessService): Promise { + private async createStorageService(payload: IWorkspaceInitializationPayload, environmentService: IWorkbenchEnvironmentService, logService: ILogService, mainProcessService: IMainProcessService): Promise { const globalStorageDatabase = new GlobalStorageDatabaseChannelClient(mainProcessService.getChannel('storage')); const storageService = new StorageService(globalStorageDatabase, logService, environmentService); - return storageService.initialize(payload).then(() => storageService, error => { + try { + await storageService.initialize(payload); + + return storageService; + } catch (error) { onUnexpectedError(error); logService.error(error); return storageService; - }); + } } private createLogService(mainProcessService: IMainProcessService, environmentService: IWorkbenchEnvironmentService): ILogService { - const spdlogService = createSpdLogService(`renderer${this.configuration.windowId}`, this.configuration.logLevel, environmentService.logsPath); + const spdlogService = new SpdLogService(`renderer${this.configuration.windowId}`, environmentService.logsPath, this.configuration.logLevel); const consoleLogService = new ConsoleLogService(this.configuration.logLevel); const logService = new MultiplexLogService([consoleLogService, spdlogService]); const logLevelClient = new LogLevelSetterChannelClient(mainProcessService.getChannel('loglevel')); diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 7687183710f..a1001064bfc 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -21,14 +21,14 @@ import { IWorkbenchThemeService, VS_HC_THEME } from 'vs/workbench/services/theme import * as browser from 'vs/base/browser/browser'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IResourceInput } from 'vs/platform/editor/common/editor'; -import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; +import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/nativeKeymapService'; import { ipcRenderer as ipc, webFrame, crashReporter, Event } from 'electron'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; import { IMenuService, MenuId, IMenu, MenuItemAction, ICommandAction } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { fillInActionBarActions } from 'vs/platform/actions/browser/menuItemActionItem'; +import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { LifecyclePhase, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; import { IIntegrityService } from 'vs/workbench/services/integrity/common/integrity'; @@ -43,6 +43,8 @@ import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessi import { WorkbenchState, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { coalesce } from 'vs/base/common/arrays'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { isEqual } from 'vs/base/common/resources'; const TextInputActions: IAction[] = [ new Action('undo', nls.localize('undo', "Undo"), undefined, true, () => Promise.resolve(document.execCommand('undo'))), @@ -59,7 +61,7 @@ export class ElectronWindow extends Disposable { private touchBarMenu?: IMenu; private touchBarUpdater: RunOnceScheduler; - private touchBarDisposables: IDisposable[]; + private readonly touchBarDisposables = this._register(new DisposableStore()); private lastInstalledTouchedBar: ICommandAction[][]; private previousConfiguredZoomLevel: number; @@ -88,12 +90,11 @@ export class ElectronWindow extends Disposable { @IIntegrityService private readonly integrityService: IIntegrityService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IAccessibilityService private readonly accessibilityService: IAccessibilityService, - @IWorkspaceContextService private readonly contextService: IWorkspaceContextService + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @ITextFileService private readonly textFileService: ITextFileService ) { super(); - this.touchBarDisposables = []; - this.pendingFoldersToAdd = []; this.addFoldersScheduler = this._register(new RunOnceScheduler(() => this.doAddFolders(), 100)); @@ -114,7 +115,7 @@ export class ElectronWindow extends Disposable { }); // Support runAction event - ipc.on('vscode:runAction', (event: Event, request: IRunActionInWindowRequest) => { + ipc.on('vscode:runAction', async (event: Event, request: IRunActionInWindowRequest) => { const args: unknown[] = request.args || []; // If we run an action from the touchbar, we fill in the currently active resource @@ -131,7 +132,9 @@ export class ElectronWindow extends Disposable { args.push({ from: request.from }); // TODO@telemetry this is a bit weird to send this to every action? } - this.commandService.executeCommand(request.id, ...args).then(_ => { + try { + await this.commandService.executeCommand(request.id, ...args); + /* __GDPR__ "commandExecuted" : { "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, @@ -139,9 +142,9 @@ export class ElectronWindow extends Disposable { } */ this.telemetryService.publicLog('commandExecuted', { id: request.id, from: request.from }); - }, err => { - this.notificationService.error(err); - }); + } catch (error) { + this.notificationService.error(error); + } }); // Support runKeybinding event @@ -170,34 +173,30 @@ export class ElectronWindow extends Disposable { }); // Fullscreen Events - ipc.on('vscode:enterFullScreen', () => { - this.lifecycleService.when(LifecyclePhase.Ready).then(() => { - browser.setFullscreen(true); - }); + ipc.on('vscode:enterFullScreen', async () => { + await this.lifecycleService.when(LifecyclePhase.Ready); + browser.setFullscreen(true); }); - ipc.on('vscode:leaveFullScreen', () => { - this.lifecycleService.when(LifecyclePhase.Ready).then(() => { - browser.setFullscreen(false); - }); + ipc.on('vscode:leaveFullScreen', async () => { + await this.lifecycleService.when(LifecyclePhase.Ready); + browser.setFullscreen(false); }); // High Contrast Events - ipc.on('vscode:enterHighContrast', () => { + ipc.on('vscode:enterHighContrast', async () => { const windowConfig = this.configurationService.getValue('window'); if (windowConfig && windowConfig.autoDetectHighContrast) { - this.lifecycleService.when(LifecyclePhase.Ready).then(() => { - this.themeService.setColorTheme(VS_HC_THEME, undefined); - }); + await this.lifecycleService.when(LifecyclePhase.Ready); + this.themeService.setColorTheme(VS_HC_THEME, undefined); } }); - ipc.on('vscode:leaveHighContrast', () => { + ipc.on('vscode:leaveHighContrast', async () => { const windowConfig = this.configurationService.getValue('window'); if (windowConfig && windowConfig.autoDetectHighContrast) { - this.lifecycleService.when(LifecyclePhase.Ready).then(() => { - this.themeService.restoreColorTheme(); - }); + await this.lifecycleService.when(LifecyclePhase.Ready); + this.themeService.restoreColorTheme(); } }); @@ -228,11 +227,10 @@ export class ElectronWindow extends Disposable { // Listen to editor closing (if we run with --wait) const filesToWait = this.environmentService.configuration.filesToWait; if (filesToWait) { - const resourcesToWaitFor = coalesce(filesToWait.paths.map(p => p.fileUri)); const waitMarkerFile = filesToWait.waitMarkerFileUri; - const listenerDispose = this.editorService.onDidCloseEditor(() => this.onEditorClosed(listenerDispose, resourcesToWaitFor, waitMarkerFile)); + const resourcesToWaitFor = coalesce(filesToWait.paths.map(p => p.fileUri)); - this._register(listenerDispose); + this._register(this.trackClosedWaitFiles(waitMarkerFile, resourcesToWaitFor)); } } @@ -257,17 +255,6 @@ export class ElectronWindow extends Disposable { } } - private onEditorClosed(listenerDispose: IDisposable, resourcesToWaitFor: URI[], waitMarkerFile: URI): void { - - // In wait mode, listen to changes to the editors and wait until the files - // are closed that the user wants to wait for. When this happens we delete - // the wait marker file to signal to the outside that editing is done. - if (resourcesToWaitFor.every(resource => !this.editorService.isOpen({ resource }))) { - listenerDispose.dispose(); - this.fileService.del(waitMarkerFile); - } - } - private onContextMenu(e: MouseEvent): void { if (e.target instanceof HTMLElement) { const target = e.target; @@ -319,9 +306,7 @@ export class ElectronWindow extends Disposable { }; // Emit event when vscode is ready - this.lifecycleService.when(LifecyclePhase.Ready).then(() => { - ipc.send('vscode:workbenchReady', this.windowService.windowId); - }); + this.lifecycleService.when(LifecyclePhase.Ready).then(() => ipc.send('vscode:workbenchReady', this.windowService.windowId)); // Integrity warning this.integrityService.isPure().then(res => this.titleService.updateProperties({ isPure: res.isPure })); @@ -330,7 +315,7 @@ export class ElectronWindow extends Disposable { this.lifecycleService.when(LifecyclePhase.Restored).then(() => { let isAdminPromise: Promise; if (isWindows) { - isAdminPromise = import('native-is-elevated').then(isElevated => isElevated()); + isAdminPromise = import('native-is-elevated').then(isElevated => isElevated()); // not using async here due to https://github.com/microsoft/vscode/issues/74321 } else { isAdminPromise = Promise.resolve(isRootUser()); } @@ -365,26 +350,26 @@ export class ElectronWindow extends Disposable { } // Dispose old - this.touchBarDisposables = dispose(this.touchBarDisposables); + this.touchBarDisposables.clear(); this.touchBarMenu = undefined; // Create new (delayed) this.touchBarUpdater = new RunOnceScheduler(() => this.doUpdateTouchbarMenu(), 300); - this.touchBarDisposables.push(this.touchBarUpdater); + this.touchBarDisposables.add(this.touchBarUpdater); this.touchBarUpdater.schedule(); } private doUpdateTouchbarMenu(): void { if (!this.touchBarMenu) { this.touchBarMenu = this.editorService.invokeWithinEditorContext(accessor => this.menuService.createMenu(MenuId.TouchBarContext, accessor.get(IContextKeyService))); - this.touchBarDisposables.push(this.touchBarMenu); - this.touchBarDisposables.push(this.touchBarMenu.onDidChange(() => this.touchBarUpdater.schedule())); + this.touchBarDisposables.add(this.touchBarMenu); + this.touchBarDisposables.add(this.touchBarMenu.onDidChange(() => this.touchBarUpdater.schedule())); } const actions: Array = []; // Fill actions into groups respecting order - fillInActionBarActions(this.touchBarMenu, undefined, actions); + this.touchBarDisposables.add(createAndFillInActionBarActions(this.touchBarMenu, undefined, actions)); // Convert into command action multi array const items: ICommandAction[][] = []; @@ -417,7 +402,7 @@ export class ElectronWindow extends Disposable { } } - private setupCrashReporter(): void { + private async setupCrashReporter(): Promise { // base options with product info const options = { @@ -431,18 +416,14 @@ export class ElectronWindow extends Disposable { }; // mixin telemetry info - this.telemetryService.getTelemetryInfo() - .then(info => { - assign(options.extra, { - vscode_sessionId: info.sessionId - }); + const info = await this.telemetryService.getTelemetryInfo(); + assign(options.extra, { vscode_sessionId: info.sessionId }); - // start crash reporter right here - crashReporter.start(deepClone(options)); + // start crash reporter right here + crashReporter.start(deepClone(options)); - // start crash reporter in the main process - return this.windowsService.startCrashReporter(options); - }); + // start crash reporter in the main process + return this.windowsService.startCrashReporter(options); } private onAddFoldersRequest(request: IAddFoldersRequest): void { @@ -488,38 +469,66 @@ export class ElectronWindow extends Disposable { // In wait mode, listen to changes to the editors and wait until the files // are closed that the user wants to wait for. When this happens we delete // the wait marker file to signal to the outside that editing is done. - const resourcesToWaitFor = request.filesToWait.paths.map(p => URI.revive(p.fileUri)); const waitMarkerFile = URI.revive(request.filesToWait.waitMarkerFileUri); - const unbind = this.editorService.onDidCloseEditor(() => { - if (resourcesToWaitFor.every(resource => !this.editorService.isOpen({ resource }))) { - unbind.dispose(); - this.fileService.del(waitMarkerFile); - } - }); + const resourcesToWaitFor = coalesce(request.filesToWait.paths.map(p => URI.revive(p.fileUri))); + this.trackClosedWaitFiles(waitMarkerFile, resourcesToWaitFor); } } - private openResources(resources: Array, diffMode: boolean): void { - this.lifecycleService.when(LifecyclePhase.Ready).then((): Promise => { + private trackClosedWaitFiles(waitMarkerFile: URI, resourcesToWaitFor: URI[]): IDisposable { + const listener = this.editorService.onDidCloseEditor(async () => { + // In wait mode, listen to changes to the editors and wait until the files + // are closed that the user wants to wait for. When this happens we delete + // the wait marker file to signal to the outside that editing is done. + if (resourcesToWaitFor.every(resource => !this.editorService.isOpen({ resource }))) { + // If auto save is configured with the default delay (1s) it is possible + // to close the editor while the save still continues in the background. As such + // we have to also check if the files to wait for are dirty and if so wait + // for them to get saved before deleting the wait marker file. + const dirtyFilesToWait = this.textFileService.getDirty(resourcesToWaitFor); + if (dirtyFilesToWait.length > 0) { + await Promise.all(dirtyFilesToWait.map(async dirtyFileToWait => await this.joinResourceSaved(dirtyFileToWait))); + } - // In diffMode we open 2 resources as diff - if (diffMode && resources.length === 2) { - return this.editorService.openEditor({ leftResource: resources[0].resource!, rightResource: resources[1].resource!, options: { pinned: true } }); + listener.dispose(); + await this.fileService.del(waitMarkerFile); + } + }); + + return listener; + } + + private joinResourceSaved(resource: URI): Promise { + return new Promise(resolve => { + if (!this.textFileService.isDirty(resource)) { + return resolve(); // return early if resource is not dirty } - // For one file, just put it into the current active editor - if (resources.length === 1) { - return this.editorService.openEditor(resources[0]); - } + // Otherwise resolve promise when resource is saved + const listener = this.textFileService.models.onModelSaved(e => { + if (isEqual(resource, e.resource)) { + listener.dispose(); - // Otherwise open all - return this.editorService.openEditors(resources); + resolve(); + } + }); }); } - dispose(): void { - this.touchBarDisposables = dispose(this.touchBarDisposables); + private async openResources(resources: Array, diffMode: boolean): Promise { + await this.lifecycleService.when(LifecyclePhase.Ready); - super.dispose(); + // In diffMode we open 2 resources as diff + if (diffMode && resources.length === 2) { + return this.editorService.openEditor({ leftResource: resources[0].resource!, rightResource: resources[1].resource!, options: { pinned: true } }); + } + + // For one file, just put it into the current active editor + if (resources.length === 1) { + return this.editorService.openEditor(resources[0]); + } + + // Otherwise open all + return this.editorService.openEditors(resources); } } diff --git a/src/vs/workbench/services/backup/common/backup.ts b/src/vs/workbench/services/backup/common/backup.ts index b1f30ade0a3..9d1b0208434 100644 --- a/src/vs/workbench/services/backup/common/backup.ts +++ b/src/vs/workbench/services/backup/common/backup.ts @@ -9,6 +9,11 @@ import { ITextBufferFactory, ITextSnapshot } from 'vs/editor/common/model'; export const IBackupFileService = createDecorator('backupFileService'); +export interface IResolvedBackup { + value: ITextBufferFactory; + meta?: T; +} + /** * A service that handles any I/O and state associated with the backup system. */ @@ -42,8 +47,10 @@ export interface IBackupFileService { * @param resource The resource to back up. * @param content The content of the resource as snapshot. * @param versionId The version id of the resource to backup. + * @param meta The (optional) meta data of the resource to backup. This information + * can be restored later when loading the backup again. */ - backupResource(resource: URI, content: ITextSnapshot, versionId?: number): Promise; + backupResource(resource: URI, content: ITextSnapshot, versionId?: number, meta?: T): Promise; /** * Gets a list of file backups for the current workspace. @@ -55,10 +62,10 @@ export interface IBackupFileService { /** * Resolves the backup for the given resource. * - * @param value The contents from a backup resource as stream. - * @return The backup file's backed up content as text buffer factory. + * @param resource The resource to get the backup for. + * @return The backup file's backed up content and metadata if available. */ - resolveBackupContent(backup: URI): Promise; + resolveBackupContent(resource: URI): Promise>; /** * 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 21d8187ef26..624ec71cad0 100644 --- a/src/vs/workbench/services/backup/node/backupFileService.ts +++ b/src/vs/workbench/services/backup/node/backupFileService.ts @@ -3,94 +3,112 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as path from 'vs/base/common/path'; -import * as crypto from 'crypto'; -import * as pfs from 'vs/base/node/pfs'; -import { URI as Uri } from 'vs/base/common/uri'; +import { join } from 'vs/base/common/path'; +import { joinPath } from 'vs/base/common/resources'; +import { createHash } from 'crypto'; +import { URI } from 'vs/base/common/uri'; +import { coalesce } from 'vs/base/common/arrays'; +import { equals, deepClone } from 'vs/base/common/objects'; import { ResourceQueue } from 'vs/base/common/async'; -import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup'; import { IFileService } from 'vs/platform/files/common/files'; import { readToMatchingString } from 'vs/base/node/stream'; -import { ITextBufferFactory, ITextSnapshot } from 'vs/editor/common/model'; +import { ITextSnapshot } from 'vs/editor/common/model'; import { createTextBufferFactoryFromStream, createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; -import { keys } from 'vs/base/common/map'; +import { keys, ResourceMap } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { VSBuffer } from 'vs/base/common/buffer'; import { TextSnapshotReadable } from 'vs/workbench/services/textfile/common/textfiles'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; export interface IBackupFilesModel { - resolve(backupRoot: string): Promise; + resolve(backupRoot: URI): Promise; - add(resource: Uri, versionId?: number): void; - has(resource: Uri, versionId?: number): boolean; - get(): Uri[]; - remove(resource: Uri): void; + add(resource: URI, versionId?: number, meta?: object): void; + has(resource: URI, versionId?: number, meta?: object): boolean; + get(): URI[]; + remove(resource: URI): void; count(): number; clear(): void; } +interface IBackupCacheEntry { + versionId?: number; + meta?: object; +} + export class BackupFilesModel implements IBackupFilesModel { - private cache: { [resource: string]: number /* version ID */ } = Object.create(null); + private cache: ResourceMap = new ResourceMap(); - resolve(backupRoot: string): Promise { - return pfs.readDirsInDir(backupRoot).then(backupSchemas => { + constructor(private fileService: IFileService) { } - // For all supported schemas - return Promise.all(backupSchemas.map(backupSchema => { + async resolve(backupRoot: URI): Promise { + try { + const backupRootStat = await this.fileService.resolve(backupRoot); + if (backupRootStat.children) { + await Promise.all(backupRootStat.children + .filter(child => child.isDirectory) + .map(async backupSchema => { - // Read backup directory for backups - const backupSchemaPath = path.join(backupRoot, backupSchema); - return pfs.readdir(backupSchemaPath).then(backupHashes => { + // Read backup directory for backups + const backupSchemaStat = await this.fileService.resolve(backupSchema.resource); - // Remember known backups in our caches - backupHashes.forEach(backupHash => { - const backupResource = Uri.file(path.join(backupSchemaPath, backupHash)); - this.add(backupResource); - }); - }); - })); - }).then(() => this, error => this); + // Remember known backups in our caches + if (backupSchemaStat.children) { + backupSchemaStat.children.forEach(backupHash => this.add(backupHash.resource)); + } + })); + } + } catch (error) { + // ignore any errors + } + + return this; } - add(resource: Uri, versionId = 0): void { - this.cache[resource.toString()] = versionId; + add(resource: URI, versionId = 0, meta?: object): void { + this.cache.set(resource, { versionId, meta: deepClone(meta) }); // make sure to not store original meta in our cache... } count(): number { - return Object.keys(this.cache).length; + return this.cache.size; } - has(resource: Uri, versionId?: number): boolean { - const cachedVersionId = this.cache[resource.toString()]; - if (typeof cachedVersionId !== 'number') { + has(resource: URI, versionId?: number, meta?: object): boolean { + const entry = this.cache.get(resource); + if (!entry) { return false; // unknown resource } - if (typeof versionId === 'number') { - return versionId === cachedVersionId; // if we are asked with a specific version ID, make sure to test for it + if (typeof versionId === 'number' && versionId !== entry.versionId) { + return false; // different versionId + } + + if (meta && !equals(meta, entry.meta)) { + return false; // different metadata } return true; } - get(): Uri[] { - return Object.keys(this.cache).map(k => Uri.parse(k)); + get(): URI[] { + return this.cache.keys(); } - remove(resource: Uri): void { - delete this.cache[resource.toString()]; + remove(resource: URI): void { + this.cache.delete(resource); } clear(): void { - this.cache = Object.create(null); + this.cache.clear(); } } export class BackupFileService implements IBackupFileService { - _serviceBrand: any; + _serviceBrand: ServiceIdentifier; private impl: IBackupFileService; @@ -116,15 +134,15 @@ export class BackupFileService implements IBackupFileService { return this.impl.hasBackups(); } - loadBackupResource(resource: Uri): Promise { + loadBackupResource(resource: URI): Promise { return this.impl.loadBackupResource(resource); } - backupResource(resource: Uri, content: ITextSnapshot, versionId?: number): Promise { - return this.impl.backupResource(resource, content, versionId); + backupResource(resource: URI, content: ITextSnapshot, versionId?: number, meta?: T): Promise { + return this.impl.backupResource(resource, content, versionId, meta); } - discardResourceBackup(resource: Uri): Promise { + discardResourceBackup(resource: URI): Promise { return this.impl.discardResourceBackup(resource); } @@ -132,26 +150,28 @@ export class BackupFileService implements IBackupFileService { return this.impl.discardAllWorkspaceBackups(); } - getWorkspaceFileBackups(): Promise { + getWorkspaceFileBackups(): Promise { return this.impl.getWorkspaceFileBackups(); } - resolveBackupContent(backup: Uri): Promise { + resolveBackupContent(backup: URI): Promise> { return this.impl.resolveBackupContent(backup); } - toBackupResource(resource: Uri): Uri { + toBackupResource(resource: URI): URI { return this.impl.toBackupResource(resource); } } class BackupFileServiceImpl implements IBackupFileService { - private static readonly META_MARKER = '\n'; + private static readonly PREAMBLE_END_MARKER = '\n'; + private static readonly PREAMBLE_META_SEPARATOR = ' '; // using a character that is know to be escaped in a URI as separator + private static readonly PREAMBLE_MAX_LENGTH = 10000; _serviceBrand: any; - private backupWorkspacePath: string; + private backupWorkspacePath: URI; private isShuttingDown: boolean; private ready: Promise; @@ -168,121 +188,171 @@ class BackupFileServiceImpl implements IBackupFileService { } initialize(backupWorkspacePath: string): void { - this.backupWorkspacePath = backupWorkspacePath; + this.backupWorkspacePath = URI.file(backupWorkspacePath); this.ready = this.init(); } private init(): Promise { - const model = new BackupFilesModel(); + const model = new BackupFilesModel(this.fileService); return model.resolve(this.backupWorkspacePath); } - hasBackups(): Promise { - return this.ready.then(model => { - return model.count() > 0; - }); + async hasBackups(): Promise { + const model = await this.ready; + + return model.count() > 0; } - loadBackupResource(resource: Uri): Promise { - return this.ready.then(model => { + async loadBackupResource(resource: URI): Promise { + const model = await this.ready; - // Return directly if we have a known backup with that resource - const backupResource = this.toBackupResource(resource); - if (model.has(backupResource)) { - return backupResource; - } - - return undefined; - }); - } - - backupResource(resource: Uri, content: ITextSnapshot, versionId?: number): Promise { - if (this.isShuttingDown) { - return Promise.resolve(); + // Return directly if we have a known backup with that resource + const backupResource = this.toBackupResource(resource); + if (model.has(backupResource)) { + return backupResource; } - return this.ready.then(model => { - const backupResource = this.toBackupResource(resource); - if (model.has(backupResource, versionId)) { - return undefined; // return early if backup version id matches requested one + return undefined; + } + + async backupResource(resource: URI, content: ITextSnapshot, versionId?: number, meta?: T): Promise { + if (this.isShuttingDown) { + return; + } + + const model = await this.ready; + + const backupResource = this.toBackupResource(resource); + if (model.has(backupResource, versionId, meta)) { + return; // return early if backup version id matches requested one + } + + return this.ioOperationQueues.queueFor(backupResource).queue(async () => { + let preamble: string | undefined = undefined; + + // With Metadata: URI + META-START + Meta + END + if (meta) { + const preambleWithMeta = `${resource.toString()}${BackupFileServiceImpl.PREAMBLE_META_SEPARATOR}${JSON.stringify(meta)}${BackupFileServiceImpl.PREAMBLE_END_MARKER}`; + if (preambleWithMeta.length < BackupFileServiceImpl.PREAMBLE_MAX_LENGTH) { + preamble = preambleWithMeta; + } } - return this.ioOperationQueues.queueFor(backupResource).queue(() => { - const preamble = `${resource.toString()}${BackupFileServiceImpl.META_MARKER}`; + // Without Metadata: URI + END + if (!preamble) { + preamble = `${resource.toString()}${BackupFileServiceImpl.PREAMBLE_END_MARKER}`; + } - // Update content with value - return this.fileService.writeFile(backupResource, new TextSnapshotReadable(content, preamble)).then(() => model.add(backupResource, versionId)); - }); + // Update content with value + await this.fileService.writeFile(backupResource, new TextSnapshotReadable(content, preamble)); + + // Update model + model.add(backupResource, versionId, meta); }); } - discardResourceBackup(resource: Uri): Promise { - return this.ready.then(model => { - const backupResource = this.toBackupResource(resource); + async discardResourceBackup(resource: URI): Promise { + const model = await this.ready; + const backupResource = this.toBackupResource(resource); - return this.ioOperationQueues.queueFor(backupResource).queue(() => { - return pfs.rimraf(backupResource.fsPath, pfs.RimRafMode.MOVE).then(() => model.remove(backupResource)); - }); + return this.ioOperationQueues.queueFor(backupResource).queue(async () => { + await this.fileService.del(backupResource, { recursive: true }); + + model.remove(backupResource); }); } - discardAllWorkspaceBackups(): Promise { + async discardAllWorkspaceBackups(): Promise { this.isShuttingDown = true; - return this.ready.then(model => { - return pfs.rimraf(this.backupWorkspacePath, pfs.RimRafMode.MOVE).then(() => model.clear()); - }); + const model = await this.ready; + + await this.fileService.del(this.backupWorkspacePath, { recursive: true }); + + model.clear(); } - getWorkspaceFileBackups(): Promise { - return this.ready.then(model => { - const readPromises: Promise[] = []; + async getWorkspaceFileBackups(): Promise { + const model = await this.ready; - model.get().forEach(fileBackup => { - readPromises.push( - readToMatchingString(fileBackup.fsPath, BackupFileServiceImpl.META_MARKER, 2000, 10000).then(Uri.parse) - ); - }); + const backups = await Promise.all(model.get().map(async fileBackup => { + const backupPreamble = await readToMatchingString(fileBackup.fsPath, BackupFileServiceImpl.PREAMBLE_END_MARKER, BackupFileServiceImpl.PREAMBLE_MAX_LENGTH / 5, BackupFileServiceImpl.PREAMBLE_MAX_LENGTH); + if (!backupPreamble) { + return undefined; + } - return Promise.all(readPromises); - }); + // Preamble with metadata: URI + META-START + Meta + END + const metaStartIndex = backupPreamble.indexOf(BackupFileServiceImpl.PREAMBLE_META_SEPARATOR); + if (metaStartIndex > 0) { + return URI.parse(backupPreamble.substring(0, metaStartIndex)); + } + + // Preamble without metadata: URI + END + else { + return URI.parse(backupPreamble); + } + })); + + return coalesce(backups); } - resolveBackupContent(backup: Uri): Promise { - return this.fileService.readFileStream(backup).then(content => { + async resolveBackupContent(backup: URI): Promise> { - // Add a filter method to filter out everything until the meta marker - let metaFound = false; - const metaPreambleFilter = (chunk: VSBuffer) => { - const chunkString = chunk.toString(); + // Metadata extraction + let metaRaw = ''; + let metaEndFound = false; - if (!metaFound && chunk) { - const metaIndex = chunkString.indexOf(BackupFileServiceImpl.META_MARKER); - if (metaIndex === -1) { - return VSBuffer.fromString(''); // meta not yet found, return empty string - } + // Add a filter method to filter out everything until the meta end marker + const metaPreambleFilter = (chunk: VSBuffer) => { + const chunkString = chunk.toString(); - metaFound = true; - return VSBuffer.fromString(chunkString.substr(metaIndex + 1)); // meta found, return everything after + if (!metaEndFound) { + const metaEndIndex = chunkString.indexOf(BackupFileServiceImpl.PREAMBLE_END_MARKER); + if (metaEndIndex === -1) { + metaRaw += chunkString; + + return VSBuffer.fromString(''); // meta not yet found, return empty string } - return chunk; - }; + metaEndFound = true; + metaRaw += chunkString.substring(0, metaEndIndex); // ensure to get last chunk from metadata - return createTextBufferFactoryFromStream(content.value, metaPreambleFilter); - }); + return VSBuffer.fromString(chunkString.substr(metaEndIndex + 1)); // meta found, return everything after + } + + return chunk; + }; + + // Read backup into factory + const content = await this.fileService.readFileStream(backup); + const factory = await createTextBufferFactoryFromStream(content.value, metaPreambleFilter); + + // Trigger read for meta data extraction from the filter above + factory.getFirstLineText(1); + + let meta: T | undefined; + const metaStartIndex = metaRaw.indexOf(BackupFileServiceImpl.PREAMBLE_META_SEPARATOR); + if (metaStartIndex !== -1) { + try { + meta = JSON.parse(metaRaw.substr(metaStartIndex + 1)); + } catch (error) { + // ignore JSON parse errors + } + } + + return { value: factory, meta }; } - toBackupResource(resource: Uri): Uri { - return Uri.file(path.join(this.backupWorkspacePath, resource.scheme, hashPath(resource))); + toBackupResource(resource: URI): URI { + return joinPath(this.backupWorkspacePath, resource.scheme, hashPath(resource)); } } export class InMemoryBackupFileService implements IBackupFileService { - _serviceBrand: any; + _serviceBrand: ServiceIdentifier; private backups: Map = new Map(); @@ -290,7 +360,7 @@ export class InMemoryBackupFileService implements IBackupFileService { return Promise.resolve(this.backups.size > 0); } - loadBackupResource(resource: Uri): Promise { + loadBackupResource(resource: URI): Promise { const backupResource = this.toBackupResource(resource); if (this.backups.has(backupResource.toString())) { return Promise.resolve(backupResource); @@ -299,27 +369,27 @@ export class InMemoryBackupFileService implements IBackupFileService { return Promise.resolve(undefined); } - backupResource(resource: Uri, content: ITextSnapshot, versionId?: number): Promise { + backupResource(resource: URI, content: ITextSnapshot, versionId?: number, meta?: T): Promise { const backupResource = this.toBackupResource(resource); this.backups.set(backupResource.toString(), content); return Promise.resolve(); } - resolveBackupContent(backupResource: Uri): Promise { + resolveBackupContent(backupResource: URI): Promise> { const snapshot = this.backups.get(backupResource.toString()); if (snapshot) { - return Promise.resolve(createTextBufferFactoryFromSnapshot(snapshot)); + return Promise.resolve({ value: createTextBufferFactoryFromSnapshot(snapshot) }); } - return Promise.resolve(undefined); + return Promise.reject('Unexpected backup resource to resolve'); } - getWorkspaceFileBackups(): Promise { - return Promise.resolve(keys(this.backups).map(key => Uri.parse(key))); + getWorkspaceFileBackups(): Promise { + return Promise.resolve(keys(this.backups).map(key => URI.parse(key))); } - discardResourceBackup(resource: Uri): Promise { + discardResourceBackup(resource: URI): Promise { this.backups.delete(this.toBackupResource(resource).toString()); return Promise.resolve(); @@ -331,17 +401,17 @@ export class InMemoryBackupFileService implements IBackupFileService { return Promise.resolve(); } - toBackupResource(resource: Uri): Uri { - return Uri.file(path.join(resource.scheme, hashPath(resource))); + toBackupResource(resource: URI): URI { + return URI.file(join(resource.scheme, hashPath(resource))); } } /* * Exported only for testing */ -export function hashPath(resource: Uri): string { +export function hashPath(resource: URI): string { const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString(); - return crypto.createHash('md5').update(str).digest('hex'); + return createHash('md5').update(str).digest('hex'); } registerSingleton(IBackupFileService, BackupFileService); \ No newline at end of file diff --git a/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts b/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts deleted file mode 100644 index fff74b2b701..00000000000 --- a/src/vs/workbench/services/backup/test/electron-browser/backupFileService.test.ts +++ /dev/null @@ -1,402 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import * as platform from 'vs/base/common/platform'; -import * as crypto from 'crypto'; -import * as os from 'os'; -import * as fs from 'fs'; -import * as path from 'vs/base/common/path'; -import * as pfs from 'vs/base/node/pfs'; -import { URI as Uri } from 'vs/base/common/uri'; -import { BackupFileService, BackupFilesModel, hashPath } from 'vs/workbench/services/backup/node/backupFileService'; -import { TextModel, createTextBufferFactory } from 'vs/editor/common/model/textModel'; -import { getRandomTestPath } from 'vs/base/test/node/testUtils'; -import { DefaultEndOfLine } from 'vs/editor/common/model'; -import { Schemas } from 'vs/base/common/network'; -import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; -import { FileService } from 'vs/workbench/services/files/common/fileService'; -import { NullLogService } from 'vs/platform/log/common/log'; -import { DiskFileSystemProvider } from 'vs/workbench/services/files/node/diskFileSystemProvider'; -import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/node/environmentService'; -import { parseArgs } from 'vs/platform/environment/node/argv'; -import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; - -const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupfileservice'); -const backupHome = path.join(parentDir, 'Backups'); -const workspacesJsonPath = path.join(backupHome, 'workspaces.json'); - -const workspaceResource = Uri.file(platform.isWindows ? 'c:\\workspace' : '/workspace'); -const workspaceBackupPath = path.join(backupHome, hashPath(workspaceResource)); -const fooFile = Uri.file(platform.isWindows ? 'c:\\Foo' : '/Foo'); -const barFile = Uri.file(platform.isWindows ? 'c:\\Bar' : '/Bar'); -const untitledFile = Uri.from({ scheme: Schemas.untitled, path: 'Untitled-1' }); -const fooBackupPath = path.join(workspaceBackupPath, 'file', hashPath(fooFile)); -const barBackupPath = path.join(workspaceBackupPath, 'file', hashPath(barFile)); -const untitledBackupPath = path.join(workspaceBackupPath, 'untitled', hashPath(untitledFile)); - -class TestBackupEnvironmentService extends WorkbenchEnvironmentService { - - private config: IWindowConfiguration; - - constructor(workspaceBackupPath: string) { - super(parseArgs(process.argv) as IWindowConfiguration, process.execPath); - - this.config = Object.create(null); - this.config.backupPath = workspaceBackupPath; - } - - get configuration(): IWindowConfiguration { - return this.config; - } -} - -class TestBackupFileService extends BackupFileService { - constructor(workspace: Uri, backupHome: string, workspacesJsonPath: string) { - const fileService = new FileService(new NullLogService()); - fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); - const environmentService = new TestBackupEnvironmentService(workspaceBackupPath); - - super(environmentService, fileService); - } - - public toBackupResource(resource: Uri): Uri { - return super.toBackupResource(resource); - } -} - -suite('BackupFileService', () => { - let service: TestBackupFileService; - - setup(() => { - service = new TestBackupFileService(workspaceResource, backupHome, workspacesJsonPath); - - // Delete any existing backups completely and then re-create it. - return pfs.rimraf(backupHome, pfs.RimRafMode.MOVE).then(() => { - return pfs.mkdirp(backupHome).then(() => { - return pfs.writeFile(workspacesJsonPath, ''); - }); - }); - }); - - teardown(() => { - return pfs.rimraf(backupHome, pfs.RimRafMode.MOVE); - }); - - suite('hashPath', () => { - test('should correctly hash the path for untitled scheme URIs', () => { - const uri = Uri.from({ - scheme: 'untitled', - path: 'Untitled-1' - }); - const actual = hashPath(uri); - // If these hashes change people will lose their backed up files! - assert.equal(actual, '13264068d108c6901b3592ea654fcd57'); - assert.equal(actual, crypto.createHash('md5').update(uri.fsPath).digest('hex')); - }); - - test('should correctly hash the path for file scheme URIs', () => { - const uri = Uri.file('/foo'); - const actual = hashPath(uri); - // If these hashes change people will lose their backed up files! - if (platform.isWindows) { - assert.equal(actual, 'dec1a583f52468a020bd120c3f01d812'); - } else { - assert.equal(actual, '1effb2475fcfba4f9e8b8a1dbc8f3caf'); - } - assert.equal(actual, crypto.createHash('md5').update(uri.fsPath).digest('hex')); - }); - }); - - suite('getBackupResource', () => { - test('should get the correct backup path for text files', () => { - // Format should be: /// - const backupResource = fooFile; - const workspaceHash = hashPath(workspaceResource); - const filePathHash = hashPath(backupResource); - const expectedPath = Uri.file(path.join(backupHome, workspaceHash, 'file', filePathHash)).fsPath; - assert.equal(service.toBackupResource(backupResource).fsPath, expectedPath); - }); - - test('should get the correct backup path for untitled files', () => { - // Format should be: /// - const backupResource = Uri.from({ scheme: Schemas.untitled, path: 'Untitled-1' }); - const workspaceHash = hashPath(workspaceResource); - const filePathHash = hashPath(backupResource); - const expectedPath = Uri.file(path.join(backupHome, workspaceHash, 'untitled', filePathHash)).fsPath; - assert.equal(service.toBackupResource(backupResource).fsPath, expectedPath); - }); - }); - - suite('loadBackupResource', () => { - test('should return whether a backup resource exists', () => { - return pfs.mkdirp(path.dirname(fooBackupPath)).then(() => { - fs.writeFileSync(fooBackupPath, 'foo'); - service = new TestBackupFileService(workspaceResource, backupHome, workspacesJsonPath); - return service.loadBackupResource(fooFile).then(resource => { - assert.ok(resource); - assert.equal(path.basename(resource!.fsPath), path.basename(fooBackupPath)); - return service.hasBackups().then(hasBackups => { - assert.ok(hasBackups); - }); - }); - }); - }); - }); - - suite('backupResource', () => { - test('text file', function () { - return service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); - assert.equal(fs.existsSync(fooBackupPath), true); - assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\ntest`); - }); - }); - - test('untitled file', function () { - return service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); - assert.equal(fs.existsSync(untitledBackupPath), true); - assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\ntest`); - }); - }); - - test('text file (ITextSnapshot)', function () { - const model = TextModel.createFromString('test'); - - return service.backupResource(fooFile, model.createSnapshot()).then(() => { - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); - assert.equal(fs.existsSync(fooBackupPath), true); - assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\ntest`); - model.dispose(); - }); - }); - - test('untitled file (ITextSnapshot)', function () { - const model = TextModel.createFromString('test'); - - return service.backupResource(untitledFile, model.createSnapshot()).then(() => { - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); - assert.equal(fs.existsSync(untitledBackupPath), true); - assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\ntest`); - model.dispose(); - }); - }); - - test('text file (large file, ITextSnapshot)', function () { - const largeString = (new Array(10 * 1024)).join('Large String\n'); - const model = TextModel.createFromString(largeString); - - return service.backupResource(fooFile, model.createSnapshot()).then(() => { - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); - assert.equal(fs.existsSync(fooBackupPath), true); - assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\n${largeString}`); - model.dispose(); - }); - }); - - test('untitled file (large file, ITextSnapshot)', function () { - const largeString = (new Array(10 * 1024)).join('Large String\n'); - const model = TextModel.createFromString(largeString); - - return service.backupResource(untitledFile, model.createSnapshot()).then(() => { - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); - assert.equal(fs.existsSync(untitledBackupPath), true); - assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\n${largeString}`); - model.dispose(); - }); - }); - }); - - suite('discardResourceBackup', () => { - test('text file', function () { - return service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); - return service.discardResourceBackup(fooFile).then(() => { - assert.equal(fs.existsSync(fooBackupPath), false); - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 0); - }); - }); - }); - - test('untitled file', function () { - return service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); - return service.discardResourceBackup(untitledFile).then(() => { - assert.equal(fs.existsSync(untitledBackupPath), false); - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 0); - }); - }); - }); - }); - - suite('discardAllWorkspaceBackups', () => { - test('text file', function () { - return service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); - return service.backupResource(barFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 2); - return service.discardAllWorkspaceBackups().then(() => { - assert.equal(fs.existsSync(fooBackupPath), false); - assert.equal(fs.existsSync(barBackupPath), false); - assert.equal(fs.existsSync(path.join(workspaceBackupPath, 'file')), false); - }); - }); - }); - }); - - test('untitled file', function () { - return service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); - return service.discardAllWorkspaceBackups().then(() => { - assert.equal(fs.existsSync(untitledBackupPath), false); - assert.equal(fs.existsSync(path.join(workspaceBackupPath, 'untitled')), false); - }); - }); - }); - - test('should disable further backups', function () { - return service.discardAllWorkspaceBackups().then(() => { - return service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - assert.equal(fs.existsSync(workspaceBackupPath), false); - }); - }); - }); - }); - - suite('getWorkspaceFileBackups', () => { - test('("file") - text file', () => { - return service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - return service.getWorkspaceFileBackups().then(textFiles => { - assert.deepEqual(textFiles.map(f => f.fsPath), [fooFile.fsPath]); - return service.backupResource(barFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - return service.getWorkspaceFileBackups().then(textFiles => { - assert.deepEqual(textFiles.map(f => f.fsPath), [fooFile.fsPath, barFile.fsPath]); - }); - }); - }); - }); - }); - - test('("file") - untitled file', () => { - return service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - return service.getWorkspaceFileBackups().then(textFiles => { - assert.deepEqual(textFiles.map(f => f.fsPath), [untitledFile.fsPath]); - }); - }); - }); - - test('("untitled") - untitled file', () => { - return service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - return service.getWorkspaceFileBackups().then(textFiles => { - assert.deepEqual(textFiles.map(f => f.fsPath), ['Untitled-1']); - }); - }); - }); - }); - - test('resolveBackupContent', () => { - test('should restore the original contents (untitled file)', () => { - const contents = 'test\nand more stuff'; - service.backupResource(untitledFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - service.resolveBackupContent(service.toBackupResource(untitledFile)).then(factory => { - assert.equal(contents, snapshotToString(factory!.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true))); - }); - }); - }); - - test('should restore the original contents (text file)', () => { - const contents = [ - 'Lorem ipsum ', - 'dolor öäü sit amet ', - 'consectetur ', - 'adipiscing ßß elit', - ].join(''); - - service.backupResource(fooFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false)).then(() => { - service.resolveBackupContent(service.toBackupResource(untitledFile)).then(factory => { - assert.equal(contents, snapshotToString(factory!.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true))); - }); - }); - }); - }); -}); - -suite('BackupFilesModel', () => { - test('simple', () => { - const model = new BackupFilesModel(); - - const resource1 = Uri.file('test.html'); - - assert.equal(model.has(resource1), false); - - model.add(resource1); - - assert.equal(model.has(resource1), true); - assert.equal(model.has(resource1, 0), true); - assert.equal(model.has(resource1, 1), false); - - model.remove(resource1); - - assert.equal(model.has(resource1), false); - - model.add(resource1); - - assert.equal(model.has(resource1), true); - assert.equal(model.has(resource1, 0), true); - assert.equal(model.has(resource1, 1), false); - - model.clear(); - - assert.equal(model.has(resource1), false); - - model.add(resource1, 1); - - assert.equal(model.has(resource1), true); - assert.equal(model.has(resource1, 0), false); - assert.equal(model.has(resource1, 1), true); - - const resource2 = Uri.file('test1.html'); - const resource3 = Uri.file('test2.html'); - const resource4 = Uri.file('test3.html'); - - model.add(resource2); - model.add(resource3); - model.add(resource4); - - assert.equal(model.has(resource1), true); - assert.equal(model.has(resource2), true); - assert.equal(model.has(resource3), true); - assert.equal(model.has(resource4), true); - }); - - test('resolve', () => { - return pfs.mkdirp(path.dirname(fooBackupPath)).then(() => { - fs.writeFileSync(fooBackupPath, 'foo'); - - const model = new BackupFilesModel(); - - return model.resolve(workspaceBackupPath).then(model => { - assert.equal(model.has(Uri.file(fooBackupPath)), true); - }); - }); - }); - - test('get', () => { - const model = new BackupFilesModel(); - - assert.deepEqual(model.get(), []); - - const file1 = Uri.file('/root/file/foo.html'); - const file2 = Uri.file('/root/file/bar.html'); - const untitled = Uri.file('/root/untitled/bar.html'); - - model.add(file1); - model.add(file2); - model.add(untitled); - - assert.deepEqual(model.get().map(f => f.fsPath), [file1.fsPath, file2.fsPath, untitled.fsPath]); - }); -}); diff --git a/src/vs/workbench/services/backup/test/node/backupFileService.test.ts b/src/vs/workbench/services/backup/test/node/backupFileService.test.ts new file mode 100644 index 00000000000..52a5bc95d1c --- /dev/null +++ b/src/vs/workbench/services/backup/test/node/backupFileService.test.ts @@ -0,0 +1,579 @@ +/*--------------------------------------------------------------------------------------------- + * 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 platform from 'vs/base/common/platform'; +import * as crypto from 'crypto'; +import * as os from 'os'; +import * as fs from 'fs'; +import * as path from 'vs/base/common/path'; +import * as pfs from 'vs/base/node/pfs'; +import { URI } from 'vs/base/common/uri'; +import { BackupFileService, BackupFilesModel, hashPath } from 'vs/workbench/services/backup/node/backupFileService'; +import { TextModel, createTextBufferFactory } from 'vs/editor/common/model/textModel'; +import { getRandomTestPath } from 'vs/base/test/node/testUtils'; +import { DefaultEndOfLine } from 'vs/editor/common/model'; +import { Schemas } from 'vs/base/common/network'; +import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; +import { FileService } from 'vs/workbench/services/files/common/fileService'; +import { NullLogService } from 'vs/platform/log/common/log'; +import { DiskFileSystemProvider } from 'vs/workbench/services/files/node/diskFileSystemProvider'; +import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/node/environmentService'; +import { parseArgs } from 'vs/platform/environment/node/argv'; +import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; +import { IFileService } from 'vs/platform/files/common/files'; + +const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupfileservice'); +const backupHome = path.join(parentDir, 'Backups'); +const workspacesJsonPath = path.join(backupHome, 'workspaces.json'); + +const workspaceResource = URI.file(platform.isWindows ? 'c:\\workspace' : '/workspace'); +const workspaceBackupPath = path.join(backupHome, hashPath(workspaceResource)); +const fooFile = URI.file(platform.isWindows ? 'c:\\Foo' : '/Foo'); +const customFile = URI.parse('customScheme://some/path'); +const customFileWithFragment = URI.parse('customScheme2://some/path#fragment'); +const barFile = URI.file(platform.isWindows ? 'c:\\Bar' : '/Bar'); +const fooBarFile = URI.file(platform.isWindows ? 'c:\\Foo Bar' : '/Foo Bar'); +const untitledFile = URI.from({ scheme: Schemas.untitled, path: 'Untitled-1' }); +const fooBackupPath = path.join(workspaceBackupPath, 'file', hashPath(fooFile)); +const barBackupPath = path.join(workspaceBackupPath, 'file', hashPath(barFile)); +const untitledBackupPath = path.join(workspaceBackupPath, 'untitled', hashPath(untitledFile)); + +class TestBackupEnvironmentService extends WorkbenchEnvironmentService { + + private config: IWindowConfiguration; + + constructor(workspaceBackupPath: string) { + super(parseArgs(process.argv) as IWindowConfiguration, process.execPath); + + this.config = Object.create(null); + this.config.backupPath = workspaceBackupPath; + } + + get configuration(): IWindowConfiguration { + return this.config; + } +} + +class TestBackupFileService extends BackupFileService { + + readonly fileService: IFileService; + + constructor(workspace: URI, backupHome: string, workspacesJsonPath: string) { + const fileService = new FileService(new NullLogService()); + fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); + const environmentService = new TestBackupEnvironmentService(workspaceBackupPath); + + super(environmentService, fileService); + + this.fileService = fileService; + } + + toBackupResource(resource: URI): URI { + return super.toBackupResource(resource); + } +} + +suite('BackupFileService', () => { + let service: TestBackupFileService; + + setup(async () => { + service = new TestBackupFileService(workspaceResource, backupHome, workspacesJsonPath); + + // Delete any existing backups completely and then re-create it. + await pfs.rimraf(backupHome, pfs.RimRafMode.MOVE); + await pfs.mkdirp(backupHome); + + return pfs.writeFile(workspacesJsonPath, ''); + }); + + teardown(() => { + return pfs.rimraf(backupHome, pfs.RimRafMode.MOVE); + }); + + suite('hashPath', () => { + test('should correctly hash the path for untitled scheme URIs', () => { + const uri = URI.from({ + scheme: 'untitled', + path: 'Untitled-1' + }); + const actual = hashPath(uri); + // If these hashes change people will lose their backed up files! + assert.equal(actual, '13264068d108c6901b3592ea654fcd57'); + assert.equal(actual, crypto.createHash('md5').update(uri.fsPath).digest('hex')); + }); + + test('should correctly hash the path for file scheme URIs', () => { + const uri = URI.file('/foo'); + const actual = hashPath(uri); + // If these hashes change people will lose their backed up files! + if (platform.isWindows) { + assert.equal(actual, 'dec1a583f52468a020bd120c3f01d812'); + } else { + assert.equal(actual, '1effb2475fcfba4f9e8b8a1dbc8f3caf'); + } + assert.equal(actual, crypto.createHash('md5').update(uri.fsPath).digest('hex')); + }); + }); + + suite('getBackupResource', () => { + test('should get the correct backup path for text files', () => { + // Format should be: /// + const backupResource = fooFile; + const workspaceHash = hashPath(workspaceResource); + const filePathHash = hashPath(backupResource); + const expectedPath = URI.file(path.join(backupHome, workspaceHash, 'file', filePathHash)).fsPath; + assert.equal(service.toBackupResource(backupResource).fsPath, expectedPath); + }); + + test('should get the correct backup path for untitled files', () => { + // Format should be: /// + const backupResource = URI.from({ scheme: Schemas.untitled, path: 'Untitled-1' }); + const workspaceHash = hashPath(workspaceResource); + const filePathHash = hashPath(backupResource); + const expectedPath = URI.file(path.join(backupHome, workspaceHash, 'untitled', filePathHash)).fsPath; + assert.equal(service.toBackupResource(backupResource).fsPath, expectedPath); + }); + }); + + suite('loadBackupResource', () => { + test('should return whether a backup resource exists', async () => { + await pfs.mkdirp(path.dirname(fooBackupPath)); + fs.writeFileSync(fooBackupPath, 'foo'); + service = new TestBackupFileService(workspaceResource, backupHome, workspacesJsonPath); + const resource = await service.loadBackupResource(fooFile); + assert.ok(resource); + assert.equal(path.basename(resource!.fsPath), path.basename(fooBackupPath)); + const hasBackups = await service.hasBackups(); + assert.ok(hasBackups); + }); + }); + + suite('backupResource', () => { + test('text file', async () => { + await service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); + assert.equal(fs.existsSync(fooBackupPath), true); + assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\ntest`); + }); + + test('text file (with meta)', async () => { + await service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false), undefined, { etag: '678', orphaned: true }); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); + assert.equal(fs.existsSync(fooBackupPath), true); + assert.equal(fs.readFileSync(fooBackupPath).toString(), `${fooFile.toString()} {"etag":"678","orphaned":true}\ntest`); + }); + + test('untitled file', async () => { + await service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); + assert.equal(fs.existsSync(untitledBackupPath), true); + assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\ntest`); + }); + + test('text file (ITextSnapshot)', async () => { + const model = TextModel.createFromString('test'); + + await service.backupResource(fooFile, model.createSnapshot()); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); + assert.equal(fs.existsSync(fooBackupPath), true); + assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\ntest`); + model.dispose(); + }); + + test('untitled file (ITextSnapshot)', async () => { + const model = TextModel.createFromString('test'); + + await service.backupResource(untitledFile, model.createSnapshot()); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); + assert.equal(fs.existsSync(untitledBackupPath), true); + assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\ntest`); + model.dispose(); + }); + + test('text file (large file, ITextSnapshot)', async () => { + const largeString = (new Array(10 * 1024)).join('Large String\n'); + const model = TextModel.createFromString(largeString); + + await service.backupResource(fooFile, model.createSnapshot()); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); + assert.equal(fs.existsSync(fooBackupPath), true); + assert.equal(fs.readFileSync(fooBackupPath), `${fooFile.toString()}\n${largeString}`); + model.dispose(); + }); + + test('untitled file (large file, ITextSnapshot)', async () => { + const largeString = (new Array(10 * 1024)).join('Large String\n'); + const model = TextModel.createFromString(largeString); + + await service.backupResource(untitledFile, model.createSnapshot()); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); + assert.equal(fs.existsSync(untitledBackupPath), true); + assert.equal(fs.readFileSync(untitledBackupPath), `${untitledFile.toString()}\n${largeString}`); + model.dispose(); + }); + }); + + suite('discardResourceBackup', () => { + test('text file', async () => { + await service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); + await service.discardResourceBackup(fooFile); + assert.equal(fs.existsSync(fooBackupPath), false); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 0); + }); + + test('untitled file', async () => { + await service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); + await service.discardResourceBackup(untitledFile); + assert.equal(fs.existsSync(untitledBackupPath), false); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 0); + }); + }); + + suite('discardAllWorkspaceBackups', () => { + test('text file', async () => { + await service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 1); + await service.backupResource(barFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 2); + await service.discardAllWorkspaceBackups(); + assert.equal(fs.existsSync(fooBackupPath), false); + assert.equal(fs.existsSync(barBackupPath), false); + assert.equal(fs.existsSync(path.join(workspaceBackupPath, 'file')), false); + }); + + test('untitled file', async () => { + await service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'untitled')).length, 1); + await service.discardAllWorkspaceBackups(); + assert.equal(fs.existsSync(untitledBackupPath), false); + assert.equal(fs.existsSync(path.join(workspaceBackupPath, 'untitled')), false); + }); + + test('should disable further backups', async () => { + await service.discardAllWorkspaceBackups(); + await service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + assert.equal(fs.existsSync(workspaceBackupPath), false); + }); + }); + + suite('getWorkspaceFileBackups', () => { + test('("file") - text file', async () => { + await service.backupResource(fooFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + const textFiles = await service.getWorkspaceFileBackups(); + assert.deepEqual(textFiles.map(f => f.fsPath), [fooFile.fsPath]); + await service.backupResource(barFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + const textFiles_1 = await service.getWorkspaceFileBackups(); + assert.deepEqual(textFiles_1.map(f => f.fsPath), [fooFile.fsPath, barFile.fsPath]); + }); + + test('("file") - untitled file', async () => { + await service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + const textFiles = await service.getWorkspaceFileBackups(); + assert.deepEqual(textFiles.map(f => f.fsPath), [untitledFile.fsPath]); + }); + + test('("untitled") - untitled file', async () => { + await service.backupResource(untitledFile, createTextBufferFactory('test').create(DefaultEndOfLine.LF).createSnapshot(false)); + const textFiles = await service.getWorkspaceFileBackups(); + assert.deepEqual(textFiles.map(f => f.fsPath), ['Untitled-1']); + }); + }); + + suite('resolveBackupContent', () => { + + interface IBackupTestMetaData { + mtime?: number; + size?: number; + etag?: string; + orphaned?: boolean; + } + + test('should restore the original contents (untitled file)', async () => { + const contents = 'test\nand more stuff'; + + await testResolveBackup(untitledFile, contents); + }); + + test('should restore the original contents (untitled file with metadata)', async () => { + const contents = 'test\nand more stuff'; + + const meta = { + etag: 'the Etag', + size: 666, + mtime: Date.now(), + orphaned: true + }; + + await testResolveBackup(untitledFile, contents, meta); + }); + + test('should restore the original contents (text file)', async () => { + const contents = [ + 'Lorem ipsum ', + 'dolor öäü sit amet ', + 'consectetur ', + 'adipiscing ßß elit' + ].join(''); + + await testResolveBackup(fooFile, contents); + }); + + test('should restore the original contents (text file - custom scheme)', async () => { + const contents = [ + 'Lorem ipsum ', + 'dolor öäü sit amet ', + 'consectetur ', + 'adipiscing ßß elit' + ].join(''); + + await testResolveBackup(customFile, contents); + }); + + test('should restore the original contents (text file with metadata)', async () => { + const contents = [ + 'Lorem ipsum ', + 'dolor öäü sit amet ', + 'adipiscing ßß elit', + 'consectetur ' + ].join(''); + + const meta = { + etag: 'theEtag', + size: 888, + mtime: Date.now(), + orphaned: false + }; + + await testResolveBackup(fooFile, contents, meta); + }); + + test('should restore the original contents (text file with metadata changed once)', async () => { + const contents = [ + 'Lorem ipsum ', + 'dolor öäü sit amet ', + 'adipiscing ßß elit', + 'consectetur ' + ].join(''); + + const meta = { + etag: 'theEtag', + size: 888, + mtime: Date.now(), + orphaned: false + }; + + await testResolveBackup(fooFile, contents, meta); + + // Change meta and test again + meta.size = 999; + await testResolveBackup(fooFile, contents, meta); + }); + + test('should restore the original contents (text file with broken metadata)', async () => { + const contents = [ + 'Lorem ipsum ', + 'dolor öäü sit amet ', + 'adipiscing ßß elit', + 'consectetur ' + ].join(''); + + const meta = { + etag: 'theEtag', + size: 888, + mtime: Date.now(), + orphaned: false + }; + + await service.backupResource(fooFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false), 1, meta); + + assert.ok(await service.loadBackupResource(fooFile)); + + const fileContents = fs.readFileSync(fooBackupPath).toString(); + assert.equal(fileContents.indexOf(fooFile.toString()), 0); + + const metaIndex = fileContents.indexOf('{'); + const newFileContents = fileContents.substring(0, metaIndex) + '{{' + fileContents.substr(metaIndex); + fs.writeFileSync(fooBackupPath, newFileContents); + + const backup = await service.resolveBackupContent(service.toBackupResource(fooFile)); + assert.equal(contents, snapshotToString(backup.value.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true))); + assert.ok(!backup.meta); + }); + + test('should restore the original contents (text file with metadata and fragment URI)', async () => { + const contents = [ + 'Lorem ipsum ', + 'dolor öäü sit amet ', + 'adipiscing ßß elit', + 'consectetur ' + ].join(''); + + const meta = { + etag: 'theEtag', + size: 888, + mtime: Date.now(), + orphaned: false + }; + + await testResolveBackup(customFileWithFragment, contents, meta); + }); + + test('should restore the original contents (text file with space in name with metadata)', async () => { + const contents = [ + 'Lorem ipsum ', + 'dolor öäü sit amet ', + 'adipiscing ßß elit', + 'consectetur ' + ].join(''); + + const meta = { + etag: 'theEtag', + size: 888, + mtime: Date.now(), + orphaned: false + }; + + await testResolveBackup(fooBarFile, contents, meta); + }); + + test('should restore the original contents (text file with too large metadata to persist)', async () => { + const contents = [ + 'Lorem ipsum ', + 'dolor öäü sit amet ', + 'adipiscing ßß elit', + 'consectetur ' + ].join(''); + + const meta = { + etag: (new Array(100 * 1024)).join('Large String'), + size: 888, + mtime: Date.now(), + orphaned: false + }; + + await testResolveBackup(fooBarFile, contents, meta, null); + }); + + async function testResolveBackup(resource: URI, contents: string, meta?: IBackupTestMetaData, expectedMeta?: IBackupTestMetaData | null) { + if (typeof expectedMeta === 'undefined') { + expectedMeta = meta; + } + + await service.backupResource(resource, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false), 1, meta); + + assert.ok(await service.loadBackupResource(resource)); + + const backup = await service.resolveBackupContent(service.toBackupResource(resource)); + assert.equal(contents, snapshotToString(backup.value.create(platform.isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).createSnapshot(true))); + + if (expectedMeta) { + assert.equal(backup.meta!.etag, expectedMeta.etag); + assert.equal(backup.meta!.size, expectedMeta.size); + assert.equal(backup.meta!.mtime, expectedMeta.mtime); + assert.equal(backup.meta!.orphaned, expectedMeta.orphaned); + } else { + assert.ok(!backup.meta); + } + } + }); +}); + +suite('BackupFilesModel', () => { + + let service: TestBackupFileService; + + setup(async () => { + service = new TestBackupFileService(workspaceResource, backupHome, workspacesJsonPath); + + // Delete any existing backups completely and then re-create it. + await pfs.rimraf(backupHome, pfs.RimRafMode.MOVE); + await pfs.mkdirp(backupHome); + + return pfs.writeFile(workspacesJsonPath, ''); + }); + + teardown(() => { + return pfs.rimraf(backupHome, pfs.RimRafMode.MOVE); + }); + + test('simple', () => { + const model = new BackupFilesModel(service.fileService); + + const resource1 = URI.file('test.html'); + + assert.equal(model.has(resource1), false); + + model.add(resource1); + + assert.equal(model.has(resource1), true); + assert.equal(model.has(resource1, 0), true); + assert.equal(model.has(resource1, 1), false); + assert.equal(model.has(resource1, 1, { foo: 'bar' }), false); + + model.remove(resource1); + + assert.equal(model.has(resource1), false); + + model.add(resource1); + + assert.equal(model.has(resource1), true); + assert.equal(model.has(resource1, 0), true); + assert.equal(model.has(resource1, 1), false); + + model.clear(); + + assert.equal(model.has(resource1), false); + + model.add(resource1, 1); + + assert.equal(model.has(resource1), true); + assert.equal(model.has(resource1, 0), false); + assert.equal(model.has(resource1, 1), true); + + const resource2 = URI.file('test1.html'); + const resource3 = URI.file('test2.html'); + const resource4 = URI.file('test3.html'); + + model.add(resource2); + model.add(resource3); + model.add(resource4, undefined, { foo: 'bar' }); + + assert.equal(model.has(resource1), true); + assert.equal(model.has(resource2), true); + assert.equal(model.has(resource3), true); + + assert.equal(model.has(resource4), true); + assert.equal(model.has(resource4, undefined, { foo: 'bar' }), true); + assert.equal(model.has(resource4, undefined, { bar: 'foo' }), false); + }); + + test('resolve', async () => { + await pfs.mkdirp(path.dirname(fooBackupPath)); + fs.writeFileSync(fooBackupPath, 'foo'); + const model = new BackupFilesModel(service.fileService); + + const resolvedModel = await model.resolve(URI.file(workspaceBackupPath)); + assert.equal(resolvedModel.has(URI.file(fooBackupPath)), true); + }); + + test('get', () => { + const model = new BackupFilesModel(service.fileService); + + assert.deepEqual(model.get(), []); + + const file1 = URI.file('/root/file/foo.html'); + const file2 = URI.file('/root/file/bar.html'); + const untitled = URI.file('/root/untitled/bar.html'); + + model.add(file1); + model.add(file2); + model.add(untitled); + + assert.deepEqual(model.get().map(f => f.fsPath), [file1.fsPath, file2.fsPath, untitled.fsPath]); + }); +}); diff --git a/src/vs/workbench/services/broadcast/common/broadcast.ts b/src/vs/workbench/services/broadcast/common/broadcast.ts deleted file mode 100644 index fc9e13031b6..00000000000 --- a/src/vs/workbench/services/broadcast/common/broadcast.ts +++ /dev/null @@ -1,30 +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 { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { Event } from 'vs/base/common/event'; - -export const IBroadcastService = createDecorator('broadcastService'); - -export interface IBroadcast { - channel: string; - payload: any; -} - -export interface IBroadcastService { - _serviceBrand: any; - - onBroadcast: Event; - - broadcast(b: IBroadcast): void; -} - -export class NullBroadcastService implements IBroadcastService { - _serviceBrand: any; - onBroadcast: Event = Event.None; - broadcast(_b: IBroadcast): void { - - } -} diff --git a/src/vs/workbench/services/broadcast/electron-browser/broadcastService.ts b/src/vs/workbench/services/broadcast/electron-browser/broadcastService.ts deleted file mode 100644 index c4a5f3d79f7..00000000000 --- a/src/vs/workbench/services/broadcast/electron-browser/broadcastService.ts +++ /dev/null @@ -1,51 +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 { Event, Emitter } from 'vs/base/common/event'; -import { ipcRenderer as ipc } from 'electron'; -import { ILogService } from 'vs/platform/log/common/log'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IWindowService } from 'vs/platform/windows/common/windows'; -import { IBroadcastService, IBroadcast } from 'vs/workbench/services/broadcast/common/broadcast'; - -class BroadcastService extends Disposable implements IBroadcastService { - _serviceBrand: any; - - private readonly _onBroadcast: Emitter = this._register(new Emitter()); - get onBroadcast(): Event { return this._onBroadcast.event; } - - private windowId: number; - - constructor( - @IWindowService readonly windowService: IWindowService, - @ILogService private readonly logService: ILogService - ) { - super(); - - this.windowId = windowService.windowId; - - this.registerListeners(); - } - - private registerListeners(): void { - ipc.on('vscode:broadcast', (event: unknown, b: IBroadcast) => { - this.logService.trace(`Received broadcast from main in window ${this.windowId}: `, b); - - this._onBroadcast.fire(b); - }); - } - - broadcast(b: IBroadcast): void { - this.logService.trace(`Sending broadcast to main from window ${this.windowId}: `, b); - - ipc.send('vscode:broadcast', this.windowId, { - channel: b.channel, - payload: b.payload - }); - } -} - -registerSingleton(IBroadcastService, BroadcastService, true); diff --git a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts index 079e00df824..f698782a600 100644 --- a/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/browser/bulkEditService.ts @@ -18,7 +18,7 @@ import { localize } from 'vs/nls'; import { IFileService, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; -import { emptyProgressRunner, IProgress, IProgressRunner } from 'vs/platform/progress/common/progress'; +import { IProgress, IProgressStep, emptyProgress } from 'vs/platform/progress/common/progress'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -231,11 +231,11 @@ export class BulkEdit { private _edits: Edit[] = []; private _editor: ICodeEditor | undefined; - private _progress?: IProgressRunner; + private _progress: IProgress; constructor( editor: ICodeEditor | undefined, - progress: IProgressRunner | undefined, + progress: IProgress | undefined, @ILogService private readonly _logService: ILogService, @ITextModelService private readonly _textModelService: ITextModelService, @IFileService private readonly _fileService: IFileService, @@ -244,7 +244,7 @@ export class BulkEdit { @IConfigurationService private readonly _configurationService: IConfigurationService ) { this._editor = editor; - this._progress = progress || emptyProgressRunner; + this._progress = progress || emptyProgress; } add(edits: Edit[] | Edit): void { @@ -294,10 +294,9 @@ export class BulkEdit { // define total work and progress callback // for child operations - if (this._progress) { - this._progress.total(total); - } - let progress: IProgress = { report: _ => this._progress && this._progress.worked(1) }; + this._progress.report({ total }); + + let progress: IProgress = { report: _ => this._progress.report({ increment: 1 }) }; // do it. for (const group of groups) { 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 e32440778d7..1500f8566ad 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, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { CommandService } from 'vs/workbench/services/commands/common/commandService'; import { NullExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -103,7 +103,7 @@ suite('CommandService', function () { test('Stop waiting for * extensions to activate when trigger is satisfied #62457', function () { let callCounter = 0; - let dispoables: IDisposable[] = []; + const dispoables = new DisposableStore(); let events: string[] = []; let service = new CommandService(new InstantiationService(), new class extends NullExtensionService { @@ -118,7 +118,7 @@ suite('CommandService', function () { let reg = CommandsRegistry.registerCommand(event.substr('onCommand:'.length), () => { callCounter += 1; }); - dispoables.push(reg); + dispoables.add(reg); resolve(); }, 0); }); @@ -131,14 +131,15 @@ suite('CommandService', function () { return service.executeCommand('farboo').then(() => { assert.equal(callCounter, 1); assert.deepEqual(events.sort(), ['*', 'onCommand:farboo'].sort()); - dispose(dispoables); + }).finally(() => { + dispoables.dispose(); }); }); test('issue #71471: wait for onCommand activation even if a command is registered', () => { let expectedOrder: string[] = ['registering command', 'resolving activation event', 'executing command']; let actualOrder: string[] = []; - let disposables: IDisposable[] = []; + const disposables = new DisposableStore(); let service = new CommandService(new InstantiationService(), new class extends NullExtensionService { activateByEvent(event: string): Promise { @@ -153,7 +154,7 @@ suite('CommandService', function () { let reg = CommandsRegistry.registerCommand(event.substr('onCommand:'.length), () => { actualOrder.push('executing command'); }); - disposables.push(reg); + disposables.add(reg); setTimeout(() => { // Resolve the activation event after some more time @@ -170,7 +171,8 @@ suite('CommandService', function () { return service.executeCommand('farboo2').then(() => { assert.deepEqual(actualOrder, expectedOrder); - dispose(disposables); + }).finally(() => { + disposables.dispose(); }); }); }); diff --git a/src/vs/workbench/services/configuration/browser/configuration.ts b/src/vs/workbench/services/configuration/browser/configuration.ts index 1c107e7d785..a2f76032809 100644 --- a/src/vs/workbench/services/configuration/browser/configuration.ts +++ b/src/vs/workbench/services/configuration/browser/configuration.ts @@ -12,7 +12,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files'; import { ConfigurationModel, ConfigurationModelParser } from 'vs/platform/configuration/common/configurationModels'; import { WorkspaceConfigurationModelParser, StandaloneConfigurationModelParser } from 'vs/workbench/services/configuration/common/configurationModels'; -import { FOLDER_SETTINGS_PATH, TASKS_CONFIGURATION_KEY, FOLDER_SETTINGS_NAME, LAUNCH_CONFIGURATION_KEY, IConfigurationCache, ConfigurationKey, IConfigurationFileService, MACHINE_SCOPES, FOLDER_SCOPES, WORKSPACE_SCOPES } from 'vs/workbench/services/configuration/common/configuration'; +import { FOLDER_SETTINGS_PATH, TASKS_CONFIGURATION_KEY, FOLDER_SETTINGS_NAME, LAUNCH_CONFIGURATION_KEY, IConfigurationCache, ConfigurationKey, REMOTE_MACHINE_SCOPES, FOLDER_SCOPES, WORKSPACE_SCOPES, ConfigurationFileService } from 'vs/workbench/services/configuration/common/configuration'; import { IStoredWorkspaceFolder, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { JSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditingService'; import { WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; @@ -20,15 +20,16 @@ import { ConfigurationScope } from 'vs/platform/configuration/common/configurati import { extname, join } from 'vs/base/common/path'; import { equals } from 'vs/base/common/objects'; import { Schemas } from 'vs/base/common/network'; -import { IConfigurationModel, compare } from 'vs/platform/configuration/common/configuration'; -import { createSHA1 } from 'vs/base/browser/hash'; +import { IConfigurationModel } from 'vs/platform/configuration/common/configuration'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { hash } from 'vs/base/common/hash'; export class RemoteUserConfiguration extends Disposable { private readonly _cachedConfiguration: CachedUserConfiguration; - private readonly _configurationFileService: IConfigurationFileService; + private readonly _configurationFileService: ConfigurationFileService; private _userConfiguration: UserConfiguration | CachedUserConfiguration; + private _userConfigurationInitializationPromise: Promise | null = null; private readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); public readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; @@ -36,7 +37,7 @@ export class RemoteUserConfiguration extends Disposable { constructor( remoteAuthority: string, configurationCache: IConfigurationCache, - configurationFileService: IConfigurationFileService, + configurationFileService: ConfigurationFileService, remoteAgentService: IRemoteAgentService ) { super(); @@ -44,9 +45,10 @@ export class RemoteUserConfiguration extends Disposable { this._userConfiguration = this._cachedConfiguration = new CachedUserConfiguration(remoteAuthority, configurationCache); remoteAgentService.getEnvironment().then(async environment => { if (environment) { - const userConfiguration = this._register(new UserConfiguration(environment.settingsPath, MACHINE_SCOPES, this._configurationFileService)); + const userConfiguration = this._register(new UserConfiguration(environment.settingsPath, REMOTE_MACHINE_SCOPES, this._configurationFileService)); this._register(userConfiguration.onDidChangeConfiguration(configurationModel => this.onDidUserConfigurationChange(configurationModel))); - const configurationModel = await userConfiguration.initialize(); + this._userConfigurationInitializationPromise = userConfiguration.initialize(); + const configurationModel = await this._userConfigurationInitializationPromise; this._userConfiguration.dispose(); this._userConfiguration = userConfiguration; this.onDidUserConfigurationChange(configurationModel); @@ -54,8 +56,20 @@ export class RemoteUserConfiguration extends Disposable { }); } - initialize(): Promise { - return this._userConfiguration.initialize(); + async initialize(): Promise { + if (this._userConfiguration instanceof UserConfiguration) { + return this._userConfiguration.initialize(); + } + + // Initialize cached configuration + let configurationModel = await this._userConfiguration.initialize(); + if (this._userConfigurationInitializationPromise) { + // Use user configuration + configurationModel = await this._userConfigurationInitializationPromise; + this._userConfigurationInitializationPromise = null; + } + + return configurationModel; } reload(): Promise { @@ -89,7 +103,7 @@ export class UserConfiguration extends Disposable { constructor( private readonly configurationResource: URI, private readonly scopes: ConfigurationScope[] | undefined, - private readonly configurationFileService: IConfigurationFileService + private readonly configurationFileService: ConfigurationFileService ) { super(); @@ -124,11 +138,7 @@ export class UserConfiguration extends Disposable { async initialize(): Promise { const exists = await this.configurationFileService.exists(this.configurationResource); this.onResourceExists(exists); - const configuraitonModel = await this.reload(); - if (!this.configurationFileService.isWatching) { - this.configurationFileService.whenWatchingStarted.then(() => this.onWatchStarted(configuraitonModel)); - } - return configuraitonModel; + return this.reload(); } async reload(): Promise { @@ -146,14 +156,6 @@ export class UserConfiguration extends Disposable { return this.parser.configurationModel; } - private async onWatchStarted(currentModel: ConfigurationModel): Promise { - const configuraitonModel = await this.reload(); - const { added, removed, updated } = compare(currentModel, configuraitonModel); - if (added.length || removed.length || updated.length) { - this._onDidChangeConfiguration.fire(configuraitonModel); - } - } - private async handleFileEvents(event: FileChangesEvent): Promise { const events = event.changes; @@ -238,7 +240,7 @@ class CachedUserConfiguration extends Disposable { export class WorkspaceConfiguration extends Disposable { - private readonly _configurationFileService: IConfigurationFileService; + private readonly _configurationFileService: ConfigurationFileService; private readonly _cachedConfiguration: CachedWorkspaceConfiguration; private _workspaceConfiguration: IWorkspaceConfiguration; private _workspaceConfigurationChangeDisposable: IDisposable = Disposable.None; @@ -252,7 +254,7 @@ export class WorkspaceConfiguration extends Disposable { constructor( configurationCache: IConfigurationCache, - configurationFileService: IConfigurationFileService + configurationFileService: ConfigurationFileService ) { super(); this._configurationFileService = configurationFileService; @@ -355,7 +357,7 @@ class FileServiceBasedWorkspaceConfiguration extends Disposable implements IWork protected readonly _onDidChange: Emitter = this._register(new Emitter()); readonly onDidChange: Event = this._onDidChange.event; - constructor(private configurationFileService: IConfigurationFileService) { + constructor(private configurationFileService: ConfigurationFileService) { super(); this.workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser(''); @@ -364,10 +366,6 @@ class FileServiceBasedWorkspaceConfiguration extends Disposable implements IWork this._register(configurationFileService.onFileChanges(e => this.handleWorkspaceFileEvents(e))); this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this._onDidChange.fire(), 50)); this.workspaceConfigWatcher = this._register(this.watchWorkspaceConfigurationFile()); - - if (!this.configurationFileService.isWatching) { - this.configurationFileService.whenWatchingStarted.then(() => this.onWatchStarted()); - } } get workspaceIdentifier(): IWorkspaceIdentifier | null { @@ -412,18 +410,6 @@ class FileServiceBasedWorkspaceConfiguration extends Disposable implements IWork return this.getWorkspaceSettings(); } - private async onWatchStarted(): Promise { - if (this.workspaceIdentifier) { - const currentModel = this.getConfigurationModel(); - await this.load(this.workspaceIdentifier); - const newModel = this.getConfigurationModel(); - const { added, removed, updated } = compare(currentModel, newModel); - if (added.length || removed.length || updated.length) { - this._onDidChange.fire(); - } - } - } - private consolidate(): void { this.workspaceSettings = this.workspaceConfigurationModelParser.settingsModel.merge(this.workspaceConfigurationModelParser.launchModel); } @@ -532,7 +518,7 @@ class FileServiceBasedFolderConfiguration extends Disposable implements IFolderC protected readonly _onDidChange: Emitter = this._register(new Emitter()); readonly onDidChange: Event = this._onDidChange.event; - constructor(protected readonly configurationFolder: URI, workbenchState: WorkbenchState, private configurationFileService: IConfigurationFileService) { + constructor(protected readonly configurationFolder: URI, workbenchState: WorkbenchState, private configurationFileService: ConfigurationFileService) { super(); this.configurationNames = [FOLDER_SETTINGS_NAME /*First one should be settings */, TASKS_CONFIGURATION_KEY, LAUNCH_CONFIGURATION_KEY]; @@ -543,9 +529,6 @@ class FileServiceBasedFolderConfiguration extends Disposable implements IFolderC this.changeEventTriggerScheduler = this._register(new RunOnceScheduler(() => this._onDidChange.fire(), 50)); this._register(configurationFileService.onFileChanges(e => this.handleWorkspaceFileEvents(e))); - if (!this.configurationFileService.isWatching) { - this.configurationFileService.whenWatchingStarted.then(() => this.onWatchStarted()); - } } async loadConfiguration(): Promise { @@ -597,15 +580,6 @@ class FileServiceBasedFolderConfiguration extends Disposable implements IFolderC this._cache = this._folderSettingsModelParser.configurationModel.merge(...this._standAloneConfigurations); } - private async onWatchStarted(): Promise { - const currentModel = this._cache; - const newModel = await this.loadConfiguration(); - const { added, removed, updated } = compare(currentModel, newModel); - if (added.length || removed.length || updated.length) { - this._onDidChange.fire(); - } - } - private handleWorkspaceFileEvents(event: FileChangesEvent): void { const events = event.changes; let affectedByChanges = false; @@ -658,7 +632,7 @@ class CachedFolderConfiguration extends Disposable implements IFolderConfigurati readonly onDidChange: Event = this._onDidChange.event; private configurationModel: ConfigurationModel; - private readonly key: Thenable; + private readonly key: ConfigurationKey; constructor( folder: URI, @@ -666,14 +640,13 @@ class CachedFolderConfiguration extends Disposable implements IFolderConfigurati private readonly configurationCache: IConfigurationCache ) { super(); - this.key = createSHA1(join(folder.path, configFolderRelativePath)).then(key => ({ type: 'folder', key })); + this.key = { type: 'folder', key: hash(join(folder.path, configFolderRelativePath)).toString(16) }; this.configurationModel = new ConfigurationModel(); } async loadConfiguration(): Promise { try { - const key = await this.key; - const contents = await this.configurationCache.read(key); + const contents = await this.configurationCache.read(this.key); const parsed: IConfigurationModel = JSON.parse(contents.toString()); this.configurationModel = new ConfigurationModel(parsed.contents, parsed.keys, parsed.overrides); } catch (e) { @@ -682,11 +655,10 @@ class CachedFolderConfiguration extends Disposable implements IFolderConfigurati } async updateConfiguration(configurationModel: ConfigurationModel): Promise { - const key = await this.key; if (configurationModel.keys.length) { - await this.configurationCache.write(key, JSON.stringify(configurationModel.toJSON())); + await this.configurationCache.write(this.key, JSON.stringify(configurationModel.toJSON())); } else { - await this.configurationCache.remove(key); + await this.configurationCache.remove(this.key); } } @@ -713,7 +685,7 @@ export class FolderConfiguration extends Disposable implements IFolderConfigurat readonly workspaceFolder: IWorkspaceFolder, configFolderRelativePath: string, private readonly workbenchState: WorkbenchState, - configurationFileService: IConfigurationFileService, + configurationFileService: ConfigurationFileService, configurationCache: IConfigurationCache ) { super(); diff --git a/src/vs/workbench/services/configuration/browser/configurationCache.ts b/src/vs/workbench/services/configuration/browser/configurationCache.ts new file mode 100644 index 00000000000..f3e8375e9fc --- /dev/null +++ b/src/vs/workbench/services/configuration/browser/configurationCache.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IConfigurationCache, ConfigurationKey } from 'vs/workbench/services/configuration/common/configuration'; + +export class ConfigurationCache implements IConfigurationCache { + + constructor() { + } + + async read(key: ConfigurationKey): Promise { + return ''; + } + + async write(key: ConfigurationKey, content: string): Promise { + } + + async remove(key: ConfigurationKey): Promise { + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 547484bfb5d..0152a86f84a 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -15,7 +15,7 @@ import { isLinux } from 'vs/base/common/platform'; import { ConfigurationChangeEvent, ConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, keyFromOverrideIdentifier, isConfigurationOverrides, IConfigurationData, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Configuration, WorkspaceConfigurationChangeEvent, AllKeysConfigurationChangeEvent } from 'vs/workbench/services/configuration/common/configurationModels'; -import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId, IConfigurationCache, IConfigurationFileService, machineSettingsSchemaId } from 'vs/workbench/services/configuration/common/configuration'; +import { FOLDER_CONFIG_FOLDER_NAME, defaultSettingsSchemaId, userSettingsSchemaId, workspaceSettingsSchemaId, folderSettingsSchemaId, IConfigurationCache, machineSettingsSchemaId, LOCAL_MACHINE_SCOPES, ConfigurationFileService } from 'vs/workbench/services/configuration/common/configuration'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions, allSettings, windowSettings, resourceSettings, applicationSettings, machineSettings } from 'vs/platform/configuration/common/configurationRegistry'; import { IWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IWorkspaceInitializationPayload, isSingleFolderWorkspaceInitializationPayload, ISingleFolderWorkspaceInitializationPayload, IEmptyWorkspaceInitializationPayload, useSlashForPath, getStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; @@ -28,6 +28,7 @@ import { localize } from 'vs/nls'; import { isEqual, dirname } from 'vs/base/common/resources'; import { mark } from 'vs/base/common/performance'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { IFileService } from 'vs/platform/files/common/files'; export class WorkspaceService extends Disposable implements IConfigurationService, IWorkspaceContextService { @@ -37,14 +38,16 @@ export class WorkspaceService extends Disposable implements IConfigurationServic private completeWorkspaceBarrier: Barrier; private readonly configurationCache: IConfigurationCache; private _configuration: Configuration; + private initialized: boolean = false; private defaultConfiguration: DefaultConfigurationModel; - private localUserConfiguration: UserConfiguration | null = null; + private localUserConfiguration: UserConfiguration; private remoteUserConfiguration: RemoteUserConfiguration | null = null; private workspaceConfiguration: WorkspaceConfiguration; private cachedFolderConfigs: ResourceMap; - private workspaceEditingQueue: Queue; + private readonly configurationFileService: ConfigurationFileService; + protected readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); public readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; @@ -64,8 +67,8 @@ export class WorkspaceService extends Disposable implements IConfigurationServic private cyclicDependency = new Promise(resolve => this.cyclicDependencyReady = resolve); constructor( - { userSettingsResource, remoteAuthority, configurationCache }: { userSettingsResource?: URI, remoteAuthority?: string, configurationCache: IConfigurationCache }, - private readonly configurationFileService: IConfigurationFileService, + { userSettingsResource, remoteAuthority, configurationCache }: { userSettingsResource: URI, remoteAuthority?: string, configurationCache: IConfigurationCache }, + fileService: IFileService, remoteAgentService: IRemoteAgentService, ) { super(); @@ -73,12 +76,13 @@ export class WorkspaceService extends Disposable implements IConfigurationServic this.completeWorkspaceBarrier = new Barrier(); this.defaultConfiguration = new DefaultConfigurationModel(); this.configurationCache = configurationCache; - if (userSettingsResource) { - this.localUserConfiguration = this._register(new UserConfiguration(userSettingsResource, undefined, configurationFileService)); - this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration))); - } + this.configurationFileService = new ConfigurationFileService(fileService); + this._configuration = new Configuration(this.defaultConfiguration, new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), this.workspace); + this.cachedFolderConfigs = new ResourceMap(); + this.localUserConfiguration = this._register(new UserConfiguration(userSettingsResource, remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, this.configurationFileService)); + this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration))); if (remoteAuthority) { - this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(remoteAuthority, configurationCache, configurationFileService, remoteAgentService)); + this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(remoteAuthority, configurationCache, this.configurationFileService, remoteAgentService)); this._register(this.remoteUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onRemoteUserConfigurationChanged(userConfiguration))); } this.workspaceConfiguration = this._register(new WorkspaceConfiguration(configurationCache, this.configurationFileService)); @@ -396,7 +400,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic } private compareFolders(currentFolders: IWorkspaceFolder[], newFolders: IWorkspaceFolder[]): IWorkspaceFoldersChangeEvent { - const result = { added: [], removed: [], changed: [] } as IWorkspaceFoldersChangeEvent; + const result: IWorkspaceFoldersChangeEvent = { added: [], removed: [], changed: [] }; result.added = newFolders.filter(newFolder => !currentFolders.some(currentFolder => newFolder.uri.toString() === currentFolder.uri.toString())); for (let currentIndex = 0; currentIndex < currentFolders.length; currentIndex++) { let currentFolder = currentFolders[currentIndex]; @@ -420,7 +424,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic } private initializeUserConfiguration(): Promise<{ local: ConfigurationModel, remote: ConfigurationModel }> { - return Promise.all([this.localUserConfiguration ? this.localUserConfiguration.initialize() : Promise.resolve(new ConfigurationModel()), this.remoteUserConfiguration ? this.remoteUserConfiguration.initialize() : Promise.resolve(new ConfigurationModel())]) + return Promise.all([this.localUserConfiguration.initialize(), this.remoteUserConfiguration ? this.remoteUserConfiguration.initialize() : Promise.resolve(new ConfigurationModel())]) .then(([local, remote]) => ({ local, remote })); } @@ -429,7 +433,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic } private reloadLocalUserConfiguration(key?: string): Promise { - return this.localUserConfiguration ? this.localUserConfiguration.reload() : Promise.resolve(new ConfigurationModel()); + return this.localUserConfiguration.reload(); } private reloadRemoeUserConfiguration(key?: string): Promise { @@ -466,11 +470,12 @@ export class WorkspaceService extends Disposable implements IConfigurationServic const currentConfiguration = this._configuration; this._configuration = new Configuration(this.defaultConfiguration, userConfigurationModel, remoteUserConfigurationModel, workspaceConfiguration, folderConfigurationModels, new ConfigurationModel(), new ResourceMap(), this.workspace); - if (currentConfiguration) { + if (this.initialized) { const changedKeys = this._configuration.compare(currentConfiguration); this.triggerConfigurationChange(new ConfigurationChangeEvent().change(changedKeys), ConfigurationTarget.WORKSPACE); } else { this._onDidChangeConfiguration.fire(new AllKeysConfigurationChangeEvent(this._configuration, ConfigurationTarget.WORKSPACE, this.getTargetConfiguration(ConfigurationTarget.WORKSPACE))); + this.initialized = true; } }); } @@ -489,7 +494,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic private onDefaultConfigurationChanged(keys: string[]): void { this.defaultConfiguration = new DefaultConfigurationModel(); this.registerConfigurationSchemas(); - if (this.workspace && this._configuration) { + if (this.workspace) { this._configuration.updateDefaultConfiguration(this.defaultConfiguration); if (this.remoteUserConfiguration) { this._configuration.updateRemoteUserConfiguration(this.remoteUserConfiguration.reprocess()); @@ -515,14 +520,16 @@ export class WorkspaceService extends Disposable implements IConfigurationServic }, {}); }; + const unsupportedApplicationSettings = convertToNotSuggestedProperties(applicationSettings.properties, localize('unsupportedApplicationSetting', "This setting can be applied only in application user settings")); + const unsupportedMachineSettings = convertToNotSuggestedProperties(machineSettings.properties, localize('unsupportedMachineSetting', "This setting can be applied only in user settings")); + const unsupportedRemoteMachineSettings = convertToNotSuggestedProperties(machineSettings.properties, localize('unsupportedRemoteMachineSetting', "This setting can be applied only in remote machine settings")); const allSettingsSchema: IJSONSchema = { properties: allSettings.properties, patternProperties: allSettings.patternProperties, additionalProperties: false, errorMessage: 'Unknown configuration setting' }; - const unsupportedApplicationSettings = convertToNotSuggestedProperties(applicationSettings.properties, localize('unsupportedApplicationSetting', "This setting can be applied only in application user Settings")); - const unsupportedMachineSettings = convertToNotSuggestedProperties(machineSettings.properties, localize('unsupportedMachineSetting', "This setting can be applied only in user Settings")); + const userSettingsSchema: IJSONSchema = this.remoteUserConfiguration ? { properties: { ...applicationSettings.properties, ...unsupportedRemoteMachineSettings, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: false, errorMessage: 'Unknown configuration setting' } : allSettingsSchema; const machineSettingsSchema: IJSONSchema = { properties: { ...unsupportedApplicationSettings, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: false, errorMessage: 'Unknown configuration setting' }; const workspaceSettingsSchema: IJSONSchema = { properties: { ...unsupportedApplicationSettings, ...unsupportedMachineSettings, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: false, errorMessage: 'Unknown configuration setting' }; jsonRegistry.registerSchema(defaultSettingsSchemaId, allSettingsSchema); - jsonRegistry.registerSchema(userSettingsSchemaId, allSettingsSchema); + jsonRegistry.registerSchema(userSettingsSchemaId, userSettingsSchema); jsonRegistry.registerSchema(machineSettingsSchemaId, machineSettingsSchema); if (WorkbenchState.WORKSPACE === this.getWorkbenchState()) { @@ -548,7 +555,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic } private onWorkspaceConfigurationChanged(): Promise { - if (this.workspace && this.workspace.configuration && this._configuration) { + if (this.workspace && this.workspace.configuration) { const workspaceConfigurationChangeEvent = this._configuration.compareAndUpdateWorkspaceConfiguration(this.workspaceConfiguration.getConfiguration()); let configuredFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), this.workspace.configuration); const changes = this.compareFolders(this.workspace.folders, configuredFolders); diff --git a/src/vs/workbench/services/configuration/common/configuration.ts b/src/vs/workbench/services/configuration/common/configuration.ts index 02192c4c871..2910b6feccc 100644 --- a/src/vs/workbench/services/configuration/common/configuration.ts +++ b/src/vs/workbench/services/configuration/common/configuration.ts @@ -5,10 +5,10 @@ import { URI } from 'vs/base/common/uri'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { Event } from 'vs/base/common/event'; -import { FileChangesEvent, IFileService } from 'vs/platform/files/common/files'; +import { IFileService } from 'vs/platform/files/common/files'; import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +export const USER_CONFIGURATION_KEY = 'settings.json'; export const FOLDER_CONFIG_FOLDER_NAME = '.vscode'; export const FOLDER_SETTINGS_NAME = 'settings'; export const FOLDER_SETTINGS_PATH = `${FOLDER_CONFIG_FOLDER_NAME}/${FOLDER_SETTINGS_NAME}.json`; @@ -20,7 +20,8 @@ export const workspaceSettingsSchemaId = 'vscode://schemas/settings/workspace'; export const folderSettingsSchemaId = 'vscode://schemas/settings/folder'; export const launchSchemaId = 'vscode://schemas/launch'; -export const MACHINE_SCOPES = [ConfigurationScope.MACHINE, ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE]; +export const LOCAL_MACHINE_SCOPES = [ConfigurationScope.APPLICATION, ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE]; +export const REMOTE_MACHINE_SCOPES = [ConfigurationScope.MACHINE, ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE]; export const WORKSPACE_SCOPES = [ConfigurationScope.WINDOW, ConfigurationScope.RESOURCE]; export const FOLDER_SCOPES = [ConfigurationScope.RESOURCE]; @@ -41,24 +42,11 @@ export interface IConfigurationCache { } -export interface IConfigurationFileService { - fileService: IFileService | null; - readonly onFileChanges: Event; - readonly isWatching: boolean; - readonly whenWatchingStarted: Promise; - whenProviderRegistered(scheme: string): Promise; - watch(resource: URI): IDisposable; - exists(resource: URI): Promise; - readFile(resource: URI): Promise; -} +export class ConfigurationFileService { -export class ConfigurationFileService implements IConfigurationFileService { - - constructor(public fileService: IFileService) { } + constructor(private readonly fileService: IFileService) { } get onFileChanges() { return this.fileService.onFileChanges; } - readonly whenWatchingStarted: Promise = Promise.resolve(); - readonly isWatching: boolean = true; whenProviderRegistered(scheme: string): Promise { if (this.fileService.canHandleResource(URI.from({ scheme }))) { diff --git a/src/vs/workbench/services/configuration/common/configurationEditingService.ts b/src/vs/workbench/services/configuration/common/configurationEditingService.ts index 7f4147d64a3..ffef24c5573 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditingService.ts @@ -528,7 +528,7 @@ export class ConfigurationEditingService { private getConfigurationFileResource(target: EditableConfigurationTarget, config: IConfigurationValue, relativePath: string, resource: URI | null | undefined): URI | null { if (target === EditableConfigurationTarget.USER_LOCAL) { - return URI.file(this.environmentService.appSettingsPath); + return this.environmentService.settingsResource; } if (target === EditableConfigurationTarget.USER_REMOTE) { return this.remoteSettingsResource; diff --git a/src/vs/workbench/services/configuration/common/jsonEditingService.ts b/src/vs/workbench/services/configuration/common/jsonEditingService.ts index baeccb314dd..9313c53ccb9 100644 --- a/src/vs/workbench/services/configuration/common/jsonEditingService.ts +++ b/src/vs/workbench/services/configuration/common/jsonEditingService.ts @@ -39,10 +39,11 @@ export class JSONEditingService implements IJSONEditingService { return Promise.resolve(this.queue.queue(() => this.doWriteConfiguration(resource, value, save))); // queue up writes to prevent race conditions } - private doWriteConfiguration(resource: URI, value: IJSONValue, save: boolean): Promise { - return this.resolveAndValidate(resource, save) - .then(reference => this.writeToBuffer(reference.object.textEditorModel, value) - .then(() => reference.dispose())); + private async doWriteConfiguration(resource: URI, value: IJSONValue, save: boolean): Promise { + const reference = await this.resolveAndValidate(resource, save); + await this.writeToBuffer(reference.object.textEditorModel, value); + + reference.dispose(); } private async writeToBuffer(model: ITextModel, value: IJSONValue): Promise { @@ -97,21 +98,21 @@ export class JSONEditingService implements IJSONEditingService { return parseErrors.length > 0; } - private resolveAndValidate(resource: URI, checkDirty: boolean): Promise> { - return this.resolveModelReference(resource) - .then(reference => { - const model = reference.object.textEditorModel; + private async resolveAndValidate(resource: URI, checkDirty: boolean): Promise> { + const reference = await this.resolveModelReference(resource); - if (this.hasParseErrors(model)) { - return this.reject>(JSONEditingErrorCode.ERROR_INVALID_FILE); - } + const model = reference.object.textEditorModel; - // Target cannot be dirty if not writing into buffer - if (checkDirty && this.textFileService.isDirty(resource)) { - return this.reject>(JSONEditingErrorCode.ERROR_FILE_DIRTY); - } - return reference; - }); + if (this.hasParseErrors(model)) { + return this.reject>(JSONEditingErrorCode.ERROR_INVALID_FILE); + } + + // Target cannot be dirty if not writing into buffer + if (checkDirty && this.textFileService.isDirty(resource)) { + return this.reject>(JSONEditingErrorCode.ERROR_FILE_DIRTY); + } + + return reference; } private reject(code: JSONEditingErrorCode): Promise { diff --git a/src/vs/workbench/services/configuration/node/configurationFileService.ts b/src/vs/workbench/services/configuration/node/configurationFileService.ts deleted file mode 100644 index 18690c44040..00000000000 --- a/src/vs/workbench/services/configuration/node/configurationFileService.ts +++ /dev/null @@ -1,79 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as pfs from 'vs/base/node/pfs'; -import { IConfigurationFileService, ConfigurationFileService as FileServiceBasedConfigurationFileService } from 'vs/workbench/services/configuration/common/configuration'; -import { URI } from 'vs/base/common/uri'; -import { Event, Emitter } from 'vs/base/common/event'; -import { FileChangesEvent, IFileService } from 'vs/platform/files/common/files'; -import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { Schemas } from 'vs/base/common/network'; - -export class ConfigurationFileService extends Disposable implements IConfigurationFileService { - - private _fileServiceBasedConfigurationFileService: FileServiceBasedConfigurationFileService | null = null; - private _fileServiceBasedConfigurationFileServiceCallback: (fileServiceBasedConfigurationFileService: FileServiceBasedConfigurationFileService) => void; - private _whenFileServiceBasedConfigurationFileServiceAvailable: Promise = new Promise((c) => this._fileServiceBasedConfigurationFileServiceCallback = c); - private _watchResources: { resource: URI, disposable: { disposable: IDisposable | null } }[] = []; - readonly whenWatchingStarted: Promise = this._whenFileServiceBasedConfigurationFileServiceAvailable.then(() => undefined); - - private readonly _onFileChanges: Emitter = this._register(new Emitter()); - readonly onFileChanges: Event = this._onFileChanges.event; - - get isWatching(): boolean { - return this._fileServiceBasedConfigurationFileService ? this._fileServiceBasedConfigurationFileService.isWatching : false; - } - - watch(resource: URI): IDisposable { - if (this._fileServiceBasedConfigurationFileService) { - return this._fileServiceBasedConfigurationFileService.watch(resource); - } - const disposable: { disposable: IDisposable | null } = { disposable: null }; - this._watchResources.push({ resource, disposable }); - return toDisposable(() => { - if (disposable.disposable) { - disposable.disposable.dispose(); - } - }); - } - - whenProviderRegistered(scheme: string): Promise { - if (scheme === Schemas.file) { - return Promise.resolve(); - } - return this._whenFileServiceBasedConfigurationFileServiceAvailable - .then(fileServiceBasedConfigurationFileService => fileServiceBasedConfigurationFileService.whenProviderRegistered(scheme)); - } - - exists(resource: URI): Promise { - return this._fileServiceBasedConfigurationFileService ? this._fileServiceBasedConfigurationFileService.exists(resource) : pfs.exists(resource.fsPath); - } - - async readFile(resource: URI): Promise { - if (this._fileServiceBasedConfigurationFileService) { - return this._fileServiceBasedConfigurationFileService.readFile(resource); - } else { - const contents = await pfs.readFile(resource.fsPath); - return contents.toString(); - } - } - - private _fileService: IFileService | null; - get fileService(): IFileService | null { - return this._fileService; - } - - set fileService(fileService: IFileService | null) { - if (fileService && !this._fileServiceBasedConfigurationFileService) { - this._fileServiceBasedConfigurationFileService = new FileServiceBasedConfigurationFileService(fileService); - this._fileService = fileService; - this._register(this._fileServiceBasedConfigurationFileService.onFileChanges(e => this._onFileChanges.fire(e))); - for (const { resource, disposable } of this._watchResources) { - disposable.disposable = this._fileServiceBasedConfigurationFileService.watch(resource); - } - this._fileServiceBasedConfigurationFileServiceCallback(this._fileServiceBasedConfigurationFileService); - } - } -} 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 b3c90ce98d9..64b5b994371 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 @@ -39,7 +39,6 @@ import { Schemas } from 'vs/base/common/network'; import { DiskFileSystemProvider } from 'vs/workbench/services/files/node/diskFileSystemProvider'; import { IFileService } from 'vs/platform/files/common/files'; import { ConfigurationCache } from 'vs/workbench/services/configuration/node/configurationCache'; -import { ConfigurationFileService } from 'vs/workbench/services/configuration/node/configurationFileService'; class SettingsTestEnvironmentService extends EnvironmentService { @@ -47,7 +46,7 @@ class SettingsTestEnvironmentService extends EnvironmentService { super(args, _execPath); } - get appSettingsPath(): string { return this.customAppSettingsHome; } + get settingsResource(): URI { return URI.file(this.customAppSettingsHome); } } suite('ConfigurationEditingService', () => { @@ -104,14 +103,14 @@ suite('ConfigurationEditingService', () => { const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); instantiationService.stub(IEnvironmentService, environmentService); const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {}); + const fileService = new FileService(new NullLogService()); + fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); + instantiationService.stub(IFileService, fileService); instantiationService.stub(IRemoteAgentService, remoteAgentService); - const workspaceService = new WorkspaceService({ userSettingsResource: URI.file(environmentService.appSettingsPath), configurationCache: new ConfigurationCache(environmentService) }, new ConfigurationFileService(), remoteAgentService); + const workspaceService = new WorkspaceService({ userSettingsResource: environmentService.settingsResource, configurationCache: new ConfigurationCache(environmentService) }, fileService, remoteAgentService); instantiationService.stub(IWorkspaceContextService, workspaceService); return workspaceService.initialize(noWorkspace ? { id: '' } : { folder: URI.file(workspaceDir), id: createHash('md5').update(URI.file(workspaceDir).toString()).digest('hex') }).then(() => { instantiationService.stub(IConfigurationService, workspaceService); - const fileService = new FileService(new NullLogService()); - fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); - instantiationService.stub(IFileService, fileService); instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); instantiationService.stub(ICommandService, CommandService); @@ -186,7 +185,7 @@ suite('ConfigurationEditingService', () => { test('do not notify error', () => { instantiationService.stub(ITextFileService, 'isDirty', true); const target = sinon.stub(); - instantiationService.stub(INotificationService, { prompt: target, _serviceBrand: null, notify: null!, error: null!, info: null!, warn: null! }); + instantiationService.stub(INotificationService, { prompt: target, _serviceBrand: null!, notify: null!, error: null!, info: null!, warn: null!, status: null! }); return testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: 'value' }, { donotNotifyError: true }) .then(() => assert.fail('Should fail with ERROR_CONFIGURATION_FILE_DIRTY error.'), (error: ConfigurationEditingError) => { diff --git a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts index e35f5b0de41..cbd400263ee 100644 --- a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts @@ -22,7 +22,7 @@ import { ConfigurationEditingErrorCode } from 'vs/workbench/services/configurati import { IFileService } from 'vs/platform/files/common/files'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; import { ConfigurationTarget, IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; -import { workbenchInstantiationService, TestTextFileService } from 'vs/workbench/test/workbenchTestServices'; +import { workbenchInstantiationService, TestTextFileService, RemoteFileSystemProvider } from 'vs/workbench/test/workbenchTestServices'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; @@ -41,7 +41,10 @@ import { FileService } from 'vs/workbench/services/files/common/fileService'; import { NullLogService } from 'vs/platform/log/common/log'; import { DiskFileSystemProvider } from 'vs/workbench/services/files/node/diskFileSystemProvider'; import { ConfigurationCache } from 'vs/workbench/services/configuration/node/configurationCache'; -import { ConfigurationFileService } from 'vs/workbench/services/configuration/node/configurationFileService'; +import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; +import { IConfigurationCache } from 'vs/workbench/services/configuration/common/configuration'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { SignService } from 'vs/platform/sign/browser/signService'; class SettingsTestEnvironmentService extends EnvironmentService { @@ -49,7 +52,7 @@ class SettingsTestEnvironmentService extends EnvironmentService { super(args, _execPath); } - get appSettingsPath(): string { return this.customAppSettingsHome; } + get settingsResource(): URI { return URI.file(this.customAppSettingsHome); } } function setUpFolderWorkspace(folderName: string): Promise<{ parentDir: string, folderDir: string }> { @@ -100,7 +103,7 @@ suite('WorkspaceContextService - Folder', () => { workspaceResource = folderDir; const globalSettingsFile = path.join(parentDir, 'settings.json'); const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); - workspaceContextService = new WorkspaceService({ userSettingsResource: URI.file(environmentService.appSettingsPath), configurationCache: new ConfigurationCache(environmentService) }, new ConfigurationFileService(), new RemoteAgentService({}, environmentService, new RemoteAuthorityResolverService())); + workspaceContextService = new WorkspaceService({ userSettingsResource: environmentService.settingsResource, configurationCache: new ConfigurationCache(environmentService) }, new FileService(new NullLogService()), new RemoteAgentService({}, environmentService, new RemoteAuthorityResolverService(), new SignService())); return (workspaceContextService).initialize(convertToWorkspacePayload(URI.file(folderDir))); }); }); @@ -162,7 +165,9 @@ suite('WorkspaceContextService - Workspace', () => { const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, path.join(parentDir, 'settings.json')); const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {}); instantiationService.stub(IRemoteAgentService, remoteAgentService); - const workspaceService = new WorkspaceService({ userSettingsResource: URI.file(environmentService.appSettingsPath), configurationCache: new ConfigurationCache(environmentService) }, new ConfigurationFileService(), remoteAgentService); + const fileService = new FileService(new NullLogService()); + fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); + const workspaceService = new WorkspaceService({ userSettingsResource: environmentService.settingsResource, configurationCache: new ConfigurationCache(environmentService) }, fileService, remoteAgentService); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); @@ -220,9 +225,8 @@ suite('WorkspaceContextService - Workspace Editing', () => { instantiationService.stub(IRemoteAgentService, remoteAgentService); const fileService = new FileService(new NullLogService()); fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); - const configurationFileService = new ConfigurationFileService(); - configurationFileService.fileService = fileService; - const workspaceService = new WorkspaceService({ userSettingsResource: URI.file(environmentService.appSettingsPath), configurationCache: new ConfigurationCache(environmentService) }, configurationFileService, remoteAgentService); + const configurationFileService = fileService; + const workspaceService = new WorkspaceService({ userSettingsResource: environmentService.settingsResource, configurationCache: new ConfigurationCache(environmentService) }, configurationFileService, remoteAgentService); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); @@ -481,9 +485,8 @@ suite('WorkspaceService - Initialization', () => { instantiationService.stub(IRemoteAgentService, remoteAgentService); const fileService = new FileService(new NullLogService()); fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); - const configurationFileService = new ConfigurationFileService(); - configurationFileService.fileService = fileService; - const workspaceService = new WorkspaceService({ userSettingsResource: URI.file(environmentService.appSettingsPath), configurationCache: new ConfigurationCache(environmentService) }, configurationFileService, remoteAgentService); + const configurationFileService = fileService; + const workspaceService = new WorkspaceService({ userSettingsResource: environmentService.settingsResource, configurationCache: new ConfigurationCache(environmentService) }, configurationFileService, remoteAgentService); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); instantiationService.stub(IEnvironmentService, environmentService); @@ -745,9 +748,8 @@ suite('WorkspaceConfigurationService - Folder', () => { instantiationService.stub(IRemoteAgentService, remoteAgentService); const fileService = new FileService(new NullLogService()); fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); - const configurationFileService = new ConfigurationFileService(); - configurationFileService.fileService = fileService; - const workspaceService = new WorkspaceService({ userSettingsResource: URI.file(environmentService.appSettingsPath), configurationCache: new ConfigurationCache(environmentService) }, configurationFileService, remoteAgentService); + const configurationFileService = fileService; + const workspaceService = new WorkspaceService({ userSettingsResource: environmentService.settingsResource, configurationCache: new ConfigurationCache(environmentService) }, configurationFileService, remoteAgentService); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); instantiationService.stub(IEnvironmentService, environmentService); @@ -1073,9 +1075,8 @@ suite('WorkspaceConfigurationService-Multiroot', () => { instantiationService.stub(IRemoteAgentService, remoteAgentService); const fileService = new FileService(new NullLogService()); fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); - const configurationFileService = new ConfigurationFileService(); - configurationFileService.fileService = fileService; - const workspaceService = new WorkspaceService({ userSettingsResource: URI.file(environmentService.appSettingsPath), configurationCache: new ConfigurationCache(environmentService) }, configurationFileService, remoteAgentService); + const configurationFileService = fileService; + const workspaceService = new WorkspaceService({ userSettingsResource: environmentService.settingsResource, configurationCache: new ConfigurationCache(environmentService) }, configurationFileService, remoteAgentService); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); @@ -1105,21 +1106,21 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace', () => { - fs.writeFileSync(environmentService.appSettingsPath, '{ "configurationService.workspace.applicationSetting": "userValue" }'); + fs.writeFileSync(environmentService.settingsResource.fsPath, '{ "configurationService.workspace.applicationSetting": "userValue" }'); return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, { key: 'settings', value: { 'configurationService.workspace.applicationSetting': 'workspaceValue' } }, true) .then(() => testObject.reloadConfiguration()) .then(() => assert.equal(testObject.getValue('configurationService.workspace.applicationSetting'), 'userValue')); }); test('machine settings are not read from workspace', () => { - fs.writeFileSync(environmentService.appSettingsPath, '{ "configurationService.workspace.machineSetting": "userValue" }'); + fs.writeFileSync(environmentService.settingsResource.fsPath, '{ "configurationService.workspace.machineSetting": "userValue" }'); return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, { key: 'settings', value: { 'configurationService.workspace.machineSetting': 'workspaceValue' } }, true) .then(() => testObject.reloadConfiguration()) .then(() => assert.equal(testObject.getValue('configurationService.workspace.machineSetting'), 'userValue')); }); test('workspace settings override user settings after defaults are registered ', () => { - fs.writeFileSync(environmentService.appSettingsPath, '{ "configurationService.workspace.newSetting": "userValue" }'); + fs.writeFileSync(environmentService.settingsResource.fsPath, '{ "configurationService.workspace.newSetting": "userValue" }'); return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, { key: 'settings', value: { 'configurationService.workspace.newSetting': 'workspaceValue' } }, true) .then(() => testObject.reloadConfiguration()) .then(() => { @@ -1138,21 +1139,21 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace folder', () => { - fs.writeFileSync(environmentService.appSettingsPath, '{ "configurationService.workspace.applicationSetting": "userValue" }'); + fs.writeFileSync(environmentService.settingsResource.fsPath, '{ "configurationService.workspace.applicationSetting": "userValue" }'); fs.writeFileSync(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json').fsPath, '{ "configurationService.workspace.applicationSetting": "workspaceFolderValue" }'); return testObject.reloadConfiguration() .then(() => assert.equal(testObject.getValue('configurationService.workspace.applicationSetting'), 'userValue')); }); test('machine settings are not read from workspace folder', () => { - fs.writeFileSync(environmentService.appSettingsPath, '{ "configurationService.workspace.machineSetting": "userValue" }'); + fs.writeFileSync(environmentService.settingsResource.fsPath, '{ "configurationService.workspace.machineSetting": "userValue" }'); fs.writeFileSync(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json').fsPath, '{ "configurationService.workspace.machineSetting": "workspaceFolderValue" }'); return testObject.reloadConfiguration() .then(() => assert.equal(testObject.getValue('configurationService.workspace.machineSetting'), 'userValue')); }); test('application settings are not read from workspace folder after defaults are registered', () => { - fs.writeFileSync(environmentService.appSettingsPath, '{ "configurationService.workspace.testNewApplicationSetting": "userValue" }'); + fs.writeFileSync(environmentService.settingsResource.fsPath, '{ "configurationService.workspace.testNewApplicationSetting": "userValue" }'); fs.writeFileSync(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json').fsPath, '{ "configurationService.workspace.testNewApplicationSetting": "workspaceFolderValue" }'); return testObject.reloadConfiguration() .then(() => { @@ -1172,7 +1173,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace folder after defaults are registered', () => { - fs.writeFileSync(environmentService.appSettingsPath, '{ "configurationService.workspace.testNewMachineSetting": "userValue" }'); + fs.writeFileSync(environmentService.settingsResource.fsPath, '{ "configurationService.workspace.testNewMachineSetting": "userValue" }'); fs.writeFileSync(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json').fsPath, '{ "configurationService.workspace.testNewMachineSetting": "workspaceFolderValue" }'); return testObject.reloadConfiguration() .then(() => { @@ -1226,7 +1227,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { assert.equal(actual.workspaceFolder, undefined); assert.equal(actual.value, 'isSet'); - fs.writeFileSync(environmentService.appSettingsPath, '{ "configurationService.workspace.testResourceSetting": "userValue" }'); + fs.writeFileSync(environmentService.settingsResource.fsPath, '{ "configurationService.workspace.testResourceSetting": "userValue" }'); return testObject.reloadConfiguration() .then(() => { actual = testObject.inspect('configurationService.workspace.testResourceSetting'); @@ -1408,6 +1409,198 @@ suite('WorkspaceConfigurationService-Multiroot', () => { assert.equal(actual.workspace, undefined); }); }); + + test('configuration of newly added folder is available on configuration change event', async () => { + const workspaceService = testObject; + const uri = workspaceService.getWorkspace().folders[1].uri; + await workspaceService.removeFolders([uri]); + fs.writeFileSync(path.join(uri.fsPath, '.vscode', 'settings.json'), '{ "configurationService.workspace.testResourceSetting": "workspaceFolderValue" }'); + + return new Promise((c, e) => { + testObject.onDidChangeConfiguration(() => { + try { + assert.equal(testObject.getValue('configurationService.workspace.testResourceSetting', { resource: uri }), 'workspaceFolderValue'); + c(); + } catch (error) { + e(error); + } + }); + workspaceService.addFolders([{ uri }]); + }); + }); +}); + +suite('WorkspaceConfigurationService - Remote Folder', () => { + + let workspaceName = `testWorkspace${uuid.generateUuid()}`, parentResource: string, workspaceDir: string, testObject: WorkspaceService, globalSettingsFile: string, remoteSettingsFile: string, instantiationService: TestInstantiationService, resolveRemoteEnvironment: () => void; + const remoteAuthority = 'configuraiton-tests'; + const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); + + suiteSetup(() => { + configurationRegistry.registerConfiguration({ + 'id': '_test', + 'type': 'object', + 'properties': { + 'configurationService.remote.applicationSetting': { + 'type': 'string', + 'default': 'isSet', + scope: ConfigurationScope.APPLICATION + }, + 'configurationService.remote.machineSetting': { + 'type': 'string', + 'default': 'isSet', + scope: ConfigurationScope.MACHINE + }, + 'configurationService.remote.testSetting': { + 'type': 'string', + 'default': 'isSet', + scope: ConfigurationScope.RESOURCE + } + } + }); + }); + + setup(() => { + return setUpFolderWorkspace(workspaceName) + .then(({ parentDir, folderDir }) => { + + parentResource = parentDir; + workspaceDir = folderDir; + globalSettingsFile = path.join(parentDir, 'settings.json'); + remoteSettingsFile = path.join(parentDir, 'remote-settings.json'); + + instantiationService = workbenchInstantiationService(); + const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); + const remoteEnvironmentPromise = new Promise>(c => resolveRemoteEnvironment = () => c({ settingsPath: URI.file(remoteSettingsFile).with({ scheme: Schemas.vscodeRemote, authority: remoteAuthority }) })); + const remoteAgentService = instantiationService.stub(IRemoteAgentService, >{ getEnvironment: () => remoteEnvironmentPromise }); + const fileService = new FileService(new NullLogService()); + fileService.registerProvider(Schemas.file, diskFileSystemProvider); + const configurationFileService = fileService; + const configurationCache: IConfigurationCache = { read: () => Promise.resolve(''), write: () => Promise.resolve(), remove: () => Promise.resolve() }; + testObject = new WorkspaceService({ userSettingsResource: environmentService.settingsResource, configurationCache, remoteAuthority }, configurationFileService, remoteAgentService); + instantiationService.stub(IWorkspaceContextService, testObject); + instantiationService.stub(IConfigurationService, testObject); + instantiationService.stub(IEnvironmentService, environmentService); + instantiationService.stub(IFileService, fileService); + }); + }); + + async function initialize(): Promise { + await testObject.initialize(convertToWorkspacePayload(URI.file(workspaceDir))); + instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); + instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); + testObject.acquireInstantiationService(instantiationService); + } + + function registerRemoteFileSystemProvider(): void { + instantiationService.get(IFileService).registerProvider(Schemas.vscodeRemote, new RemoteFileSystemProvider(diskFileSystemProvider, remoteAuthority)); + } + + function registerRemoteFileSystemProviderOnActivation(): void { + const disposable = instantiationService.get(IFileService).onWillActivateFileSystemProvider(e => { + if (e.scheme === Schemas.vscodeRemote) { + disposable.dispose(); + e.join(Promise.resolve().then(() => registerRemoteFileSystemProvider())); + } + }); + } + + teardown(() => { + if (testObject) { + (testObject).dispose(); + } + if (parentResource) { + return pfs.rimraf(parentResource, pfs.RimRafMode.MOVE); + } + return undefined; + }); + + test('remote settings override globals', async () => { + fs.writeFileSync(remoteSettingsFile, '{ "configurationService.remote.machineSetting": "remoteValue" }'); + registerRemoteFileSystemProvider(); + resolveRemoteEnvironment(); + await initialize(); + assert.equal(testObject.getValue('configurationService.remote.machineSetting'), 'remoteValue'); + }); + + test('remote settings override globals after remote provider is registered on activation', async () => { + fs.writeFileSync(remoteSettingsFile, '{ "configurationService.remote.machineSetting": "remoteValue" }'); + resolveRemoteEnvironment(); + registerRemoteFileSystemProviderOnActivation(); + await initialize(); + assert.equal(testObject.getValue('configurationService.remote.machineSetting'), 'remoteValue'); + }); + + test('remote settings override globals after remote environment is resolved', async () => { + fs.writeFileSync(remoteSettingsFile, '{ "configurationService.remote.machineSetting": "remoteValue" }'); + registerRemoteFileSystemProvider(); + await initialize(); + const promise = new Promise((c, e) => { + testObject.onDidChangeConfiguration(event => { + try { + assert.equal(event.source, ConfigurationTarget.USER); + assert.deepEqual(event.affectedKeys, ['configurationService.remote.machineSetting']); + assert.equal(testObject.getValue('configurationService.remote.machineSetting'), 'remoteValue'); + c(); + } catch (error) { + e(error); + } + }); + }); + resolveRemoteEnvironment(); + return promise; + }); + + test('remote settings override globals after remote provider is registered on activation and remote environment is resolved', async () => { + fs.writeFileSync(remoteSettingsFile, '{ "configurationService.remote.machineSetting": "remoteValue" }'); + registerRemoteFileSystemProviderOnActivation(); + await initialize(); + const promise = new Promise((c, e) => { + testObject.onDidChangeConfiguration(event => { + try { + assert.equal(event.source, ConfigurationTarget.USER); + assert.deepEqual(event.affectedKeys, ['configurationService.remote.machineSetting']); + assert.equal(testObject.getValue('configurationService.remote.machineSetting'), 'remoteValue'); + c(); + } catch (error) { + e(error); + } + }); + }); + resolveRemoteEnvironment(); + return promise; + }); + + test('update remote settings', async () => { + registerRemoteFileSystemProvider(); + resolveRemoteEnvironment(); + await initialize(); + assert.equal(testObject.getValue('configurationService.remote.machineSetting'), 'isSet'); + const promise = new Promise((c, e) => { + testObject.onDidChangeConfiguration(event => { + try { + assert.equal(event.source, ConfigurationTarget.USER); + assert.deepEqual(event.affectedKeys, ['configurationService.remote.machineSetting']); + assert.equal(testObject.getValue('configurationService.remote.machineSetting'), 'remoteValue'); + c(); + } catch (error) { + e(error); + } + }); + }); + await instantiationService.get(IFileService).writeFile(URI.file(remoteSettingsFile), VSBuffer.fromString('{ "configurationService.remote.machineSetting": "remoteValue" }')); + return promise; + }); + + test('machine settings in local user settings does not override defaults', async () => { + fs.writeFileSync(globalSettingsFile, '{ "configurationService.remote.machineSetting": "globalValue" }'); + registerRemoteFileSystemProvider(); + resolveRemoteEnvironment(); + await initialize(); + assert.equal(testObject.getValue('configurationService.remote.machineSetting'), 'isSet'); + }); + }); function getWorkspaceId(configPath: URI): string { diff --git a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts index c9a8760aa96..bcf4b31cef9 100644 --- a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts @@ -19,8 +19,9 @@ import { AbstractVariableResolverService } from 'vs/workbench/services/configura import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { IQuickInputService, IInputOptions, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput'; -import { ConfiguredInput } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; +import { ConfiguredInput, IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { IProcessEnvironment } from 'vs/base/common/platform'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export abstract class BaseConfigurationResolverService extends AbstractVariableResolverService { @@ -304,4 +305,6 @@ export class ConfigurationResolverService extends BaseConfigurationResolverServi ) { super(environmentService.configuration.userEnv, editorService, environmentService, configurationService, commandService, workspaceContextService, quickInputService); } -} \ No newline at end of file +} + +registerSingleton(IConfigurationResolverService, ConfigurationResolverService, true); \ No newline at end of file diff --git a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts index 3d1e2a813c7..43d8013dc31 100644 --- a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts @@ -35,7 +35,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe private _context: IVariableResolveContext, private _envVariables: IProcessEnvironment ) { - if (isWindows) { + if (isWindows && _envVariables) { this._envVariables = Object.create(null); Object.keys(_envVariables).forEach(key => { this._envVariables[key.toLowerCase()] = _envVariables[key]; diff --git a/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts b/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts index f4dae2a33c1..8a389cc3c44 100644 --- a/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts +++ b/src/vs/workbench/services/contextmenu/electron-browser/contextmenuService.ts @@ -97,7 +97,7 @@ class NativeContextMenuService extends Disposable implements IContextMenuService x = elementPosition.left; y = elementPosition.top + elementPosition.height; } else { - const pos = <{ x: number; y: number; }>anchor; + const pos: { x: number; y: number; } = anchor; x = pos.x + 1; /* prevent first item from being selected automatically under mouse */ y = pos.y; } @@ -115,7 +115,7 @@ class NativeContextMenuService extends Disposable implements IContextMenuService } } - private createMenu(delegate: IContextMenuDelegate, entries: Array, onHide: () => void): IContextMenuItem[] { + private createMenu(delegate: IContextMenuDelegate, entries: ReadonlyArray, onHide: () => void): IContextMenuItem[] { const actionRunner = delegate.actionRunner || new ActionRunner(); return entries.map(entry => this.createMenuItem(delegate, entry, actionRunner, onHide)); @@ -172,7 +172,7 @@ class NativeContextMenuService extends Disposable implements IContextMenuService } } - private runAction(actionRunner: IActionRunner, actionToRun: IAction, delegate: IContextMenuDelegate, event: IContextMenuEvent): void { + private async runAction(actionRunner: IActionRunner, actionToRun: IAction, delegate: IContextMenuDelegate, event: IContextMenuEvent): Promise { /* __GDPR__ "workbenchActionExecuted" : { "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, @@ -182,9 +182,15 @@ class NativeContextMenuService extends Disposable implements IContextMenuService this.telemetryService.publicLog('workbenchActionExecuted', { id: actionToRun.id, from: 'contextMenu' }); const context = delegate.getActionsContext ? delegate.getActionsContext(event) : event; - const res = actionRunner.run(actionToRun, context) || Promise.resolve(null); - res.then(undefined, e => this.notificationService.error(e)); + const runnable = actionRunner.run(actionToRun, context); + if (runnable) { + try { + await runnable; + } catch (error) { + this.notificationService.error(error); + } + } } } diff --git a/src/vs/workbench/services/decorations/browser/decorationsService.ts b/src/vs/workbench/services/decorations/browser/decorationsService.ts index 28e5a9f5fe7..b183a2629d8 100644 --- a/src/vs/workbench/services/decorations/browser/decorationsService.ts +++ b/src/vs/workbench/services/decorations/browser/decorationsService.ts @@ -7,7 +7,7 @@ import { URI } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; import { IDecorationsService, IDecoration, IResourceDecorationChangeEvent, IDecorationsProvider, IDecorationData } from './decorations'; import { TernarySearchTree } from 'vs/base/common/map'; -import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle'; import { isThenable } from 'vs/base/common/async'; import { LinkedList } from 'vs/base/common/linkedList'; import { createStyleSheet, createCSSRule, removeCSSRulesContainingSelector } from 'vs/base/browser/dom'; @@ -95,20 +95,20 @@ class DecorationRule { } } -class DecorationStyles { +class DecorationStyles extends Disposable { - private readonly _disposables: IDisposable[]; private readonly _styleElement = createStyleSheet(); private readonly _decorationRules = new Map(); constructor( private _themeService: IThemeService, ) { - this._disposables = [this._themeService.onThemeChange(this._onThemeChange, this)]; + super(); + this._register(this._themeService.onThemeChange(this._onThemeChange, this)); } dispose(): void { - dispose(this._disposables); + super.dispose(); const parent = this._styleElement.parentElement; if (parent) { diff --git a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts index 4d50a0b8ff0..fe43bbd5c3b 100644 --- a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts @@ -85,15 +85,16 @@ export class FileDialogService implements IFileDialogService { } private shouldUseSimplified(schema: string): boolean { - const setting = this.configurationService.getValue('workbench.dialogs.useSimplified'); - return (schema !== Schemas.file) || ((setting === 'true') || (setting === true)); + const setting = this.configurationService.getValue('files.simpleDialog.enable'); + + return (schema !== Schemas.file) || (setting === true); } private ensureFileSchema(schema: string): string[] { return schema !== Schemas.file ? [schema, Schemas.file] : [schema]; } - pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise { + async pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise { const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { @@ -103,21 +104,23 @@ export class FileDialogService implements IFileDialogService { if (this.shouldUseSimplified(schema)) { const title = nls.localize('openFileOrFolder.title', 'Open File Or Folder'); const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well - return this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }).then(uri => { - if (uri) { - return (this.fileService.resolve(uri)).then(stat => { - const toOpen: IURIToOpen = stat.isDirectory ? { folderUri: uri } : { fileUri: uri }; - return this.windowService.openWindow([toOpen], { forceNewWindow: options.forceNewWindow }); - }); - } - return undefined; - }); + + const uri = await this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }); + + if (uri) { + const stat = await this.fileService.resolve(uri); + + const toOpen: IURIToOpen = stat.isDirectory ? { folderUri: uri } : { fileUri: uri }; + return this.windowService.openWindow([toOpen], { forceNewWindow: options.forceNewWindow }); + } + + return; } return this.windowService.pickFileFolderAndOpen(this.toNativeOpenDialogOptions(options)); } - pickFileAndOpen(options: IPickAndOpenOptions): Promise { + async pickFileAndOpen(options: IPickAndOpenOptions): Promise { const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { @@ -127,18 +130,19 @@ export class FileDialogService implements IFileDialogService { if (this.shouldUseSimplified(schema)) { const title = nls.localize('openFile.title', 'Open File'); const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well - return this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }).then(uri => { - if (uri) { - return this.windowService.openWindow([{ fileUri: uri }], { forceNewWindow: options.forceNewWindow }); - } - return undefined; - }); + + const uri = await this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }); + if (uri) { + return this.windowService.openWindow([{ fileUri: uri }], { forceNewWindow: options.forceNewWindow }); + } + + return; } return this.windowService.pickFileAndOpen(this.toNativeOpenDialogOptions(options)); } - pickFolderAndOpen(options: IPickAndOpenOptions): Promise { + async pickFolderAndOpen(options: IPickAndOpenOptions): Promise { const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { @@ -148,18 +152,19 @@ export class FileDialogService implements IFileDialogService { if (this.shouldUseSimplified(schema)) { const title = nls.localize('openFolder.title', 'Open Folder'); const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well - return this.pickRemoteResource({ canSelectFiles: false, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }).then(uri => { - if (uri) { - return this.windowService.openWindow([{ folderUri: uri }], { forceNewWindow: options.forceNewWindow }); - } - return undefined; - }); + + const uri = await this.pickRemoteResource({ canSelectFiles: false, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }); + if (uri) { + return this.windowService.openWindow([{ folderUri: uri }], { forceNewWindow: options.forceNewWindow }); + } + + return; } return this.windowService.pickFolderAndOpen(this.toNativeOpenDialogOptions(options)); } - pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise { + async pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise { const schema = this.getFileSystemSchema(options); if (!options.defaultUri) { @@ -170,17 +175,37 @@ export class FileDialogService implements IFileDialogService { const title = nls.localize('openWorkspace.title', 'Open Workspace'); const filters: FileFilter[] = [{ name: nls.localize('filterName.workspace', 'Workspace'), extensions: [WORKSPACE_EXTENSION] }]; const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well - return this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, filters, availableFileSystems }).then(uri => { - if (uri) { - return this.windowService.openWindow([{ workspaceUri: uri }], { forceNewWindow: options.forceNewWindow }); - } - return undefined; - }); + + const uri = await this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, filters, availableFileSystems }); + if (uri) { + return this.windowService.openWindow([{ workspaceUri: uri }], { forceNewWindow: options.forceNewWindow }); + } + + return; } return this.windowService.pickWorkspaceAndOpen(this.toNativeOpenDialogOptions(options)); } + async pickFileToSave(options: ISaveDialogOptions): Promise { + const schema = this.getFileSystemSchema(options); + if (this.shouldUseSimplified(schema)) { + if (!options.availableFileSystems) { + options.availableFileSystems = this.ensureFileSchema(schema); // always allow file as well + } + + options.title = nls.localize('saveFileAs.title', 'Save As'); + return this.saveRemoteResource(options); + } + + const result = await this.windowService.showSaveDialog(this.toNativeSaveDialogOptions(options)); + if (result) { + return URI.file(result); + } + + return; + } + private toNativeSaveDialogOptions(options: ISaveDialogOptions): Electron.SaveDialogOptions { return { defaultPath: options.defaultUri && options.defaultUri.fsPath, @@ -190,33 +215,34 @@ export class FileDialogService implements IFileDialogService { }; } - showSaveDialog(options: ISaveDialogOptions): Promise { + async showSaveDialog(options: ISaveDialogOptions): Promise { const schema = this.getFileSystemSchema(options); if (this.shouldUseSimplified(schema)) { if (!options.availableFileSystems) { options.availableFileSystems = [schema]; // by default only allow saving in the own file system } + return this.saveRemoteResource(options); } - return this.windowService.showSaveDialog(this.toNativeSaveDialogOptions(options)).then(result => { - if (result) { - return URI.file(result); - } + const result = await this.windowService.showSaveDialog(this.toNativeSaveDialogOptions(options)); + if (result) { + return URI.file(result); + } - return undefined; - }); + return; } - showOpenDialog(options: IOpenDialogOptions): Promise { + async showOpenDialog(options: IOpenDialogOptions): Promise { const schema = this.getFileSystemSchema(options); if (this.shouldUseSimplified(schema)) { if (!options.availableFileSystems) { options.availableFileSystems = [schema]; // by default only allow loading in the own file system } - return this.pickRemoteResource(options).then(uri => { - return uri ? [uri] : undefined; - }); + + const uri = await this.pickRemoteResource(options); + + return uri ? [uri] : undefined; } const defaultUri = options.defaultUri; @@ -243,16 +269,20 @@ export class FileDialogService implements IFileDialogService { newOptions.properties!.push('multiSelections'); } - return this.windowService.showOpenDialog(newOptions).then(result => result ? result.map(URI.file) : undefined); + const result = await this.windowService.showOpenDialog(newOptions); + + return result ? result.map(URI.file) : undefined; } private pickRemoteResource(options: IOpenDialogOptions): Promise { const remoteFileDialog = this.instantiationService.createInstance(RemoteFileDialog); + return remoteFileDialog.showOpenDialog(options); } private saveRemoteResource(options: ISaveDialogOptions): Promise { const remoteFileDialog = this.instantiationService.createInstance(RemoteFileDialog); + return remoteFileDialog.showSaveDialog(options); } @@ -263,7 +293,6 @@ export class FileDialogService implements IFileDialogService { private getFileSystemSchema(options: { availableFileSystems?: string[], defaultUri?: URI }): string { return options.availableFileSystems && options.availableFileSystems[0] || options.defaultUri && options.defaultUri.scheme || this.getSchemeFilterForWindow(); } - } function isUntitledWorkspace(path: URI, environmentService: IWorkbenchEnvironmentService): boolean { diff --git a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts index 2a5d47b85cc..0ae7ef8a596 100644 --- a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts @@ -22,11 +22,14 @@ import { Schemas } from 'vs/base/common/network'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { RemoteFileDialogContext } from 'vs/workbench/common/contextkeys'; -import { equalsIgnoreCase, format } from 'vs/base/common/strings'; -import { OpenLocalFileAction, OpenLocalFileFolderAction, OpenLocalFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; +import { equalsIgnoreCase, format, startsWithIgnoreCase } from 'vs/base/common/strings'; +import { OpenLocalFileCommand, OpenLocalFileFolderCommand, OpenLocalFolderCommand, SaveLocalFileCommand } from 'vs/workbench/browser/actions/workspaceActions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; +import { isValidBasename } from 'vs/base/common/extpath'; +import { RemoteFileDialogContext } from 'vs/workbench/browser/contextkeys'; +import { Emitter } from 'vs/base/common/event'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; interface FileQuickPickItem extends IQuickPickItem { uri: URI; @@ -40,11 +43,6 @@ enum UpdateResult { InvalidPath } -// Reference: https://en.wikipedia.org/wiki/Filename -const WINDOWS_INVALID_FILE_CHARS = /[\\/:\*\?"<>\|]/g; -const UNIX_INVALID_FILE_CHARS = /[\\/]/g; -const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])$/i; - export class RemoteFileDialog { private options: IOpenDialogOptions; private currentFolder: URI; @@ -63,6 +61,12 @@ export class RemoteFileDialog { private userHome: URI; private badPath: string | undefined; private remoteAgentEnvironment: IRemoteAgentEnvironment | null; + private separator: string; + private onBusyChangeEmitter = new Emitter(); + + protected disposables: IDisposable[] = [ + this.onBusyChangeEmitter + ]; constructor( @IFileService private readonly fileService: IFileService, @@ -82,8 +86,19 @@ export class RemoteFileDialog { this.contextKey = RemoteFileDialogContext.bindTo(contextKeyService); } + set busy(busy: boolean) { + if (this.filePickBox.busy !== busy) { + this.filePickBox.busy = busy; + this.onBusyChangeEmitter.fire(busy); + } + } + + get busy(): boolean { + return this.filePickBox.busy; + } + public async showOpenDialog(options: IOpenDialogOptions = {}): Promise { - this.scheme = this.getScheme(options.defaultUri, options.availableFileSystems); + this.scheme = this.getScheme(options.availableFileSystems); this.userHome = await this.getUserHome(); const newOptions = await this.getOptions(options); if (!newOptions) { @@ -94,7 +109,7 @@ export class RemoteFileDialog { } public async showSaveDialog(options: ISaveDialogOptions): Promise { - this.scheme = this.getScheme(options.defaultUri, options.availableFileSystems); + this.scheme = this.getScheme(options.availableFileSystems); this.userHome = await this.getUserHome(); this.requiresTrailing = true; const newOptions = await this.getOptions(options, true); @@ -113,9 +128,13 @@ export class RemoteFileDialog { } private getOptions(options: ISaveDialogOptions | IOpenDialogOptions, isSave: boolean = false): IOpenDialogOptions | undefined { - let defaultUri = options.defaultUri; - const filename = (defaultUri && isSave && (resources.dirname(defaultUri).path === '/')) ? resources.basename(defaultUri) : undefined; - if (!defaultUri || filename) { + let defaultUri: URI | undefined = undefined; + let filename: string | undefined = undefined; + if (options.defaultUri) { + defaultUri = (this.scheme === options.defaultUri.scheme) ? options.defaultUri : undefined; + filename = isSave ? resources.basename(options.defaultUri) : undefined; + } + if (!defaultUri) { defaultUri = this.userHome; if (filename) { defaultUri = resources.joinPath(defaultUri, filename); @@ -135,8 +154,8 @@ export class RemoteFileDialog { return resources.toLocalResource(URI.from({ scheme: this.scheme, path }), this.scheme === Schemas.file ? undefined : this.remoteAuthority); } - private getScheme(defaultUri: URI | undefined, available: string[] | undefined): string { - return defaultUri ? defaultUri.scheme : (available ? available[0] : Schemas.file); + private getScheme(available: string[] | undefined): string { + return available ? available[0] : Schemas.file; } private async getRemoteAgentEnvironment(): Promise { @@ -159,6 +178,7 @@ export class RemoteFileDialog { private async pickResource(isSave: boolean = false): Promise { this.allowFolderSelection = !!this.options.canSelectFolders; this.allowFileSelection = !!this.options.canSelectFiles; + this.separator = this.labelService.getSeparator(this.scheme, this.remoteAuthority); this.hidden = false; let homedir: URI = this.options.defaultUri ? this.options.defaultUri : this.workspaceContextService.getWorkspace().folders[0].uri; let stat: IFileStat | undefined; @@ -187,7 +207,7 @@ export class RemoteFileDialog { return new Promise(async (resolve) => { this.filePickBox = this.quickInputService.createQuickPick(); - this.filePickBox.busy = true; + this.busy = true; this.filePickBox.matchOnLabel = false; this.filePickBox.autoFocusOnList = false; this.filePickBox.ignoreFocusOut = true; @@ -195,7 +215,12 @@ export class RemoteFileDialog { if (this.options && this.options.availableFileSystems && (this.options.availableFileSystems.length > 1)) { this.filePickBox.customButton = true; this.filePickBox.customLabel = nls.localize('remoteFileDialog.local', 'Show Local'); - const action = this.allowFileSelection ? (this.allowFolderSelection ? OpenLocalFileFolderAction : OpenLocalFileAction) : OpenLocalFolderAction; + let action; + if (isSave) { + action = SaveLocalFileCommand; + } else { + action = this.allowFileSelection ? (this.allowFolderSelection ? OpenLocalFileFolderCommand : OpenLocalFileCommand) : OpenLocalFolderCommand; + } const keybinding = this.keybindingService.lookupKeybinding(action.ID); if (keybinding) { const label = keybinding.getLabel(); @@ -205,7 +230,7 @@ export class RemoteFileDialog { } } - let isResolving = false; + let isResolving: number = 0; let isAcceptHandled = false; this.currentFolder = homedir; this.userEnteredPathSegment = ''; @@ -217,24 +242,27 @@ export class RemoteFileDialog { this.filePickBox.items = []; function doResolve(dialog: RemoteFileDialog, uri: URI | undefined) { + if (uri) { + uri = resources.removeTrailingPathSeparator(uri); + } resolve(uri); dialog.contextKey.set(false); dialog.filePickBox.dispose(); + dispose(dialog.disposables); } this.filePickBox.onDidCustom(() => { - if (isAcceptHandled || this.filePickBox.busy) { + if (isAcceptHandled || this.busy) { return; } isAcceptHandled = true; - isResolving = true; + isResolving++; if (this.options.availableFileSystems && (this.options.availableFileSystems.length > 1)) { this.options.availableFileSystems.shift(); } - this.options.defaultUri = undefined; this.filePickBox.hide(); - if (this.requiresTrailing) { + if (isSave) { return this.fileDialogService.showSaveDialog(this.options).then(result => { doResolve(this, result); }); @@ -245,25 +273,38 @@ export class RemoteFileDialog { } }); - this.filePickBox.onDidAccept(_ => { - if (isAcceptHandled || this.filePickBox.busy) { + function handleAccept(dialog: RemoteFileDialog) { + if (dialog.busy) { + // Save the accept until the file picker is not busy. + dialog.onBusyChangeEmitter.event((busy: boolean) => { + if (!busy) { + handleAccept(dialog); + } + }); + return; + } else if (isAcceptHandled) { return; } isAcceptHandled = true; - isResolving = true; - this.onDidAccept().then(resolveValue => { + isResolving++; + dialog.onDidAccept().then(resolveValue => { if (resolveValue) { - this.filePickBox.hide(); - doResolve(this, resolveValue); - } else if (this.hidden) { - doResolve(this, undefined); + dialog.filePickBox.hide(); + doResolve(dialog, resolveValue); + } else if (dialog.hidden) { + doResolve(dialog, undefined); } else { - isResolving = false; + isResolving--; isAcceptHandled = false; } }); + } + + this.filePickBox.onDidAccept(_ => { + handleAccept(this); }); + this.filePickBox.onDidChangeActive(i => { isAcceptHandled = false; // update input box to match the first selected item @@ -280,16 +321,17 @@ export class RemoteFileDialog { // If the user has just entered more bad path, don't change anything if (!equalsIgnoreCase(value, this.constructFullUserPath()) && !this.isBadSubpath(value)) { this.filePickBox.validationMessage = undefined; - const valueUri = this.remoteUriFrom(this.trimTrailingSlash(this.filePickBox.value)); + const filePickBoxUri = this.filePickBoxValue(); let updated: UpdateResult = UpdateResult.NotUpdated; - if (!resources.isEqual(this.remoteUriFrom(this.trimTrailingSlash(this.pathFromUri(this.currentFolder))), valueUri, true)) { - updated = await this.tryUpdateItems(value, this.remoteUriFrom(this.filePickBox.value)); + if (!resources.isEqual(this.currentFolder, filePickBoxUri, true)) { + updated = await this.tryUpdateItems(value, filePickBoxUri); } if (updated === UpdateResult.NotUpdated) { this.setActiveItems(value); } } else { this.filePickBox.activeItems = []; + this.userEnteredPathSegment = ''; } } } catch { @@ -298,20 +340,20 @@ export class RemoteFileDialog { }); this.filePickBox.onDidHide(() => { this.hidden = true; - if (!isResolving) { + if (isResolving === 0) { doResolve(this, undefined); } }); this.filePickBox.show(); this.contextKey.set(true); - await this.updateItems(homedir, false, this.trailing); + await this.updateItems(homedir, true, this.trailing); if (this.trailing) { this.filePickBox.valueSelection = [this.filePickBox.value.length - this.trailing.length, this.filePickBox.value.length - ext.length]; } else { this.filePickBox.valueSelection = [this.filePickBox.value.length, this.filePickBox.value.length]; } - this.filePickBox.busy = false; + this.busy = false; }); } @@ -328,75 +370,90 @@ export class RemoteFileDialog { } private constructFullUserPath(): string { - return this.pathAppend(this.currentFolder, this.userEnteredPathSegment); + if (equalsIgnoreCase(this.filePickBox.value.substr(0, this.userEnteredPathSegment.length), this.userEnteredPathSegment)) { + return this.pathFromUri(this.currentFolder); + } else { + return this.pathAppend(this.currentFolder, this.userEnteredPathSegment); + } + } + + private filePickBoxValue(): URI { + // The file pick box can't render everything, so we use the current folder to create the uri so that it is an existing path. + const directUri = this.remoteUriFrom(this.filePickBox.value); + const currentPath = this.pathFromUri(this.currentFolder); + if (equalsIgnoreCase(this.filePickBox.value, currentPath)) { + return this.currentFolder; + } + const currentDisplayUri = this.remoteUriFrom(currentPath); + const relativePath = resources.relativePath(currentDisplayUri, directUri); + const isSameRoot = (this.filePickBox.value.length > 1 && currentPath.length > 1) ? equalsIgnoreCase(this.filePickBox.value.substr(0, 2), currentPath.substr(0, 2)) : false; + if (relativePath && isSameRoot) { + const path = resources.joinPath(this.currentFolder, relativePath); + return resources.hasTrailingPathSeparator(directUri) ? resources.addTrailingPathSeparator(path) : path; + } else { + return directUri; + } } private async onDidAccept(): Promise { - this.filePickBox.busy = true; - let resolveValue: URI | undefined; - let navigateValue: URI | undefined; - let inputUri: URI | undefined; - let inputUriDirname: URI | undefined; - let stat: IFileStat | undefined; - let statDirname: IFileStat | undefined; - try { - inputUri = resources.removeTrailingPathSeparator(this.remoteUriFrom(this.filePickBox.value)); - inputUriDirname = resources.dirname(inputUri); - statDirname = await this.fileService.resolve(inputUriDirname); - stat = await this.fileService.resolve(inputUri); - } catch (e) { - // do nothing + this.busy = true; + if (this.filePickBox.activeItems.length === 1) { + const item = this.filePickBox.selectedItems[0]; + if (item.isFolder) { + if (this.trailing) { + await this.updateItems(item.uri, true, this.trailing); + } else { + // When possible, cause the update to happen by modifying the input box. + // This allows all input box updates to happen first, and uses the same code path as the user typing. + const newPath = this.pathFromUri(item.uri); + if (startsWithIgnoreCase(newPath, this.filePickBox.value) && (equalsIgnoreCase(item.label, resources.basename(item.uri)))) { + this.filePickBox.valueSelection = [this.pathFromUri(this.currentFolder).length, this.filePickBox.value.length]; + this.insertText(newPath, item.label); + } else if ((item.label === '..') && startsWithIgnoreCase(this.filePickBox.value, newPath)) { + this.filePickBox.valueSelection = [newPath.length, this.filePickBox.value.length]; + this.insertText(newPath, ''); + } else { + await this.updateItems(item.uri, true); + } + } + this.filePickBox.busy = false; + return; + } + } else { + // If the items have updated, don't try to resolve + if ((await this.tryUpdateItems(this.filePickBox.value, this.filePickBoxValue())) !== UpdateResult.NotUpdated) { + this.filePickBox.busy = false; + return; + } } + let resolveValue: URI | undefined; // Find resolve value if (this.filePickBox.activeItems.length === 0) { - if (!this.requiresTrailing && resources.isEqual(this.currentFolder, inputUri, true)) { - resolveValue = inputUri; - } else if (statDirname && statDirname.isDirectory) { - resolveValue = inputUri; - } else if (stat && stat.isDirectory) { - navigateValue = inputUri; - } + resolveValue = this.filePickBoxValue(); } else if (this.filePickBox.activeItems.length === 1) { - const item = this.filePickBox.selectedItems[0]; - if (item) { - if (!item.isFolder) { - resolveValue = item.uri; - } else { - navigateValue = item.uri; - } - } + resolveValue = this.filePickBox.selectedItems[0].uri; } - - - if (navigateValue) { - // Try to navigate into the folder. - await this.updateItems(navigateValue, true, this.trailing); - } else { - if (resolveValue) { - resolveValue = this.addPostfix(resolveValue); - } - if (await this.validate(resolveValue)) { - this.filePickBox.busy = false; - return resolveValue; - } + if (resolveValue) { + resolveValue = this.addPostfix(resolveValue); } - this.filePickBox.busy = false; + if (await this.validate(resolveValue)) { + this.busy = false; + return resolveValue; + } + this.busy = false; return undefined; } private async tryUpdateItems(value: string, valueUri: URI): Promise { - if (this.filePickBox.busy) { - this.badPath = undefined; - return UpdateResult.Updating; - } else if ((value.length > 0) && ((value[value.length - 1] === '~') || (value[0] === '~'))) { + if ((value.length > 0) && ((value[value.length - 1] === '~') || (value[0] === '~'))) { let newDir = this.userHome; if ((value[0] === '~') && (value.length > 1)) { newDir = resources.joinPath(newDir, value.substring(1)); } await this.updateItems(newDir, true); return UpdateResult.Updated; - } else if (this.endsWithSlash(value) || (!resources.isEqual(this.currentFolder, resources.dirname(valueUri), true) && resources.isEqualOrParent(this.currentFolder, resources.dirname(valueUri), true))) { + } else if (!resources.isEqual(this.currentFolder, valueUri, true) && (this.endsWithSlash(value) || (!resources.isEqual(this.currentFolder, resources.dirname(valueUri), true) && resources.isEqualOrParent(this.currentFolder, resources.dirname(valueUri), true)))) { let stat: IFileStat | undefined; try { stat = await this.fileService.resolve(valueUri); @@ -415,7 +472,7 @@ export class RemoteFileDialog { return UpdateResult.InvalidPath; } else { const inputUriDirname = resources.dirname(valueUri); - if (!resources.isEqual(this.remoteUriFrom(this.trimTrailingSlash(this.pathFromUri(this.currentFolder))), inputUriDirname, true)) { + if (!resources.isEqual(resources.removeTrailingPathSeparator(this.currentFolder), inputUriDirname, true)) { let statWithoutTrailing: IFileStat | undefined; try { statWithoutTrailing = await this.fileService.resolve(inputUriDirname); @@ -440,11 +497,13 @@ export class RemoteFileDialog { const userPath = this.constructFullUserPath(); if (equalsIgnoreCase(userPath, value.substring(0, userPath.length))) { let hasMatch = false; - for (let i = 0; i < this.filePickBox.items.length; i++) { - const item = this.filePickBox.items[i]; - if (this.setAutoComplete(value, inputBasename, item)) { - hasMatch = true; - break; + if (inputBasename.length > this.userEnteredPathSegment.length) { + for (let i = 0; i < this.filePickBox.items.length; i++) { + const item = this.filePickBox.items[i]; + if (this.setAutoComplete(value, inputBasename, item)) { + hasMatch = true; + break; + } } } if (!hasMatch) { @@ -453,17 +512,13 @@ export class RemoteFileDialog { this.filePickBox.activeItems = []; } } else { - if (!equalsIgnoreCase(inputBasename, resources.basename(this.currentFolder))) { - this.userEnteredPathSegment = inputBasename; - } else { - this.userEnteredPathSegment = ''; - } + this.userEnteredPathSegment = inputBasename; this.autoCompletePathSegment = ''; } } private setAutoComplete(startingValue: string, startingBasename: string, quickPickItem: FileQuickPickItem, force: boolean = false): boolean { - if (this.filePickBox.busy) { + if (this.busy) { // We're in the middle of something else. Doing an auto complete now can result jumbled or incorrect autocompletes. this.userEnteredPathSegment = startingBasename; this.autoCompletePathSegment = ''; @@ -487,9 +542,6 @@ export class RemoteFileDialog { // Changing the active items will trigger the onDidActiveItemsChanged. Clear the autocomplete first, then set it after. this.autoCompletePathSegment = ''; this.filePickBox.activeItems = [quickPickItem]; - this.autoCompletePathSegment = this.trimTrailingSlash(itemBasename.substr(startingBasename.length)); - this.insertText(startingValue + this.autoCompletePathSegment, this.autoCompletePathSegment); - this.filePickBox.valueSelection = [startingValue.length, this.filePickBox.value.length]; return true; } else if (force && (!equalsIgnoreCase(quickPickItem.label, (this.userEnteredPathSegment + this.autoCompletePathSegment)))) { this.userEnteredPathSegment = ''; @@ -606,7 +658,7 @@ export class RemoteFileDialog { // Show a yes/no prompt const message = nls.localize('remoteFileDialog.validateExisting', '{0} already exists. Are you sure you want to overwrite it?', resources.basename(uri)); return this.yesNoPrompt(uri, message); - } else if (!(await this.isValidBaseName(resources.basename(uri)))) { + } else if (!(isValidBasename(resources.basename(uri), await this.isWindowsOS()))) { // Filename not allowed this.filePickBox.validationMessage = nls.localize('remoteFileDialog.validateBadFilename', 'Please enter a valid file name.'); return Promise.resolve(false); @@ -634,49 +686,41 @@ export class RemoteFileDialog { } private async updateItems(newFolder: URI, force: boolean = false, trailing?: string) { - this.filePickBox.busy = true; + this.busy = true; this.userEnteredPathSegment = trailing ? trailing : ''; this.autoCompletePathSegment = ''; const newValue = trailing ? this.pathFromUri(resources.joinPath(newFolder, trailing)) : this.pathFromUri(newFolder, true); - const oldFolder = this.currentFolder; - const newFolderPath = this.pathFromUri(newFolder, true); - this.currentFolder = this.remoteUriFrom(newFolderPath); + this.currentFolder = resources.addTrailingPathSeparator(newFolder, this.separator); return this.createItems(this.currentFolder).then(items => { this.filePickBox.items = items; if (this.allowFolderSelection) { this.filePickBox.activeItems = []; } - if (!equalsIgnoreCase(this.filePickBox.value, newValue)) { - // the user might have continued typing while we were updating. Only update the input box if it doesn't match the directory. - if (!equalsIgnoreCase(this.filePickBox.value.substring(0, newValue.length), newValue)) { - this.filePickBox.valueSelection = [0, this.filePickBox.value.length]; - this.insertText(newValue, newValue); - } else if (force || equalsIgnoreCase(this.pathFromUri(resources.dirname(oldFolder), true), newFolderPath)) { - // This is the case where the user went up one dir or is clicking on dirs. We need to make sure that we remove the final dir. - this.filePickBox.valueSelection = [newFolderPath.length, this.filePickBox.value.length]; - this.insertText(newValue, ''); - } + // the user might have continued typing while we were updating. Only update the input box if it doesn't match the directory. + if (!equalsIgnoreCase(this.filePickBox.value, newValue) && force) { + this.filePickBox.valueSelection = [0, this.filePickBox.value.length]; + this.insertText(newValue, newValue); } if (force && trailing) { // Keep the cursor position in front of the save as name. this.filePickBox.valueSelection = [this.filePickBox.value.length - trailing.length, this.filePickBox.value.length - trailing.length]; - } else { + } else if (!trailing) { + // If there is trailing, we don't move the cursor. If there is no trailing, cursor goes at the end. this.filePickBox.valueSelection = [this.filePickBox.value.length, this.filePickBox.value.length]; } - this.filePickBox.busy = false; + this.busy = false; }); } private pathFromUri(uri: URI, endWithSeparator: boolean = false): string { - const sep = this.labelService.getSeparator(uri.scheme, uri.authority); - let result: string; - if (sep === '/') { - result = uri.fsPath.replace(/\\/g, sep); + let result: string = uri.fsPath.replace(/\n/g, ''); + if (this.separator === '/') { + result = result.replace(/\\/g, this.separator); } else { - result = uri.fsPath.replace(/\//g, sep); + result = result.replace(/\//g, this.separator); } if (endWithSeparator && !this.endsWithSlash(result)) { - result = result + sep; + result = result + this.separator; } return result; } @@ -684,7 +728,7 @@ export class RemoteFileDialog { private pathAppend(uri: URI, additional: string): string { if ((additional === '..') || (additional === '.')) { const basePath = this.pathFromUri(uri); - return basePath + (this.endsWithSlash(basePath) ? '' : this.labelService.getSeparator(uri.scheme, uri.authority)) + additional; + return basePath + (this.endsWithSlash(basePath) ? '' : this.separator) + additional; } else { return this.pathFromUri(resources.joinPath(uri, additional)); } @@ -699,37 +743,6 @@ export class RemoteFileDialog { return isWindowsOS; } - private async isValidBaseName(name: string): Promise { - if (!name || name.length === 0 || /^\s+$/.test(name)) { - return false; // require a name that is not just whitespace - } - - const isWindowsOS = await this.isWindowsOS(); - const INVALID_FILE_CHARS = isWindowsOS ? WINDOWS_INVALID_FILE_CHARS : UNIX_INVALID_FILE_CHARS; - INVALID_FILE_CHARS.lastIndex = 0; // the holy grail of software development - if (INVALID_FILE_CHARS.test(name)) { - return false; // check for certain invalid file characters - } - - if (isWindowsOS && WINDOWS_FORBIDDEN_NAMES.test(name)) { - return false; // check for certain invalid file names - } - - if (name === '.' || name === '..') { - return false; // check for reserved values - } - - if (isWindowsOS && name[name.length - 1] === '.') { - return false; // Windows: file cannot end with a "." - } - - if (isWindowsOS && name.length !== name.trim().length) { - return false; // Windows: file cannot end with a whitespace - } - - return true; - } - private endsWithSlash(s: string) { return /[\/\\]$/.test(s); } @@ -743,7 +756,7 @@ export class RemoteFileDialog { private createBackItem(currFolder: URI): FileQuickPickItem | null { const parentFolder = resources.dirname(currFolder)!; if (!resources.isEqual(currFolder, parentFolder, true)) { - return { label: '..', uri: resources.dirname(currFolder), isFolder: true }; + return { label: '..', uri: resources.addTrailingPathSeparator(parentFolder, this.separator), isFolder: true }; } return null; } @@ -801,6 +814,7 @@ export class RemoteFileDialog { const stat = await this.fileService.resolve(fullPath); if (stat.isDirectory) { filename = this.basenameWithTrailingSlash(fullPath); + fullPath = resources.addTrailingPathSeparator(fullPath, this.separator); return { label: filename, uri: fullPath, isFolder: true, iconClasses: getIconClasses(this.modelService, this.modeService, fullPath || undefined, FileKind.FOLDER) }; } else if (!stat.isDirectory && this.allowFileSelection && this.filterFile(fullPath)) { return { label: filename, uri: fullPath, isFolder: false, iconClasses: getIconClasses(this.modelService, this.modeService, fullPath || undefined) }; diff --git a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts index 3a79b914643..02c36b66e4c 100644 --- a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts @@ -78,17 +78,16 @@ class NativeDialogService implements IDialogService { sharedProcessService.registerChannel('dialog', new DialogChannel(this)); } - confirm(confirmation: IConfirmation): Promise { + async confirm(confirmation: IConfirmation): Promise { this.logService.trace('DialogService#confirm', confirmation.message); const { options, buttonIndexMap } = this.massageMessageBoxOptions(this.getConfirmOptions(confirmation)); - return this.windowService.showMessageBox(options).then(result => { - return { - confirmed: buttonIndexMap[result.button] === 0 ? true : false, - checkboxChecked: result.checkboxChecked - }; - }); + const result = await this.windowService.showMessageBox(options); + return { + confirmed: buttonIndexMap[result.button] === 0 ? true : false, + checkboxChecked: result.checkboxChecked + }; } private getConfirmOptions(confirmation: IConfirmation): Electron.MessageBoxOptions { @@ -128,7 +127,7 @@ class NativeDialogService implements IDialogService { return opts; } - show(severity: Severity, message: string, buttons: string[], dialogOptions?: IDialogOptions): Promise { + async show(severity: Severity, message: string, buttons: string[], dialogOptions?: IDialogOptions): Promise { this.logService.trace('DialogService#show', message); const { options, buttonIndexMap } = this.massageMessageBoxOptions({ @@ -139,7 +138,8 @@ class NativeDialogService implements IDialogService { detail: dialogOptions ? dialogOptions.detail : undefined }); - return this.windowService.showMessageBox(options).then(result => buttonIndexMap[result.button]); + const result = await this.windowService.showMessageBox(options); + return buttonIndexMap[result.button]; } private massageMessageBoxOptions(options: Electron.MessageBoxOptions): IMassagedMessageBoxOptions { diff --git a/src/vs/workbench/services/editor/browser/codeEditorService.ts b/src/vs/workbench/services/editor/browser/codeEditorService.ts index fe5bb0a5372..86a5d8bcfb9 100644 --- a/src/vs/workbench/services/editor/browser/codeEditorService.ts +++ b/src/vs/workbench/services/editor/browser/codeEditorService.ts @@ -62,17 +62,16 @@ export class CodeEditorService extends CodeEditorServiceImpl { return this.doOpenCodeEditor(input, source, sideBySide); } - private doOpenCodeEditor(input: IResourceInput, source: ICodeEditor | null, sideBySide?: boolean): Promise { - return this.editorService.openEditor(input, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(control => { - if (control) { - const widget = control.getControl(); - if (isCodeEditor(widget)) { - return widget; - } + private async doOpenCodeEditor(input: IResourceInput, source: ICodeEditor | null, sideBySide?: boolean): Promise { + const control = await this.editorService.openEditor(input, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); + if (control) { + const widget = control.getControl(); + if (isCodeEditor(widget)) { + return widget; } + } - return null; - }); + return null; } } diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 6969fe75956..1aff720e6e3 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -21,7 +21,7 @@ import { localize } from 'vs/nls'; import { IEditorGroupsService, IEditorGroup, GroupsOrder, IEditorReplacement, GroupChangeKind, preferredSideBySideGroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IResourceEditor, ACTIVE_GROUP_TYPE, SIDE_GROUP_TYPE, SIDE_GROUP, IResourceEditorReplacement, IOpenEditorOverrideHandler, IVisibleEditor, IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { coalesce } from 'vs/base/common/arrays'; import { isCodeEditor, isDiffEditor, ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorGroupView, IEditorOpeningEvent, EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; @@ -118,24 +118,24 @@ export class EditorService extends Disposable implements EditorServiceImpl { } private registerGroupListeners(group: IEditorGroupView): void { - const groupDisposeables: IDisposable[] = []; + const groupDisposeables = new DisposableStore(); - groupDisposeables.push(group.onDidGroupChange(e => { + groupDisposeables.add(group.onDidGroupChange(e => { if (e.kind === GroupChangeKind.EDITOR_ACTIVE) { this.handleActiveEditorChange(group); this._onDidVisibleEditorsChange.fire(); } })); - groupDisposeables.push(group.onDidCloseEditor(event => { + groupDisposeables.add(group.onDidCloseEditor(event => { this._onDidCloseEditor.fire(event); })); - groupDisposeables.push(group.onWillOpenEditor(event => { + groupDisposeables.add(group.onWillOpenEditor(event => { this.onGroupWillOpenEditor(group, event); })); - groupDisposeables.push(group.onDidOpenEditorFail(editor => { + groupDisposeables.add(group.onDidOpenEditorFail(editor => { this._onDidOpenEditorFail.fire({ editor, groupId: group.id }); })); @@ -328,7 +328,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { openEditors(editors: IEditorInputWithOptions[], group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; openEditors(editors: IResourceEditor[], group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise; - openEditors(editors: Array, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise { + async openEditors(editors: Array, group?: IEditorGroup | GroupIdentifier | SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): Promise { // Convert to typed editors and options const typedEditors: IEditorInputWithOptions[] = []; @@ -364,7 +364,9 @@ export class EditorService extends Disposable implements EditorServiceImpl { result.push(group.openEditors(editorsWithOptions)); }); - return Promise.all(result).then(editors => coalesce(editors)); + const openedEditors = await Promise.all(result); + + return coalesce(openedEditors); } //#endregion @@ -528,7 +530,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { // Untitled file support const untitledInput = input; if (untitledInput.forceUntitled || !untitledInput.resource || (untitledInput.resource && untitledInput.resource.scheme === Schemas.untitled)) { - return this.untitledEditorService.createOrGet(untitledInput.resource, untitledInput.language, untitledInput.contents, untitledInput.encoding); + return this.untitledEditorService.createOrGet(untitledInput.resource, untitledInput.mode, untitledInput.contents, untitledInput.encoding); } // Resource Editor Support @@ -539,13 +541,13 @@ export class EditorService extends Disposable implements EditorServiceImpl { label = basename(resourceInput.resource); // derive the label from the path (but not for data URIs) } - return this.createOrGet(resourceInput.resource, this.instantiationService, label, resourceInput.description, resourceInput.encoding, resourceInput.forceFile) as EditorInput; + return this.createOrGet(resourceInput.resource, this.instantiationService, label, resourceInput.description, resourceInput.encoding, resourceInput.mode, resourceInput.forceFile) as EditorInput; } throw new Error('Unknown input type'); } - private createOrGet(resource: URI, instantiationService: IInstantiationService, label: string | undefined, description: string | undefined, encoding: string | undefined, forceFile: boolean | undefined): ICachedEditorInput { + private createOrGet(resource: URI, instantiationService: IInstantiationService, label: string | undefined, description: string | undefined, encoding: string | undefined, mode: string | undefined, forceFile: boolean | undefined): ICachedEditorInput { if (EditorService.CACHE.has(resource)) { const input = EditorService.CACHE.get(resource)!; if (input instanceof ResourceEditorInput) { @@ -556,10 +558,18 @@ export class EditorService extends Disposable implements EditorServiceImpl { if (description) { input.setDescription(description); } + + if (mode) { + input.setPreferredMode(mode); + } } else if (!(input instanceof DataUriEditorInput)) { if (encoding) { input.setPreferredEncoding(encoding); } + + if (mode) { + input.setPreferredMode(mode); + } } return input; @@ -569,7 +579,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { // File if (forceFile /* fix for https://github.com/Microsoft/vscode/issues/48275 */ || this.fileService.canHandleResource(resource)) { - input = this.fileInputFactory.createFileInput(resource, encoding, instantiationService); + input = this.fileInputFactory.createFileInput(resource, encoding, mode, instantiationService); } // Data URI @@ -579,13 +589,12 @@ export class EditorService extends Disposable implements EditorServiceImpl { // Resource else { - input = instantiationService.createInstance(ResourceEditorInput, label, description, resource); + input = instantiationService.createInstance(ResourceEditorInput, label, description, resource, mode); } + // Add to cache and remove when input gets disposed EditorService.CACHE.set(resource, input); - Event.once(input.onDispose)(() => { - EditorService.CACHE.delete(resource); - }); + Event.once(input.onDispose)(() => EditorService.CACHE.delete(resource)); return input; } @@ -641,18 +650,17 @@ export class DelegatingEditorService extends EditorService { this.editorOpenHandler = handler; } - protected doOpenEditor(group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions): Promise { + protected async doOpenEditor(group: IEditorGroup, editor: IEditorInput, options?: IEditorOptions): Promise { if (!this.editorOpenHandler) { return super.doOpenEditor(group, editor, options); } - return this.editorOpenHandler(group, editor, options).then(control => { - if (control) { - return control; // the opening was handled, so return early - } + const control = await this.editorOpenHandler(group, editor, options); + if (control) { + return control; // the opening was handled, so return early + } - return super.doOpenEditor(group, editor, options); - }); + return super.doOpenEditor(group, editor, options); } } diff --git a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts index 0fe83803a0c..28720257840 100644 --- a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts @@ -24,10 +24,10 @@ export class TestEditorControl extends BaseEditor { constructor(@ITelemetryService telemetryService: ITelemetryService) { super('MyFileEditorForEditorGroupService', NullTelemetryService, new TestThemeService(), new TestStorageService()); } - setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Promise { + async setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Promise { super.setInput(input, options, token); - return input.resolve().then(() => undefined); + await input.resolve(); } getId(): string { return 'MyFileEditorForEditorGroupService'; } @@ -45,11 +45,13 @@ export class TestEditorInput extends EditorInput implements IFileEditorInput { setEncoding(encoding: string) { } getEncoding(): string { return null!; } setPreferredEncoding(encoding: string) { } + setMode(mode: string) { } + setPreferredMode(mode: string) { } getResource(): URI { return this.resource; } setForceOpenAsBinary(): void { } } -suite('Editor groups service', () => { +suite('EditorGroupsService', () => { function registerTestEditorInput(): void { @@ -291,7 +293,7 @@ suite('Editor groups service', () => { part.dispose(); }); - test('copy/merge groups', function () { + test('copy/merge groups', async () => { const part = createPart(); let groupAddedCounter = 0; @@ -312,40 +314,32 @@ suite('Editor groups service', () => { const input = new TestEditorInput(URI.file('foo/bar')); - return rootGroup.openEditor(input, EditorOptions.create({ pinned: true })).then(() => { - const rightGroup = part.addGroup(rootGroup, GroupDirection.RIGHT, { activate: true }); - const downGroup = part.copyGroup(rootGroup, rightGroup, GroupDirection.DOWN); - - assert.equal(groupAddedCounter, 2); - assert.equal(downGroup.count, 1); - assert.ok(downGroup.activeEditor instanceof TestEditorInput); - - part.mergeGroup(rootGroup, rightGroup, { mode: MergeGroupMode.COPY_EDITORS }); - assert.equal(rightGroup.count, 1); - assert.ok(rightGroup.activeEditor instanceof TestEditorInput); - - part.mergeGroup(rootGroup, rightGroup, { mode: MergeGroupMode.MOVE_EDITORS }); - assert.equal(rootGroup.count, 0); - - part.mergeGroup(rootGroup, downGroup); - assert.equal(groupRemovedCounter, 1); - assert.equal(rootGroupDisposed, true); - - groupAddedListener.dispose(); - groupRemovedListener.dispose(); - disposeListener.dispose(); - - part.dispose(); - }); + await rootGroup.openEditor(input, EditorOptions.create({ pinned: true })); + const rightGroup = part.addGroup(rootGroup, GroupDirection.RIGHT, { activate: true }); + const downGroup = part.copyGroup(rootGroup, rightGroup, GroupDirection.DOWN); + assert.equal(groupAddedCounter, 2); + assert.equal(downGroup.count, 1); + assert.ok(downGroup.activeEditor instanceof TestEditorInput); + part.mergeGroup(rootGroup, rightGroup, { mode: MergeGroupMode.COPY_EDITORS }); + assert.equal(rightGroup.count, 1); + assert.ok(rightGroup.activeEditor instanceof TestEditorInput); + part.mergeGroup(rootGroup, rightGroup, { mode: MergeGroupMode.MOVE_EDITORS }); + assert.equal(rootGroup.count, 0); + part.mergeGroup(rootGroup, downGroup); + assert.equal(groupRemovedCounter, 1); + assert.equal(rootGroupDisposed, true); + groupAddedListener.dispose(); + groupRemovedListener.dispose(); + disposeListener.dispose(); + part.dispose(); }); - test('whenRestored', () => { + test('whenRestored', async () => { const part = createPart(); - return part.whenRestored.then(() => { - assert.ok(true); - part.dispose(); - }); + await part.whenRestored; + assert.ok(true); + part.dispose(); }); test('options', () => { @@ -467,7 +461,7 @@ suite('Editor groups service', () => { part.dispose(); }); - test('openEditors / closeEditors', function () { + test('openEditors / closeEditors', async () => { const part = createPart(); const group = part.activeGroup; assert.equal(group.isEmpty(), true); @@ -475,20 +469,17 @@ suite('Editor groups service', () => { const input = new TestEditorInput(URI.file('foo/bar')); const inputInactive = new TestEditorInput(URI.file('foo/bar/inactive')); - return group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]).then(() => { - assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input); - assert.equal(group.getEditor(1), inputInactive); + await group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); + assert.equal(group.count, 2); + assert.equal(group.getEditor(0), input); + assert.equal(group.getEditor(1), inputInactive); - return group.closeEditors([input, inputInactive]).then(() => { - assert.equal(group.isEmpty(), true); - - part.dispose(); - }); - }); + await group.closeEditors([input, inputInactive]); + assert.equal(group.isEmpty(), true); + part.dispose(); }); - test('closeEditors (except one)', function () { + test('closeEditors (except one)', async () => { const part = createPart(); const group = part.activeGroup; assert.equal(group.isEmpty(), true); @@ -497,22 +488,19 @@ suite('Editor groups service', () => { const input2 = new TestEditorInput(URI.file('foo/bar2')); const input3 = new TestEditorInput(URI.file('foo/bar3')); - return group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]).then(() => { - assert.equal(group.count, 3); - assert.equal(group.getEditor(0), input1); - assert.equal(group.getEditor(1), input2); - assert.equal(group.getEditor(2), input3); + await group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]); + assert.equal(group.count, 3); + assert.equal(group.getEditor(0), input1); + assert.equal(group.getEditor(1), input2); + assert.equal(group.getEditor(2), input3); - return group.closeEditors({ except: input2 }).then(() => { - assert.equal(group.count, 1); - assert.equal(group.getEditor(0), input2); - - part.dispose(); - }); - }); + await group.closeEditors({ except: input2 }); + assert.equal(group.count, 1); + assert.equal(group.getEditor(0), input2); + part.dispose(); }); - test('closeEditors (saved only)', function () { + test('closeEditors (saved only)', async () => { const part = createPart(); const group = part.activeGroup; assert.equal(group.isEmpty(), true); @@ -521,21 +509,18 @@ suite('Editor groups service', () => { const input2 = new TestEditorInput(URI.file('foo/bar2')); const input3 = new TestEditorInput(URI.file('foo/bar3')); - return group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]).then(() => { - assert.equal(group.count, 3); - assert.equal(group.getEditor(0), input1); - assert.equal(group.getEditor(1), input2); - assert.equal(group.getEditor(2), input3); + await group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]); + assert.equal(group.count, 3); + assert.equal(group.getEditor(0), input1); + assert.equal(group.getEditor(1), input2); + assert.equal(group.getEditor(2), input3); - return group.closeEditors({ savedOnly: true }).then(() => { - assert.equal(group.count, 0); - - part.dispose(); - }); - }); + await group.closeEditors({ savedOnly: true }); + assert.equal(group.count, 0); + part.dispose(); }); - test('closeEditors (direction: right)', function () { + test('closeEditors (direction: right)', async () => { const part = createPart(); const group = part.activeGroup; assert.equal(group.isEmpty(), true); @@ -544,23 +529,20 @@ suite('Editor groups service', () => { const input2 = new TestEditorInput(URI.file('foo/bar2')); const input3 = new TestEditorInput(URI.file('foo/bar3')); - return group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]).then(() => { - assert.equal(group.count, 3); - assert.equal(group.getEditor(0), input1); - assert.equal(group.getEditor(1), input2); - assert.equal(group.getEditor(2), input3); + await group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]); + assert.equal(group.count, 3); + assert.equal(group.getEditor(0), input1); + assert.equal(group.getEditor(1), input2); + assert.equal(group.getEditor(2), input3); - return group.closeEditors({ direction: CloseDirection.RIGHT, except: input2 }).then(() => { - assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input1); - assert.equal(group.getEditor(1), input2); - - part.dispose(); - }); - }); + await group.closeEditors({ direction: CloseDirection.RIGHT, except: input2 }); + assert.equal(group.count, 2); + assert.equal(group.getEditor(0), input1); + assert.equal(group.getEditor(1), input2); + part.dispose(); }); - test('closeEditors (direction: left)', function () { + test('closeEditors (direction: left)', async () => { const part = createPart(); const group = part.activeGroup; assert.equal(group.isEmpty(), true); @@ -569,23 +551,20 @@ suite('Editor groups service', () => { const input2 = new TestEditorInput(URI.file('foo/bar2')); const input3 = new TestEditorInput(URI.file('foo/bar3')); - return group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]).then(() => { - assert.equal(group.count, 3); - assert.equal(group.getEditor(0), input1); - assert.equal(group.getEditor(1), input2); - assert.equal(group.getEditor(2), input3); + await group.openEditors([{ editor: input1, options: { pinned: true } }, { editor: input2, options: { pinned: true } }, { editor: input3 }]); + assert.equal(group.count, 3); + assert.equal(group.getEditor(0), input1); + assert.equal(group.getEditor(1), input2); + assert.equal(group.getEditor(2), input3); - return group.closeEditors({ direction: CloseDirection.LEFT, except: input2 }).then(() => { - assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input2); - assert.equal(group.getEditor(1), input3); - - part.dispose(); - }); - }); + await group.closeEditors({ direction: CloseDirection.LEFT, except: input2 }); + assert.equal(group.count, 2); + assert.equal(group.getEditor(0), input2); + assert.equal(group.getEditor(1), input3); + part.dispose(); }); - test('closeAllEditors', () => { + test('closeAllEditors', async () => { const part = createPart(); const group = part.activeGroup; assert.equal(group.isEmpty(), true); @@ -593,20 +572,17 @@ suite('Editor groups service', () => { const input = new TestEditorInput(URI.file('foo/bar')); const inputInactive = new TestEditorInput(URI.file('foo/bar/inactive')); - return group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]).then(() => { - assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input); - assert.equal(group.getEditor(1), inputInactive); + await group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); + assert.equal(group.count, 2); + assert.equal(group.getEditor(0), input); + assert.equal(group.getEditor(1), inputInactive); - return group.closeAllEditors().then(() => { - assert.equal(group.isEmpty(), true); - - part.dispose(); - }); - }); + await group.closeAllEditors(); + assert.equal(group.isEmpty(), true); + part.dispose(); }); - test('moveEditor (same group)', function () { + test('moveEditor (same group)', async () => { const part = createPart(); const group = part.activeGroup; assert.equal(group.isEmpty(), true); @@ -622,22 +598,19 @@ suite('Editor groups service', () => { } }); - return group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]).then(() => { - assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input); - assert.equal(group.getEditor(1), inputInactive); - - group.moveEditor(inputInactive, group, { index: 0 }); - assert.equal(editorMoveCounter, 1); - assert.equal(group.getEditor(0), inputInactive); - assert.equal(group.getEditor(1), input); - - editorGroupChangeListener.dispose(); - part.dispose(); - }); + await group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); + assert.equal(group.count, 2); + assert.equal(group.getEditor(0), input); + assert.equal(group.getEditor(1), inputInactive); + group.moveEditor(inputInactive, group, { index: 0 }); + assert.equal(editorMoveCounter, 1); + assert.equal(group.getEditor(0), inputInactive); + assert.equal(group.getEditor(1), input); + editorGroupChangeListener.dispose(); + part.dispose(); }); - test('moveEditor (across groups)', function () { + test('moveEditor (across groups)', async () => { const part = createPart(); const group = part.activeGroup; assert.equal(group.isEmpty(), true); @@ -647,23 +620,19 @@ suite('Editor groups service', () => { const input = new TestEditorInput(URI.file('foo/bar')); const inputInactive = new TestEditorInput(URI.file('foo/bar/inactive')); - return group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]).then(() => { - assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input); - assert.equal(group.getEditor(1), inputInactive); - - group.moveEditor(inputInactive, rightGroup, { index: 0 }); - assert.equal(group.count, 1); - assert.equal(group.getEditor(0), input); - - assert.equal(rightGroup.count, 1); - assert.equal(rightGroup.getEditor(0), inputInactive); - - part.dispose(); - }); + await group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); + assert.equal(group.count, 2); + assert.equal(group.getEditor(0), input); + assert.equal(group.getEditor(1), inputInactive); + group.moveEditor(inputInactive, rightGroup, { index: 0 }); + assert.equal(group.count, 1); + assert.equal(group.getEditor(0), input); + assert.equal(rightGroup.count, 1); + assert.equal(rightGroup.getEditor(0), inputInactive); + part.dispose(); }); - test('copyEditor (across groups)', function () { + test('copyEditor (across groups)', async () => { const part = createPart(); const group = part.activeGroup; assert.equal(group.isEmpty(), true); @@ -673,24 +642,20 @@ suite('Editor groups service', () => { const input = new TestEditorInput(URI.file('foo/bar')); const inputInactive = new TestEditorInput(URI.file('foo/bar/inactive')); - return group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]).then(() => { - assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input); - assert.equal(group.getEditor(1), inputInactive); - - group.copyEditor(inputInactive, rightGroup, { index: 0 }); - assert.equal(group.count, 2); - assert.equal(group.getEditor(0), input); - assert.equal(group.getEditor(1), inputInactive); - - assert.equal(rightGroup.count, 1); - assert.equal(rightGroup.getEditor(0), inputInactive); - - part.dispose(); - }); + await group.openEditors([{ editor: input, options: { pinned: true } }, { editor: inputInactive }]); + assert.equal(group.count, 2); + assert.equal(group.getEditor(0), input); + assert.equal(group.getEditor(1), inputInactive); + group.copyEditor(inputInactive, rightGroup, { index: 0 }); + assert.equal(group.count, 2); + assert.equal(group.getEditor(0), input); + assert.equal(group.getEditor(1), inputInactive); + assert.equal(rightGroup.count, 1); + assert.equal(rightGroup.getEditor(0), inputInactive); + part.dispose(); }); - test('replaceEditors', () => { + test('replaceEditors', async () => { const part = createPart(); const group = part.activeGroup; assert.equal(group.isEmpty(), true); @@ -698,17 +663,14 @@ suite('Editor groups service', () => { const input = new TestEditorInput(URI.file('foo/bar')); const inputInactive = new TestEditorInput(URI.file('foo/bar/inactive')); - return group.openEditor(input).then(() => { - assert.equal(group.count, 1); - assert.equal(group.getEditor(0), input); + await group.openEditor(input); + assert.equal(group.count, 1); + assert.equal(group.getEditor(0), input); - return group.replaceEditors([{ editor: input, replacement: inputInactive }]).then(() => { - assert.equal(group.count, 1); - assert.equal(group.getEditor(0), inputInactive); - - part.dispose(); - }); - }); + await group.replaceEditors([{ editor: input, replacement: inputInactive }]); + assert.equal(group.count, 1); + assert.equal(group.getEditor(0), inputInactive); + part.dispose(); }); test('find neighbour group (left/right)', function () { diff --git a/src/vs/workbench/services/editor/test/browser/editorService.test.ts b/src/vs/workbench/services/editor/test/browser/editorService.test.ts index a670ff43ae3..658a4a84e77 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -29,15 +29,17 @@ import { timeout } from 'vs/base/common/async'; import { toResource } from 'vs/base/test/common/utils'; import { IFileService } from 'vs/platform/files/common/files'; import { Disposable } from 'vs/base/common/lifecycle'; +import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; +import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel'; export class TestEditorControl extends BaseEditor { constructor(@ITelemetryService telemetryService: ITelemetryService) { super('MyTestEditorForEditorService', NullTelemetryService, new TestThemeService(), new TestStorageService()); } - setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Promise { + async setInput(input: EditorInput, options: EditorOptions, token: CancellationToken): Promise { super.setInput(input, options, token); - return input.resolve().then(() => undefined); + await input.resolve(); } getId(): string { return 'MyTestEditorForEditorService'; } @@ -56,6 +58,8 @@ export class TestEditorInput extends EditorInput implements IFileEditorInput { setEncoding(encoding: string) { } getEncoding(): string { return null!; } setPreferredEncoding(encoding: string) { } + setMode(mode: string) { } + setPreferredMode(mode: string) { } getResource(): URI { return this.resource; } setForceOpenAsBinary(): void { } setFailToOpen(): void { @@ -75,7 +79,7 @@ class FileServiceProvider extends Disposable { } } -suite('Editor service', () => { +suite('EditorService', () => { function registerTestEditorInput(): void { Registry.as(Extensions.Editors).registerEditor(new EditorDescriptor(TestEditorControl, 'MyTestEditorForEditorService', 'My Test Editor For Next Editor Service'), new SyncDescriptor(TestEditorInput)); @@ -83,7 +87,7 @@ suite('Editor service', () => { registerTestEditorInput(); - test('basics', function () { + test('basics', async () => { const partInstantiator = workbenchInstantiationService(); const part = partInstantiator.createInstance(EditorPart); @@ -112,51 +116,49 @@ suite('Editor service', () => { didCloseEditorListenerCounter++; }); - return part.whenRestored.then(() => { + await part.whenRestored; - // Open input - return service.openEditor(input, { pinned: true }).then(editor => { - assert.ok(editor instanceof TestEditorControl); - assert.equal(editor, service.activeControl); - assert.equal(input, service.activeEditor); - assert.equal(service.visibleControls.length, 1); - assert.equal(service.visibleControls[0], editor); - assert.ok(!service.activeTextEditorWidget); - assert.equal(service.visibleTextEditorWidgets.length, 0); - assert.equal(service.isOpen(input), true); - assert.equal(service.getOpened({ resource: input.getResource() }), input); - assert.equal(service.isOpen(input, part.activeGroup), true); - assert.equal(activeEditorChangeEventCounter, 1); - assert.equal(visibleEditorChangeEventCounter, 1); + // Open input + let editor = await service.openEditor(input, { pinned: true }); - // Close input - return editor!.group!.closeEditor(input).then(() => { - assert.equal(didCloseEditorListenerCounter, 1); - assert.equal(activeEditorChangeEventCounter, 2); - assert.equal(visibleEditorChangeEventCounter, 2); - assert.ok(input.gotDisposed); + assert.ok(editor instanceof TestEditorControl); + assert.equal(editor, service.activeControl); + assert.equal(input, service.activeEditor); + assert.equal(service.visibleControls.length, 1); + assert.equal(service.visibleControls[0], editor); + assert.ok(!service.activeTextEditorWidget); + assert.equal(service.visibleTextEditorWidgets.length, 0); + assert.equal(service.isOpen(input), true); + assert.equal(service.getOpened({ resource: input.getResource() }), input); + assert.equal(service.isOpen(input, part.activeGroup), true); + assert.equal(activeEditorChangeEventCounter, 1); + assert.equal(visibleEditorChangeEventCounter, 1); - // Open again 2 inputs - return service.openEditor(input, { pinned: true }).then(editor => { - return service.openEditor(otherInput, { pinned: true }).then(editor => { - assert.equal(service.visibleControls.length, 1); - assert.equal(service.isOpen(input), true); - assert.equal(service.isOpen(otherInput), true); + // Close input + await editor!.group!.closeEditor(input); - assert.equal(activeEditorChangeEventCounter, 4); - assert.equal(visibleEditorChangeEventCounter, 4); + assert.equal(didCloseEditorListenerCounter, 1); + assert.equal(activeEditorChangeEventCounter, 2); + assert.equal(visibleEditorChangeEventCounter, 2); + assert.ok(input.gotDisposed); - activeEditorChangeListener.dispose(); - visibleEditorChangeListener.dispose(); - didCloseEditorListener.dispose(); - }); - }); - }); - }); - }); + // Open again 2 inputs + await service.openEditor(input, { pinned: true }); + editor = await service.openEditor(otherInput, { pinned: true }); + + assert.equal(service.visibleControls.length, 1); + assert.equal(service.isOpen(input), true); + assert.equal(service.isOpen(otherInput), true); + + assert.equal(activeEditorChangeEventCounter, 4); + assert.equal(visibleEditorChangeEventCounter, 4); + + activeEditorChangeListener.dispose(); + visibleEditorChangeListener.dispose(); + didCloseEditorListener.dispose(); }); - test('openEditors() / replaceEditors()', function () { + test('openEditors() / replaceEditors()', async () => { const partInstantiator = workbenchInstantiationService(); const part = partInstantiator.createInstance(EditorPart); @@ -171,18 +173,16 @@ suite('Editor service', () => { const otherInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource2-openEditors')); const replaceInput = testInstantiationService.createInstance(TestEditorInput, URI.parse('my://resource3-openEditors')); - return part.whenRestored.then(() => { + await part.whenRestored; - // Open editors - return service.openEditors([{ editor: input }, { editor: otherInput }]).then(() => { - assert.equal(part.activeGroup.count, 2); + // Open editors + await service.openEditors([{ editor: input }, { editor: otherInput }]); + assert.equal(part.activeGroup.count, 2); - return service.replaceEditors([{ editor: input, replacement: replaceInput }], part.activeGroup).then(() => { - assert.equal(part.activeGroup.count, 2); - assert.equal(part.activeGroup.getIndexOfEditor(replaceInput), 0); - }); - }); - }); + // Replace editors + await service.replaceEditors([{ editor: input, replacement: replaceInput }], part.activeGroup); + assert.equal(part.activeGroup.count, 2); + assert.equal(part.activeGroup.getIndexOfEditor(replaceInput), 0); }); test('caching', function () { @@ -234,10 +234,15 @@ suite('Editor service', () => { assert.ok(!input1AgainAndAgain!.isDisposed()); }); - test('createInput', function () { + test('createInput', async function () { const instantiationService = workbenchInstantiationService(); const service: EditorService = instantiationService.createInstance(EditorService); + const mode = 'create-input-test'; + ModesRegistry.registerLanguage({ + id: mode, + }); + // Untyped Input (file) let input = service.createInput({ resource: toResource.call(this, '/index.html'), options: { selection: { startLineNumber: 1, startColumn: 1 } } }); assert(input instanceof FileEditorInput); @@ -250,6 +255,18 @@ suite('Editor service', () => { contentInput = input; assert.equal(contentInput.getPreferredEncoding(), 'utf16le'); + // Untyped Input (file, mode) + input = service.createInput({ resource: toResource.call(this, '/index.html'), mode }); + assert(input instanceof FileEditorInput); + contentInput = input; + assert.equal(contentInput.getPreferredMode(), mode); + + // Untyped Input (file, different mode) + input = service.createInput({ resource: toResource.call(this, '/index.html'), mode: 'text' }); + assert(input instanceof FileEditorInput); + contentInput = input; + assert.equal(contentInput.getPreferredMode(), 'text'); + // Untyped Input (untitled) input = service.createInput({ options: { selection: { startLineNumber: 1, startColumn: 1 } } }); assert(input instanceof UntitledEditorInput); @@ -257,6 +274,14 @@ suite('Editor service', () => { // Untyped Input (untitled with contents) input = service.createInput({ contents: 'Hello Untitled', options: { selection: { startLineNumber: 1, startColumn: 1 } } }); assert(input instanceof UntitledEditorInput); + let model = await input.resolve() as UntitledEditorModel; + assert.equal(model.textEditorModel!.getValue(), 'Hello Untitled'); + + // Untyped Input (untitled with mode) + input = service.createInput({ mode, options: { selection: { startLineNumber: 1, startColumn: 1 } } }); + assert(input instanceof UntitledEditorInput); + model = await input.resolve() as UntitledEditorModel; + assert.equal(model.getMode(), mode); // Untyped Input (untitled with file path) input = service.createInput({ resource: URI.file('/some/path.txt'), forceUntitled: true, options: { selection: { startLineNumber: 1, startColumn: 1 } } }); @@ -276,6 +301,10 @@ suite('Editor service', () => { assert.ok((input as UntitledEditorInput).hasAssociatedFilePath); provider.dispose(); + + // Untyped Input (resource) + input = service.createInput({ resource: URI.parse('custom:resource') }); + assert(input instanceof ResourceEditorInput); }); test('delegate', function (done) { @@ -298,7 +327,7 @@ suite('Editor service', () => { const ed = instantiationService.createInstance(MyEditor, 'my.editor'); - const inp = instantiationService.createInstance(ResourceEditorInput, 'name', 'description', URI.parse('my://resource-delegate')); + const inp = instantiationService.createInstance(ResourceEditorInput, 'name', 'description', URI.parse('my://resource-delegate'), undefined); const delegate = instantiationService.createInstance(DelegatingEditorService); delegate.setEditorOpenHandler((group: IEditorGroup, input: IEditorInput, options?: EditorOptions) => { assert.strictEqual(input, inp); @@ -311,7 +340,7 @@ suite('Editor service', () => { delegate.openEditor(inp); }); - test('close editor does not dispose when editor opened in other group', function () { + test('close editor does not dispose when editor opened in other group', async () => { const partInstantiator = workbenchInstantiationService(); const part = partInstantiator.createInstance(EditorPart); @@ -327,30 +356,26 @@ suite('Editor service', () => { const rootGroup = part.activeGroup; const rightGroup = part.addGroup(rootGroup, GroupDirection.RIGHT); - return part.whenRestored.then(() => { + await part.whenRestored; - // Open input - return service.openEditor(input, { pinned: true }).then(editor => { - return service.openEditor(input, { pinned: true }, rightGroup).then(editor => { - const editors = service.editors; - assert.equal(editors.length, 2); - assert.equal(editors[0], input); - assert.equal(editors[1], input); + // Open input + await service.openEditor(input, { pinned: true }); + await service.openEditor(input, { pinned: true }, rightGroup); - // Close input - return rootGroup.closeEditor(input).then(() => { - assert.equal(input.isDisposed(), false); + const editors = service.editors; + assert.equal(editors.length, 2); + assert.equal(editors[0], input); + assert.equal(editors[1], input); - return rightGroup.closeEditor(input).then(() => { - assert.equal(input.isDisposed(), true); - }); - }); - }); - }); - }); + // Close input + await rootGroup.closeEditor(input); + assert.equal(input.isDisposed(), false); + + await rightGroup.closeEditor(input); + assert.equal(input.isDisposed(), true); }); - test('open to the side', function () { + test('open to the side', async () => { const partInstantiator = workbenchInstantiationService(); const part = partInstantiator.createInstance(EditorPart); @@ -366,22 +391,20 @@ suite('Editor service', () => { const rootGroup = part.activeGroup; - return part.whenRestored.then(() => { - return service.openEditor(input1, { pinned: true }, rootGroup).then(editor => { - return service.openEditor(input1, { pinned: true, preserveFocus: true }, SIDE_GROUP).then(editor => { - assert.equal(part.activeGroup, rootGroup); - assert.equal(part.count, 2); - assert.equal(editor!.group, part.groups[1]); + await part.whenRestored; - // Open to the side uses existing neighbour group if any - return service.openEditor(input2, { pinned: true, preserveFocus: true }, SIDE_GROUP).then(editor => { - assert.equal(part.activeGroup, rootGroup); - assert.equal(part.count, 2); - assert.equal(editor!.group, part.groups[1]); - }); - }); - }); - }); + await service.openEditor(input1, { pinned: true }, rootGroup); + let editor = await service.openEditor(input1, { pinned: true, preserveFocus: true }, SIDE_GROUP); + + assert.equal(part.activeGroup, rootGroup); + assert.equal(part.count, 2); + assert.equal(editor!.group, part.groups[1]); + + // Open to the side uses existing neighbour group if any + editor = await service.openEditor(input2, { pinned: true, preserveFocus: true }, SIDE_GROUP); + assert.equal(part.activeGroup, rootGroup); + assert.equal(part.count, 2); + assert.equal(editor!.group, part.groups[1]); }); test('active editor change / visible editor change events', async function () { diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts new file mode 100644 index 00000000000..3525569601e --- /dev/null +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -0,0 +1,143 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IWindowConfiguration, IPath, IPathsToWaitFor } from 'vs/platform/windows/common/windows'; +import { IEnvironmentService, IExtensionHostDebugParams, IDebugParams } from 'vs/platform/environment/common/environment'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { URI } from 'vs/base/common/uri'; +import { IProcessEnvironment } from 'vs/base/common/platform'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { ExportData } from 'vs/base/common/performance'; +import { LogLevel } from 'vs/platform/log/common/log'; +import { joinPath } from 'vs/base/common/resources'; +import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; +import { Schemas } from 'vs/base/common/network'; + +export class BrowserWindowConfiguration implements IWindowConfiguration { + + _: any[]; + + machineId: string; + windowId: number; + logLevel: LogLevel; + + mainPid: number; + + appRoot: string; + execPath: string; + isInitialStartup?: boolean; + + userEnv: IProcessEnvironment; + nodeCachedDataDir?: string; + + backupPath?: string; + + workspace?: IWorkspaceIdentifier; + folderUri?: ISingleFolderWorkspaceIdentifier; + + remoteAuthority: string; + + zoomLevel?: number; + fullscreen?: boolean; + maximized?: boolean; + highContrast?: boolean; + frameless?: boolean; + accessibilitySupport?: boolean; + partsSplashPath?: string; + + perfStartTime?: number; + perfAppReady?: number; + perfWindowLoadTime?: number; + perfEntries: ExportData; + + filesToOpenOrCreate?: IPath[]; + filesToDiff?: IPath[]; + filesToWait?: IPathsToWaitFor; + termProgram?: string; +} + +export class BrowserWorkbenchEnvironmentService implements IEnvironmentService { + _serviceBrand: ServiceIdentifier; + + readonly configuration: IWindowConfiguration = new BrowserWindowConfiguration(); + + constructor(configuration: IWorkbenchConstructionOptions) { + this.args = { _: [] }; + this.appRoot = '/web/'; + this.appNameLong = 'Visual Studio Code - Web'; + + this.configuration.remoteAuthority = configuration.remoteAuthority; + + this.appSettingsHome = joinPath(URI.revive(JSON.parse(document.getElementById('vscode-remote-user-data-uri')!.getAttribute('data-settings')!)), 'User'); + this.settingsResource = joinPath(this.appSettingsHome, 'settings.json'); + this.keybindingsResource = joinPath(this.appSettingsHome, 'keybindings.json'); + this.keyboardLayoutResource = joinPath(this.appSettingsHome, 'keyboardLayout.json'); + + this.logsPath = '/web/logs'; + + this.debugExtensionHost = { + port: null, + break: false + }; + + this.webviewEndpoint = configuration.webviewEndpoint; + this.untitledWorkspacesHome = URI.from({ scheme: Schemas.untitled, path: 'Workspaces' }); + } + + untitledWorkspacesHome: URI; + extensionTestsLocationURI?: URI; + args: any; + execPath: string; + cliPath: string; + appRoot: string; + userHome: string; + userDataPath: string; + appNameLong: string; + appQuality?: string; + appSettingsHome: URI; + settingsResource: URI; + keybindingsResource: URI; + keyboardLayoutResource: URI; + machineSettingsHome: URI; + machineSettingsResource: URI; + settingsSearchBuildId?: number; + settingsSearchUrl?: string; + globalStorageHome: string; + workspaceStorageHome: string; + backupHome: string; + backupWorkspacesPath: string; + workspacesHome: string; + isExtensionDevelopment: boolean; + disableExtensions: boolean | string[]; + builtinExtensionsPath: string; + extensionsPath: string; + extensionDevelopmentLocationURI?: URI[]; + extensionTestsPath?: string; + debugExtensionHost: IExtensionHostDebugParams; + debugSearch: IDebugParams; + logExtensionHostCommunication: boolean; + isBuilt: boolean; + wait: boolean; + status: boolean; + log?: string; + logsPath: string; + verbose: boolean; + skipGettingStarted: boolean; + skipReleaseNotes: boolean; + skipAddToRecentlyOpened: boolean; + mainIPCHandle: string; + sharedIPCHandle: string; + nodeCachedDataDir?: string; + installSourcePath: string; + disableUpdates: boolean; + disableCrashReporter: boolean; + driverHandle?: string; + driverVerbose: boolean; + webviewEndpoint?: string; + + get webviewResourceRoot(): string { + return this.webviewEndpoint ? this.webviewEndpoint + '/vscode-resource' : 'vscode-resource:'; + } +} diff --git a/src/vs/workbench/services/environment/common/environmentService.ts b/src/vs/workbench/services/environment/common/environmentService.ts index 84c501ee1ea..fd4beaf134d 100644 --- a/src/vs/workbench/services/environment/common/environmentService.ts +++ b/src/vs/workbench/services/environment/common/environmentService.ts @@ -3,14 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; export const IWorkbenchEnvironmentService = createDecorator('environmentService'); export interface IWorkbenchEnvironmentService extends IEnvironmentService { - _serviceBrand: any; + + _serviceBrand: ServiceIdentifier; readonly configuration: IWindowConfiguration; } diff --git a/src/vs/workbench/services/extensionManagement/node/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/node/extensionEnablementService.ts index 2cc63fe6305..4e1a3788010 100644 --- a/src/vs/workbench/services/extensionManagement/node/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/node/extensionEnablementService.ts @@ -14,8 +14,9 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { isUndefinedOrNull } from 'vs/base/common/types'; import { ExtensionType, IExtension } from 'vs/platform/extensions/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { isUIExtension } from 'vs/workbench/services/extensions/node/extensionsUtil'; +import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IProductService } from 'vs/platform/product/common/product'; const DISABLED_EXTENSIONS_STORAGE_PATH = 'extensionsIdentifiers/disabled'; const ENABLED_EXTENSIONS_STORAGE_PATH = 'extensionsIdentifiers/enabled'; @@ -36,6 +37,7 @@ export class ExtensionEnablementService extends Disposable implements IExtension @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @IConfigurationService private readonly configurationService: IConfigurationService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, + @IProductService private readonly productService: IProductService, ) { super(); this.storageManger = this._register(new StorageManager(storageService)); @@ -137,7 +139,7 @@ export class ExtensionEnablementService extends Disposable implements IExtension return disabledExtensions.some(id => areSameExtensions({ id }, extension.identifier)); } if (this.environmentService.configuration.remoteAuthority) { - const server = isUIExtension(extension.manifest, this.configurationService) ? this.extensionManagementServerService.localExtensionManagementServer : this.extensionManagementServerService.remoteExtensionManagementServer; + const server = isUIExtension(extension.manifest, this.productService, this.configurationService) ? this.extensionManagementServerService.localExtensionManagementServer : this.extensionManagementServerService.remoteExtensionManagementServer; return this.extensionManagementServerService.getExtensionManagementServer(extension.location) !== server; } return false; diff --git a/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts b/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts index 9faa4c1c7ba..cdb4509a617 100644 --- a/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts +++ b/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts @@ -15,6 +15,7 @@ import { IExtensionContributions, ExtensionType, IExtension } from 'vs/platform/ import { isUndefinedOrNull } from 'vs/base/common/types'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ProductService } from 'vs/platform/product/node/productService'; function storageService(instantiationService: TestInstantiationService): IStorageService { let service = instantiationService.get(IStorageService); @@ -38,7 +39,9 @@ export class TestExtensionEnablementService extends ExtensionEnablementService { instantiationService.get(IWorkbenchEnvironmentService) || instantiationService.stub(IWorkbenchEnvironmentService, { configuration: Object.create(null) } as IWorkbenchEnvironmentService), instantiationService.get(IExtensionManagementService) || instantiationService.stub(IExtensionManagementService, { onDidInstallExtension: new Emitter().event, onDidUninstallExtension: new Emitter().event } as IExtensionManagementService), - instantiationService.get(IConfigurationService), instantiationService.get(IExtensionManagementServerService)); + instantiationService.get(IConfigurationService), instantiationService.get(IExtensionManagementServerService), + new ProductService() + ); } public reset(): void { diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts new file mode 100644 index 00000000000..23fa0b21c4a --- /dev/null +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -0,0 +1,101 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IProductService } from 'vs/platform/product/common/product'; +import { AbstractExtensionService } from 'vs/workbench/services/extensions/common/abstractExtensionService'; +import { browserWebSocketFactory } from 'vs/platform/remote/browser/browserWebSocketFactory'; +import { ExtensionHostProcessManager } from 'vs/workbench/services/extensions/common/extensionHostProcessManager'; +import { RemoteExtensionHostClient, IInitDataProvider } from 'vs/workbench/services/extensions/common/remoteExtensionHostClient'; +import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; + +export class ExtensionService extends AbstractExtensionService implements IExtensionService { + + private _remoteExtensionsEnvironmentData: IRemoteAgentEnvironment | null; + + constructor( + @IInstantiationService instantiationService: IInstantiationService, + @INotificationService notificationService: INotificationService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @ITelemetryService telemetryService: ITelemetryService, + @IExtensionEnablementService extensionEnablementService: IExtensionEnablementService, + @IFileService fileService: IFileService, + @IProductService productService: IProductService, + @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService, + ) { + super( + instantiationService, + notificationService, + environmentService, + telemetryService, + extensionEnablementService, + fileService, + productService, + ); + + this._remoteExtensionsEnvironmentData = null; + this._initialize(); + } + + private _createProvider(remoteAuthority: string): IInitDataProvider { + return { + remoteAuthority: remoteAuthority, + getInitData: () => { + return this.whenInstalledExtensionsRegistered().then(() => { + return this._remoteExtensionsEnvironmentData!; + }); + } + }; + } + + protected _createExtensionHosts(isInitialStart: boolean, initialActivationEvents: string[]): ExtensionHostProcessManager[] { + const result: ExtensionHostProcessManager[] = []; + + const remoteAgentConnection = this._remoteAgentService.getConnection()!; + const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, this.getExtensions(), this._createProvider(remoteAgentConnection.remoteAuthority), browserWebSocketFactory); + const remoteExtHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, remoteExtHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); + result.push(remoteExtHostProcessManager); + + return result; + } + + protected async _scanAndHandleExtensions(): Promise { + // fetch the remote environment + const remoteEnv = (await this._remoteAgentService.getEnvironment())!; + + // enable or disable proposed API per extension + this._checkEnableProposedApi(remoteEnv.extensions); + + // remove disabled extensions + remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension)); + + // save for remote extension's init data + this._remoteExtensionsEnvironmentData = remoteEnv; + + // this._handleExtensionPoints(([]).concat(remoteEnv.extensions).concat(localExtensions)); + const result = this._registry.deltaExtensions(remoteEnv.extensions, []); + if (result.removedDueToLooping.length > 0) { + this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))); + } + + this._doHandleExtensionPoints(this._registry.getAllExtensionDescriptions()); + } + + public _onExtensionHostExit(code: number): void { + console.log(`vscode:exit`, code); + // ipc.send('vscode:exit', code); + } +} + +registerSingleton(IExtensionService, ExtensionService); diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts new file mode 100644 index 00000000000..246e032b2bc --- /dev/null +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -0,0 +1,499 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { isNonEmptyArray } from 'vs/base/common/arrays'; +import { Barrier } from 'vs/base/common/async'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import * as perf from 'vs/base/common/performance'; +import { isEqualOrParent } from 'vs/base/common/resources'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { BetterMergeId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ActivationTimes, ExtensionPointContribution, IExtensionService, IExtensionsStatus, IMessage, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; +import { ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol'; +import { ExtensionHostProcessManager } from 'vs/workbench/services/extensions/common/extensionHostProcessManager'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { IFileService } from 'vs/platform/files/common/files'; +import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; +import { IProductService } from 'vs/platform/product/common/product'; + +const hasOwnProperty = Object.hasOwnProperty; +const NO_OP_VOID_PROMISE = Promise.resolve(undefined); + +export abstract class AbstractExtensionService extends Disposable implements IExtensionService { + + public _serviceBrand: any; + + protected readonly _onDidRegisterExtensions: Emitter = this._register(new Emitter()); + public readonly onDidRegisterExtensions = this._onDidRegisterExtensions.event; + + protected readonly _onDidChangeExtensionsStatus: Emitter = this._register(new Emitter()); + public readonly onDidChangeExtensionsStatus: Event = this._onDidChangeExtensionsStatus.event; + + protected readonly _onDidChangeExtensions: Emitter = this._register(new Emitter()); + public readonly onDidChangeExtensions: Event = this._onDidChangeExtensions.event; + + protected readonly _onWillActivateByEvent = this._register(new Emitter()); + public readonly onWillActivateByEvent: Event = this._onWillActivateByEvent.event; + + protected readonly _onDidChangeResponsiveChange = this._register(new Emitter()); + public readonly onDidChangeResponsiveChange: Event = this._onDidChangeResponsiveChange.event; + + protected readonly _registry: ExtensionDescriptionRegistry; + private readonly _installedExtensionsReady: Barrier; + protected readonly _isDev: boolean; + private readonly _extensionsMessages: Map; + protected readonly _allRequestedActivateEvents: { [activationEvent: string]: boolean; }; + private readonly _proposedApiController: ProposedApiController; + private readonly _isExtensionDevHost: boolean; + protected readonly _isExtensionDevTestFromCli: boolean; + + // --- Members used per extension host process + protected _extensionHostProcessManagers: ExtensionHostProcessManager[]; + protected _extensionHostActiveExtensions: Map; + private _extensionHostProcessActivationTimes: Map; + private _extensionHostExtensionRuntimeErrors: Map; + + constructor( + @IInstantiationService protected readonly _instantiationService: IInstantiationService, + @INotificationService protected readonly _notificationService: INotificationService, + @IWorkbenchEnvironmentService protected readonly _environmentService: IWorkbenchEnvironmentService, + @ITelemetryService protected readonly _telemetryService: ITelemetryService, + @IExtensionEnablementService protected readonly _extensionEnablementService: IExtensionEnablementService, + @IFileService protected readonly _fileService: IFileService, + @IProductService protected readonly _productService: IProductService + ) { + super(); + + // help the file service to activate providers by activating extensions by file system event + this._register(this._fileService.onWillActivateFileSystemProvider(e => { + e.join(this.activateByEvent(`onFileSystem:${e.scheme}`)); + })); + + this._registry = new ExtensionDescriptionRegistry([]); + this._installedExtensionsReady = new Barrier(); + this._isDev = !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment; + this._extensionsMessages = new Map(); + this._allRequestedActivateEvents = Object.create(null); + this._proposedApiController = new ProposedApiController(this._environmentService, this._productService); + + this._extensionHostProcessManagers = []; + this._extensionHostActiveExtensions = new Map(); + this._extensionHostProcessActivationTimes = new Map(); + this._extensionHostExtensionRuntimeErrors = new Map(); + + const devOpts = parseExtensionDevOptions(this._environmentService); + this._isExtensionDevHost = devOpts.isExtensionDevHost; + this._isExtensionDevTestFromCli = devOpts.isExtensionDevTestFromCli; + } + + protected async _initialize(): Promise { + perf.mark('willLoadExtensions'); + this._startExtensionHostProcess(true, []); + this.whenInstalledExtensionsRegistered().then(() => perf.mark('didLoadExtensions')); + await this._scanAndHandleExtensions(); + this._releaseBarrier(); + } + + private _releaseBarrier(): void { + perf.mark('extensionHostReady'); + this._installedExtensionsReady.open(); + this._onDidRegisterExtensions.fire(undefined); + this._onDidChangeExtensionsStatus.fire(this._registry.getAllExtensionDescriptions().map(e => e.identifier)); + } + + private _stopExtensionHostProcess(): void { + let previouslyActivatedExtensionIds: ExtensionIdentifier[] = []; + this._extensionHostActiveExtensions.forEach((value) => { + previouslyActivatedExtensionIds.push(value); + }); + + for (const manager of this._extensionHostProcessManagers) { + manager.dispose(); + } + this._extensionHostProcessManagers = []; + this._extensionHostActiveExtensions = new Map(); + this._extensionHostProcessActivationTimes = new Map(); + this._extensionHostExtensionRuntimeErrors = new Map(); + + if (previouslyActivatedExtensionIds.length > 0) { + this._onDidChangeExtensionsStatus.fire(previouslyActivatedExtensionIds); + } + } + + private _startExtensionHostProcess(isInitialStart: boolean, initialActivationEvents: string[]): void { + this._stopExtensionHostProcess(); + + const processManagers = this._createExtensionHosts(isInitialStart, initialActivationEvents); + processManagers.forEach((processManager) => { + processManager.onDidExit(([code, signal]) => this._onExtensionHostCrashOrExit(processManager, code, signal)); + processManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ isResponsive: responsiveState === ResponsiveState.Responsive }); }); + this._extensionHostProcessManagers.push(processManager); + }); + } + + private _onExtensionHostCrashOrExit(extensionHost: ExtensionHostProcessManager, code: number, signal: string | null): void { + + // Unexpected termination + if (!this._isExtensionDevHost) { + this._onExtensionHostCrashed(extensionHost, code, signal); + return; + } + + this._onExtensionHostExit(code); + } + + protected _onExtensionHostCrashed(extensionHost: ExtensionHostProcessManager, code: number, signal: string | null): void { + console.error('Extension host terminated unexpectedly. Code: ', code, ' Signal: ', signal); + this._stopExtensionHostProcess(); + } + + //#region IExtensionService + + public canAddExtension(extension: IExtensionDescription): boolean { + return false; + } + + public canRemoveExtension(extension: IExtensionDescription): boolean { + return false; + } + + public restartExtensionHost(): void { + this._stopExtensionHostProcess(); + this._startExtensionHostProcess(false, Object.keys(this._allRequestedActivateEvents)); + } + + public startExtensionHost(): void { + this._startExtensionHostProcess(false, Object.keys(this._allRequestedActivateEvents)); + } + + public stopExtensionHost(): void { + this._stopExtensionHostProcess(); + } + + public activateByEvent(activationEvent: string): Promise { + if (this._installedExtensionsReady.isOpen()) { + // Extensions have been scanned and interpreted + + // Record the fact that this activationEvent was requested (in case of a restart) + this._allRequestedActivateEvents[activationEvent] = true; + + if (!this._registry.containsActivationEvent(activationEvent)) { + // There is no extension that is interested in this activation event + return NO_OP_VOID_PROMISE; + } + + return this._activateByEvent(activationEvent); + } else { + // Extensions have not been scanned yet. + + // Record the fact that this activationEvent was requested (in case of a restart) + this._allRequestedActivateEvents[activationEvent] = true; + + return this._installedExtensionsReady.wait().then(() => this._activateByEvent(activationEvent)); + } + } + + private _activateByEvent(activationEvent: string): Promise { + const result = Promise.all( + this._extensionHostProcessManagers.map(extHostManager => extHostManager.activateByEvent(activationEvent)) + ).then(() => { }); + this._onWillActivateByEvent.fire({ + event: activationEvent, + activation: result + }); + return result; + } + + public whenInstalledExtensionsRegistered(): Promise { + return this._installedExtensionsReady.wait(); + } + + public getExtensions(): Promise { + return this._installedExtensionsReady.wait().then(() => { + return this._registry.getAllExtensionDescriptions(); + }); + } + + public getExtension(id: string): Promise { + return this._installedExtensionsReady.wait().then(() => { + return this._registry.getExtensionDescription(id); + }); + } + + public readExtensionPointContributions(extPoint: IExtensionPoint): Promise[]> { + return this._installedExtensionsReady.wait().then(() => { + let availableExtensions = this._registry.getAllExtensionDescriptions(); + + let result: ExtensionPointContribution[] = [], resultLen = 0; + for (let i = 0, len = availableExtensions.length; i < len; i++) { + let desc = availableExtensions[i]; + + if (desc.contributes && hasOwnProperty.call(desc.contributes, extPoint.name)) { + result[resultLen++] = new ExtensionPointContribution(desc, desc.contributes[extPoint.name]); + } + } + + return result; + }); + } + + public getExtensionsStatus(): { [id: string]: IExtensionsStatus; } { + let result: { [id: string]: IExtensionsStatus; } = Object.create(null); + if (this._registry) { + const extensions = this._registry.getAllExtensionDescriptions(); + for (const extension of extensions) { + const extensionKey = ExtensionIdentifier.toKey(extension.identifier); + result[extension.identifier.value] = { + messages: this._extensionsMessages.get(extensionKey) || [], + activationTimes: this._extensionHostProcessActivationTimes.get(extensionKey), + runtimeErrors: this._extensionHostExtensionRuntimeErrors.get(extensionKey) || [], + }; + } + } + return result; + } + + public getInspectPort(): number { + return 0; + } + + //#endregion + + // --- impl + + protected _checkEnableProposedApi(extensions: IExtensionDescription[]): void { + for (let extension of extensions) { + this._proposedApiController.updateEnableProposedApi(extension); + } + } + + private _isExtensionUnderDevelopment(extension: IExtensionDescription): boolean { + if (this._environmentService.isExtensionDevelopment) { + const extDevLocs = this._environmentService.extensionDevelopmentLocationURI; + if (extDevLocs) { + const extLocation = extension.extensionLocation; + for (let p of extDevLocs) { + if (isEqualOrParent(extLocation, p)) { + return true; + } + } + } + } + return false; + } + + protected _isEnabled(extension: IExtensionDescription): boolean { + if (this._isExtensionUnderDevelopment(extension)) { + // Never disable extensions under development + return true; + } + + if (ExtensionIdentifier.equals(extension.identifier, BetterMergeId)) { + // Check if this is the better merge extension which was migrated to a built-in extension + return false; + } + + return this._extensionEnablementService.isEnabled(toExtension(extension)); + } + + protected _doHandleExtensionPoints(affectedExtensions: IExtensionDescription[]): void { + const affectedExtensionPoints: { [extPointName: string]: boolean; } = Object.create(null); + for (let extensionDescription of affectedExtensions) { + if (extensionDescription.contributes) { + for (let extPointName in extensionDescription.contributes) { + if (hasOwnProperty.call(extensionDescription.contributes, extPointName)) { + affectedExtensionPoints[extPointName] = true; + } + } + } + } + + const messageHandler = (msg: IMessage) => this._handleExtensionPointMessage(msg); + const availableExtensions = this._registry.getAllExtensionDescriptions(); + const extensionPoints = ExtensionsRegistry.getExtensionPoints(); + for (let i = 0, len = extensionPoints.length; i < len; i++) { + if (affectedExtensionPoints[extensionPoints[i].name]) { + AbstractExtensionService._handleExtensionPoint(extensionPoints[i], availableExtensions, messageHandler); + } + } + } + + private _handleExtensionPointMessage(msg: IMessage) { + const extensionKey = ExtensionIdentifier.toKey(msg.extensionId); + + if (!this._extensionsMessages.has(extensionKey)) { + this._extensionsMessages.set(extensionKey, []); + } + this._extensionsMessages.get(extensionKey)!.push(msg); + + const extension = this._registry.getExtensionDescription(msg.extensionId); + const strMsg = `[${msg.extensionId.value}]: ${msg.message}`; + if (extension && extension.isUnderDevelopment) { + // This message is about the extension currently being developed + this._showMessageToUser(msg.type, strMsg); + } else { + this._logMessageInConsole(msg.type, strMsg); + } + + if (!this._isDev && msg.extensionId) { + const { type, extensionId, extensionPointId, message } = msg; + /* __GDPR__ + "extensionsMessage" : { + "type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "extensionId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "extensionPointId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "message": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } + } + */ + this._telemetryService.publicLog('extensionsMessage', { + type, extensionId: extensionId.value, extensionPointId, message + }); + } + } + + private static _handleExtensionPoint(extensionPoint: ExtensionPoint, availableExtensions: IExtensionDescription[], messageHandler: (msg: IMessage) => void): void { + let users: IExtensionPointUser[] = [], usersLen = 0; + for (let i = 0, len = availableExtensions.length; i < len; i++) { + let desc = availableExtensions[i]; + + if (desc.contributes && hasOwnProperty.call(desc.contributes, extensionPoint.name)) { + users[usersLen++] = { + description: desc, + value: desc.contributes[extensionPoint.name], + collector: new ExtensionMessageCollector(messageHandler, desc, extensionPoint.name) + }; + } + } + perf.mark(`willHandleExtensionPoint/${extensionPoint.name}`); + extensionPoint.acceptUsers(users); + perf.mark(`didHandleExtensionPoint/${extensionPoint.name}`); + } + + private _showMessageToUser(severity: Severity, msg: string): void { + if (severity === Severity.Error || severity === Severity.Warning) { + this._notificationService.notify({ severity, message: msg }); + } else { + this._logMessageInConsole(severity, msg); + } + } + + private _logMessageInConsole(severity: Severity, msg: string): void { + if (severity === Severity.Error) { + console.error(msg); + } else if (severity === Severity.Warning) { + console.warn(msg); + } else { + console.log(msg); + } + } + + //#region Called by extension host + + public _logOrShowMessage(severity: Severity, msg: string): void { + if (this._isDev) { + this._showMessageToUser(severity, msg); + } else { + this._logMessageInConsole(severity, msg); + } + } + + public async _activateById(extensionId: ExtensionIdentifier, activationEvent: string): Promise { + const results = await Promise.all( + this._extensionHostProcessManagers.map(manager => manager.activate(extensionId, activationEvent)) + ); + const activated = results.some(e => e); + if (!activated) { + throw new Error(`Unknown extension ${extensionId.value}`); + } + } + + public _onWillActivateExtension(extensionId: ExtensionIdentifier): void { + this._extensionHostActiveExtensions.set(ExtensionIdentifier.toKey(extensionId), extensionId); + } + + public _onDidActivateExtension(extensionId: ExtensionIdentifier, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void { + this._extensionHostProcessActivationTimes.set(ExtensionIdentifier.toKey(extensionId), new ActivationTimes(startup, codeLoadingTime, activateCallTime, activateResolvedTime, activationEvent)); + this._onDidChangeExtensionsStatus.fire([extensionId]); + } + + public _onExtensionRuntimeError(extensionId: ExtensionIdentifier, err: Error): void { + const extensionKey = ExtensionIdentifier.toKey(extensionId); + if (!this._extensionHostExtensionRuntimeErrors.has(extensionKey)) { + this._extensionHostExtensionRuntimeErrors.set(extensionKey, []); + } + this._extensionHostExtensionRuntimeErrors.get(extensionKey)!.push(err); + this._onDidChangeExtensionsStatus.fire([extensionId]); + } + + //#endregion + + protected abstract _createExtensionHosts(isInitialStart: boolean, initialActivationEvents: string[]): ExtensionHostProcessManager[]; + protected abstract _scanAndHandleExtensions(): Promise; + public abstract _onExtensionHostExit(code: number): void; +} + +class ProposedApiController { + + private readonly enableProposedApiFor: string | string[]; + private readonly enableProposedApiForAll: boolean; + private readonly productAllowProposedApi: Set; + + constructor( + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IProductService productService: IProductService + ) { + this.enableProposedApiFor = environmentService.args['enable-proposed-api'] || []; + if (this.enableProposedApiFor.length) { + // Make enabled proposed API be lowercase for case insensitive comparison + if (Array.isArray(this.enableProposedApiFor)) { + this.enableProposedApiFor = this.enableProposedApiFor.map(id => id.toLowerCase()); + } else { + this.enableProposedApiFor = this.enableProposedApiFor.toLowerCase(); + } + } + + this.enableProposedApiForAll = !environmentService.isBuilt || + (!!environmentService.extensionDevelopmentLocationURI && productService.nameLong !== 'Visual Studio Code') || + (this.enableProposedApiFor.length === 0 && 'enable-proposed-api' in environmentService.args); + + this.productAllowProposedApi = new Set(); + if (isNonEmptyArray(productService.extensionAllowedProposedApi)) { + productService.extensionAllowedProposedApi.forEach((id) => this.productAllowProposedApi.add(ExtensionIdentifier.toKey(id))); + } + } + + public updateEnableProposedApi(extension: IExtensionDescription): void { + if (this._allowProposedApiFromProduct(extension.identifier)) { + // fast lane -> proposed api is available to all extensions + // that are listed in product.json-files + extension.enableProposedApi = true; + + } else if (extension.enableProposedApi && !extension.isBuiltin) { + if ( + !this.enableProposedApiForAll && + this.enableProposedApiFor.indexOf(extension.identifier.value.toLowerCase()) < 0 + ) { + extension.enableProposedApi = false; + console.error(`Extension '${extension.identifier.value} cannot use PROPOSED API (must started out of dev or enabled via --enable-proposed-api)`); + + } else { + // proposed api is available when developing or when an extension was explicitly + // spelled out via a command line argument + console.warn(`Extension '${extension.identifier.value}' uses PROPOSED API which is subject to change and removal without notice.`); + } + } + } + + private _allowProposedApiFromProduct(id: ExtensionIdentifier): boolean { + return this.productAllowProposedApi.has(ExtensionIdentifier.toKey(id)); + } +} diff --git a/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts b/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts index 4e63fa92494..c3f651cd23f 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostProcessManager.ts @@ -35,7 +35,7 @@ const NO_OP_VOID_PROMISE = Promise.resolve(undefined); export class ExtensionHostProcessManager extends Disposable { - public readonly onDidCrash: Event<[number, string | null]>; + public readonly onDidExit: Event<[number, string | null]>; private readonly _onDidChangeResponsiveState: Emitter = this._register(new Emitter()); public readonly onDidChangeResponsiveState: Event = this._onDidChangeResponsiveState.event; @@ -54,6 +54,7 @@ export class ExtensionHostProcessManager extends Disposable { private _resolveAuthorityAttempt: number; constructor( + public readonly isLocal: boolean, extensionHostProcessWorker: IExtensionHostStarter, private readonly _remoteAuthority: string, initialActivationEvents: string[], @@ -66,7 +67,7 @@ export class ExtensionHostProcessManager extends Disposable { this._extensionHostProcessCustomers = []; this._extensionHostProcessWorker = extensionHostProcessWorker; - this.onDidCrash = this._extensionHostProcessWorker.onCrashed; + this.onDidExit = this._extensionHostProcessWorker.onExit; this._extensionHostProcessProxy = this._extensionHostProcessWorker.start()!.then( (protocol) => { return { value: this._createExtensionHostCustomers(protocol) }; diff --git a/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts b/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts index bfba4c2300e..f85f7fa4031 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts @@ -12,6 +12,7 @@ export interface IExtHostReadyMessage { export interface IExtHostSocketMessage { type: 'VSCODE_EXTHOST_IPC_SOCKET'; initialDataChunk: string; + skipWebSocketFrames: boolean; } export const enum MessageType { diff --git a/src/vs/workbench/services/extensions/common/extensionPoints.ts b/src/vs/workbench/services/extensions/common/extensionPoints.ts new file mode 100644 index 00000000000..58a5a483575 --- /dev/null +++ b/src/vs/workbench/services/extensions/common/extensionPoints.ts @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Severity } from 'vs/platform/notification/common/notification'; + +export interface Translations { + [id: string]: string; +} + +export namespace Translations { + export function equals(a: Translations, b: Translations): boolean { + if (a === b) { + return true; + } + let aKeys = Object.keys(a); + let bKeys: Set = new Set(); + for (let key of Object.keys(b)) { + bKeys.add(key); + } + if (aKeys.length !== bKeys.size) { + return false; + } + + for (let key of aKeys) { + if (a[key] !== b[key]) { + return false; + } + bKeys.delete(key); + } + return bKeys.size === 0; + } +} + +export interface ILog { + error(source: string, message: string): void; + warn(source: string, message: string): void; + info(source: string, message: string): void; +} + +export class Logger implements ILog { + + private readonly _messageHandler: (severity: Severity, source: string, message: string) => void; + + constructor( + messageHandler: (severity: Severity, source: string, message: string) => void + ) { + this._messageHandler = messageHandler; + } + + public error(source: string, message: string): void { + this._messageHandler(Severity.Error, source, message); + } + + public warn(source: string, message: string): void { + this._messageHandler(Severity.Warning, source, message); + } + + public info(source: string, message: string): void { + this._messageHandler(Severity.Info, source, message); + } +} diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 2daf6549b81..1bd329231b6 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -84,7 +84,8 @@ export interface IExtensionHostProfile { } export interface IExtensionHostStarter { - readonly onCrashed: Event<[number, string | null]>; + readonly onExit: Event<[number, string | null]>; + start(): Promise | null; getInspectPort(): number | undefined; dispose(): void; diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index 6fc035817de..577cc633360 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -341,6 +341,19 @@ export const schema = { pattern: EXTENSION_IDENTIFIER_PATTERN } }, + extensionKind: { + description: nls.localize('extensionKind', "Define the kind of an extension. `ui` extensions are installed and run on the local machine while `workspace` extensions are run on the remote."), + type: 'string', + enum: [ + 'ui', + 'workspace' + ], + enumDescriptions: [ + nls.localize('ui', "UI extension kind. In a remote window, such extensions are enabled only when available on the local machine."), + nls.localize('workspace', "Workspace extension kind. In a remote window, such extensions are enabled only when available on the remote.") + ], + default: 'workspace' + }, scripts: { type: 'object', properties: { diff --git a/src/vs/platform/extensions/node/extensionsUtil.ts b/src/vs/workbench/services/extensions/common/extensionsUtil.ts similarity index 65% rename from src/vs/platform/extensions/node/extensionsUtil.ts rename to src/vs/workbench/services/extensions/common/extensionsUtil.ts index a84379d312e..e03ddb10239 100644 --- a/src/vs/platform/extensions/node/extensionsUtil.ts +++ b/src/vs/workbench/services/extensions/common/extensionsUtil.ts @@ -5,29 +5,37 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; +import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { getGalleryExtensionId, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { isNonEmptyArray } from 'vs/base/common/arrays'; -import product from 'vs/platform/product/node/product'; +import { IProductService } from 'vs/platform/product/common/product'; -export function isUIExtension(manifest: IExtensionManifest, uiContributions: string[], configurationService: IConfigurationService): boolean { +export function isUIExtension(manifest: IExtensionManifest, productService: IProductService, configurationService: IConfigurationService): boolean { + const uiContributions = ExtensionsRegistry.getExtensionPoints().filter(e => e.defaultExtensionKind !== 'workspace').map(e => e.name); const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name); const extensionKind = getExtensionKind(manifest, configurationService); switch (extensionKind) { case 'ui': return true; case 'workspace': return false; default: { - if (isNonEmptyArray(product.uiExtensions) && product.uiExtensions.some(id => areSameExtensions({ id }, { id: extensionId }))) { + // Tagged as UI extension in product + if (isNonEmptyArray(productService.uiExtensions) && productService.uiExtensions.some(id => areSameExtensions({ id }, { id: extensionId }))) { return true; } + // Not an UI extension if it has main if (manifest.main) { return false; } + // Not an UI extension if it has dependencies or an extension pack + if (isNonEmptyArray(manifest.extensionDependencies) || isNonEmptyArray(manifest.extensionPack)) { + return false; + } if (manifest.contributes) { + // Not an UI extension if it has no ui contributions if (!uiContributions.length || Object.keys(manifest.contributes).some(contribution => uiContributions.indexOf(contribution) === -1)) { return false; } } - // Default is UI Extension return true; } } diff --git a/src/vs/workbench/services/extensions/common/inactiveExtensionUrlHandler.ts b/src/vs/workbench/services/extensions/common/inactiveExtensionUrlHandler.ts index 263a45a9821..351bbe5ddc9 100644 --- a/src/vs/workbench/services/extensions/common/inactiveExtensionUrlHandler.ts +++ b/src/vs/workbench/services/extensions/common/inactiveExtensionUrlHandler.ts @@ -5,7 +5,7 @@ import { localize } from 'vs/nls'; import { Action } from 'vs/base/common/actions'; -import { IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { EnablementState, IExtensionEnablementService, IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; @@ -70,10 +70,10 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { this.handleURL(URI.revive(JSON.parse(urlToHandleValue)), true); } - this.disposable = combinedDisposable([ + this.disposable = combinedDisposable( urlService.registerHandler(this), toDisposable(() => clearInterval(interval)) - ]); + ); } async handleURL(uri: URI, confirmed?: boolean): Promise { diff --git a/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts b/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts new file mode 100644 index 00000000000..f43a59b9663 --- /dev/null +++ b/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts @@ -0,0 +1,235 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { ILogService } from 'vs/platform/log/common/log'; +import { connectRemoteAgentExtensionHost, IRemoteExtensionHostStartParams, IConnectionOptions, IWebSocketFactory } from 'vs/platform/remote/common/remoteAgentConnection'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IInitData } from 'vs/workbench/api/common/extHost.protocol'; +import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; +import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensions'; +import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; +import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; +import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import * as platform from 'vs/base/common/platform'; +import { Schemas } from 'vs/base/common/network'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { IExtensionHostDebugService } from 'vs/workbench/services/extensions/common/extensionHostDebug'; +import { IProductService } from 'vs/platform/product/common/product'; +import { ISignService } from 'vs/platform/sign/common/sign'; + +export interface IInitDataProvider { + readonly remoteAuthority: string; + getInitData(): Promise; +} + +export class RemoteExtensionHostClient extends Disposable implements IExtensionHostStarter { + + private _onExit: Emitter<[number, string | null]> = this._register(new Emitter<[number, string | null]>()); + public readonly onExit: Event<[number, string | null]> = this._onExit.event; + + private _protocol: PersistentProtocol | null; + + private readonly _isExtensionDevHost: boolean; + + private _terminating: boolean; + + constructor( + private readonly _allExtensions: Promise, + private readonly _initDataProvider: IInitDataProvider, + private readonly _webSocketFactory: IWebSocketFactory, + @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, + @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @ITelemetryService private readonly _telemetryService: ITelemetryService, + @ILifecycleService private readonly _lifecycleService: ILifecycleService, + @ILogService private readonly _logService: ILogService, + @ILabelService private readonly _labelService: ILabelService, + @IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService, + @IExtensionHostDebugService private readonly _extensionHostDebugService: IExtensionHostDebugService, + @IProductService private readonly _productService: IProductService, + @ISignService private readonly _signService: ISignService + ) { + super(); + this._protocol = null; + this._terminating = false; + + this._register(this._lifecycleService.onShutdown(reason => this.dispose())); + + const devOpts = parseExtensionDevOptions(this._environmentService); + this._isExtensionDevHost = devOpts.isExtensionDevHost; + } + + public start(): Promise { + const options: IConnectionOptions = { + isBuilt: this._environmentService.isBuilt, + commit: this._productService.commit, + webSocketFactory: this._webSocketFactory, + addressProvider: { + getAddress: async () => { + const { host, port } = await this.remoteAuthorityResolverService.resolveAuthority(this._initDataProvider.remoteAuthority); + return { host, port }; + } + }, + signService: this._signService + }; + return this.remoteAuthorityResolverService.resolveAuthority(this._initDataProvider.remoteAuthority).then((resolvedAuthority) => { + + const startParams: IRemoteExtensionHostStartParams = { + language: platform.language, + debugId: this._environmentService.debugExtensionHost.debugId, + break: this._environmentService.debugExtensionHost.break, + port: this._environmentService.debugExtensionHost.port, + }; + + const extDevLocs = this._environmentService.extensionDevelopmentLocationURI; + + let debugOk = true; + if (extDevLocs && extDevLocs.length > 0) { + // TODO@AW: handles only first path in array + if (extDevLocs[0].scheme === Schemas.file) { + debugOk = false; + } + } + + if (!debugOk) { + startParams.break = false; + } + + return connectRemoteAgentExtensionHost(options, startParams).then(result => { + let { protocol, debugPort } = result; + const isExtensionDevelopmentDebug = typeof debugPort === 'number'; + if (debugOk && this._environmentService.isExtensionDevelopment && this._environmentService.debugExtensionHost.debugId && debugPort) { + this._extensionHostDebugService.attachSession(this._environmentService.debugExtensionHost.debugId, debugPort, this._initDataProvider.remoteAuthority); + } + + protocol.onClose(() => { + this._onExtHostConnectionLost(); + }); + + protocol.onSocketClose(() => { + if (this._isExtensionDevHost) { + this._onExtHostConnectionLost(); + } + }); + + // 1) wait for the incoming `ready` event and send the initialization data. + // 2) wait for the incoming `initialized` event. + return new Promise((resolve, reject) => { + + let handle = setTimeout(() => { + reject('timeout'); + }, 60 * 1000); + + const disposable = protocol.onMessage(msg => { + + if (isMessageOfType(msg, MessageType.Ready)) { + // 1) Extension Host is ready to receive messages, initialize it + this._createExtHostInitData(isExtensionDevelopmentDebug).then(data => protocol.send(VSBuffer.fromString(JSON.stringify(data)))); + return; + } + + if (isMessageOfType(msg, MessageType.Initialized)) { + // 2) Extension Host is initialized + + clearTimeout(handle); + + // stop listening for messages here + disposable.dispose(); + + // release this promise + this._protocol = protocol; + resolve(protocol); + return; + } + + console.error(`received unexpected message during handshake phase from the extension host: `, msg); + }); + + }); + }); + }); + } + + private _onExtHostConnectionLost(): void { + if (this._terminating) { + // Expected termination path (we asked the process to terminate) + return; + } + + this._onExit.fire([0, null]); + } + + private _createExtHostInitData(isExtensionDevelopmentDebug: boolean): Promise { + return Promise.all([this._allExtensions, this._telemetryService.getTelemetryInfo(), this._initDataProvider.getInitData()]).then(([allExtensions, telemetryInfo, remoteExtensionHostData]) => { + // Collect all identifiers for extension ids which can be considered "resolved" + const resolvedExtensions = allExtensions.filter(extension => !extension.main).map(extension => extension.identifier); + const hostExtensions = allExtensions.filter(extension => extension.main && extension.api === 'none').map(extension => extension.identifier); + const workspace = this._contextService.getWorkspace(); + const r: IInitData = { + commit: this._productService.commit, + version: this._productService.version, + parentPid: remoteExtensionHostData.pid, + environment: { + isExtensionDevelopmentDebug, + appRoot: remoteExtensionHostData.appRoot, + appSettingsHome: remoteExtensionHostData.appSettingsHome, + appName: this._productService.nameLong, + appUriScheme: this._productService.urlProtocol, + appLanguage: platform.language, + extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, + extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, + globalStorageHome: remoteExtensionHostData.globalStorageHome, + userHome: remoteExtensionHostData.userHome, + }, + workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : { + configuration: workspace.configuration, + id: workspace.id, + name: this._labelService.getWorkspaceLabel(workspace) + }, + remote: { + isRemote: true, + authority: this._initDataProvider.remoteAuthority + }, + resolvedExtensions: resolvedExtensions, + hostExtensions: hostExtensions, + extensions: remoteExtensionHostData.extensions, + telemetryInfo, + logLevel: this._logService.getLevel(), + logsLocation: remoteExtensionHostData.extensionHostLogsPath, + autoStart: true, + }; + return r; + }); + } + + getInspectPort(): number | undefined { + return undefined; + } + + dispose(): void { + super.dispose(); + + this._terminating = true; + + if (this._protocol) { + // Send the extension host a request to terminate itself + // (graceful termination) + const socket = this._protocol.getSocket(); + this._protocol.send(createMessageOfType(MessageType.Terminate)); + this._protocol.sendDisconnect(); + this._protocol.dispose(); + socket.end(); + this._protocol = null; + } + } +} diff --git a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts index 40a8c8363ee..f24f320674b 100644 --- a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts +++ b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts @@ -21,7 +21,8 @@ import pkg from 'vs/platform/product/node/package'; import product from 'vs/platform/product/node/product'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IWindowService } from 'vs/platform/windows/common/windows'; -import { ExtensionScanner, ExtensionScannerInput, IExtensionReference, IExtensionResolver, ILog, IRelaxedExtensionDescription, Translations } from 'vs/workbench/services/extensions/node/extensionPoints'; +import { ExtensionScanner, ExtensionScannerInput, IExtensionReference, IExtensionResolver, IRelaxedExtensionDescription } from 'vs/workbench/services/extensions/node/extensionPoints'; +import { Translations, ILog } from 'vs/workbench/services/extensions/common/extensionPoints'; interface IExtensionCacheData { input: ExtensionScannerInput; @@ -387,26 +388,3 @@ class NullLogger implements ILog { public info(source: string, message: string): void { } } - -export class Logger implements ILog { - - private readonly _messageHandler: (severity: Severity, source: string, message: string) => void; - - constructor( - messageHandler: (severity: Severity, source: string, message: string) => void - ) { - this._messageHandler = messageHandler; - } - - public error(source: string, message: string): void { - this._messageHandler(Severity.Error, source, message); - } - - public warn(source: string, message: string): void { - this._messageHandler(Severity.Warning, source, message); - } - - public info(source: string, message: string): void { - this._messageHandler(Severity.Info, source, message); - } -} diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 1707b523b1a..3d324296b01 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -5,13 +5,12 @@ import * as nls from 'vs/nls'; import { ChildProcess, fork } from 'child_process'; -import { ipcRenderer as ipc } from 'electron'; import { Server, Socket, createServer } from 'net'; import { getPathFromAmdModule } from 'vs/base/common/amd'; import { timeout } from 'vs/base/common/async'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Emitter, Event } from 'vs/base/common/event'; -import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import * as objects from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; import pkg from 'vs/platform/product/node/package'; @@ -42,10 +41,10 @@ import { isEqualOrParent } from 'vs/base/common/resources'; export class ExtensionHostProcessWorker implements IExtensionHostStarter { - private readonly _onCrashed: Emitter<[number, string]> = new Emitter<[number, string]>(); - public readonly onCrashed: Event<[number, string]> = this._onCrashed.event; + private readonly _onExit: Emitter<[number, string]> = new Emitter<[number, string]>(); + public readonly onExit: Event<[number, string]> = this._onExit.event; - private readonly _toDispose: IDisposable[]; + private readonly _toDispose = new DisposableStore(); private readonly _isExtensionDevHost: boolean; private readonly _isExtensionDevDebug: boolean; @@ -58,7 +57,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { // Resources, in order they get acquired/created when .start() is called: private _namedPipeServer: Server | null; - private _inspectPort: number; + private _inspectPort: number | null; private _extensionHostProcess: ChildProcess | null; private _extensionHostConnection: Socket | null; private _messageProtocol: Promise | null; @@ -92,16 +91,15 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { this._extensionHostConnection = null; this._messageProtocol = null; - this._toDispose = []; - this._toDispose.push(this._onCrashed); - this._toDispose.push(this._lifecycleService.onWillShutdown(e => this._onWillShutdown(e))); - this._toDispose.push(this._lifecycleService.onShutdown(reason => this.terminate())); - this._toDispose.push(this._extensionHostDebugService.onClose(event => { + this._toDispose.add(this._onExit); + this._toDispose.add(this._lifecycleService.onWillShutdown(e => this._onWillShutdown(e))); + this._toDispose.add(this._lifecycleService.onShutdown(reason => this.terminate())); + this._toDispose.add(this._extensionHostDebugService.onClose(event => { if (this._isExtensionDevHost && this._environmentService.debugExtensionHost.debugId === event.sessionId) { this._windowService.closeWindow(); } })); - this._toDispose.push(this._extensionHostDebugService.onReload(event => { + this._toDispose.add(this._extensionHostDebugService.onReload(event => { if (this._isExtensionDevHost && this._environmentService.debugExtensionHost.debugId === event.sessionId) { this._windowService.reloadWindow(); } @@ -109,7 +107,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { const globalExitListener = () => this.terminate(); process.once('exit', globalExitListener); - this._toDispose.push(toDisposable(() => { + this._toDispose.add(toDisposable(() => { process.removeListener('exit', globalExitListener); })); } @@ -125,7 +123,10 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { } if (!this._messageProtocol) { - this._messageProtocol = Promise.all([this._tryListenOnPipe(), this._tryFindDebugPort()]).then(data => { + this._messageProtocol = Promise.all([ + this._tryListenOnPipe(), + !this._environmentService.args['disable-inspect'] ? this._tryFindDebugPort() : Promise.resolve(null) + ]).then(data => { const pipeName = data[0]; const portData = data[1]; @@ -148,7 +149,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { silent: true }; - if (portData.actual) { + if (portData && portData.actual) { opts.execArgv = [ '--nolazy', (this._isExtensionDevDebugBrk ? '--inspect-brk=' : '--inspect=') + portData.actual @@ -215,10 +216,12 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { this._extensionHostProcess.on('exit', (code: number, signal: string) => this._onExtHostProcessExit(code, signal)); // Notify debugger that we are ready to attach to the process if we run a development extension - if (this._isExtensionDevHost && portData.actual && this._isExtensionDevDebug && this._environmentService.debugExtensionHost.debugId) { - this._extensionHostDebugService.attachSession(this._environmentService.debugExtensionHost.debugId, portData.actual); + if (portData) { + if (this._isExtensionDevHost && portData.actual && this._isExtensionDevDebug && this._environmentService.debugExtensionHost.debugId) { + this._extensionHostDebugService.attachSession(this._environmentService.debugExtensionHost.debugId, portData.actual); + } + this._inspectPort = portData.actual; } - this._inspectPort = portData.actual; // Help in case we fail to start it let startupTimeoutHandle: any; @@ -389,14 +392,14 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { environment: { isExtensionDevelopmentDebug: this._isExtensionDevDebug, appRoot: this._environmentService.appRoot ? URI.file(this._environmentService.appRoot) : undefined, - appSettingsHome: this._environmentService.appSettingsHome ? URI.file(this._environmentService.appSettingsHome) : undefined, + appSettingsHome: this._environmentService.appSettingsHome ? this._environmentService.appSettingsHome : undefined, appName: product.nameLong, appUriScheme: product.urlProtocol, appLanguage: platform.language, extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, globalStorageHome: URI.file(this._environmentService.globalStorageHome), - userHome: URI.file(this._environmentService.userHome) + userHome: URI.file(this._environmentService.userHome), }, workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : { configuration: withNullAsUndefined(workspace.configuration), @@ -404,6 +407,10 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { name: this._labelService.getWorkspaceLabel(workspace), isUntitled: workspace.configuration ? isEqualOrParent(workspace.configuration, this._environmentService.untitledWorkspacesHome) : false }, + remote: { + authority: this._environmentService.configuration.remoteAuthority, + isRemote: false + }, resolvedExtensions: [], hostExtensions: [], extensions: extensionDescriptions, @@ -451,36 +458,11 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { return; } - // Unexpected termination - if (!this._isExtensionDevHost) { - this._onCrashed.fire([code, signal]); - } - - // Expected development extension termination: When the extension host goes down we also shutdown the window - else if (!this._isExtensionDevTestFromCli) { - this._windowService.closeWindow(); - } - - // When CLI testing make sure to exit with proper exit code - else { - ipc.send('vscode:exit', code); - } + this._onExit.fire([code, signal]); } - public enableInspector(): Promise { - if (this._inspectPort) { - return Promise.resolve(); - } - // send SIGUSR1 and wait a little the actual port is read from the process stdout which we - // scan here: https://github.com/Microsoft/vscode/blob/67ffab8dcd1a6752d8b62bcd13d7020101eef568/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts#L225-L240 - if (this._extensionHostProcess) { - this._extensionHostProcess.kill('SIGUSR1'); - } - return timeout(1000); - } - - public getInspectPort(): number { - return this._inspectPort; + public getInspectPort(): number | undefined { + return withNullAsUndefined(this._inspectPort); } public terminate(): void { @@ -489,7 +471,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { } this._terminating = true; - dispose(this._toDispose); + this._toDispose.dispose(); if (!this._messageProtocol) { // .start() was not called diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionManagementServerService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionManagementServerService.ts index 64343563a11..611ab9aec95 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionManagementServerService.ts @@ -6,13 +6,17 @@ import { localize } from 'vs/nls'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; -import { IExtensionManagementServer, IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementServer, IExtensionManagementServerService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/node/extensionManagementIpc'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ILogService } from 'vs/platform/log/common/log'; +import { RemoteExtensionManagementChannelClient } from 'vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IProductService } from 'vs/platform/product/common/product'; const localExtensionManagementServerAuthority: string = 'vscode-local'; @@ -25,14 +29,18 @@ export class ExtensionManagementServerService implements IExtensionManagementSer constructor( @ISharedProcessService sharedProcessService: ISharedProcessService, - @IRemoteAgentService remoteAgentService: IRemoteAgentService + @IRemoteAgentService remoteAgentService: IRemoteAgentService, + @IExtensionGalleryService galleryService: IExtensionGalleryService, + @IConfigurationService configurationService: IConfigurationService, + @IProductService productService: IProductService, + @ILogService logService: ILogService ) { const localExtensionManagementService = new ExtensionManagementChannelClient(sharedProcessService.getChannel('extensions')); this.localExtensionManagementServer = { extensionManagementService: localExtensionManagementService, authority: localExtensionManagementServerAuthority, label: localize('local', "Local") }; const remoteAgentConnection = remoteAgentService.getConnection(); if (remoteAgentConnection) { - const extensionManagementService = new ExtensionManagementChannelClient(remoteAgentConnection.getChannel('extensions')); + const extensionManagementService = new RemoteExtensionManagementChannelClient(remoteAgentConnection.getChannel('extensions'), this.localExtensionManagementServer.extensionManagementService, galleryService, logService, configurationService, productService); this.remoteExtensionManagementServer = { authority: remoteAgentConnection.remoteAuthority, extensionManagementService, label: localize('remote', "Remote") }; } } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 6692f75ea0b..98b5cf01ba1 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -3,55 +3,37 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { ipcRenderer as ipc } from 'electron'; +import { ExtensionHostProcessWorker } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; +import { CachedExtensionScanner } from 'vs/workbench/services/extensions/electron-browser/cachedExtensionScanner'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { nodeWebSocketFactory } from 'vs/platform/remote/node/nodeWebSocketFactory'; +import { AbstractExtensionService } from 'vs/workbench/services/extensions/common/abstractExtensionService'; import * as nls from 'vs/nls'; import * as path from 'vs/base/common/path'; -import { ipcRenderer as ipc } from 'electron'; -import { isNonEmptyArray } from 'vs/base/common/arrays'; -import { Barrier, runWhenIdle } from 'vs/base/common/async'; -import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; -import * as perf from 'vs/base/common/performance'; -import { isEqualOrParent } from 'vs/base/common/resources'; +import { runWhenIdle } from 'vs/base/common/async'; import { URI } from 'vs/base/common/uri'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { EnablementState, IExtensionEnablementService, IExtensionIdentifier, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { BetterMergeId, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { IExtensionEnablementService, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IInitDataProvider, RemoteExtensionHostClient } from 'vs/workbench/services/extensions/common/remoteExtensionHostClient'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { IRemoteAuthorityResolverService, ResolvedAuthority, RemoteAuthorityResolverError } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import pkg from 'vs/platform/product/node/package'; -import product from 'vs/platform/product/node/product'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; -import { ActivationTimes, ExtensionPointContribution, IExtensionService, IExtensionsStatus, IMessage, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension } from 'vs/workbench/services/extensions/common/extensions'; -import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser, schema } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { ExtensionHostProcessWorker } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; -import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; -import { ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol'; -import { CachedExtensionScanner, Logger } from 'vs/workbench/services/extensions/electron-browser/cachedExtensionScanner'; +import { IExtensionService, toExtension } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionHostProcessManager } from 'vs/workbench/services/extensions/common/extensionHostProcessManager'; import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { Schemas } from 'vs/base/common/network'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; -import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; - -const hasOwnProperty = Object.hasOwnProperty; -const NO_OP_VOID_PROMISE = Promise.resolve(undefined); - -schema.properties.engines.properties.vscode.default = `^${pkg.version}`; - -let productAllowProposedApi: Set | null = null; -function allowProposedApiFromProduct(id: ExtensionIdentifier): boolean { - // create set if needed - if (!productAllowProposedApi) { - productAllowProposedApi = new Set(); - if (isNonEmptyArray(product.extensionAllowedProposedApi)) { - product.extensionAllowedProposedApi.forEach((id) => productAllowProposedApi!.add(ExtensionIdentifier.toKey(id))); - } - } - return productAllowProposedApi.has(ExtensionIdentifier.toKey(id)); -} +import { PersistenConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection'; +import { IProductService } from 'vs/platform/product/common/product'; +import { Logger } from 'vs/workbench/services/extensions/common/extensionPoints'; class DeltaExtensionsQueueItem { constructor( @@ -60,73 +42,38 @@ class DeltaExtensionsQueueItem { ) { } } -export class ExtensionService extends Disposable implements IExtensionService { +export class ExtensionService extends AbstractExtensionService implements IExtensionService { - public _serviceBrand: any; + private readonly _remoteExtensionsEnvironmentData: Map; private readonly _extensionHostLogsLocation: URI; - private readonly _registry: ExtensionDescriptionRegistry; - private readonly _installedExtensionsReady: Barrier; - private readonly _isDev: boolean; - private readonly _extensionsMessages: Map; - private _allRequestedActivateEvents: { [activationEvent: string]: boolean; }; private readonly _extensionScanner: CachedExtensionScanner; private _deltaExtensionsQueue: DeltaExtensionsQueueItem[]; - private readonly _onDidRegisterExtensions: Emitter = this._register(new Emitter()); - public readonly onDidRegisterExtensions = this._onDidRegisterExtensions.event; - - private readonly _onDidChangeExtensionsStatus: Emitter = this._register(new Emitter()); - public readonly onDidChangeExtensionsStatus: Event = this._onDidChangeExtensionsStatus.event; - - private readonly _onDidChangeExtensions: Emitter = this._register(new Emitter()); - public readonly onDidChangeExtensions: Event = this._onDidChangeExtensions.event; - - private readonly _onWillActivateByEvent = this._register(new Emitter()); - public readonly onWillActivateByEvent: Event = this._onWillActivateByEvent.event; - - private readonly _onDidChangeResponsiveChange = this._register(new Emitter()); - public readonly onDidChangeResponsiveChange: Event = this._onDidChangeResponsiveChange.event; - - // --- Members used per extension host process - private _extensionHostProcessManagers: ExtensionHostProcessManager[]; - private _extensionHostActiveExtensions: Map; - private _extensionHostProcessActivationTimes: Map; - private _extensionHostExtensionRuntimeErrors: Map; - constructor( - @IInstantiationService private readonly _instantiationService: IInstantiationService, - @INotificationService private readonly _notificationService: INotificationService, - @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, - @ITelemetryService private readonly _telemetryService: ITelemetryService, - @IExtensionEnablementService private readonly _extensionEnablementService: IExtensionEnablementService, + @IInstantiationService instantiationService: IInstantiationService, + @INotificationService notificationService: INotificationService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @ITelemetryService telemetryService: ITelemetryService, + @IExtensionEnablementService extensionEnablementService: IExtensionEnablementService, + @IFileService fileService: IFileService, + @IProductService productService: IProductService, @IExtensionManagementService private readonly _extensionManagementService: IExtensionManagementService, - @IWindowService private readonly _windowService: IWindowService, + @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService, + @IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService, + @IConfigurationService private readonly _configurationService: IConfigurationService, @ILifecycleService private readonly _lifecycleService: ILifecycleService, - @IFileService fileService: IFileService + @IWindowService protected readonly _windowService: IWindowService, ) { - super(); - - // help the file service to activate providers by activating extensions by file system event - this._register(fileService.onWillActivateFileSystemProvider(e => { - e.join(this.activateByEvent(`onFileSystem:${e.scheme}`)); - })); - - this._extensionHostLogsLocation = URI.file(path.join(this._environmentService.logsPath, `exthost${this._windowService.windowId}`)); - this._registry = new ExtensionDescriptionRegistry([]); - this._installedExtensionsReady = new Barrier(); - this._isDev = !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment; - this._extensionsMessages = new Map(); - this._allRequestedActivateEvents = Object.create(null); - this._extensionScanner = this._instantiationService.createInstance(CachedExtensionScanner); - this._deltaExtensionsQueue = []; - - this._extensionHostProcessManagers = []; - this._extensionHostActiveExtensions = new Map(); - this._extensionHostProcessActivationTimes = new Map(); - this._extensionHostExtensionRuntimeErrors = new Map(); - - this._startDelayed(this._lifecycleService); + super( + instantiationService, + notificationService, + environmentService, + telemetryService, + extensionEnablementService, + fileService, + productService, + ); if (this._extensionEnablementService.allUserExtensionsDisabled) { this._notificationService.prompt(Severity.Info, nls.localize('extensionsDisabled', "All installed extensions are temporarily disabled. Reload the window to return to the previous state."), [{ @@ -137,6 +84,12 @@ export class ExtensionService extends Disposable implements IExtensionService { }]); } + this._remoteExtensionsEnvironmentData = new Map(); + + this._extensionHostLogsLocation = URI.file(path.join(this._environmentService.logsPath, `exthost${this._windowService.windowId}`)); + this._extensionScanner = instantiationService.createInstance(CachedExtensionScanner); + this._deltaExtensionsQueue = []; + this._register(this._extensionEnablementService.onEnablementChanged((extensions) => { let toAdd: IExtension[] = []; let toRemove: string[] = []; @@ -167,8 +120,24 @@ export class ExtensionService extends Disposable implements IExtensionService { this._handleDeltaExtensions(new DeltaExtensionsQueueItem([], [event.identifier.id])); } })); + + // delay extension host creation and extension scanning + // until the workbench is running. we cannot defer the + // extension host more (LifecyclePhase.Restored) because + // some editors require the extension host to restore + // and this would result in a deadlock + // see https://github.com/Microsoft/vscode/issues/41322 + this._lifecycleService.when(LifecyclePhase.Ready).then(() => { + // reschedule to ensure this runs after restoring viewlets, panels, and editors + runWhenIdle(() => { + this._initialize(); + }, 50 /*max delay*/); + }); } + + //#region deltaExtensions + private _inHandleDeltaExtensions = false; private async _handleDeltaExtensions(item: DeltaExtensionsQueueItem): Promise { this._deltaExtensionsQueue.push(item); @@ -197,13 +166,7 @@ export class ExtensionService extends Disposable implements IExtensionService { for (let i = 0, len = _toAdd.length; i < len; i++) { const extension = _toAdd[i]; - if (extension.location.scheme !== Schemas.file) { - continue; - } - - const existingExtensionDescription = this._registry.getExtensionDescription(extension.identifier.id); - if (existingExtensionDescription) { - // this extension is already running (most likely at a different version) + if (!this._canAddExtension(extension)) { continue; } @@ -244,6 +207,9 @@ export class ExtensionService extends Disposable implements IExtensionService { this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))); } + // enable or disable proposed API per extension + this._checkEnableProposedApi(toAdd); + // Update extension points this._rehandleExtensionPoints(([]).concat(toAdd).concat(toRemove)); @@ -260,43 +226,31 @@ export class ExtensionService extends Disposable implements IExtensionService { } private _rehandleExtensionPoints(extensionDescriptions: IExtensionDescription[]): void { - const affectedExtensionPoints: { [extPointName: string]: boolean; } = Object.create(null); - for (let extensionDescription of extensionDescriptions) { - if (extensionDescription.contributes) { - for (let extPointName in extensionDescription.contributes) { - if (hasOwnProperty.call(extensionDescription.contributes, extPointName)) { - affectedExtensionPoints[extPointName] = true; - } - } - } - } - - const messageHandler = (msg: IMessage) => this._handleExtensionPointMessage(msg); - - const availableExtensions = this._registry.getAllExtensionDescriptions(); - const extensionPoints = ExtensionsRegistry.getExtensionPoints(); - for (let i = 0, len = extensionPoints.length; i < len; i++) { - if (affectedExtensionPoints[extensionPoints[i].name]) { - ExtensionService._handleExtensionPoint(extensionPoints[i], availableExtensions, messageHandler); - } - } + this._doHandleExtensionPoints(extensionDescriptions); } - public canAddExtension(extension: IExtensionDescription): boolean { + public canAddExtension(extensionDescription: IExtensionDescription): boolean { + return this._canAddExtension(toExtension(extensionDescription)); + } + + public _canAddExtension(extension: IExtension): boolean { if (this._environmentService.configuration.remoteAuthority) { return false; } - if (extension.extensionLocation.scheme !== Schemas.file) { + if (extension.location.scheme !== Schemas.file) { return false; } - const extensionDescription = this._registry.getExtensionDescription(extension.identifier); + const extensionDescription = this._registry.getExtensionDescription(extension.identifier.id); if (extensionDescription) { - // ignore adding an extension which is already running and cannot be removed - if (!this._canRemoveExtension(extensionDescription)) { - return false; - } + // this extension is already running (most likely at a different version) + return false; + } + + // Check if extension is renamed + if (extension.identifier.uuid && this._registry.getAllExtensionDescriptions().some(e => e.uuid === extension.identifier.uuid)) { + return false; } return true; @@ -369,215 +323,80 @@ export class ExtensionService extends Disposable implements IExtensionService { } } - private _startDelayed(lifecycleService: ILifecycleService): void { - // delay extension host creation and extension scanning - // until the workbench is running. we cannot defer the - // extension host more (LifecyclePhase.Restored) because - // some editors require the extension host to restore - // and this would result in a deadlock - // see https://github.com/Microsoft/vscode/issues/41322 - lifecycleService.when(LifecyclePhase.Ready).then(() => { - // reschedule to ensure this runs after restoring viewlets, panels, and editors - runWhenIdle(() => { - perf.mark('willLoadExtensions'); - this._startExtensionHostProcess(true, []); - this._scanAndHandleExtensions(); - this.whenInstalledExtensionsRegistered().then(() => perf.mark('didLoadExtensions')); - }, 50 /*max delay*/); - }); + //#endregion + + private _createProvider(remoteAuthority: string): IInitDataProvider { + return { + remoteAuthority: remoteAuthority, + getInitData: () => { + return this.whenInstalledExtensionsRegistered().then(() => { + return this._remoteExtensionsEnvironmentData.get(remoteAuthority)!; + }); + } + }; } - public dispose(): void { - super.dispose(); - this._onWillActivateByEvent.dispose(); - this._onDidChangeResponsiveChange.dispose(); - } - - public restartExtensionHost(): void { - this._stopExtensionHostProcess(); - this._startExtensionHostProcess(false, Object.keys(this._allRequestedActivateEvents)); - } - - public startExtensionHost(): void { - this._startExtensionHostProcess(false, Object.keys(this._allRequestedActivateEvents)); - } - - public stopExtensionHost(): void { - this._stopExtensionHostProcess(); - } - - private _stopExtensionHostProcess(): void { - let previouslyActivatedExtensionIds: ExtensionIdentifier[] = []; - this._extensionHostActiveExtensions.forEach((value) => { - previouslyActivatedExtensionIds.push(value); - }); - - for (const manager of this._extensionHostProcessManagers) { - manager.dispose(); - } - this._extensionHostProcessManagers = []; - this._extensionHostActiveExtensions = new Map(); - this._extensionHostProcessActivationTimes = new Map(); - this._extensionHostExtensionRuntimeErrors = new Map(); - - if (previouslyActivatedExtensionIds.length > 0) { - this._onDidChangeExtensionsStatus.fire(previouslyActivatedExtensionIds); - } - } - - private _startExtensionHostProcess(isInitialStart: boolean, initialActivationEvents: string[]): void { - this._stopExtensionHostProcess(); - + protected _createExtensionHosts(isInitialStart: boolean, initialActivationEvents: string[]): ExtensionHostProcessManager[] { let autoStart: boolean; let extensions: Promise; if (isInitialStart) { autoStart = false; - extensions = this._extensionScanner.scannedExtensions; + extensions = this._extensionScanner.scannedExtensions.then(extensions => extensions.filter(extension => this._isEnabled(extension))); // remove disabled extensions } else { // restart case autoStart = true; - extensions = this.getExtensions(); + extensions = this.getExtensions().then((extensions) => extensions.filter(ext => ext.extensionLocation.scheme === Schemas.file)); } + const result: ExtensionHostProcessManager[] = []; const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, autoStart, extensions, this._extensionHostLogsLocation); - const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostProcessWorker, null, initialActivationEvents); - extHostProcessManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal)); - extHostProcessManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ isResponsive: responsiveState === ResponsiveState.Responsive }); }); - this._extensionHostProcessManagers.push(extHostProcessManager); + const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, true, extHostProcessWorker, null, initialActivationEvents); + result.push(extHostProcessManager); + + const remoteAgentConnection = this._remoteAgentService.getConnection(); + if (remoteAgentConnection) { + const remoteExtHostProcessWorker = this._instantiationService.createInstance(RemoteExtensionHostClient, this.getExtensions(), this._createProvider(remoteAgentConnection.remoteAuthority), nodeWebSocketFactory); + const remoteExtHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, remoteExtHostProcessWorker, remoteAgentConnection.remoteAuthority, initialActivationEvents); + result.push(remoteExtHostProcessManager); + } + + return result; } - private _onExtensionHostCrashed(code: number, signal: string | null): void { - console.error('Extension host terminated unexpectedly. Code: ', code, ' Signal: ', signal); - this._stopExtensionHostProcess(); + protected _onExtensionHostCrashed(extensionHost: ExtensionHostProcessManager, code: number, signal: string | null): void { + super._onExtensionHostCrashed(extensionHost, code, signal); - if (code === 55) { - this._notificationService.prompt( - Severity.Error, - nls.localize('extensionService.versionMismatchCrash', "Extension host cannot start: version mismatch."), + if (extensionHost.isLocal) { + if (code === 55) { + this._notificationService.prompt( + Severity.Error, + nls.localize('extensionService.versionMismatchCrash', "Extension host cannot start: version mismatch."), + [{ + label: nls.localize('relaunch', "Relaunch VS Code"), + run: () => { + this._instantiationService.invokeFunction((accessor) => { + const windowsService = accessor.get(IWindowsService); + windowsService.relaunch({}); + }); + } + }] + ); + return; + } + + this._notificationService.prompt(Severity.Error, nls.localize('extensionService.crash', "Extension host terminated unexpectedly."), [{ - label: nls.localize('relaunch', "Relaunch VS Code"), - run: () => { - this._instantiationService.invokeFunction((accessor) => { - const windowsService = accessor.get(IWindowsService); - windowsService.relaunch({}); - }); - } + label: nls.localize('devTools', "Open Developer Tools"), + run: () => this._windowService.openDevTools() + }, + { + label: nls.localize('restart', "Restart Extension Host"), + run: () => this.startExtensionHost() }] ); - return; - } - - let message = nls.localize('extensionService.crash', "Extension host terminated unexpectedly."); - if (code === 87) { - message = nls.localize('extensionService.unresponsiveCrash', "Extension host terminated because it was not responsive."); - } - - this._notificationService.prompt(Severity.Error, message, - [{ - label: nls.localize('devTools', "Open Developer Tools"), - run: () => this._windowService.openDevTools() - }, - { - label: nls.localize('restart', "Restart Extension Host"), - run: () => this._startExtensionHostProcess(false, Object.keys(this._allRequestedActivateEvents)) - }] - ); - } - - // ---- begin IExtensionService - - public activateByEvent(activationEvent: string): Promise { - if (this._installedExtensionsReady.isOpen()) { - // Extensions have been scanned and interpreted - - // Record the fact that this activationEvent was requested (in case of a restart) - this._allRequestedActivateEvents[activationEvent] = true; - - if (!this._registry.containsActivationEvent(activationEvent)) { - // There is no extension that is interested in this activation event - return NO_OP_VOID_PROMISE; - } - - return this._activateByEvent(activationEvent); - } else { - // Extensions have not been scanned yet. - - // Record the fact that this activationEvent was requested (in case of a restart) - this._allRequestedActivateEvents[activationEvent] = true; - - return this._installedExtensionsReady.wait().then(() => this._activateByEvent(activationEvent)); } } - private _activateByEvent(activationEvent: string): Promise { - const result = Promise.all( - this._extensionHostProcessManagers.map(extHostManager => extHostManager.activateByEvent(activationEvent)) - ).then(() => { }); - this._onWillActivateByEvent.fire({ - event: activationEvent, - activation: result - }); - return result; - } - - public whenInstalledExtensionsRegistered(): Promise { - return this._installedExtensionsReady.wait(); - } - - public getExtensions(): Promise { - return this._installedExtensionsReady.wait().then(() => { - return this._registry.getAllExtensionDescriptions(); - }); - } - - public getExtension(id: string): Promise { - return this._installedExtensionsReady.wait().then(() => { - return this._registry.getExtensionDescription(id); - }); - } - - public readExtensionPointContributions(extPoint: IExtensionPoint): Promise[]> { - return this._installedExtensionsReady.wait().then(() => { - let availableExtensions = this._registry.getAllExtensionDescriptions(); - - let result: ExtensionPointContribution[] = [], resultLen = 0; - for (let i = 0, len = availableExtensions.length; i < len; i++) { - let desc = availableExtensions[i]; - - if (desc.contributes && hasOwnProperty.call(desc.contributes, extPoint.name)) { - result[resultLen++] = new ExtensionPointContribution(desc, desc.contributes[extPoint.name]); - } - } - - return result; - }); - } - - public getExtensionsStatus(): { [id: string]: IExtensionsStatus; } { - let result: { [id: string]: IExtensionsStatus; } = Object.create(null); - if (this._registry) { - const extensions = this._registry.getAllExtensionDescriptions(); - for (const extension of extensions) { - const extensionKey = ExtensionIdentifier.toKey(extension.identifier); - result[extension.identifier.value] = { - messages: this._extensionsMessages.get(extensionKey) || [], - activationTimes: this._extensionHostProcessActivationTimes.get(extensionKey), - runtimeErrors: this._extensionHostExtensionRuntimeErrors.get(extensionKey) || [], - }; - } - } - return result; - } - - public getInspectPort(): number { - if (this._extensionHostProcessManagers.length > 0) { - return this._extensionHostProcessManagers[0].getInspectPort(); - } - return 0; - } - - // ---- end IExtensionService - // --- impl private createLogger(): Logger { @@ -590,16 +409,110 @@ export class ExtensionService extends Disposable implements IExtensionService { }); } - private async _scanAndHandleExtensions(): Promise { - this._extensionScanner.startScanningExtensions(this.createLogger()); + private async _resolveAuthorityAgain(): Promise { + const remoteAuthority = this._environmentService.configuration.remoteAuthority; + if (!remoteAuthority) { + return; + } const extensionHost = this._extensionHostProcessManagers[0]; - const extensions = await this._extensionScanner.scannedExtensions; - const enabledExtensions = await this._getRuntimeExtensions(extensions); + this._remoteAuthorityResolverService.clearResolvedAuthority(remoteAuthority); + try { + const resolvedAuthority = await extensionHost.resolveAuthority(remoteAuthority); + this._remoteAuthorityResolverService.setResolvedAuthority(resolvedAuthority); + } catch (err) { + this._remoteAuthorityResolverService.setResolvedAuthorityError(remoteAuthority, err); + } + } - this._handleExtensionPoints(enabledExtensions); - extensionHost.start(enabledExtensions.map(extension => extension.identifier).filter(id => this._registry.containsExtension(id))); - this._releaseBarrier(); + protected async _scanAndHandleExtensions(): Promise { + this._extensionScanner.startScanningExtensions(this.createLogger()); + + const remoteAuthority = this._environmentService.configuration.remoteAuthority; + const extensionHost = this._extensionHostProcessManagers[0]; + + let localExtensions = await this._extensionScanner.scannedExtensions; + + // enable or disable proposed API per extension + this._checkEnableProposedApi(localExtensions); + + // remove disabled extensions + localExtensions = localExtensions.filter(extension => this._isEnabled(extension)); + + if (remoteAuthority) { + let resolvedAuthority: ResolvedAuthority; + + try { + resolvedAuthority = await extensionHost.resolveAuthority(remoteAuthority); + } catch (err) { + console.error(err); + const plusIndex = remoteAuthority.indexOf('+'); + const authorityFriendlyName = plusIndex > 0 ? remoteAuthority.substr(0, plusIndex) : remoteAuthority; + if (!RemoteAuthorityResolverError.isHandledNotAvailable(err)) { + this._notificationService.notify({ severity: Severity.Error, message: nls.localize('resolveAuthorityFailure', "Resolving the authority `{0}` failed", authorityFriendlyName) }); + } else { + console.log(`Not showing a notification for the error`); + } + + this._remoteAuthorityResolverService.setResolvedAuthorityError(remoteAuthority, err); + + // Proceed with the local extension host + await this._startLocalExtensionHost(extensionHost, localExtensions); + return; + } + + // set the resolved authority + this._remoteAuthorityResolverService.setResolvedAuthority(resolvedAuthority); + + // monitor for breakage + const connection = this._remoteAgentService.getConnection(); + if (connection) { + connection.onDidStateChange(async (e) => { + const remoteAuthority = this._environmentService.configuration.remoteAuthority; + if (!remoteAuthority) { + return; + } + if (e.type === PersistenConnectionEventType.ConnectionLost) { + this._remoteAuthorityResolverService.clearResolvedAuthority(remoteAuthority); + } + }); + connection.onReconnecting(() => this._resolveAuthorityAgain()); + } + + // fetch the remote environment + const remoteEnv = (await this._remoteAgentService.getEnvironment())!; + + // enable or disable proposed API per extension + this._checkEnableProposedApi(remoteEnv.extensions); + + // remove disabled extensions + remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension)); + + // remove UI extensions from the remote extensions + remoteEnv.extensions = remoteEnv.extensions.filter(extension => !isUIExtension(extension, this._productService, this._configurationService)); + + // remove non-UI extensions from the local extensions + localExtensions = localExtensions.filter(extension => extension.isBuiltin || isUIExtension(extension, this._productService, this._configurationService)); + + // in case of overlap, the remote wins + const isRemoteExtension = new Set(); + remoteEnv.extensions.forEach(extension => isRemoteExtension.add(ExtensionIdentifier.toKey(extension.identifier))); + localExtensions = localExtensions.filter(extension => !isRemoteExtension.has(ExtensionIdentifier.toKey(extension.identifier))); + + // save for remote extension's init data + this._remoteExtensionsEnvironmentData.set(remoteAuthority, remoteEnv); + + this._handleExtensionPoints(([]).concat(remoteEnv.extensions).concat(localExtensions)); + extensionHost.start(localExtensions.map(extension => extension.identifier)); + + } else { + await this._startLocalExtensionHost(extensionHost, localExtensions); + } + } + + private async _startLocalExtensionHost(extensionHost: ExtensionHostProcessManager, localExtensions: IExtensionDescription[]): Promise { + this._handleExtensionPoints(localExtensions); + extensionHost.start(localExtensions.map(extension => extension.identifier).filter(id => this._registry.containsExtension(id))); } private _handleExtensionPoints(allExtensions: IExtensionDescription[]): void { @@ -608,234 +521,19 @@ export class ExtensionService extends Disposable implements IExtensionService { this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', '))); } - let availableExtensions = this._registry.getAllExtensionDescriptions(); - let extensionPoints = ExtensionsRegistry.getExtensionPoints(); - - let messageHandler = (msg: IMessage) => this._handleExtensionPointMessage(msg); - - for (let i = 0, len = extensionPoints.length; i < len; i++) { - ExtensionService._handleExtensionPoint(extensionPoints[i], availableExtensions, messageHandler); - } + this._doHandleExtensionPoints(this._registry.getAllExtensionDescriptions()); } - private _releaseBarrier(): void { - perf.mark('extensionHostReady'); - this._installedExtensionsReady.open(); - this._onDidRegisterExtensions.fire(undefined); - this._onDidChangeExtensionsStatus.fire(this._registry.getAllExtensionDescriptions().map(e => e.identifier)); - } - - private isExtensionUnderDevelopment(extension: IExtensionDescription): boolean { - if (this._environmentService.isExtensionDevelopment) { - const extDevLocs = this._environmentService.extensionDevelopmentLocationURI; - if (extDevLocs) { - const extLocation = extension.extensionLocation; - for (let p of extDevLocs) { - if (isEqualOrParent(extLocation, p)) { - return true; - } - } - } + public getInspectPort(): number { + if (this._extensionHostProcessManagers.length > 0) { + return this._extensionHostProcessManagers[0].getInspectPort(); } - return false; - } - - private async _getRuntimeExtensions(allExtensions: IExtensionDescription[]): Promise { - - const runtimeExtensions: IExtensionDescription[] = []; - const extensionsToDisable: IExtensionDescription[] = []; - const userMigratedSystemExtensions: IExtensionIdentifier[] = [{ id: BetterMergeId }]; - - let enableProposedApiFor: string | string[] = this._environmentService.args['enable-proposed-api'] || []; - - const notFound = (id: string) => nls.localize('notFound', "Extension \`{0}\` cannot use PROPOSED API as it cannot be found", id); - - if (enableProposedApiFor.length) { - let allProposed = (enableProposedApiFor instanceof Array ? enableProposedApiFor : [enableProposedApiFor]); - allProposed.forEach(id => { - if (!allExtensions.some(description => ExtensionIdentifier.equals(description.identifier, id))) { - console.error(notFound(id)); - } - }); - // Make enabled proposed API be lowercase for case insensitive comparison - if (Array.isArray(enableProposedApiFor)) { - enableProposedApiFor = enableProposedApiFor.map(id => id.toLowerCase()); - } else { - enableProposedApiFor = enableProposedApiFor.toLowerCase(); - } - } - - const enableProposedApiForAll = !this._environmentService.isBuilt || - (!!this._environmentService.extensionDevelopmentLocationURI && product.nameLong !== 'Visual Studio Code') || - (enableProposedApiFor.length === 0 && 'enable-proposed-api' in this._environmentService.args); - - - for (const extension of allExtensions) { - - // Do not disable extensions under development - if (!this.isExtensionUnderDevelopment(extension)) { - if (!this._extensionEnablementService.isEnabled(toExtension(extension))) { - continue; - } - } - - if (!extension.isBuiltin) { - // Check if the extension is changed to system extension - const userMigratedSystemExtension = userMigratedSystemExtensions.filter(userMigratedSystemExtension => areSameExtensions(userMigratedSystemExtension, { id: extension.identifier.value }))[0]; - if (userMigratedSystemExtension) { - extensionsToDisable.push(extension); - continue; - } - } - runtimeExtensions.push(this._updateEnableProposedApi(extension, enableProposedApiForAll, enableProposedApiFor)); - } - - this._telemetryService.publicLog('extensionsScanned', { - totalCount: runtimeExtensions.length, - disabledCount: allExtensions.length - runtimeExtensions.length - }); - - if (extensionsToDisable.length) { - return this._extensionEnablementService.setEnablement(extensionsToDisable.map(e => toExtension(e)), EnablementState.Disabled) - .then(() => runtimeExtensions); - } else { - return runtimeExtensions; - } - } - - private _updateEnableProposedApi(extension: IExtensionDescription, enableProposedApiForAll: boolean, enableProposedApiFor: string | string[]): IExtensionDescription { - if (allowProposedApiFromProduct(extension.identifier)) { - // fast lane -> proposed api is available to all extensions - // that are listed in product.json-files - extension.enableProposedApi = true; - - } else if (extension.enableProposedApi && !extension.isBuiltin) { - if ( - !enableProposedApiForAll && - enableProposedApiFor.indexOf(extension.identifier.value.toLowerCase()) < 0 - ) { - extension.enableProposedApi = false; - console.error(`Extension '${extension.identifier.value} cannot use PROPOSED API (must started out of dev or enabled via --enable-proposed-api)`); - - } else { - // proposed api is available when developing or when an extension was explicitly - // spelled out via a command line argument - console.warn(`Extension '${extension.identifier.value}' uses PROPOSED API which is subject to change and removal without notice.`); - } - } - return extension; - } - - private _handleExtensionPointMessage(msg: IMessage) { - const extensionKey = ExtensionIdentifier.toKey(msg.extensionId); - - if (!this._extensionsMessages.has(extensionKey)) { - this._extensionsMessages.set(extensionKey, []); - } - this._extensionsMessages.get(extensionKey)!.push(msg); - - const extension = this._registry.getExtensionDescription(msg.extensionId); - const strMsg = `[${msg.extensionId.value}]: ${msg.message}`; - if (extension && extension.isUnderDevelopment) { - // This message is about the extension currently being developed - this._showMessageToUser(msg.type, strMsg); - } else { - this._logMessageInConsole(msg.type, strMsg); - } - - if (!this._isDev && msg.extensionId) { - const { type, extensionId, extensionPointId, message } = msg; - /* __GDPR__ - "extensionsMessage" : { - "type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "extensionId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "extensionPointId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "message": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } - } - */ - this._telemetryService.publicLog('extensionsMessage', { - type, extensionId: extensionId.value, extensionPointId, message - }); - } - } - - private static _handleExtensionPoint(extensionPoint: ExtensionPoint, availableExtensions: IExtensionDescription[], messageHandler: (msg: IMessage) => void): void { - let users: IExtensionPointUser[] = [], usersLen = 0; - for (let i = 0, len = availableExtensions.length; i < len; i++) { - let desc = availableExtensions[i]; - - if (desc.contributes && hasOwnProperty.call(desc.contributes, extensionPoint.name)) { - users[usersLen++] = { - description: desc, - value: desc.contributes[extensionPoint.name], - collector: new ExtensionMessageCollector(messageHandler, desc, extensionPoint.name) - }; - } - } - - extensionPoint.acceptUsers(users); - } - - private _showMessageToUser(severity: Severity, msg: string): void { - if (severity === Severity.Error || severity === Severity.Warning) { - this._notificationService.notify({ severity, message: msg }); - } else { - this._logMessageInConsole(severity, msg); - } - } - - private _logMessageInConsole(severity: Severity, msg: string): void { - if (severity === Severity.Error) { - console.error(msg); - } else if (severity === Severity.Warning) { - console.warn(msg); - } else { - console.log(msg); - } - } - - // -- called by extension host - - public _logOrShowMessage(severity: Severity, msg: string): void { - if (this._isDev) { - this._showMessageToUser(severity, msg); - } else { - this._logMessageInConsole(severity, msg); - } - } - - public async _activateById(extensionId: ExtensionIdentifier, activationEvent: string): Promise { - const results = await Promise.all( - this._extensionHostProcessManagers.map(manager => manager.activate(extensionId, activationEvent)) - ); - const activated = results.some(e => e); - if (!activated) { - throw new Error(`Unknown extension ${extensionId.value}`); - } - } - - public _onWillActivateExtension(extensionId: ExtensionIdentifier): void { - this._extensionHostActiveExtensions.set(ExtensionIdentifier.toKey(extensionId), extensionId); - } - - public _onDidActivateExtension(extensionId: ExtensionIdentifier, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void { - this._extensionHostProcessActivationTimes.set(ExtensionIdentifier.toKey(extensionId), new ActivationTimes(startup, codeLoadingTime, activateCallTime, activateResolvedTime, activationEvent)); - this._onDidChangeExtensionsStatus.fire([extensionId]); - } - - public _onExtensionRuntimeError(extensionId: ExtensionIdentifier, err: Error): void { - const extensionKey = ExtensionIdentifier.toKey(extensionId); - if (!this._extensionHostExtensionRuntimeErrors.has(extensionKey)) { - this._extensionHostExtensionRuntimeErrors.set(extensionKey, []); - } - this._extensionHostExtensionRuntimeErrors.get(extensionKey)!.push(err); - this._onDidChangeExtensionsStatus.fire([extensionId]); + return 0; } public _onExtensionHostExit(code: number): void { // Expected development extension termination: When the extension host goes down we also shutdown the window - const devOpts = parseExtensionDevOptions(this._environmentService); - if (!devOpts.isExtensionDevTestFromCli) { + if (!this._isExtensionDevTestFromCli) { this._windowService.closeWindow(); } diff --git a/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts b/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts new file mode 100644 index 00000000000..6fd6dc821e8 --- /dev/null +++ b/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts @@ -0,0 +1,142 @@ +/*--------------------------------------------------------------------------------------------- + * 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/common/ipc'; +import { IExtensionManagementService, ILocalExtension, IGalleryExtension, IExtensionGalleryService, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { URI } from 'vs/base/common/uri'; +import { ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { ILogService } from 'vs/platform/log/common/log'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; +import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; +import { values } from 'vs/base/common/map'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { localize } from 'vs/nls'; +import { IProductService } from 'vs/platform/product/common/product'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/node/extensionManagementIpc'; + +export class RemoteExtensionManagementChannelClient extends ExtensionManagementChannelClient { + + _serviceBrand: any; + + constructor( + channel: IChannel, + private readonly localExtensionManagementService: IExtensionManagementService, + private readonly galleryService: IExtensionGalleryService, + private readonly logService: ILogService, + private readonly configurationService: IConfigurationService, + private readonly productService: IProductService + ) { + super(channel); + } + + async install(vsix: URI): Promise { + const local = await super.install(vsix); + await this.installUIDependenciesAndPackedExtensions(local); + return local; + } + + async installFromGallery(extension: IGalleryExtension): Promise { + const local = await this.doInstallFromGallery(extension); + await this.installUIDependenciesAndPackedExtensions(local); + return local; + } + + private async doInstallFromGallery(extension: IGalleryExtension): Promise { + try { + const local = await super.installFromGallery(extension); + return local; + } catch (error) { + try { + this.logService.error(`Error while installing '${extension.identifier.id}' extension in the remote server.`, toErrorMessage(error)); + this.logService.info(`Trying to download '${extension.identifier.id}' extension locally and install`); + const local = await this.downloadCompatibleAndInstall(extension); + this.logService.info(`Successfully installed '${extension.identifier.id}' extension`); + return local; + } catch (e) { + this.logService.error(e); + throw error; + } + } + } + + private async downloadCompatibleAndInstall(extension: IGalleryExtension): Promise { + const installed = await this.getInstalled(ExtensionType.User); + const compatible = await this.galleryService.getCompatibleExtension(extension); + if (!compatible) { + return Promise.reject(new Error(localize('incompatible', "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", extension.identifier.id, this.productService.version))); + } + const manifest = await this.galleryService.getManifest(compatible, CancellationToken.None); + if (manifest) { + const workspaceExtensions = await this.getAllWorkspaceDependenciesAndPackedExtensions(manifest, CancellationToken.None); + await Promise.all(workspaceExtensions.map(e => this.downloadAndInstall(e, installed))); + } + return this.downloadAndInstall(extension, installed); + } + + private async downloadAndInstall(extension: IGalleryExtension, installed: ILocalExtension[]): Promise { + const location = await this.galleryService.download(extension, installed.filter(i => areSameExtensions(i.identifier, extension.identifier))[0] ? InstallOperation.Update : InstallOperation.Install); + return super.install(URI.file(location)); + } + + private async installUIDependenciesAndPackedExtensions(local: ILocalExtension): Promise { + const uiExtensions = await this.getAllUIDependenciesAndPackedExtensions(local.manifest, CancellationToken.None); + const installed = await this.localExtensionManagementService.getInstalled(); + const toInstall = uiExtensions.filter(e => installed.every(i => !areSameExtensions(i.identifier, e.identifier))); + await Promise.all(toInstall.map(d => this.localExtensionManagementService.installFromGallery(d))); + } + + private async getAllUIDependenciesAndPackedExtensions(manifest: IExtensionManifest, token: CancellationToken): Promise { + const result = new Map(); + const extensions = [...(manifest.extensionPack || []), ...(manifest.extensionDependencies || [])]; + await this.getDependenciesAndPackedExtensionsRecursively(extensions, result, true, token); + return values(result); + } + + private async getAllWorkspaceDependenciesAndPackedExtensions(manifest: IExtensionManifest, token: CancellationToken): Promise { + const result = new Map(); + const extensions = [...(manifest.extensionPack || []), ...(manifest.extensionDependencies || [])]; + await this.getDependenciesAndPackedExtensionsRecursively(extensions, result, false, token); + return values(result); + } + + private async getDependenciesAndPackedExtensionsRecursively(toGet: string[], result: Map, uiExtension: boolean, token: CancellationToken): Promise { + if (toGet.length === 0) { + return Promise.resolve(); + } + + const extensions = (await this.galleryService.query({ names: toGet, pageSize: toGet.length }, token)).firstPage; + const manifests = await Promise.all(extensions.map(e => this.galleryService.getManifest(e, token))); + const extensionsManifests: IExtensionManifest[] = []; + for (let idx = 0; idx < extensions.length; idx++) { + const extension = extensions[idx]; + const manifest = manifests[idx]; + if (manifest && isUIExtension(manifest, this.productService, this.configurationService) === uiExtension) { + result.set(extension.identifier.id.toLowerCase(), extension); + extensionsManifests.push(manifest); + } + } + toGet = []; + for (const extensionManifest of extensionsManifests) { + if (isNonEmptyArray(extensionManifest.extensionDependencies)) { + for (const id of extensionManifest.extensionDependencies) { + if (!result.has(id.toLowerCase())) { + toGet.push(id); + } + } + } + if (isNonEmptyArray(extensionManifest.extensionPack)) { + for (const id of extensionManifest.extensionPack) { + if (!result.has(id.toLowerCase())) { + toGet.push(id); + } + } + } + } + return this.getDependenciesAndPackedExtensionsRecursively(toGet, result, uiExtension, token); + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/extensions/node/extensionHostMain.ts b/src/vs/workbench/services/extensions/node/extensionHostMain.ts index 67d2ba56ada..9e58237b634 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostMain.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostMain.ts @@ -19,7 +19,6 @@ import { RPCProtocol } from 'vs/workbench/services/extensions/common/rpcProtocol import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { withNullAsUndefined } from 'vs/base/common/types'; import { ILogService } from 'vs/platform/log/common/log'; -import { ISchemeTransformer } from 'vs/workbench/api/common/extHostLanguageFeatures'; // we don't (yet) throw when extensions parse // uris that have no scheme @@ -53,9 +52,7 @@ export class ExtensionHostMain { hostUtils: IHostUtils, consolePatchFn: IConsolePatchFn, logServiceFn: ILogServiceFn, - uriTransformer: IURITransformer | null, - schemeTransformer: ISchemeTransformer | null, - outputChannelName: string, + uriTransformer: IURITransformer | null ) { this._isTerminating = false; this._hostUtils = hostUtils; @@ -86,8 +83,7 @@ export class ExtensionHostMain { extHostConfiguraiton, initData.environment, this._extHostLogService, - schemeTransformer, - outputChannelName + uriTransformer ); // error forwarding and stack trace scanning diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcess.ts b/src/vs/workbench/services/extensions/node/extensionHostProcess.ts index c7227dcdd98..4c890beb60d 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostProcess.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcess.ts @@ -3,11 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; import { startExtensionHostProcess } from 'vs/workbench/services/extensions/node/extensionHostProcessSetup'; -startExtensionHostProcess( - _ => null, - _ => null, - _ => nls.localize('extension host Log', "Extension Host") -).catch((err) => console.log(err)); +startExtensionHostProcess().catch((err) => console.log(err)); diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts index cd9d907c66d..1a5822e645f 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts @@ -5,23 +5,33 @@ import * as nativeWatchdog from 'native-watchdog'; import * as net from 'net'; +import * as minimist from 'minimist'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { Event } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; -import { PersistentProtocol, ProtocolConstants } from 'vs/base/parts/ipc/common/ipc.net'; -import { NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; +import { PersistentProtocol, ProtocolConstants, createBufferedEvent } from 'vs/base/parts/ipc/common/ipc.net'; +import { NodeSocket, WebSocketNodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; import product from 'vs/platform/product/node/product'; import { IInitData, MainThreadConsoleShape } from 'vs/workbench/api/common/extHost.protocol'; import { MessageType, createMessageOfType, isMessageOfType, IExtHostSocketMessage, IExtHostReadyMessage } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { ExtensionHostMain, IExitFn, ILogServiceFn } from 'vs/workbench/services/extensions/node/extensionHostMain'; import { VSBuffer } from 'vs/base/common/buffer'; -import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions'; -import { ISchemeTransformer } from 'vs/workbench/api/common/extHostLanguageFeatures'; -import { IURITransformer } from 'vs/base/common/uriIpc'; +import { IURITransformer, URITransformer, IRawURITransformer } from 'vs/base/common/uriIpc'; import { exists } from 'vs/base/node/pfs'; import { realpath } from 'vs/base/node/extpath'; import { IHostUtils } from 'vs/workbench/api/node/extHostExtensionService'; +import { SpdLogService } from 'vs/platform/log/node/spdlogService'; + +interface ParsedExtHostArgs { + uriTransformerPath?: string; +} + +const args = minimist(process.argv.slice(2), { + string: [ + 'uriTransformerPath' + ] +}) as ParsedExtHostArgs; // With Electron 2.x and node.js 8.x the "natives" module // can cause a native crash (see https://github.com/nodejs/node/issues/19891 and @@ -72,7 +82,7 @@ function patchPatchedConsole(mainThreadConsole: MainThreadConsoleShape): void { }; } -const createLogService: ILogServiceFn = initData => createSpdLogService(ExtensionHostLogFileName, initData.logLevel, initData.logsLocation.fsPath); +const createLogService: ILogServiceFn = initData => new SpdLogService(ExtensionHostLogFileName, initData.logsLocation.fsPath, initData.logLevel); interface IRendererConnection { protocol: IMessagePassingProtocol; @@ -101,27 +111,41 @@ function _createExtHostProtocol(): Promise { process.on('message', (msg: IExtHostSocketMessage, handle: net.Socket) => { if (msg && msg.type === 'VSCODE_EXTHOST_IPC_SOCKET') { const initialDataChunk = VSBuffer.wrap(Buffer.from(msg.initialDataChunk, 'base64')); + let socket: NodeSocket | WebSocketNodeSocket; + if (msg.skipWebSocketFrames) { + socket = new NodeSocket(handle); + } else { + socket = new WebSocketNodeSocket(new NodeSocket(handle)); + } if (protocol) { // reconnection case if (disconnectWaitTimer) { clearTimeout(disconnectWaitTimer); disconnectWaitTimer = null; } - protocol.beginAcceptReconnection(new NodeSocket(handle), initialDataChunk); + protocol.beginAcceptReconnection(socket, initialDataChunk); protocol.endAcceptReconnection(); } else { clearTimeout(timer); - protocol = new PersistentProtocol(new NodeSocket(handle), initialDataChunk); + protocol = new PersistentProtocol(socket, initialDataChunk); protocol.onClose(() => onTerminate()); resolve(protocol); - protocol.onSocketClose(() => { - // The socket has closed, let's give the renderer a certain amount of time to reconnect - disconnectWaitTimer = setTimeout(() => { - disconnectWaitTimer = null; + if (msg.skipWebSocketFrames) { + // Wait for rich client to reconnect + protocol.onSocketClose(() => { + // The socket has closed, let's give the renderer a certain amount of time to reconnect + disconnectWaitTimer = setTimeout(() => { + disconnectWaitTimer = null; + onTerminate(); + }, ProtocolConstants.ReconnectionGraceTime); + }); + } else { + // Do not wait for web companion to reconnect + protocol.onSocketClose(() => { onTerminate(); - }, ProtocolConstants.ReconnectionGraceTime); - }); + }); + } } } }); @@ -155,16 +179,22 @@ async function createExtHostProtocol(): Promise { return new class implements IMessagePassingProtocol { - private _terminating = false; + private readonly _onMessage = new Emitter(); + readonly onMessage: Event = createBufferedEvent(this._onMessage.event); - readonly onMessage: Event = Event.filter(protocol.onMessage, msg => { - if (!isMessageOfType(msg, MessageType.Terminate)) { - return true; - } - this._terminating = true; - onTerminate(); - return false; - }); + private _terminating: boolean; + + constructor() { + this._terminating = false; + protocol.onMessage((msg) => { + if (isMessageOfType(msg, MessageType.Terminate)) { + this._terminating = true; + onTerminate(); + } else { + this._onMessage.fire(msg); + } + }); + } send(msg: any): void { if (!this._terminating) { @@ -272,11 +302,7 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise IURITransformer | null, - schemeTransformerFn: (initData: IInitData) => ISchemeTransformer | null, - outputChannelNameFn: (initData: IInitData) => string, -): Promise { +export async function startExtensionHostProcess(): Promise { const protocol = await createExtHostProtocol(); const renderer = await connectToRenderer(protocol); @@ -291,6 +317,17 @@ export async function startExtensionHostProcess( realpath(path: string) { return realpath(path); } }; + // Attempt to load uri transformer + let uriTransformer: IURITransformer | null = null; + if (initData.remote.authority && args.uriTransformerPath) { + try { + const rawURITransformerFactory = require.__$__nodeRequire(args.uriTransformerPath); + const rawURITransformer = rawURITransformerFactory(initData.remote.authority); + uriTransformer = new URITransformer(rawURITransformer); + } catch (e) { + console.error(e); + } + } const extensionHostMain = new ExtensionHostMain( renderer.protocol, @@ -298,9 +335,7 @@ export async function startExtensionHostProcess( hostUtils, patchPatchedConsole, createLogService, - uriTransformerFn(initData), - schemeTransformerFn(initData), - outputChannelNameFn(initData) + uriTransformer ); // rewrite onTerminate-function to be a proper shutdown diff --git a/src/vs/workbench/services/extensions/node/extensionPoints.ts b/src/vs/workbench/services/extensions/node/extensionPoints.ts index 6e2179ddd8e..6a1083c003b 100644 --- a/src/vs/workbench/services/extensions/node/extensionPoints.ts +++ b/src/vs/workbench/services/extensions/node/extensionPoints.ts @@ -12,40 +12,13 @@ import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages'; import * as types from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import * as pfs from 'vs/base/node/pfs'; -import { getGalleryExtensionId, groupByExtension } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { getGalleryExtensionId, groupByExtension, ExtensionIdentifierWithVersion } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { isValidExtensionVersion } from 'vs/platform/extensions/node/extensionValidator'; -import { ExtensionIdentifier, ExtensionIdentifierWithVersion, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { Translations, ILog } from 'vs/workbench/services/extensions/common/extensionPoints'; const MANIFEST_FILE = 'package.json'; -export interface Translations { - [id: string]: string; -} - -namespace Translations { - export function equals(a: Translations, b: Translations): boolean { - if (a === b) { - return true; - } - let aKeys = Object.keys(a); - let bKeys: Set = new Set(); - for (let key of Object.keys(b)) { - bKeys.add(key); - } - if (aKeys.length !== bKeys.size) { - return false; - } - - for (let key of aKeys) { - if (a[key] !== b[key]) { - return false; - } - bKeys.delete(key); - } - return bKeys.size === 0; - } -} - export interface NlsConfiguration { readonly devMode: boolean; readonly locale: string | undefined; @@ -53,12 +26,6 @@ export interface NlsConfiguration { readonly translations: Translations; } -export interface ILog { - error(source: string, message: string): void; - warn(source: string, message: string): void; - info(source: string, message: string): void; -} - abstract class ExtensionManifestHandler { protected readonly _ourVersion: string; @@ -251,7 +218,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler { * This routine makes the following assumptions: * The root element is an object literal */ - private static _replaceNLStrings(nlsConfig: NlsConfiguration, literal: T, messages: { [key: string]: string; }, originalMessages: { [key: string]: string } | null, log: ILog, messageScope: string): void { + private static _replaceNLStrings(nlsConfig: NlsConfiguration, literal: T, messages: { [key: string]: string; }, originalMessages: { [key: string]: string } | null, log: ILog, messageScope: string): void { function processEntry(obj: any, key: string | number, command?: boolean) { let value = obj[key]; if (types.isString(value)) { diff --git a/src/vs/workbench/services/extensions/node/extensionsUtil.ts b/src/vs/workbench/services/extensions/node/extensionsUtil.ts deleted file mode 100644 index d37b3f0f794..00000000000 --- a/src/vs/workbench/services/extensions/node/extensionsUtil.ts +++ /dev/null @@ -1,14 +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 { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; -import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { isUIExtension as _isUIExtension } from 'vs/platform/extensions/node/extensionsUtil'; - -export function isUIExtension(manifest: IExtensionManifest, configurationService: IConfigurationService): boolean { - const uiExtensionPoints = ExtensionsRegistry.getExtensionPoints().filter(e => e.defaultExtensionKind !== 'workspace').map(e => e.name); - return _isUIExtension(manifest, uiExtensionPoints, configurationService); -} diff --git a/src/vs/workbench/services/extensions/node/multiExtensionManagement.ts b/src/vs/workbench/services/extensions/node/multiExtensionManagement.ts index c430a811ac5..1760b4d5fc3 100644 --- a/src/vs/workbench/services/extensions/node/multiExtensionManagement.ts +++ b/src/vs/workbench/services/extensions/node/multiExtensionManagement.ts @@ -8,7 +8,7 @@ import { IExtensionManagementService, ILocalExtension, IGalleryExtension, InstallExtensionEvent, DidInstallExtensionEvent, IExtensionIdentifier, DidUninstallExtensionEvent, IReportedExtension, IGalleryMetadata, IExtensionManagementServerService, IExtensionManagementServer, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { ExtensionType, IExtensionManifest, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; +import { ExtensionType, isLanguagePackExtension } 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'; @@ -16,8 +16,9 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { localize } from 'vs/nls'; -import { isUIExtension } from 'vs/workbench/services/extensions/node/extensionsUtil'; +import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IProductService } from 'vs/platform/product/common/product'; export class MultiExtensionManagementService extends Disposable implements IExtensionManagementService { @@ -33,7 +34,8 @@ export class MultiExtensionManagementService extends Disposable implements IExte constructor( @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, - @IConfigurationService private readonly configurationService: IConfigurationService + @IConfigurationService private readonly configurationService: IConfigurationService, + @IProductService private readonly productService: IProductService, ) { super(); this.servers = this.extensionManagementServerService.remoteExtensionManagementServer ? [this.extensionManagementServerService.localExtensionManagementServer, this.extensionManagementServerService.remoteExtensionManagementServer] : [this.extensionManagementServerService.localExtensionManagementServer]; @@ -83,7 +85,7 @@ export class MultiExtensionManagementService extends Disposable implements IExte private async uninstallInServer(extension: ILocalExtension, server: IExtensionManagementServer, force?: boolean): Promise { if (server === this.extensionManagementServerService.localExtensionManagementServer) { const installedExtensions = await this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.getInstalled(ExtensionType.User); - const dependentNonUIExtensions = installedExtensions.filter(i => !isUIExtension(i.manifest, this.configurationService) + const dependentNonUIExtensions = installedExtensions.filter(i => !isUIExtension(i.manifest, this.productService, this.configurationService) && i.manifest.extensionDependencies && i.manifest.extensionDependencies.some(id => areSameExtensions({ id }, extension.identifier))); if (dependentNonUIExtensions.length) { return Promise.reject(new Error(this.getDependentsErrorMessage(extension, dependentNonUIExtensions))); @@ -130,44 +132,38 @@ export class MultiExtensionManagementService extends Disposable implements IExte return Promise.all(this.servers.map(({ extensionManagementService }) => extensionManagementService.unzip(zipLocation, type))).then(([extensionIdentifier]) => extensionIdentifier); } - async install(vsix: URI): Promise { + async install(vsix: URI): Promise { if (this.extensionManagementServerService.remoteExtensionManagementServer) { const manifest = await getManifest(vsix.fsPath); if (isLanguagePackExtension(manifest)) { // Install on both servers - const [extensionIdentifier] = await Promise.all(this.servers.map(server => server.extensionManagementService.install(vsix))); - return extensionIdentifier; + const [local] = await Promise.all(this.servers.map(server => server.extensionManagementService.install(vsix))); + return local; } - if (isUIExtension(manifest, this.configurationService)) { + if (isUIExtension(manifest, this.productService, this.configurationService)) { // Install only on local server return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.install(vsix); } // Install only on remote server - const promise = this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.install(vsix); - // Install UI Dependencies on local server - await this.installUIDependencies(manifest); - return promise; + return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.install(vsix); } return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.install(vsix); } - async installFromGallery(gallery: IGalleryExtension): Promise { + async installFromGallery(gallery: IGalleryExtension): Promise { if (this.extensionManagementServerService.remoteExtensionManagementServer) { const manifest = await this.extensionGalleryService.getManifest(gallery, CancellationToken.None); if (manifest) { if (isLanguagePackExtension(manifest)) { // Install on both servers - return Promise.all(this.servers.map(server => server.extensionManagementService.installFromGallery(gallery))).then(() => undefined); + return Promise.all(this.servers.map(server => server.extensionManagementService.installFromGallery(gallery))).then(([local]) => local); } - if (isUIExtension(manifest, this.configurationService)) { + if (isUIExtension(manifest, this.productService, this.configurationService)) { // Install only on local server return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery); } // Install only on remote server - const promise = this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.installFromGallery(gallery); - // Install UI Dependencies on local server - await this.installUIDependencies(manifest); - return promise; + return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.installFromGallery(gallery); } else { return Promise.reject(localize('Manifest is not found', "Installing Extension {0} failed: Manifest is not found.", gallery.displayName || gallery.name)); } @@ -175,20 +171,6 @@ export class MultiExtensionManagementService extends Disposable implements IExte return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery); } - private async installUIDependencies(manifest: IExtensionManifest): Promise { - if (manifest.extensionDependencies && manifest.extensionDependencies.length) { - const dependencies = await this.extensionGalleryService.loadAllDependencies(manifest.extensionDependencies.map(id => ({ id })), CancellationToken.None); - if (dependencies.length) { - await Promise.all(dependencies.map(async d => { - const manifest = await this.extensionGalleryService.getManifest(d, CancellationToken.None); - if (manifest && isUIExtension(manifest, this.configurationService)) { - await this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(d); - } - })); - } - } - } - getExtensionsReport(): Promise { return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.getExtensionsReport(); } diff --git a/src/vs/workbench/services/extensions/node/proxyResolver.ts b/src/vs/workbench/services/extensions/node/proxyResolver.ts index d85ae8c07b3..5480a882d78 100644 --- a/src/vs/workbench/services/extensions/node/proxyResolver.ts +++ b/src/vs/workbench/services/extensions/node/proxyResolver.ts @@ -457,8 +457,8 @@ async function readCaCertificates() { return undefined; } -function readWindowsCaCertificates() { - const winCA = require.__$__nodeRequire('vscode-windows-ca-certs'); +async function readWindowsCaCertificates() { + const winCA = await import('vscode-windows-ca-certs'); let ders: any[] = []; const store = winCA(); @@ -481,7 +481,14 @@ function readWindowsCaCertificates() { } async function readMacCaCertificates() { - const stdout = (await promisify(cp.execFile)('/usr/bin/security', ['find-certificate', '-a', '-p'], { encoding: 'utf8', maxBuffer: 1024 * 1024 })).stdout; + const stdout = await new Promise((resolve, reject) => { + const child = cp.spawn('/usr/bin/security', ['find-certificate', '-a', '-p']); + const stdout: string[] = []; + child.stdout.setEncoding('utf8'); + child.stdout.on('data', str => stdout.push(str)); + child.on('error', reject); + child.on('exit', code => code ? reject(code) : resolve(stdout.join(''))); + }); const seen = {}; const certs = stdout.split(/(?=-----BEGIN CERTIFICATE-----)/g) .filter(pem => !!pem.length && !seen[pem] && (seen[pem] = true)); diff --git a/src/vs/workbench/services/files/common/fileService.ts b/src/vs/workbench/services/files/common/fileService.ts index 7e06caa3de1..a788aadc1fd 100644 --- a/src/vs/workbench/services/files/common/fileService.ts +++ b/src/vs/workbench/services/files/common/fileService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, IDisposable, toDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, toDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle'; import { IFileService, IResolveFileOptions, FileChangesEvent, FileOperationEvent, IFileSystemProviderRegistrationEvent, IFileSystemProvider, IFileStat, IResolveFileResult, ICreateFileOptions, IFileSystemProviderActivationEvent, FileOperationError, FileOperationResult, FileOperation, FileSystemProviderCapabilities, FileType, toFileSystemProviderErrorCode, FileSystemProviderErrorCode, IStat, IFileStatWithMetadata, IResolveMetadataFileOptions, etag, hasReadWriteCapability, hasFileFolderCopyCapability, hasOpenReadWriteCloseCapability, toFileOperationResult, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileReadWriteCapability, IResolveFileResultWithMetadata, IWatchOptions, IWriteFileOptions, IReadFileOptions, IFileStreamContent, IFileContent, ETAG_DISABLED } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; @@ -49,20 +49,18 @@ export class FileService extends Disposable implements IFileService { this._onDidChangeFileSystemProviderRegistrations.fire({ added: true, scheme, provider }); // Forward events from provider - const providerDisposables: IDisposable[] = []; - providerDisposables.push(provider.onDidChangeFile(changes => this._onFileChanges.fire(new FileChangesEvent(changes)))); + const providerDisposables = new DisposableStore(); + providerDisposables.add(provider.onDidChangeFile(changes => this._onFileChanges.fire(new FileChangesEvent(changes)))); if (typeof provider.onDidErrorOccur === 'function') { - providerDisposables.push(provider.onDidErrorOccur(error => this._onError.fire(error))); + providerDisposables.add(provider.onDidErrorOccur(error => this._onError.fire(new Error(error)))); } - return combinedDisposable([ - toDisposable(() => { - this._onDidChangeFileSystemProviderRegistrations.fire({ added: false, scheme, provider }); - this.provider.delete(scheme); + return toDisposable(() => { + this._onDidChangeFileSystemProviderRegistrations.fire({ added: false, scheme, provider }); + this.provider.delete(scheme); - dispose(providerDisposables); - }) - ]); + dispose(providerDisposables); + }); } async activateProvider(scheme: string): Promise { @@ -80,7 +78,7 @@ export class FileService extends Disposable implements IFileService { }); if (this.provider.has(scheme)) { - return Promise.resolve(); // provider is already here so we can return directly + return; // provider is already here so we can return directly } // If the provider is not yet there, make sure to join on the listeners assuming @@ -237,7 +235,7 @@ export class FileService extends Disposable implements IFileService { return fileStat; } - return Promise.resolve(fileStat); + return fileStat; } async resolveAll(toResolve: { resource: URI, options?: IResolveFileOptions }[]): Promise; diff --git a/src/vs/workbench/services/files/node/diskFileSystemProvider.ts b/src/vs/workbench/services/files/node/diskFileSystemProvider.ts index 174a7502f18..9cdf02e1438 100644 --- a/src/vs/workbench/services/files/node/diskFileSystemProvider.ts +++ b/src/vs/workbench/services/files/node/diskFileSystemProvider.ts @@ -4,8 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { mkdir, open, close, read, write, fdatasync } from 'fs'; +import * as os from 'os'; import { promisify } from 'util'; -import { IDisposable, Disposable, toDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable, toDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle'; import { IFileSystemProvider, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileType, FileDeleteOptions, FileOverwriteOptions, FileWriteOptions, FileOpenOptions, FileSystemProviderErrorCode, createFileSystemProviderError, FileSystemProviderError } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; @@ -17,7 +18,7 @@ import { isEqual } from 'vs/base/common/extpath'; import { retry, ThrottledDelayer } from 'vs/base/common/async'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { localize } from 'vs/nls'; -import { IDiskFileChange, toFileChanges } from 'vs/workbench/services/files/node/watcher/watcher'; +import { IDiskFileChange, toFileChanges, ILogMessage } from 'vs/workbench/services/files/node/watcher/watcher'; import { FileWatcher as UnixWatcherService } from 'vs/workbench/services/files/node/watcher/unix/watcherService'; import { FileWatcher as WindowsWatcherService } from 'vs/workbench/services/files/node/watcher/win32/watcherService'; import { FileWatcher as NsfwWatcherService } from 'vs/workbench/services/files/node/watcher/nsfw/watcherService'; @@ -80,16 +81,14 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro const children = await readdir(this.toFilePath(resource)); const result: [string, FileType][] = []; - for (let i = 0; i < children.length; i++) { - const child = children[i]; - + await Promise.all(children.map(async child => { try { const stat = await this.stat(joinPath(resource, child)); result.push([child, stat.type]); } catch (error) { this.logService.trace(error); // ignore errors for individual entries that can arise from permission denied } - } + })); return result; } catch (error) { @@ -339,8 +338,8 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro //#region File Watching - private _onDidWatchErrorOccur: Emitter = this._register(new Emitter()); - get onDidErrorOccur(): Event { return this._onDidWatchErrorOccur.event; } + private _onDidWatchErrorOccur: Emitter = this._register(new Emitter()); + get onDidErrorOccur(): Event { return this._onDidWatchErrorOccur.event; } private _onDidChangeFile: Emitter = this._register(new Emitter()); get onDidChangeFile(): Event { return this._onDidChangeFile.event; } @@ -349,6 +348,8 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro private recursiveFoldersToWatch: { path: string, excludes: string[] }[] = []; private recursiveWatchRequestDelayer: ThrottledDelayer = this._register(new ThrottledDelayer(0)); + private recursiveWatcherLogLevelListener: IDisposable | undefined; + watch(resource: URI, opts: IWatchOptions): IDisposable { if (opts.recursive) { return this.watchRecursive(resource, opts.excludes); @@ -399,6 +400,7 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro // Dispose old dispose(this.recursiveWatcher); + this.recursiveWatcher = undefined; // Create new if we actually have folders to watch if (this.recursiveFoldersToWatch.length > 0) { @@ -406,44 +408,75 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro new( folders: { path: string, excludes: string[] }[], onChange: (changes: IDiskFileChange[]) => void, - onError: (msg: string) => void, - verboseLogging: boolean + onLogMessage: (msg: ILogMessage) => void, + verboseLogging: boolean, + watcherOptions?: { [key: string]: boolean | number | string } ): WindowsWatcherService | UnixWatcherService | NsfwWatcherService }; + let watcherOptions = undefined; - // Single Folder Watcher - if (this.recursiveFoldersToWatch.length === 1) { - if (isWindows) { - watcherImpl = WindowsWatcherService; - } else { - watcherImpl = UnixWatcherService; + if (this.forcePolling()) { + // WSL needs a polling watcher + watcherImpl = UnixWatcherService; + watcherOptions = { usePolling: true }; + } else { + // Single Folder Watcher + if (this.recursiveFoldersToWatch.length === 1) { + if (isWindows) { + watcherImpl = WindowsWatcherService; + } else { + watcherImpl = UnixWatcherService; + } } - } - // Multi Folder Watcher - else { - watcherImpl = NsfwWatcherService; + // Multi Folder Watcher + else { + watcherImpl = NsfwWatcherService; + } } // Create and start watching this.recursiveWatcher = new watcherImpl( this.recursiveFoldersToWatch, event => this._onDidChangeFile.fire(toFileChanges(event)), - error => this._onDidWatchErrorOccur.fire(new Error(error)), - this.logService.getLevel() === LogLevel.Trace + msg => { + if (msg.type === 'error') { + this._onDidWatchErrorOccur.fire(msg.message); + } + this.logService[msg.type](msg.message); + }, + this.logService.getLevel() === LogLevel.Trace, + watcherOptions ); + + if (!this.recursiveWatcherLogLevelListener) { + this.recursiveWatcherLogLevelListener = this.logService.onDidChangeLogLevel(_ => { + if (this.recursiveWatcher) { + this.recursiveWatcher.setVerboseLogging(this.logService.getLevel() === LogLevel.Trace); + } + }); + } } } } private watchNonRecursive(resource: URI): IDisposable { - return new NodeJSWatcherService( + const watcherService = new NodeJSWatcherService( this.toFilePath(resource), changes => this._onDidChangeFile.fire(toFileChanges(changes)), - error => this._onDidWatchErrorOccur.fire(new Error(error)), - info => this.logService.trace(info), + msg => { + if (msg.type === 'error') { + this._onDidWatchErrorOccur.fire(msg.message); + } + this.logService[msg.type](msg.message); + }, this.logService.getLevel() === LogLevel.Trace ); + const logLevelListener = this.logService.onDidChangeLogLevel(_ => { + watcherService.setVerboseLogging(this.logService.getLevel() === LogLevel.Trace); + }); + + return combinedDisposable(watcherService, logLevelListener); } //#endregion @@ -481,6 +514,12 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro return createFileSystemProviderError(error, code); } + + forcePolling(): boolean { + // wsl1 needs polling + return isLinux && /^[\.\-0-9]+-Microsoft/.test(os.release()); + } + //#endregion dispose(): void { @@ -488,5 +527,8 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro dispose(this.recursiveWatcher); this.recursiveWatcher = undefined; + + dispose(this.recursiveWatcherLogLevelListener); + this.recursiveWatcherLogLevelListener = undefined; } -} +} \ No newline at end of file diff --git a/src/vs/workbench/services/files/node/watcher/nodejs/watcherService.ts b/src/vs/workbench/services/files/node/watcher/nodejs/watcherService.ts index d3fe8552046..4b07ee0a5c0 100644 --- a/src/vs/workbench/services/files/node/watcher/nodejs/watcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/nodejs/watcherService.ts @@ -3,9 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDiskFileChange, normalizeFileChanges } from 'vs/workbench/services/files/node/watcher/watcher'; +import { IDiskFileChange, normalizeFileChanges, ILogMessage } from 'vs/workbench/services/files/node/watcher/watcher'; import { Disposable } from 'vs/base/common/lifecycle'; -import { statLink, readlink } from 'vs/base/node/pfs'; +import { statLink } from 'vs/base/node/pfs'; +import { realpath } from 'vs/base/node/extpath'; import { watchFolder, watchFile, CHANGE_BUFFER_DELAY } from 'vs/base/node/watcher'; import { FileChangeType } from 'vs/platform/files/common/files'; import { ThrottledDelayer } from 'vs/base/common/async'; @@ -20,8 +21,7 @@ export class FileWatcher extends Disposable { constructor( private path: string, private onFileChanges: (changes: IDiskFileChange[]) => void, - private errorLogger: (msg: string) => void, - private verboseLogger: (msg: string) => void, + private onLogMessage: (msg: ILogMessage) => void, private verboseLogging: boolean ) { super(); @@ -29,6 +29,10 @@ export class FileWatcher extends Disposable { this.startWatching(); } + setVerboseLogging(verboseLogging: boolean): void { + this.verboseLogging = verboseLogging; + } + private async startWatching(): Promise { try { const { stat, isSymbolicLink } = await statLink(this.path); @@ -40,7 +44,7 @@ export class FileWatcher extends Disposable { let pathToWatch = this.path; if (isSymbolicLink) { try { - pathToWatch = await readlink(pathToWatch); + pathToWatch = await realpath(pathToWatch); } catch (error) { this.onError(error); } @@ -77,7 +81,7 @@ export class FileWatcher extends Disposable { // Logging if (this.verboseLogging) { - this.onVerbose(`[File Watcher (node.js)] ${event.type === FileChangeType.ADDED ? '[ADDED]' : event.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]'} ${event.path}`); + this.onVerbose(`${event.type === FileChangeType.ADDED ? '[ADDED]' : event.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]'} ${event.path}`); } // Handle emit through delayer to accommodate for bulk changes and thus reduce spam @@ -91,7 +95,7 @@ export class FileWatcher extends Disposable { // Logging if (this.verboseLogging) { normalizedFileChanges.forEach(event => { - this.onVerbose(`[File Watcher (node.js)] >> normalized ${event.type === FileChangeType.ADDED ? '[ADDED]' : event.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]'} ${event.path}`); + this.onVerbose(`>> normalized ${event.type === FileChangeType.ADDED ? '[ADDED]' : event.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]'} ${event.path}`); }); } @@ -106,13 +110,13 @@ export class FileWatcher extends Disposable { private onError(error: string): void { if (!this.isDisposed) { - this.errorLogger(error); + this.onLogMessage({ type: 'error', message: `[File Watcher (node.js)] ${error}` }); } } - private onVerbose(msg: string): void { + private onVerbose(message: string): void { if (!this.isDisposed) { - this.verboseLogger(msg); + this.onLogMessage({ type: 'trace', message: `[File Watcher (node.js)] ${message}` }); } } diff --git a/src/vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService.ts b/src/vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService.ts index 0fd504f5ffb..87aae11e6bf 100644 --- a/src/vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService.ts @@ -7,9 +7,9 @@ import * as glob from 'vs/base/common/glob'; import * as extpath from 'vs/base/common/extpath'; import * as path from 'vs/base/common/path'; import * as platform from 'vs/base/common/platform'; -import { IDiskFileChange, normalizeFileChanges } from 'vs/workbench/services/files/node/watcher/watcher'; +import { IDiskFileChange, normalizeFileChanges, ILogMessage } from 'vs/workbench/services/files/node/watcher/watcher'; import * as nsfw from 'vscode-nsfw'; -import { IWatcherService, IWatcherRequest, IWatcherOptions, IWatchError } from 'vs/workbench/services/files/node/watcher/nsfw/watcher'; +import { IWatcherService, IWatcherRequest, IWatcherOptions } from 'vs/workbench/services/files/node/watcher/nsfw/watcher'; import { ThrottledDelayer } from 'vs/base/common/async'; import { FileChangeType } from 'vs/platform/files/common/files'; import { normalizeNFC } from 'vs/base/common/normalization'; @@ -39,11 +39,13 @@ export class NsfwWatcherService implements IWatcherService { private _verboseLogging: boolean; private enospcErrorLogged: boolean; - private _onWatchEvent = new Emitter(); + private _onWatchEvent = new Emitter(); readonly onWatchEvent = this._onWatchEvent.event; - watch(options: IWatcherOptions): Event { - this._verboseLogging = options.verboseLogging; + private _onLogMessage = new Emitter(); + readonly onLogMessage: Event = this._onLogMessage.event; + + watch(options: IWatcherOptions): Event { return this.onWatchEvent; } @@ -66,7 +68,7 @@ export class NsfwWatcherService implements IWatcherService { // See https://github.com/Microsoft/vscode/issues/7950 if (e === 'Inotify limit reached' && !this.enospcErrorLogged) { this.enospcErrorLogged = true; - this._onWatchEvent.fire({ message: 'Inotify limit reached (ENOSPC)' }); + this.error('Inotify limit reached (ENOSPC)'); } }); @@ -91,7 +93,7 @@ export class NsfwWatcherService implements IWatcherService { realBasePathLength = realBasePath.length; realBasePathDiffers = true; - console.warn(`Watcher basePath does not match version on disk and will be corrected (original: ${request.path}, real: ${realBasePath})`); + this.warn(`Watcher basePath does not match version on disk and will be corrected (original: ${request.path}, real: ${realBasePath})`); } } catch (error) { // ignore @@ -103,7 +105,7 @@ export class NsfwWatcherService implements IWatcherService { // Logging if (this._verboseLogging) { const logPath = e.action === nsfw.actions.RENAMED ? path.join(e.directory, e.oldFile || '') + ' -> ' + e.newFile : path.join(e.directory, e.file || ''); - console.log(`${e.action === nsfw.actions.CREATED ? '[CREATED]' : e.action === nsfw.actions.DELETED ? '[DELETED]' : e.action === nsfw.actions.MODIFIED ? '[CHANGED]' : '[RENAMED]'} ${logPath}`); + this.log(`${e.action === nsfw.actions.CREATED ? '[CREATED]' : e.action === nsfw.actions.DELETED ? '[DELETED]' : e.action === nsfw.actions.MODIFIED ? '[CHANGED]' : '[RENAMED]'} ${logPath}`); } // Convert nsfw event to IRawFileChange and add to queue @@ -114,13 +116,13 @@ export class NsfwWatcherService implements IWatcherService { if (!this._isPathIgnored(absolutePath, this._pathWatchers[request.path].ignored)) { undeliveredFileEvents.push({ type: FileChangeType.DELETED, path: absolutePath }); } else if (this._verboseLogging) { - console.log(' >> ignored', absolutePath); + this.log(` >> ignored ${absolutePath}`); } absolutePath = path.join(e.directory, e.newFile || ''); if (!this._isPathIgnored(absolutePath, this._pathWatchers[request.path].ignored)) { undeliveredFileEvents.push({ type: FileChangeType.ADDED, path: absolutePath }); } else if (this._verboseLogging) { - console.log(' >> ignored', absolutePath); + this.log(` >> ignored ${absolutePath}`); } } else { absolutePath = path.join(e.directory, e.file || ''); @@ -130,7 +132,7 @@ export class NsfwWatcherService implements IWatcherService { path: absolutePath }); } else if (this._verboseLogging) { - console.log(' >> ignored', absolutePath); + this.log(` >> ignored ${absolutePath}`); } } } @@ -160,7 +162,7 @@ export class NsfwWatcherService implements IWatcherService { // Logging if (this._verboseLogging) { res.forEach(r => { - console.log(` >> normalized ${r.type === FileChangeType.ADDED ? '[ADDED]' : r.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]'} ${r.path}`); + this.log(` >> normalized ${r.type === FileChangeType.ADDED ? '[ADDED]' : r.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]'} ${r.path}`); }); } @@ -190,7 +192,7 @@ export class NsfwWatcherService implements IWatcherService { // Logging if (this._verboseLogging) { - console.log(`Start watching: [${rootsToStartWatching.map(r => r.path).join(',')}]\nStop watching: [${rootsToStopWatching.join(',')}]`); + this.log(`Start watching: [${rootsToStartWatching.map(r => r.path).join(',')}]\nStop watching: [${rootsToStopWatching.join(',')}]`); } // Stop watching some roots @@ -240,4 +242,16 @@ export class NsfwWatcherService implements IWatcherService { private _isPathIgnored(absolutePath: string, ignored: glob.ParsedPattern[]): boolean { return ignored && ignored.some(i => i(absolutePath)); } + + private log(message: string) { + this._onLogMessage.fire({ type: 'trace', message: `[File Watcher (nswf)] ` + message }); + } + + private warn(message: string) { + this._onLogMessage.fire({ type: 'warn', message: `[File Watcher (nswf)] ` + message }); + } + + private error(message: string) { + this._onLogMessage.fire({ type: 'error', message: `[File Watcher (nswf)] ` + message }); + } } diff --git a/src/vs/workbench/services/files/node/watcher/nsfw/watcher.ts b/src/vs/workbench/services/files/node/watcher/nsfw/watcher.ts index 3e1fb0fdb78..fa5d4b23335 100644 --- a/src/vs/workbench/services/files/node/watcher/nsfw/watcher.ts +++ b/src/vs/workbench/services/files/node/watcher/nsfw/watcher.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { IDiskFileChange } from 'vs/workbench/services/files/node/watcher/watcher'; +import { IDiskFileChange, ILogMessage } from 'vs/workbench/services/files/node/watcher/watcher'; export interface IWatcherRequest { path: string; @@ -12,16 +12,12 @@ export interface IWatcherRequest { } export interface IWatcherOptions { - verboseLogging: boolean; -} - -export interface IWatchError { - message: string; } export interface IWatcherService { - watch(options: IWatcherOptions): Event; + watch(options: IWatcherOptions): Event; setRoots(roots: IWatcherRequest[]): Promise; setVerboseLogging(enabled: boolean): Promise; + onLogMessage: Event; stop(): Promise; } \ 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 c073759b1d2..7e1d5edd7e1 100644 --- a/src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts +++ b/src/vs/workbench/services/files/node/watcher/nsfw/watcherIpc.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IWatcherRequest, IWatcherService, IWatcherOptions, IWatchError } from './watcher'; +import { IWatcherRequest, IWatcherService, IWatcherOptions } from './watcher'; import { Event } from 'vs/base/common/event'; -import { IDiskFileChange } from 'vs/workbench/services/files/node/watcher/watcher'; +import { IDiskFileChange, ILogMessage } from 'vs/workbench/services/files/node/watcher/watcher'; export class WatcherChannel implements IServerChannel { @@ -15,6 +15,7 @@ export class WatcherChannel implements IServerChannel { listen(_: unknown, event: string, arg?: any): Event { switch (event) { case 'watch': return this.service.watch(arg); + case 'onLogMessage': return this.service.onLogMessage; } throw new Error(`Event not found: ${event}`); @@ -35,7 +36,7 @@ export class WatcherChannelClient implements IWatcherService { constructor(private channel: IChannel) { } - watch(options: IWatcherOptions): Event { + watch(options: IWatcherOptions): Event { return this.channel.listen('watch', options); } @@ -47,6 +48,10 @@ export class WatcherChannelClient implements IWatcherService { return this.channel.call('setRoots', roots); } + get onLogMessage(): Event { + return this.channel.listen('onLogMessage'); + } + stop(): Promise { return this.channel.call('stop'); } 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 8924e1216c0..dd4d542ccb6 100644 --- a/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts @@ -5,11 +5,10 @@ import { getNextTickChannel } from 'vs/base/parts/ipc/common/ipc'; import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; -import { IDiskFileChange } from 'vs/workbench/services/files/node/watcher/watcher'; +import { IDiskFileChange, ILogMessage } from 'vs/workbench/services/files/node/watcher/watcher'; import { WatcherChannelClient } from 'vs/workbench/services/files/node/watcher/nsfw/watcherIpc'; import { Disposable } from 'vs/base/common/lifecycle'; -import { Event } from 'vs/base/common/event'; -import { IWatchError, IWatcherRequest } from 'vs/workbench/services/files/node/watcher/nsfw/watcher'; +import { IWatcherRequest } from 'vs/workbench/services/files/node/watcher/nsfw/watcher'; import { getPathFromAmdModule } from 'vs/base/common/amd'; export class FileWatcher extends Disposable { @@ -22,7 +21,7 @@ export class FileWatcher extends Disposable { constructor( private folders: IWatcherRequest[], private onFileChanges: (changes: IDiskFileChange[]) => void, - private errorLogger: (msg: string) => void, + private onLogMessage: (msg: ILogMessage) => void, private verboseLogging: boolean, ) { super(); @@ -42,7 +41,7 @@ export class FileWatcher extends Disposable { env: { AMD_ENTRYPOINT: 'vs/workbench/services/files/node/watcher/nsfw/watcherApp', PIPE_LOGGING: 'true', - VERBOSE_LOGGING: this.verboseLogging + VERBOSE_LOGGING: 'true' // transmit console logs from server to client } } )); @@ -52,11 +51,11 @@ export class FileWatcher extends Disposable { // that the watcher process died and we want to restart it here. we only do it a max number of times if (!this.isDisposed) { if (this.restartCounter <= FileWatcher.MAX_RESTARTS) { - this.errorLogger('[File Watcher (nsfw)] terminated unexpectedly and is restarted again...'); + this.error('terminated unexpectedly and is restarted again...'); this.restartCounter++; this.startWatching(); } else { - this.errorLogger('[File Watcher (nsfw)] failed to start after retrying for some time, giving up. Please report this as a bug report!'); + this.error('failed to start after retrying for some time, giving up. Please report this as a bug report!'); } } })); @@ -65,19 +64,28 @@ export class FileWatcher extends Disposable { const channel = getNextTickChannel(client.getChannel('watcher')); this.service = new WatcherChannelClient(channel); - const options = { verboseLogging: this.verboseLogging }; - const onWatchEvent = Event.filter(this.service.watch(options), () => !this.isDisposed); + this.service.setVerboseLogging(this.verboseLogging); - const onError = Event.filter(onWatchEvent, (e): e is IWatchError => typeof e.message === 'string'); - this._register(onError(err => this.errorLogger(`[File Watcher (nsfw)] ${err.message}`))); + const options = {}; + this._register(this.service.watch(options)(e => !this.isDisposed && this.onFileChanges(e))); - const onFileChanges = Event.filter(onWatchEvent, (e): e is IDiskFileChange[] => Array.isArray(e) && e.length > 0); - this._register(onFileChanges(e => this.onFileChanges(e))); + this._register(this.service.onLogMessage(m => this.onLogMessage(m))); // Start watching this.setFolders(this.folders); } + setVerboseLogging(verboseLogging: boolean): void { + this.verboseLogging = verboseLogging; + if (!this.isDisposed) { + this.service.setVerboseLogging(verboseLogging); + } + } + + error(message: string) { + this.onLogMessage({ type: 'error', message: `[File Watcher (nsfw)] ${message}` }); + } + setFolders(folders: IWatcherRequest[]): void { this.folders = folders; 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 56fdbb394f4..2302d222f57 100644 --- a/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts @@ -14,8 +14,8 @@ import { ThrottledDelayer } from 'vs/base/common/async'; import { normalizeNFC } from 'vs/base/common/normalization'; import { realcaseSync } from 'vs/base/node/extpath'; import { isMacintosh, isLinux } from 'vs/base/common/platform'; -import { IDiskFileChange, normalizeFileChanges } from 'vs/workbench/services/files/node/watcher/watcher'; -import { IWatcherRequest, IWatcherService, IWatcherOptions, IWatchError } from 'vs/workbench/services/files/node/watcher/unix/watcher'; +import { IDiskFileChange, normalizeFileChanges, ILogMessage } from 'vs/workbench/services/files/node/watcher/watcher'; +import { IWatcherRequest, IWatcherService, IWatcherOptions } from 'vs/workbench/services/files/node/watcher/unix/watcher'; import { Emitter, Event } from 'vs/base/common/event'; interface IWatcher { @@ -23,10 +23,6 @@ interface IWatcher { stop(): any; } -export interface IChockidarWatcherOptions { - pollingInterval?: number; -} - interface ExtendedWatcherRequest extends IWatcherRequest { parsedPattern?: glob.ParsedPattern; } @@ -40,18 +36,22 @@ export class ChokidarWatcherService implements IWatcherService { private _watcherCount: number; private _pollingInterval?: number; + private _usePolling?: boolean; private _verboseLogging: boolean; private spamCheckStartTime: number; private spamWarningLogged: boolean; private enospcErrorLogged: boolean; - private _onWatchEvent = new Emitter(); + private _onWatchEvent = new Emitter(); readonly onWatchEvent = this._onWatchEvent.event; - public watch(options: IWatcherOptions & IChockidarWatcherOptions): Event { - this._verboseLogging = options.verboseLogging; + private _onLogMessage = new Emitter(); + readonly onLogMessage: Event = this._onLogMessage.event; + + public watch(options: IWatcherOptions): Event { this._pollingInterval = options.pollingInterval; + this._usePolling = options.usePolling; this._watchers = Object.create(null); this._watcherCount = 0; return this.onWatchEvent; @@ -100,10 +100,14 @@ export class ChokidarWatcherService implements IWatcherService { private _watch(basePath: string, requests: IWatcherRequest[]): IWatcher { if (this._verboseLogging) { - console.log(`Start watching: ${basePath}]`); + this.log(`Start watching: ${basePath}]`); } const pollingInterval = this._pollingInterval || 1000; + const usePolling = this._usePolling; + if (usePolling && this._verboseLogging) { + this.log(`Use polling instead of fs.watch: Polling interval ${pollingInterval} ms`); + } const watcherOpts: chokidar.IOptions = { ignoreInitial: true, @@ -111,6 +115,7 @@ export class ChokidarWatcherService implements IWatcherService { followSymlinks: true, // this is the default of chokidar and supports file events through symlinks interval: pollingInterval, // while not used in normal cases, if any error causes chokidar to fallback to polling, increase its intervals binaryInterval: pollingInterval, + usePolling: usePolling, disableGlobbing: true // fix https://github.com/Microsoft/vscode/issues/4586 }; @@ -137,7 +142,7 @@ export class ChokidarWatcherService implements IWatcherService { const realBasePathDiffers = (basePath !== realBasePath); if (realBasePathDiffers) { - console.warn(`Watcher basePath does not match version on disk and was corrected (original: ${basePath}, real: ${realBasePath})`); + this.warn(`Watcher basePath does not match version on disk and was corrected (original: ${basePath}, real: ${realBasePath})`); } let chokidarWatcher: chokidar.FSWatcher | null = chokidar.watch(realBasePath, watcherOpts); @@ -145,7 +150,7 @@ export class ChokidarWatcherService implements IWatcherService { // Detect if for some reason the native watcher library fails to load if (isMacintosh && !chokidarWatcher.options.useFsEvents) { - console.error('Watcher is not using native fsevents library and is falling back to unefficient polling.'); + this.warn('Watcher is not using native fsevents library and is falling back to unefficient polling.'); } let undeliveredFileEvents: IDiskFileChange[] = []; @@ -156,7 +161,7 @@ export class ChokidarWatcherService implements IWatcherService { stop: () => { try { if (this._verboseLogging) { - console.log(`Stop watching: ${basePath}]`); + this.log(`Stop watching: ${basePath}]`); } if (chokidarWatcher) { chokidarWatcher.close(); @@ -168,7 +173,7 @@ export class ChokidarWatcherService implements IWatcherService { fileEventDelayer = null; } } catch (error) { - console.error(error.toString()); + this.warn('Error while stopping watcher: ' + error.toString()); } } }; @@ -218,7 +223,7 @@ export class ChokidarWatcherService implements IWatcherService { // Logging if (this._verboseLogging) { - console.log(`${eventType === FileChangeType.ADDED ? '[ADDED]' : eventType === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]'} ${path}`); + this.log(`${eventType === FileChangeType.ADDED ? '[ADDED]' : eventType === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]'} ${path}`); } // Check for spam @@ -228,7 +233,7 @@ export class ChokidarWatcherService implements IWatcherService { this.spamCheckStartTime = now; } else if (!this.spamWarningLogged && this.spamCheckStartTime + ChokidarWatcherService.EVENT_SPAM_WARNING_THRESHOLD < now) { this.spamWarningLogged = true; - console.warn(`Watcher is busy catching up with ${undeliveredFileEvents.length} file changes in 60 seconds. Latest changed path is "${event.path}"`); + this.warn(`Watcher is busy catching up with ${undeliveredFileEvents.length} file changes in 60 seconds. Latest changed path is "${event.path}"`); } // Add to buffer @@ -247,7 +252,7 @@ export class ChokidarWatcherService implements IWatcherService { // Logging if (this._verboseLogging) { res.forEach(r => { - console.log(` >> normalized ${r.type === FileChangeType.ADDED ? '[ADDED]' : r.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]'} ${r.path}`); + this.log(` >> normalized ${r.type === FileChangeType.ADDED ? '[ADDED]' : r.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]'} ${r.path}`); }); } @@ -268,10 +273,10 @@ export class ChokidarWatcherService implements IWatcherService { if (!this.enospcErrorLogged) { this.enospcErrorLogged = true; this.stop(); - this._onWatchEvent.fire({ message: 'Inotify limit reached (ENOSPC)' }); + this.error('Inotify limit reached (ENOSPC)'); } } else { - console.error(error.toString()); + this.warn(error.toString()); } } }); @@ -286,6 +291,18 @@ export class ChokidarWatcherService implements IWatcherService { this._watchers = Object.create(null); return Promise.resolve(); } + + private log(message: string) { + this._onLogMessage.fire({ type: 'trace', message: `[File Watcher (chockidar)] ` + message }); + } + + private warn(message: string) { + this._onLogMessage.fire({ type: 'warn', message: `[File Watcher (chockidar)] ` + message }); + } + + private error(message: string) { + this._onLogMessage.fire({ type: 'error', message: `[File Watcher (chockidar)] ` + message }); + } } function isIgnored(path: string, requests: ExtendedWatcherRequest[]): boolean { diff --git a/src/vs/workbench/services/files/node/watcher/unix/test/chockidarWatcherService.test.ts b/src/vs/workbench/services/files/node/watcher/unix/test/chockidarWatcherService.test.ts index cb79e1af37e..d28dc4adfa0 100644 --- a/src/vs/workbench/services/files/node/watcher/unix/test/chockidarWatcherService.test.ts +++ b/src/vs/workbench/services/files/node/watcher/unix/test/chockidarWatcherService.test.ts @@ -137,9 +137,12 @@ suite.skip('Chockidar watching', () => { service.watch(opts)(e => { if (Array.isArray(e)) { result.push(...e); - } else { - console.log('set error', e.message); - error = e.message; + } + }); + service.onLogMessage(msg => { + if (msg.type === 'error') { + console.log('set error', msg.message); + error = msg.message; } }); }); diff --git a/src/vs/workbench/services/files/node/watcher/unix/watcher.ts b/src/vs/workbench/services/files/node/watcher/unix/watcher.ts index 3e1fb0fdb78..fc87e15803a 100644 --- a/src/vs/workbench/services/files/node/watcher/unix/watcher.ts +++ b/src/vs/workbench/services/files/node/watcher/unix/watcher.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { IDiskFileChange } from 'vs/workbench/services/files/node/watcher/watcher'; +import { IDiskFileChange, ILogMessage } from 'vs/workbench/services/files/node/watcher/watcher'; export interface IWatcherRequest { path: string; @@ -12,16 +12,14 @@ export interface IWatcherRequest { } export interface IWatcherOptions { - verboseLogging: boolean; -} - -export interface IWatchError { - message: string; + pollingInterval?: number; + usePolling?: boolean; } export interface IWatcherService { - watch(options: IWatcherOptions): Event; + watch(options: IWatcherOptions): Event; setRoots(roots: IWatcherRequest[]): Promise; setVerboseLogging(enabled: boolean): Promise; + onLogMessage: Event; stop(): Promise; } \ 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 c073759b1d2..e59a0958b22 100644 --- a/src/vs/workbench/services/files/node/watcher/unix/watcherIpc.ts +++ b/src/vs/workbench/services/files/node/watcher/unix/watcherIpc.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IWatcherRequest, IWatcherService, IWatcherOptions, IWatchError } from './watcher'; +import { IWatcherRequest, IWatcherService, IWatcherOptions } from './watcher'; import { Event } from 'vs/base/common/event'; -import { IDiskFileChange } from 'vs/workbench/services/files/node/watcher/watcher'; +import { IDiskFileChange, ILogMessage } from 'vs/workbench/services/files/node/watcher/watcher'; export class WatcherChannel implements IServerChannel { @@ -15,6 +15,7 @@ export class WatcherChannel implements IServerChannel { listen(_: unknown, event: string, arg?: any): Event { switch (event) { case 'watch': return this.service.watch(arg); + case 'onLogMessage': return this.service.onLogMessage; } throw new Error(`Event not found: ${event}`); @@ -35,7 +36,7 @@ export class WatcherChannelClient implements IWatcherService { constructor(private channel: IChannel) { } - watch(options: IWatcherOptions): Event { + watch(options: IWatcherOptions): Event { return this.channel.listen('watch', options); } @@ -43,6 +44,10 @@ export class WatcherChannelClient implements IWatcherService { return this.channel.call('setVerboseLogging', enable); } + get onLogMessage(): Event { + return this.channel.listen('onLogMessage'); + } + setRoots(roots: IWatcherRequest[]): Promise { return this.channel.call('setRoots', roots); } 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 04ce4808a85..202b505d53a 100644 --- a/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts @@ -5,11 +5,10 @@ import { getNextTickChannel } from 'vs/base/parts/ipc/common/ipc'; import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; -import { IDiskFileChange } from 'vs/workbench/services/files/node/watcher/watcher'; +import { IDiskFileChange, ILogMessage } from 'vs/workbench/services/files/node/watcher/watcher'; import { WatcherChannelClient } from 'vs/workbench/services/files/node/watcher/unix/watcherIpc'; import { Disposable } from 'vs/base/common/lifecycle'; -import { Event } from 'vs/base/common/event'; -import { IWatchError, IWatcherRequest } from 'vs/workbench/services/files/node/watcher/unix/watcher'; +import { IWatcherRequest, IWatcherOptions } from 'vs/workbench/services/files/node/watcher/unix/watcher'; import { getPathFromAmdModule } from 'vs/base/common/amd'; export class FileWatcher extends Disposable { @@ -22,8 +21,9 @@ export class FileWatcher extends Disposable { constructor( private folders: IWatcherRequest[], private onFileChanges: (changes: IDiskFileChange[]) => void, - private errorLogger: (msg: string) => void, - private verboseLogging: boolean + private onLogMessage: (msg: ILogMessage) => void, + private verboseLogging: boolean, + private watcherOptions: IWatcherOptions = {} ) { super(); @@ -42,7 +42,7 @@ export class FileWatcher extends Disposable { env: { AMD_ENTRYPOINT: 'vs/workbench/services/files/node/watcher/unix/watcherApp', PIPE_LOGGING: 'true', - VERBOSE_LOGGING: this.verboseLogging + VERBOSE_LOGGING: 'true' // transmit console logs from server to client } } )); @@ -52,11 +52,11 @@ export class FileWatcher extends Disposable { // that the watcher process died and we want to restart it here. we only do it a max number of times if (!this.isDisposed) { if (this.restartCounter <= FileWatcher.MAX_RESTARTS) { - this.errorLogger('[File Watcher (chokidar)] terminated unexpectedly and is restarted again...'); + this.error('terminated unexpectedly and is restarted again...'); this.restartCounter++; this.startWatching(); } else { - this.errorLogger('[File Watcher (chokidar)] failed to start after retrying for some time, giving up. Please report this as a bug report!'); + this.error('failed to start after retrying for some time, giving up. Please report this as a bug report!'); } } })); @@ -65,19 +65,25 @@ export class FileWatcher extends Disposable { const channel = getNextTickChannel(client.getChannel('watcher')); this.service = new WatcherChannelClient(channel); - const options = { verboseLogging: this.verboseLogging }; - const onWatchEvent = Event.filter(this.service.watch(options), () => !this.isDisposed); + this.service.setVerboseLogging(this.verboseLogging); - const onError = Event.filter(onWatchEvent, (e): e is IWatchError => typeof e.message === 'string'); - this._register(onError(err => this.errorLogger(`[File Watcher (chokidar)] ${err.message}`))); + this._register(this.service.watch(this.watcherOptions)(e => !this.isDisposed && this.onFileChanges(e))); - const onFileChanges = Event.filter(onWatchEvent, (e): e is IDiskFileChange[] => Array.isArray(e) && e.length > 0); - this._register(onFileChanges(e => this.onFileChanges(e))); + this._register(this.service.onLogMessage(m => this.onLogMessage(m))); // Start watching this.service.setRoots(this.folders); } + error(message: string) { + this.onLogMessage({ type: 'error', message: `[File Watcher (chokidar)] ${message}` }); + } + + setVerboseLogging(verboseLogging: boolean): void { + this.verboseLogging = verboseLogging; + this.service.setVerboseLogging(verboseLogging); + } + setFolders(folders: IWatcherRequest[]): void { this.folders = folders; diff --git a/src/vs/workbench/services/files/node/watcher/watcher.ts b/src/vs/workbench/services/files/node/watcher/watcher.ts index cfe12d13fa9..b8cd75e6b7f 100644 --- a/src/vs/workbench/services/files/node/watcher/watcher.ts +++ b/src/vs/workbench/services/files/node/watcher/watcher.ts @@ -12,6 +12,11 @@ export interface IDiskFileChange { path: string; } +export interface ILogMessage { + type: 'trace' | 'warn' | 'error'; + message: string; +} + export function toFileChanges(changes: IDiskFileChange[]): IFileChange[] { return changes.map(change => ({ type: change.type, diff --git a/src/vs/workbench/services/files/node/watcher/win32/csharpWatcherService.ts b/src/vs/workbench/services/files/node/watcher/win32/csharpWatcherService.ts index 95d8797a5c6..effcbc9472d 100644 --- a/src/vs/workbench/services/files/node/watcher/win32/csharpWatcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/win32/csharpWatcherService.ts @@ -7,7 +7,7 @@ import * as cp from 'child_process'; import { FileChangeType } from 'vs/platform/files/common/files'; import * as decoder from 'vs/base/node/decoder'; import * as glob from 'vs/base/common/glob'; -import { IDiskFileChange } from 'vs/workbench/services/files/node/watcher/watcher'; +import { IDiskFileChange, ILogMessage } from 'vs/workbench/services/files/node/watcher/watcher'; import { getPathFromAmdModule } from 'vs/base/common/amd'; export class OutOfProcessWin32FolderWatcher { @@ -25,7 +25,7 @@ export class OutOfProcessWin32FolderWatcher { private watchedFolder: string, ignored: string[], private eventCallback: (events: IDiskFileChange[]) => void, - private errorCallback: (error: string) => void, + private logCallback: (message: ILogMessage) => void, private verboseLogging: boolean ) { this.restartCounter = 0; @@ -38,7 +38,7 @@ export class OutOfProcessWin32FolderWatcher { // Logging if (this.verboseLogging) { - console.log('%c[File Watcher (C#)]', 'color: blue', `Start watching: ${watchedFolder}`); + this.log(`Start watching: ${watchedFolder}`); } this.startWatcher(); @@ -71,7 +71,7 @@ export class OutOfProcessWin32FolderWatcher { // Support ignores if (this.ignored && this.ignored.some(ignore => ignore(absolutePath))) { if (this.verboseLogging) { - console.log('%c[File Watcher (C#)]', 'color: blue', ' >> ignored', absolutePath); + this.log(absolutePath); } return; @@ -86,7 +86,7 @@ export class OutOfProcessWin32FolderWatcher { // 3 Logging else { - console.log('%c[File Watcher (C#)]', 'color: blue', eventParts[1]); + this.log(eventParts[1]); } } }); @@ -106,23 +106,31 @@ export class OutOfProcessWin32FolderWatcher { } private onError(error: Error | Buffer): void { - this.errorCallback('[File Watcher (C#)] process error: ' + error.toString()); + this.error('process error: ' + error.toString()); } private onExit(code: number, signal: string): void { if (this.handle) { // exit while not yet being disposed is unexpected! - this.errorCallback(`[File Watcher (C#)] terminated unexpectedly (code: ${code}, signal: ${signal})`); + this.error(`terminated unexpectedly (code: ${code}, signal: ${signal})`); if (this.restartCounter <= OutOfProcessWin32FolderWatcher.MAX_RESTARTS) { - this.errorCallback('[File Watcher (C#)] is restarted again...'); + this.error('is restarted again...'); this.restartCounter++; this.startWatcher(); // restart } else { - this.errorCallback('[File Watcher (C#)] Watcher failed to start after retrying for some time, giving up. Please report this as a bug report!'); + this.error('Watcher failed to start after retrying for some time, giving up. Please report this as a bug report!'); } } } + private error(message: string) { + this.logCallback({ type: 'error', message: `[File Watcher (C#)] ${message}` }); + } + + private log(message: string) { + this.logCallback({ type: 'trace', message: `[File Watcher (C#)] ${message}` }); + } + public dispose(): void { if (this.handle) { this.handle.kill(); diff --git a/src/vs/workbench/services/files/node/watcher/win32/watcherService.ts b/src/vs/workbench/services/files/node/watcher/win32/watcherService.ts index 0d27d68313a..bab9258c1b5 100644 --- a/src/vs/workbench/services/files/node/watcher/win32/watcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/win32/watcherService.ts @@ -3,24 +3,23 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDiskFileChange } from 'vs/workbench/services/files/node/watcher/watcher'; +import { IDiskFileChange, ILogMessage } from 'vs/workbench/services/files/node/watcher/watcher'; import { OutOfProcessWin32FolderWatcher } from 'vs/workbench/services/files/node/watcher/win32/csharpWatcherService'; import { posix } from 'vs/base/common/path'; import { rtrim, endsWith } from 'vs/base/common/strings'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable } from 'vs/base/common/lifecycle'; + +export class FileWatcher implements IDisposable { -export class FileWatcher extends Disposable { - private isDisposed: boolean; private folder: { path: string, excludes: string[] }; + private service: OutOfProcessWin32FolderWatcher | undefined = undefined; constructor( folders: { path: string, excludes: string[] }[], private onFileChanges: (changes: IDiskFileChange[]) => void, - private errorLogger: (msg: string) => void, + private onLogMessage: (msg: ILogMessage) => void, private verboseLogging: boolean ) { - super(); - this.folder = folders[0]; if (this.folder.path.indexOf('\\\\') === 0 && endsWith(this.folder.path, posix.sep)) { @@ -31,17 +30,29 @@ export class FileWatcher extends Disposable { this.folder.path = rtrim(this.folder.path, posix.sep); } - this.startWatching(); + this.service = this.startWatching(); } - private startWatching(): void { - this._register(new OutOfProcessWin32FolderWatcher( + private get isDisposed(): boolean { + return !this.service; + } + + private startWatching(): OutOfProcessWin32FolderWatcher { + return new OutOfProcessWin32FolderWatcher( this.folder.path, this.folder.excludes, events => this.onFileEvents(events), - error => this.onError(error), + message => this.onLogMessage(message), this.verboseLogging - )); + ); + } + + setVerboseLogging(verboseLogging: boolean): void { + this.verboseLogging = verboseLogging; + if (this.service) { + this.service.dispose(); + this.service = this.startWatching(); + } } private onFileEvents(events: IDiskFileChange[]): void { @@ -55,15 +66,10 @@ export class FileWatcher extends Disposable { } } - private onError(error: string): void { - if (!this.isDisposed) { - this.errorLogger(error); + dispose(): void { + if (this.service) { + this.service.dispose(); + this.service = undefined; } } - - dispose(): void { - this.isDisposed = true; - - super.dispose(); - } } \ No newline at end of file diff --git a/src/vs/workbench/services/files/test/node/diskFileService.test.ts b/src/vs/workbench/services/files/test/node/diskFileService.test.ts index a992b0fcce9..a5214b8292f 100644 --- a/src/vs/workbench/services/files/test/node/diskFileService.test.ts +++ b/src/vs/workbench/services/files/test/node/diskFileService.test.ts @@ -18,7 +18,7 @@ import { existsSync, statSync, readdirSync, readFileSync, writeFileSync, renameS import { FileOperation, FileOperationEvent, IFileStat, FileOperationResult, FileSystemProviderCapabilities, FileChangeType, IFileChange, FileChangesEvent, FileOperationError, etag, IStat } from 'vs/platform/files/common/files'; import { NullLogService } from 'vs/platform/log/common/log'; import { isLinux, isWindows } from 'vs/base/common/platform'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { isEqual } from 'vs/base/common/resources'; import { VSBuffer, VSBufferReadable } from 'vs/base/common/buffer'; @@ -125,21 +125,21 @@ suite('Disk File Service', () => { let testProvider: TestDiskFileSystemProvider; let testDir: string; - let disposables: IDisposable[] = []; + const disposables = new DisposableStore(); setup(async () => { const logService = new NullLogService(); service = new FileService(logService); - disposables.push(service); + disposables.add(service); fileProvider = new TestDiskFileSystemProvider(logService); - disposables.push(service.registerProvider(Schemas.file, fileProvider)); - disposables.push(fileProvider); + disposables.add(service.registerProvider(Schemas.file, fileProvider)); + disposables.add(fileProvider); testProvider = new TestDiskFileSystemProvider(logService); - disposables.push(service.registerProvider(testSchema, testProvider)); - disposables.push(testProvider); + disposables.add(service.registerProvider(testSchema, testProvider)); + disposables.add(testProvider); const id = generateUuid(); testDir = join(parentDir, id); @@ -149,14 +149,14 @@ suite('Disk File Service', () => { }); teardown(async () => { - disposables = dispose(disposables); + disposables.clear(); await rimraf(parentDir, RimRafMode.MOVE); }); test('createFolder', async () => { let event: FileOperationEvent | undefined; - disposables.push(service.onAfterOperation(e => event = e)); + disposables.add(service.onAfterOperation(e => event = e)); const parent = await service.resolve(URI.file(testDir)); @@ -176,7 +176,7 @@ suite('Disk File Service', () => { test('createFolder: creating multiple folders at once', async function () { let event: FileOperationEvent; - disposables.push(service.onAfterOperation(e => event = e)); + disposables.add(service.onAfterOperation(e => event = e)); const multiFolderPaths = ['a', 'couple', 'of', 'folders']; const parent = await service.resolve(URI.file(testDir)); @@ -411,7 +411,7 @@ suite('Disk File Service', () => { test('deleteFile', async () => { let event: FileOperationEvent; - disposables.push(service.onAfterOperation(e => event = e)); + disposables.add(service.onAfterOperation(e => event = e)); const resource = URI.file(join(testDir, 'deep', 'conway.js')); const source = await service.resolve(resource); @@ -426,7 +426,7 @@ suite('Disk File Service', () => { test('deleteFolder (recursive)', async () => { let event: FileOperationEvent; - disposables.push(service.onAfterOperation(e => event = e)); + disposables.add(service.onAfterOperation(e => event = e)); const resource = URI.file(join(testDir, 'deep')); const source = await service.resolve(resource); @@ -446,15 +446,14 @@ suite('Disk File Service', () => { await service.del(source.resource); return Promise.reject(new Error('Unexpected')); - } - catch (error) { + } catch (error) { return Promise.resolve(true); } }); test('move', async () => { let event: FileOperationEvent; - disposables.push(service.onAfterOperation(e => event = e)); + disposables.add(service.onAfterOperation(e => event = e)); const source = URI.file(join(testDir, 'index.html')); const sourceContents = readFileSync(source.fsPath); @@ -534,7 +533,7 @@ suite('Disk File Service', () => { async function testMoveAcrossProviders(sourceFile = 'index.html'): Promise { let event: FileOperationEvent; - disposables.push(service.onAfterOperation(e => event = e)); + disposables.add(service.onAfterOperation(e => event = e)); const source = URI.file(join(testDir, sourceFile)); const sourceContents = readFileSync(source.fsPath); @@ -558,7 +557,7 @@ suite('Disk File Service', () => { test('move - multi folder', async () => { let event: FileOperationEvent; - disposables.push(service.onAfterOperation(e => event = e)); + disposables.add(service.onAfterOperation(e => event = e)); const multiFolderPaths = ['a', 'couple', 'of', 'folders']; const renameToPath = join(...multiFolderPaths, 'other.html'); @@ -577,7 +576,7 @@ suite('Disk File Service', () => { test('move - directory', async () => { let event: FileOperationEvent; - disposables.push(service.onAfterOperation(e => event = e)); + disposables.add(service.onAfterOperation(e => event = e)); const source = URI.file(join(testDir, 'deep')); @@ -621,7 +620,7 @@ suite('Disk File Service', () => { async function testMoveFolderAcrossProviders(): Promise { let event: FileOperationEvent; - disposables.push(service.onAfterOperation(e => event = e)); + disposables.add(service.onAfterOperation(e => event = e)); const source = URI.file(join(testDir, 'deep')); const sourceChildren = readdirSync(source.fsPath); @@ -646,7 +645,7 @@ suite('Disk File Service', () => { test('move - MIX CASE', async () => { let event: FileOperationEvent; - disposables.push(service.onAfterOperation(e => event = e)); + disposables.add(service.onAfterOperation(e => event = e)); const source = URI.file(join(testDir, 'index.html')); await service.resolve(source); @@ -663,7 +662,7 @@ suite('Disk File Service', () => { test('move - source parent of target', async () => { let event: FileOperationEvent; - disposables.push(service.onAfterOperation(e => event = e)); + disposables.add(service.onAfterOperation(e => event = e)); await service.resolve(URI.file(join(testDir, 'index.html'))); try { @@ -676,7 +675,7 @@ suite('Disk File Service', () => { test('move - FILE_MOVE_CONFLICT', async () => { let event: FileOperationEvent; - disposables.push(service.onAfterOperation(e => event = e)); + disposables.add(service.onAfterOperation(e => event = e)); const source = await service.resolve(URI.file(join(testDir, 'index.html'))); try { @@ -691,7 +690,7 @@ suite('Disk File Service', () => { let createEvent: FileOperationEvent; let moveEvent: FileOperationEvent; let deleteEvent: FileOperationEvent; - disposables.push(service.onAfterOperation(e => { + disposables.add(service.onAfterOperation(e => { if (e.operation === FileOperation.CREATE) { createEvent = e; } else if (e.operation === FileOperation.DELETE) { @@ -755,7 +754,7 @@ suite('Disk File Service', () => { async function doTestCopy(sourceName: string = 'index.html') { let event: FileOperationEvent; - disposables.push(service.onAfterOperation(e => event = e)); + disposables.add(service.onAfterOperation(e => event = e)); const source = await service.resolve(URI.file(join(testDir, sourceName))); const target = URI.file(join(testDir, 'other.html')); @@ -780,7 +779,7 @@ suite('Disk File Service', () => { let createEvent: FileOperationEvent; let copyEvent: FileOperationEvent; let deleteEvent: FileOperationEvent; - disposables.push(service.onAfterOperation(e => { + disposables.add(service.onAfterOperation(e => { if (e.operation === FileOperation.CREATE) { createEvent = e; } else if (e.operation === FileOperation.DELETE) { @@ -1169,7 +1168,7 @@ suite('Disk File Service', () => { test('createFile', async () => { let event: FileOperationEvent; - disposables.push(service.onAfterOperation(e => event = e)); + disposables.add(service.onAfterOperation(e => event = e)); const contents = 'Hello World'; const resource = URI.file(join(testDir, 'test.txt')); @@ -1200,7 +1199,7 @@ suite('Disk File Service', () => { test('createFile (allows to overwrite existing)', async () => { let event: FileOperationEvent; - disposables.push(service.onAfterOperation(e => event = e)); + disposables.add(service.onAfterOperation(e => event = e)); const contents = 'Hello World'; const resource = URI.file(join(testDir, 'test.txt')); @@ -1523,6 +1522,10 @@ suite('Disk File Service', () => { }); test('watch - file - rename file', done => { + if (isWindows) { + return done(); // watch tests are flaky on other platforms + } + const toWatch = URI.file(join(testDir, 'index-watch1.html')); const toWatchRenamed = URI.file(join(testDir, 'index-watch1-renamed.html')); writeFileSync(toWatch.fsPath, 'Init'); diff --git a/src/vs/workbench/services/heap/common/heap.ts b/src/vs/workbench/services/heap/common/heap.ts deleted file mode 100644 index 4aa4cc0ec78..00000000000 --- a/src/vs/workbench/services/heap/common/heap.ts +++ /dev/null @@ -1,33 +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 { Event } from 'vs/base/common/event'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; - -export const IHeapService = createDecorator('heapService'); - -export interface ObjectIdentifier { - $ident?: number; -} - -export interface IHeapService { - _serviceBrand: any; - - readonly onGarbageCollection: Event; - - /** - * Track gc-collection for the given object - */ - trackObject(obj: ObjectIdentifier | undefined): void; -} - - - -export class NullHeapService implements IHeapService { - _serviceBrand: any; - onGarbageCollection = Event.None; - trackObject() { } -} diff --git a/src/vs/workbench/services/heap/node/heap.ts b/src/vs/workbench/services/heap/node/heap.ts deleted file mode 100644 index 8f627bacea1..00000000000 --- a/src/vs/workbench/services/heap/node/heap.ts +++ /dev/null @@ -1,80 +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 { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { Event, Emitter } from 'vs/base/common/event'; -import { GCSignal } from 'gc-signals'; -import { IHeapService, ObjectIdentifier } from 'vs/workbench/services/heap/common/heap'; - -export class HeapService implements IHeapService { - - _serviceBrand: any; - - private readonly _onGarbageCollection: Emitter = new Emitter(); - public readonly onGarbageCollection: Event = this._onGarbageCollection.event; - - private _activeSignals = new WeakMap(); - private _activeIds = new Set(); - - private _consumeHandle: any; - private _ctor: { new(id: number): GCSignal }; - private _ctorInit: Promise; - - constructor() { - // - } - - dispose() { - clearInterval(this._consumeHandle); - } - - trackObject(obj: ObjectIdentifier | undefined | null): void { - if (!obj) { - return; - } - - const ident = obj.$ident; - if (typeof ident !== 'number') { - return; - } - - if (this._activeIds.has(ident)) { - return; - } - - if (this._ctor) { - // track and leave - this._activeIds.add(ident); - this._activeSignals.set(obj, new this._ctor(ident)); - - } else { - // make sure to load gc-signals, then track and leave - if (!this._ctorInit) { - this._ctorInit = import('gc-signals').then(({ GCSignal, consumeSignals }) => { - this._ctor = GCSignal; - this._consumeHandle = setInterval(() => { - const ids = consumeSignals(); - - if (ids.length > 0) { - // local book-keeping - for (const id of ids) { - this._activeIds.delete(id); - } - // fire event - this._onGarbageCollection.fire(ids); - } - }, 15 * 1000); - }); - } - - this._ctorInit.then(() => { - this._activeIds.add(ident); - this._activeSignals.set(obj, new this._ctor(ident)); - }); - } - } -} - -registerSingleton(IHeapService, HeapService, true); diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index 74ef7b96867..5296da44a56 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -13,7 +13,7 @@ import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { FileChangesEvent, IFileService, FileChangeType, FILES_EXCLUDE_CONFIG } from 'vs/platform/files/common/files'; import { Selection } from 'vs/editor/common/core/selection'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; +import { dispose, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { Registry } from 'vs/platform/registry/common/platform'; import { Event } from 'vs/base/common/event'; @@ -105,11 +105,11 @@ export class HistoryService extends Disposable implements IHistoryService { private static readonly MAX_STACK_ITEMS = 50; private static readonly MAX_RECENTLY_CLOSED_EDITORS = 20; - private activeEditorListeners: IDisposable[]; + private readonly activeEditorListeners = this._register(new DisposableStore()); private lastActiveEditor?: IEditorIdentifier; - private editorHistoryListeners: Map = new Map(); - private editorStackListeners: Map = new Map(); + private readonly editorHistoryListeners: Map = new Map(); + private readonly editorStackListeners: Map = new Map(); private stack: IStackEntry[]; private index: number; @@ -144,8 +144,6 @@ export class HistoryService extends Disposable implements IHistoryService { ) { super(); - this.activeEditorListeners = []; - this.canNavigateBackContextKey = (new RawContextKey('canNavigateBack', false)).bindTo(this.contextKeyService); this.canNavigateForwardContextKey = (new RawContextKey('canNavigateForward', false)).bindTo(this.contextKeyService); this.canNavigateToLastEditLocationContextKey = (new RawContextKey('canNavigateToLastEditLocation', false)).bindTo(this.contextKeyService); @@ -198,8 +196,7 @@ export class HistoryService extends Disposable implements IHistoryService { this.lastActiveEditor = activeControl && activeControl.input && activeControl.group ? { editor: activeControl.input, groupId: activeControl.group.id } : undefined; // Dispose old listeners - dispose(this.activeEditorListeners); - this.activeEditorListeners = []; + this.activeEditorListeners.clear(); // Propagate to history this.handleActiveEditorChange(activeControl); @@ -212,14 +209,14 @@ export class HistoryService extends Disposable implements IHistoryService { // Debounce the event with a timeout of 0ms so that multiple calls to // editor.setSelection() are folded into one. We do not want to record // subsequent history navigations for such API calls. - this.activeEditorListeners.push(Event.debounce(activeTextEditorWidget.onDidChangeCursorPosition, (last, event) => event, 0)((event => { + this.activeEditorListeners.add(Event.debounce(activeTextEditorWidget.onDidChangeCursorPosition, (last, event) => event, 0)((event => { this.handleEditorSelectionChangeEvent(activeControl, event); }))); // Track the last edit location by tracking model content change events // Use a debouncer to make sure to capture the correct cursor position // after the model content has changed. - this.activeEditorListeners.push(Event.debounce(activeTextEditorWidget.onDidChangeModelContent, (last, event) => event, 0)((event => this.rememberLastEditLocation(activeEditor!, activeTextEditorWidget)))); + this.activeEditorListeners.add(Event.debounce(activeTextEditorWidget.onDidChangeModelContent, (last, event) => event, 0)((event => this.rememberLastEditLocation(activeEditor!, activeTextEditorWidget)))); } } @@ -478,19 +475,19 @@ export class HistoryService extends Disposable implements IHistoryService { } } - private onEditorDispose(editor: EditorInput, listener: Function, mapEditorToDispose: Map): void { + private onEditorDispose(editor: EditorInput, listener: Function, mapEditorToDispose: Map): void { const toDispose = Event.once(editor.onDispose)(() => listener()); let disposables = mapEditorToDispose.get(editor); if (!disposables) { - disposables = []; + disposables = new DisposableStore(); mapEditorToDispose.set(editor, disposables); } - disposables.push(toDispose); + disposables.add(toDispose); } - private clearOnEditorDispose(editor: IEditorInput | IResourceInput | FileChangesEvent, mapEditorToDispose: Map): void { + private clearOnEditorDispose(editor: IEditorInput | IResourceInput | FileChangesEvent, mapEditorToDispose: Map): void { if (editor instanceof EditorInput) { const disposables = mapEditorToDispose.get(editor); if (disposables) { @@ -958,7 +955,7 @@ export class HistoryService extends Disposable implements IHistoryService { getLastActiveFile(filterByScheme: string): URI | undefined { const history = this.getHistory(); for (const input of history) { - let resource: URI | null; + let resource: URI | undefined; if (input instanceof EditorInput) { resource = toResource(input, { filterByScheme }); } else { diff --git a/src/vs/workbench/services/integrity/node/integrityService.ts b/src/vs/workbench/services/integrity/node/integrityService.ts index 56b5d19f07e..00e083a9b99 100644 --- a/src/vs/workbench/services/integrity/node/integrityService.ts +++ b/src/vs/workbench/services/integrity/node/integrityService.ts @@ -71,9 +71,9 @@ export class IntegrityServiceImpl implements IIntegrityService { this.isPure().then(r => { if (r.isPure) { - // all is good - return; + return; // all is good } + this._prompt(); }); } @@ -106,29 +106,25 @@ export class IntegrityServiceImpl implements IIntegrityService { return this._isPurePromise; } - private _isPure(): Promise { + private async _isPure(): Promise { const expectedChecksums = product.checksums || {}; - return this.lifecycleService.when(LifecyclePhase.Eventually).then(() => { - let asyncResults: Promise[] = Object.keys(expectedChecksums).map((filename) => { - return this._resolve(filename, expectedChecksums[filename]); - }); + await this.lifecycleService.when(LifecyclePhase.Eventually); - return Promise.all(asyncResults).then((allResults) => { - let isPure = true; - for (let i = 0, len = allResults.length; i < len; i++) { - if (!allResults[i].isPure) { - isPure = false; - break; - } - } + const allResults = await Promise.all(Object.keys(expectedChecksums).map(filename => this._resolve(filename, expectedChecksums[filename]))); - return { - isPure: isPure, - proof: allResults - }; - }); - }); + let isPure = true; + for (let i = 0, len = allResults.length; i < len; i++) { + if (!allResults[i].isPure) { + isPure = false; + break; + } + } + + return { + isPure: isPure, + proof: allResults + }; } private _resolve(filename: string, expected: string): Promise { diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts similarity index 66% rename from src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts rename to src/vs/workbench/services/keybinding/browser/keybindingService.ts index 0c3d16f07b2..1ba9d627f16 100644 --- a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -4,17 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as nativeKeymap from 'native-keymap'; -import { release } from 'os'; +import * as browser from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import { Keybinding, ResolvedKeybinding } from 'vs/base/common/keyCodes'; +import { Keybinding, ResolvedKeybinding, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { KeybindingParser } from 'vs/base/common/keybindingParser'; -import { OS, OperatingSystem } from 'vs/base/common/platform'; -import { ConfigWatcher } from 'vs/base/node/config'; +import { OS, OperatingSystem, isWeb } from 'vs/base/common/platform'; import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Extensions as ConfigExtensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; @@ -22,140 +19,34 @@ import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/commo import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { AbstractKeybindingService } from 'vs/platform/keybinding/common/abstractKeybindingService'; -import { IKeybindingEvent, IKeyboardEvent, IUserFriendlyKeybinding, KeybindingSource, IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IKeyboardEvent, IUserFriendlyKeybinding, KeybindingSource, IKeybindingService, IKeybindingEvent } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; import { IKeybindingItem, IKeybindingRule2, KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { keybindingsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; import { ExtensionMessageCollector, ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { IUserKeybindingItem, KeybindingIO, OutputBuilder } from 'vs/workbench/services/keybinding/common/keybindingIO'; -import { CachedKeyboardMapper, IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; -import { MacLinuxFallbackKeyboardMapper } from 'vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper'; -import { IMacLinuxKeyboardMapping, MacLinuxKeyboardMapper, macLinuxKeyboardMappingEquals } from 'vs/workbench/services/keybinding/common/macLinuxKeyboardMapper'; -import { IWindowsKeyboardMapping, WindowsKeyboardMapper, windowsKeyboardMappingEquals } from 'vs/workbench/services/keybinding/common/windowsKeyboardMapper'; +import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { MenuRegistry } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; - -export class KeyboardMapperFactory { - public static readonly INSTANCE = new KeyboardMapperFactory(); - - private _layoutInfo: nativeKeymap.IKeyboardLayoutInfo | null; - private _rawMapping: nativeKeymap.IKeyboardMapping | null; - private _keyboardMapper: IKeyboardMapper | null; - private _initialized: boolean; - - private readonly _onDidChangeKeyboardMapper = new Emitter(); - public readonly onDidChangeKeyboardMapper: Event = this._onDidChangeKeyboardMapper.event; - - private constructor() { - this._layoutInfo = null; - this._rawMapping = null; - this._keyboardMapper = null; - this._initialized = false; - } - - public _onKeyboardLayoutChanged(): void { - if (this._initialized) { - this._setKeyboardData(nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); - } - } - - public getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper { - if (!this._initialized) { - this._setKeyboardData(nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); - } - if (dispatchConfig === DispatchConfig.KeyCode) { - // Forcefully set to use keyCode - return new MacLinuxFallbackKeyboardMapper(OS); - } - return this._keyboardMapper!; - } - - public getCurrentKeyboardLayout(): nativeKeymap.IKeyboardLayoutInfo | null { - if (!this._initialized) { - this._setKeyboardData(nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); - } - return this._layoutInfo; - } - - private static _isUSStandard(_kbInfo: nativeKeymap.IKeyboardLayoutInfo): boolean { - if (OS === OperatingSystem.Linux) { - const kbInfo = _kbInfo; - return (kbInfo && kbInfo.layout === 'us'); - } - - if (OS === OperatingSystem.Macintosh) { - const kbInfo = _kbInfo; - return (kbInfo && kbInfo.id === 'com.apple.keylayout.US'); - } - - if (OS === OperatingSystem.Windows) { - const kbInfo = _kbInfo; - return (kbInfo && kbInfo.name === '00000409'); - } - - return false; - } - - public getRawKeyboardMapping(): nativeKeymap.IKeyboardMapping | null { - if (!this._initialized) { - this._setKeyboardData(nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); - } - return this._rawMapping; - } - - private _setKeyboardData(layoutInfo: nativeKeymap.IKeyboardLayoutInfo, rawMapping: nativeKeymap.IKeyboardMapping): void { - this._layoutInfo = layoutInfo; - - if (this._initialized && KeyboardMapperFactory._equals(this._rawMapping, rawMapping)) { - // nothing to do... - return; - } - - this._initialized = true; - this._rawMapping = rawMapping; - this._keyboardMapper = new CachedKeyboardMapper( - KeyboardMapperFactory._createKeyboardMapper(this._layoutInfo, this._rawMapping) - ); - this._onDidChangeKeyboardMapper.fire(); - } - - private static _createKeyboardMapper(layoutInfo: nativeKeymap.IKeyboardLayoutInfo, rawMapping: nativeKeymap.IKeyboardMapping): IKeyboardMapper { - const isUSStandard = KeyboardMapperFactory._isUSStandard(layoutInfo); - if (OS === OperatingSystem.Windows) { - return new WindowsKeyboardMapper(isUSStandard, rawMapping); - } - - if (Object.keys(rawMapping).length === 0) { - // Looks like reading the mappings failed (most likely Mac + Japanese/Chinese keyboard layouts) - return new MacLinuxFallbackKeyboardMapper(OS); - } - - if (OS === OperatingSystem.Macintosh) { - const kbInfo = layoutInfo; - if (kbInfo.id === 'com.apple.keylayout.DVORAK-QWERTYCMD') { - // Use keyCode based dispatching for DVORAK - QWERTY ⌘ - return new MacLinuxFallbackKeyboardMapper(OS); - } - } - - return new MacLinuxKeyboardMapper(isUSStandard, rawMapping, OS); - } - - private static _equals(a: nativeKeymap.IKeyboardMapping | null, b: nativeKeymap.IKeyboardMapping | null): boolean { - if (OS === OperatingSystem.Windows) { - return windowsKeyboardMappingEquals(a, b); - } - - return macLinuxKeyboardMappingEquals(a, b); - } -} +// tslint:disable-next-line: import-patterns +import { commandsExtensionPoint } from 'vs/workbench/api/common/menusExtensionPoint'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { URI } from 'vs/base/common/uri'; +import { IFileService, FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files'; +import { dirname, isEqual } from 'vs/base/common/resources'; +import { parse } from 'vs/base/common/json'; +import * as objects from 'vs/base/common/objects'; +import { IKeymapService } from 'vs/workbench/services/keybinding/common/keymapInfo'; +import { getDispatchConfig } from 'vs/workbench/services/keybinding/common/dispatchConfig'; +import { isArray } from 'vs/base/common/types'; +import { INavigatorWithKeyboard } from 'vs/workbench/services/keybinding/common/navigatorKeyboard'; interface ContributedKeyBinding { command: string; @@ -215,7 +106,7 @@ let keybindingType: IJSONSchema = { description: nls.localize('vscode.extension.contributes.keybindings.args', "Arguments to pass to the command to execute.") }, key: { - description: nls.localize('vscode.extension.contributes.keybindings.key', 'Key or key sequence (separate keys with plus-sign and sequences with space, e.g Ctrl+O and Ctrl+L L for a chord).'), + description: nls.localize('vscode.extension.contributes.keybindings.key', 'Key or key sequence (separate keys with plus-sign and sequences with space, e.g. Ctrl+O and Ctrl+L L for a chord).'), type: 'string' }, mac: { @@ -239,6 +130,7 @@ let keybindingType: IJSONSchema = { const keybindingsExtPoint = ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'keybindings', + deps: [commandsExtensionPoint], jsonSchema: { description: nls.localize('vscode.extension.contributes.keybindings', "Contributes keybindings."), oneOf: [ @@ -251,23 +143,11 @@ const keybindingsExtPoint = ExtensionsRegistry.registerExtensionPointkeyboard).dispatch : null); - return (r === 'keyCode' ? DispatchConfig.KeyCode : DispatchConfig.Code); -} - export class WorkbenchKeybindingService extends AbstractKeybindingService { private _keyboardMapper: IKeyboardMapper; private _cachedResolver: KeybindingResolver | null; - private _firstTimeComputingResolver: boolean; - private userKeybindings: ConfigWatcher; + private userKeybindings: UserKeybindings; constructor( @IContextKeyService contextKeyService: IContextKeyService, @@ -275,12 +155,13 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { @ITelemetryService telemetryService: ITelemetryService, @INotificationService notificationService: INotificationService, @IEnvironmentService environmentService: IEnvironmentService, - @IStatusbarService statusBarService: IStatusbarService, @IConfigurationService configurationService: IConfigurationService, @IWindowService private readonly windowService: IWindowService, - @IExtensionService extensionService: IExtensionService + @IExtensionService extensionService: IExtensionService, + @IFileService fileService: IFileService, + @IKeymapService private readonly keymapService: IKeymapService ) { - super(contextKeyService, commandService, telemetryService, notificationService, statusBarService); + super(contextKeyService, commandService, telemetryService, notificationService); updateSchema(); @@ -292,20 +173,38 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { } dispatchConfig = newDispatchConfig; - this._keyboardMapper = KeyboardMapperFactory.INSTANCE.getKeyboardMapper(dispatchConfig); + this._keyboardMapper = this.keymapService.getKeyboardMapper(dispatchConfig); this.updateResolver({ source: KeybindingSource.Default }); }); - this._keyboardMapper = KeyboardMapperFactory.INSTANCE.getKeyboardMapper(dispatchConfig); - KeyboardMapperFactory.INSTANCE.onDidChangeKeyboardMapper(() => { - this._keyboardMapper = KeyboardMapperFactory.INSTANCE.getKeyboardMapper(dispatchConfig); + this._keyboardMapper = this.keymapService.getKeyboardMapper(dispatchConfig); + this.keymapService.onDidChangeKeyboardMapper(() => { + this._keyboardMapper = this.keymapService.getKeyboardMapper(dispatchConfig); this.updateResolver({ source: KeybindingSource.Default }); }); this._cachedResolver = null; - this._firstTimeComputingResolver = true; - this.userKeybindings = this._register(new ConfigWatcher(environmentService.appKeybindingsPath, { defaultConfig: [], onError: error => onUnexpectedError(error) })); + this.userKeybindings = this._register(new UserKeybindings(environmentService.keybindingsResource, fileService)); + this.userKeybindings.initialize().then(() => { + if (this.userKeybindings.keybindings.length) { + this.updateResolver({ source: KeybindingSource.User }); + } + }); + this._register(this.userKeybindings.onDidChange(() => { + /* __GDPR__ + "customKeybindingsChanged" : { + "keyCount" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ + this._telemetryService.publicLog('customKeybindingsChanged', { + keyCount: this.userKeybindings.keybindings.length + }); + this.updateResolver({ + source: KeybindingSource.User, + keybindings: this.userKeybindings.keybindings + }); + })); keybindingsExtPoint.setHandler((extensions) => { @@ -321,11 +220,6 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { updateSchema(); this._register(extensionService.onDidRegisterExtensions(() => updateSchema())); - this._register(this.userKeybindings.onDidUpdateConfiguration(event => this.updateResolver({ - source: KeybindingSource.User, - keybindings: event.config - }))); - this._register(dom.addDisposableListener(window, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { let keyEvent = new StandardKeyboardEvent(e); let shouldPreventDefault = this._dispatch(keyEvent, keyEvent.target); @@ -335,7 +229,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { })); keybindingsTelemetry(telemetryService, this); - let data = KeyboardMapperFactory.INSTANCE.getCurrentKeyboardLayout(); + let data = this.keymapService.getCurrentKeyboardLayout(); /* __GDPR__ "keyboardLayout" : { "currentKeyboardLayout": { "${inline}": [ "${IKeyboardLayoutInfo}" ] } @@ -344,27 +238,43 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { telemetryService.publicLog('keyboardLayout', { currentKeyboardLayout: data }); + + this._register(browser.onDidChangeFullscreen(() => { + const keyboard = (navigator).keyboard; + + if (!keyboard) { + return; + } + + if (browser.isFullscreen()) { + keyboard.lock(['Escape']); + } else { + keyboard.unlock(); + } + + // update resolver which will bring back all unbound keyboard shortcuts + this._cachedResolver = null; + this._onDidUpdateKeybindings.fire({ source: KeybindingSource.User }); + })); } public _dumpDebugInfo(): string { - const layoutInfo = JSON.stringify(KeyboardMapperFactory.INSTANCE.getCurrentKeyboardLayout(), null, '\t'); + const layoutInfo = JSON.stringify(this.keymapService.getCurrentKeyboardLayout(), null, '\t'); const mapperInfo = this._keyboardMapper.dumpDebugInfo(); - const rawMapping = JSON.stringify(KeyboardMapperFactory.INSTANCE.getRawKeyboardMapping(), null, '\t'); + const rawMapping = JSON.stringify(this.keymapService.getRawKeyboardMapping(), null, '\t'); return `Layout info:\n${layoutInfo}\n${mapperInfo}\n\nRaw mapping:\n${rawMapping}`; } - private _safeGetConfig(): IUserFriendlyKeybinding[] { - let rawConfig = this.userKeybindings.getConfig(); - if (Array.isArray(rawConfig)) { - return rawConfig; - } - return []; + public _dumpDebugInfoJSON(): string { + const info = { + layout: this.keymapService.getCurrentKeyboardLayout(), + rawMapping: this.keymapService.getRawKeyboardMapping() + }; + return JSON.stringify(info, null, '\t'); } public customKeybindingsCount(): number { - let userKeybindings = this._safeGetConfig(); - - return userKeybindings.length; + return this.userKeybindings.keybindings.length; } private updateResolver(event: IKeybindingEvent): void { @@ -375,9 +285,8 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { protected _getResolver(): KeybindingResolver { if (!this._cachedResolver) { const defaults = this._resolveKeybindingItems(KeybindingsRegistry.getDefaultKeybindings(), true); - const overrides = this._resolveUserKeybindingItems(this._getExtraKeybindings(this._firstTimeComputingResolver), false); + const overrides = this._resolveUserKeybindingItems(this.userKeybindings.keybindings.map((k) => KeybindingIO.readUserKeybindingItem(k)), false); this._cachedResolver = new KeybindingResolver(defaults, overrides); - this._firstTimeComputingResolver = false; } return this._cachedResolver; } @@ -396,8 +305,12 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { const keybinding = item.keybinding; if (!keybinding) { // This might be a removal keybinding item in user settings => accept it - result[resultLen++] = new ResolvedKeybindingItem(null, item.command, item.commandArgs, when, isDefault); + result[resultLen++] = new ResolvedKeybindingItem(undefined, item.command, item.commandArgs, when, isDefault); } else { + if (this._assertBrowserConflicts(keybinding, item.command)) { + continue; + } + const resolvedKeybindings = this.resolveKeybinding(keybinding); for (const resolvedKeybinding of resolvedKeybindings) { result[resultLen++] = new ResolvedKeybindingItem(resolvedKeybinding, item.command, item.commandArgs, when, isDefault); @@ -415,7 +328,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { const parts = item.parts; if (parts.length === 0) { // This might be a removal keybinding item in user settings => accept it - result[resultLen++] = new ResolvedKeybindingItem(null, item.command, item.commandArgs, when, isDefault); + result[resultLen++] = new ResolvedKeybindingItem(undefined, item.command, item.commandArgs, when, isDefault); } else { const resolvedKeybindings = this._keyboardMapper.resolveUserBinding(parts); for (const resolvedKeybinding of resolvedKeybindings) { @@ -427,22 +340,71 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { return result; } - private _getExtraKeybindings(isFirstTime: boolean): IUserKeybindingItem[] { - let extraUserKeybindings: IUserFriendlyKeybinding[] = this._safeGetConfig(); - if (!isFirstTime) { - let cnt = extraUserKeybindings.length; - - /* __GDPR__ - "customKeybindingsChanged" : { - "keyCount" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ - this._telemetryService.publicLog('customKeybindingsChanged', { - keyCount: cnt - }); + private _assertBrowserConflicts(kb: Keybinding, commandId: string): boolean { + if (!isWeb) { + return false; } - return extraUserKeybindings.map((k) => KeybindingIO.readUserKeybindingItem(k)); + if (browser.isFullscreen() && (navigator).keyboard) { + return false; + } + + for (let part of kb.parts) { + if (!part.metaKey && !part.altKey && !part.ctrlKey && !part.shiftKey) { + continue; + } + + const modifiersMask = KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift; + + let partModifiersMask = 0; + if (part.metaKey) { + partModifiersMask |= KeyMod.CtrlCmd; + } + + if (part.shiftKey) { + partModifiersMask |= KeyMod.Shift; + } + + if (part.altKey) { + partModifiersMask |= KeyMod.Alt; + } + + if (part.ctrlKey && OS === OperatingSystem.Macintosh) { + partModifiersMask |= KeyMod.WinCtrl; + } + + if ((partModifiersMask & modifiersMask) === KeyMod.CtrlCmd && part.keyCode === KeyCode.KEY_W) { + // console.warn('Ctrl/Cmd+W keybindings should not be used by default in web. Offender: ', kb.getHashCode(), ' for ', commandId); + + return true; + } + + if ((partModifiersMask & modifiersMask) === KeyMod.CtrlCmd && part.keyCode === KeyCode.KEY_N) { + // console.warn('Ctrl/Cmd+N keybindings should not be used by default in web. Offender: ', kb.getHashCode(), ' for ', commandId); + + return true; + } + + if ((partModifiersMask & modifiersMask) === KeyMod.CtrlCmd && part.keyCode === KeyCode.KEY_T) { + // console.warn('Ctrl/Cmd+T keybindings should not be used by default in web. Offender: ', kb.getHashCode(), ' for ', commandId); + + return true; + } + + if ((partModifiersMask & modifiersMask) === (KeyMod.CtrlCmd | KeyMod.Alt) && (part.keyCode === KeyCode.LeftArrow || part.keyCode === KeyCode.RightArrow)) { + // console.warn('Ctrl/Cmd+Arrow keybindings should not be used by default in web. Offender: ', kb.getHashCode(), ' for ', commandId); + + return true; + } + + if ((partModifiersMask & modifiersMask) === KeyMod.CtrlCmd && part.keyCode >= KeyCode.KEY_0 && part.keyCode <= KeyCode.KEY_9) { + // console.warn('Ctrl/Cmd+Num keybindings should not be used by default in web. Offender: ', kb.getHashCode(), ' for ', commandId); + + return true; + } + } + + return false; } public resolveKeybinding(kb: Keybinding): ResolvedKeybinding[] { @@ -450,6 +412,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { } public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding { + this.keymapService.validateCurrentKeyboardMapping(keyboardEvent); return this._keyboardMapper.resolveKeyboardEvent(keyboardEvent); } @@ -500,10 +463,21 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { weight = KeybindingWeight.ExternalExtension + idx; } + let commandAction = MenuRegistry.getCommand(command); + let precondition = commandAction && commandAction.precondition; + let fullWhen: ContextKeyExpr | undefined; + if (when && precondition) { + fullWhen = ContextKeyExpr.and(precondition, ContextKeyExpr.deserialize(when)); + } else if (when) { + fullWhen = ContextKeyExpr.deserialize(when); + } else if (precondition) { + fullWhen = precondition; + } + let desc: IKeybindingRule2 = { id: command, args, - when: ContextKeyExpr.deserialize(when), + when: fullWhen, weight: weight, primary: KeybindingParser.parseKeybinding(key, OS), mac: mac ? { primary: KeybindingParser.parseKeybinding(mac, OS) } : null, @@ -557,9 +531,13 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { // ignore ctrl/cmd-combination but not shift/alt-combinatios return false; } + if (event.keyCode === KeyCode.Escape) { + // https://github.com/microsoft/vscode/issues/74934 + return false; + } // consult the KeyboardMapperFactory to check the given event for // a printable value. - const mapping = KeyboardMapperFactory.INSTANCE.getRawKeyboardMapping(); + const mapping = this.keymapService.getRawKeyboardMapping(); if (!mapping) { return false; } @@ -574,6 +552,106 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { } } +class UserKeybindings extends Disposable { + + private _keybindings: IUserFriendlyKeybinding[] = []; + get keybindings(): IUserFriendlyKeybinding[] { return this._keybindings; } + private readonly reloadConfigurationScheduler: RunOnceScheduler; + protected readonly _onDidChange: Emitter = this._register(new Emitter()); + readonly onDidChange: Event = this._onDidChange.event; + + private fileWatcherDisposable: IDisposable = Disposable.None; + private directoryWatcherDisposable: IDisposable = Disposable.None; + + constructor( + private readonly keybindingsResource: URI, + private readonly fileService: IFileService + ) { + super(); + + this._register(fileService.onFileChanges(e => this.handleFileEvents(e))); + this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(changed => { + if (changed) { + this._onDidChange.fire(); + } + }), 50)); + this._register(toDisposable(() => { + this.stopWatchingResource(); + this.stopWatchingDirectory(); + })); + } + + private watchResource(): void { + this.fileWatcherDisposable = this.fileService.watch(this.keybindingsResource); + } + + private stopWatchingResource(): void { + this.fileWatcherDisposable.dispose(); + this.fileWatcherDisposable = Disposable.None; + } + + private watchDirectory(): void { + const directory = dirname(this.keybindingsResource); + this.directoryWatcherDisposable = this.fileService.watch(directory); + } + + private stopWatchingDirectory(): void { + this.directoryWatcherDisposable.dispose(); + this.directoryWatcherDisposable = Disposable.None; + } + + async initialize(): Promise { + const exists = await this.fileService.exists(this.keybindingsResource); + this.onResourceExists(exists); + await this.reload(); + } + + private async reload(): Promise { + const existing = this._keybindings; + try { + const content = await this.fileService.readFile(this.keybindingsResource); + const value = parse(content.value.toString()); + this._keybindings = isArray(value) ? value : []; + } catch (e) { + this._keybindings = []; + } + return existing ? !objects.equals(existing, this._keybindings) : true; + } + + private async handleFileEvents(event: FileChangesEvent): Promise { + const events = event.changes; + + let affectedByChanges = false; + + // Find changes that affect the resource + for (const event of events) { + affectedByChanges = isEqual(this.keybindingsResource, event.resource); + if (affectedByChanges) { + if (event.type === FileChangeType.ADDED) { + this.onResourceExists(true); + } else if (event.type === FileChangeType.DELETED) { + this.onResourceExists(false); + } + break; + } + } + + if (affectedByChanges) { + this.reloadConfigurationScheduler.schedule(); + } + } + + private onResourceExists(exists: boolean): void { + if (exists) { + this.stopWatchingDirectory(); + this.watchResource(); + } else { + this.stopWatchingResource(); + this.watchDirectory(); + } + } +} + let schemaId = 'vscode://schemas/keybindings'; let commandsSchemas: IJSONSchema[] = []; let commandsEnum: string[] = []; @@ -700,16 +778,11 @@ const keyboardConfiguration: IConfigurationNode = { 'default': 'code', 'markdownDescription': nls.localize('dispatch', "Controls the dispatching logic for key presses to use either `code` (recommended) or `keyCode`."), 'included': OS === OperatingSystem.Macintosh || OS === OperatingSystem.Linux - }, - 'keyboard.touchbar.enabled': { - 'type': 'boolean', - 'default': true, - 'description': nls.localize('touchbar.enabled', "Enables the macOS touchbar buttons on the keyboard if available."), - 'included': OS === OperatingSystem.Macintosh && parseFloat(release()) >= 16 // Minimum: macOS Sierra (10.12.x = darwin 16.x) } + // no touch bar support } }; configurationRegistry.registerConfiguration(keyboardConfiguration); -registerSingleton(IKeybindingService, WorkbenchKeybindingService); \ No newline at end of file +registerSingleton(IKeybindingService, WorkbenchKeybindingService); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution.ts new file mode 100644 index 00000000000..89ef892047a --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution.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 { IKeymapInfo } from 'vs/workbench/services/keybinding/common/keymapInfo'; + +export class KeyboardLayoutContribution { + public static readonly INSTANCE: KeyboardLayoutContribution = new KeyboardLayoutContribution(); + + private _layoutInfos: IKeymapInfo[] = []; + + get layoutInfos() { + return this._layoutInfos; + } + + private constructor() { + } + + registerKeyboardLayout(layout: IKeymapInfo) { + this._layoutInfos.push(layout); + } +} \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/cz.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/cz.win.ts new file mode 100644 index 00000000000..c187c923cec --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/cz.win.ts @@ -0,0 +1,168 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000405', id: '', text: 'Czech' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '{', '', 0, 'VK_B'], + KeyC: ['c', 'C', '&', '', 0, 'VK_C'], + KeyD: ['d', 'D', 'Đ', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '[', '', 0, 'VK_F'], + KeyG: ['g', 'G', ']', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', 'ł', '', 0, 'VK_K'], + KeyL: ['l', 'L', 'Ł', '', 0, 'VK_L'], + KeyM: ['m', 'M', '', '', 0, 'VK_M'], + KeyN: ['n', 'N', '}', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '\\', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', 'đ', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '@', '', 0, 'VK_V'], + KeyW: ['w', 'W', '|', '', 0, 'VK_W'], + KeyX: ['x', 'X', '#', '', 0, 'VK_X'], + KeyY: ['z', 'Z', '', '', 0, 'VK_Z'], + KeyZ: ['y', 'Y', '', '', 0, 'VK_Y'], + Digit1: ['+', '1', '~', '', 0, 'VK_1'], + Digit2: ['ě', '2', 'ˇ', '', 0, 'VK_2'], + Digit3: ['š', '3', '^', '', 0, 'VK_3'], + Digit4: ['č', '4', '˘', '', 0, 'VK_4'], + Digit5: ['ř', '5', '°', '', 0, 'VK_5'], + Digit6: ['ž', '6', '˛', '', 0, 'VK_6'], + Digit7: ['ý', '7', '`', '', 0, 'VK_7'], + Digit8: ['á', '8', '˙', '', 0, 'VK_8'], + Digit9: ['í', '9', '´', '', 0, 'VK_9'], + Digit0: ['é', '0', '˝', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['=', '%', '¨', '', 0, 'VK_OEM_PLUS'], + Equal: ['´', 'ˇ', '¸', '', 0, 'VK_OEM_2'], + BracketLeft: ['ú', '/', '÷', '', 0, 'VK_OEM_4'], + BracketRight: [')', '(', '×', '', 0, 'VK_OEM_6'], + Backslash: ['¨', '\'', '¤', '', 0, 'VK_OEM_5'], + Semicolon: ['ů', '"', '$', '', 0, 'VK_OEM_1'], + Quote: ['§', '!', 'ß', '', 0, 'VK_OEM_7'], + Backquote: [';', '°', '', '', 0, 'VK_OEM_3'], + Comma: [',', '?', '<', '', 0, 'VK_OEM_COMMA'], + Period: ['.', ':', '>', '', 0, 'VK_OEM_PERIOD'], + Slash: ['-', '_', '*', '', 0, 'VK_OEM_MINUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['\\', '|', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de-swiss.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de-swiss.win.ts new file mode 100644 index 00000000000..b19d2935e66 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de-swiss.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000807', id: '', text: 'Swiss German' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', '', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['z', 'Z', '', '', 0, 'VK_Z'], + KeyZ: ['y', 'Y', '', '', 0, 'VK_Y'], + Digit1: ['1', '+', '¦', '', 0, 'VK_1'], + Digit2: ['2', '"', '@', '', 0, 'VK_2'], + Digit3: ['3', '*', '#', '', 0, 'VK_3'], + Digit4: ['4', 'ç', '°', '', 0, 'VK_4'], + Digit5: ['5', '%', '§', '', 0, 'VK_5'], + Digit6: ['6', '&', '¬', '', 0, 'VK_6'], + Digit7: ['7', '/', '|', '', 0, 'VK_7'], + Digit8: ['8', '(', '¢', '', 0, 'VK_8'], + Digit9: ['9', ')', '', '', 0, 'VK_9'], + Digit0: ['0', '=', '', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['\'', '?', '´', '', 0, 'VK_OEM_4'], + Equal: ['^', '`', '~', '', 0, 'VK_OEM_6'], + BracketLeft: ['ü', 'è', '[', '', 0, 'VK_OEM_1'], + BracketRight: ['¨', '!', ']', '', 0, 'VK_OEM_3'], + Backslash: ['$', '£', '}', '', 0, 'VK_OEM_8'], + Semicolon: ['ö', 'é', '', '', 0, 'VK_OEM_7'], + Quote: ['ä', 'à', '{', '', 0, 'VK_OEM_5'], + Backquote: ['§', '°', '', '', 0, 'VK_OEM_2'], + Comma: [',', ';', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', ':', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '\\', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de.darwin.ts new file mode 100644 index 00000000000..33d2f5d5e32 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.German', lang: 'de', localizedName: 'German' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', 'å', 'Å', 0], + KeyB: ['b', 'B', '∫', '‹', 0], + KeyC: ['c', 'C', 'ç', 'Ç', 0], + KeyD: ['d', 'D', '∂', '™', 0], + KeyE: ['e', 'E', '€', '‰', 0], + KeyF: ['f', 'F', 'ƒ', 'Ï', 0], + KeyG: ['g', 'G', '©', 'Ì', 0], + KeyH: ['h', 'H', 'ª', 'Ó', 0], + KeyI: ['i', 'I', '⁄', 'Û', 0], + KeyJ: ['j', 'J', 'º', 'ı', 0], + KeyK: ['k', 'K', '∆', 'ˆ', 0], + KeyL: ['l', 'L', '@', 'fl', 0], + KeyM: ['m', 'M', 'µ', '˘', 0], + KeyN: ['n', 'N', '~', '›', 4], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'π', '∏', 0], + KeyQ: ['q', 'Q', '«', '»', 0], + KeyR: ['r', 'R', '®', '¸', 0], + KeyS: ['s', 'S', '‚', 'Í', 0], + KeyT: ['t', 'T', '†', '˝', 0], + KeyU: ['u', 'U', '¨', 'Á', 4], + KeyV: ['v', 'V', '√', '◊', 0], + KeyW: ['w', 'W', '∑', '„', 0], + KeyX: ['x', 'X', '≈', 'Ù', 0], + KeyY: ['z', 'Z', 'Ω', 'ˇ', 0], + KeyZ: ['y', 'Y', '¥', '‡', 0], + Digit1: ['1', '!', '¡', '¬', 0], + Digit2: ['2', '"', '“', '”', 0], + Digit3: ['3', '§', '¶', '#', 0], + Digit4: ['4', '$', '¢', '£', 0], + Digit5: ['5', '%', '[', 'fi', 0], + Digit6: ['6', '&', ']', '^', 8], + Digit7: ['7', '/', '|', '\\', 0], + Digit8: ['8', '(', '{', '˜', 0], + Digit9: ['9', ')', '}', '·', 0], + Digit0: ['0', '=', '≠', '¯', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['ß', '?', '¿', '˙', 0], + Equal: ['´', '`', '\'', '˚', 3], + BracketLeft: ['ü', 'Ü', '•', '°', 0], + BracketRight: ['+', '*', '±', '', 0], + Backslash: ['#', '\'', '‘', '’', 0], + Semicolon: ['ö', 'Ö', 'œ', 'Œ', 0], + Quote: ['ä', 'Ä', 'æ', 'Æ', 0], + Backquote: ['<', '>', '≤', '≥', 0], + Comma: [',', ';', '∞', '˛', 0], + Period: ['.', ':', '…', '÷', 0], + Slash: ['-', '_', '–', '—', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: [',', ',', '.', '.', 0], + IntlBackslash: ['^', '°', '„', '“', 1], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de.linux.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de.linux.ts new file mode 100644 index 00000000000..b4675240ef0 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de.linux.ts @@ -0,0 +1,187 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { model: 'pc104', layout: 'de', variant: '', options: '', rules: 'base' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', 'æ', 'Æ', 0], + KeyB: ['b', 'B', '“', '‘', 0], + KeyC: ['c', 'C', '¢', '©', 0], + KeyD: ['d', 'D', 'ð', 'Ð', 0], + KeyE: ['e', 'E', '€', '€', 0], + KeyF: ['f', 'F', 'đ', 'ª', 0], + KeyG: ['g', 'G', 'ŋ', 'Ŋ', 0], + KeyH: ['h', 'H', 'ħ', 'Ħ', 0], + KeyI: ['i', 'I', '→', 'ı', 0], + KeyJ: ['j', 'J', '̣', '̇', 0], + KeyK: ['k', 'K', 'ĸ', '&', 0], + KeyL: ['l', 'L', 'ł', 'Ł', 0], + KeyM: ['m', 'M', 'µ', 'º', 0], + KeyN: ['n', 'N', '”', '’', 0], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'þ', 'Þ', 0], + KeyQ: ['q', 'Q', '@', 'Ω', 0], + KeyR: ['r', 'R', '¶', '®', 0], + KeyS: ['s', 'S', 'ſ', 'ẞ', 0], + KeyT: ['t', 'T', 'ŧ', 'Ŧ', 0], + KeyU: ['u', 'U', '↓', '↑', 0], + KeyV: ['v', 'V', '„', '‚', 0], + KeyW: ['w', 'W', 'ł', 'Ł', 0], + KeyX: ['x', 'X', '«', '‹', 0], + KeyY: ['z', 'Z', '←', '¥', 0], + KeyZ: ['y', 'Y', '»', '›', 0], + Digit1: ['1', '!', '¹', '¡', 0], + Digit2: ['2', '"', '²', '⅛', 0], + Digit3: ['3', '§', '³', '£', 0], + Digit4: ['4', '$', '¼', '¤', 0], + Digit5: ['5', '%', '½', '⅜', 0], + Digit6: ['6', '&', '¬', '⅝', 0], + Digit7: ['7', '/', '{', '⅞', 0], + Digit8: ['8', '(', '[', '™', 0], + Digit9: ['9', ')', ']', '±', 0], + Digit0: ['0', '=', '}', '°', 0], + Enter: ['\r', '\r', '\r', '\r', 0], + Escape: ['\u001b', '\u001b', '\u001b', '\u001b', 0], + Backspace: ['\b', '\b', '\b', '\b', 0], + Tab: ['\t', '', '\t', '', 0], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['ß', '?', '\\', '¿', 0], + Equal: ['́', '̀', '̧', '̨', 0], + BracketLeft: ['ü', 'Ü', '̈', '̊', 0], + BracketRight: ['+', '*', '~', '¯', 0], + Backslash: ['#', '\'', '’', '̆', 0], + Semicolon: ['ö', 'Ö', '̋', '̣', 0], + Quote: ['ä', 'Ä', '̂', '̌', 0], + Backquote: ['̂', '°', '′', '″', 0], + Comma: [',', ';', '·', '×', 0], + Period: ['.', ':', '…', '÷', 0], + Slash: ['-', '_', '–', '—', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: ['', '', '', '', 0], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: ['/', '/', '/', '/', 0], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: [], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['', '1', '', '1', 0], + Numpad2: ['', '2', '', '2', 0], + Numpad3: ['', '3', '', '3', 0], + Numpad4: ['', '4', '', '4', 0], + Numpad5: ['', '5', '', '5', 0], + Numpad6: ['', '6', '', '6', 0], + Numpad7: ['', '7', '', '7', 0], + Numpad8: ['', '8', '', '8', 0], + Numpad9: ['', '9', '', '9', 0], + Numpad0: ['', '0', '', '0', 0], + NumpadDecimal: ['', ',', '', ',', 0], + IntlBackslash: ['<', '>', '|', '̱', 0], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Open: [], + Help: [], + Select: [], + Again: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + Find: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + Lang5: [], + NumpadParenLeft: [], + NumpadParenRight: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: ['\r', '\r', '\r', '\r', 0], + MetaRight: ['.', '.', '.', '.', 0], + BrightnessUp: [], + BrightnessDown: [], + MediaPlay: [], + MediaRecord: [], + MediaFastForward: [], + MediaRewind: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + SelectTask: [], + LaunchScreenSaver: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [], + MailReply: [], + MailForward: [], + MailSend: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de.win.ts new file mode 100644 index 00000000000..46bf5981a68 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/de.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000407', id: '', text: 'German' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', 'µ', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '@', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['z', 'Z', '', '', 0, 'VK_Z'], + KeyZ: ['y', 'Y', '', '', 0, 'VK_Y'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '"', '²', '', 0, 'VK_2'], + Digit3: ['3', '§', '³', '', 0, 'VK_3'], + Digit4: ['4', '$', '', '', 0, 'VK_4'], + Digit5: ['5', '%', '', '', 0, 'VK_5'], + Digit6: ['6', '&', '', '', 0, 'VK_6'], + Digit7: ['7', '/', '{', '', 0, 'VK_7'], + Digit8: ['8', '(', '[', '', 0, 'VK_8'], + Digit9: ['9', ')', ']', '', 0, 'VK_9'], + Digit0: ['0', '=', '}', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['ß', '?', '\\', 'ẞ', 0, 'VK_OEM_4'], + Equal: ['´', '`', '', '', 0, 'VK_OEM_6'], + BracketLeft: ['ü', 'Ü', '', '', 0, 'VK_OEM_1'], + BracketRight: ['+', '*', '~', '', 0, 'VK_OEM_PLUS'], + Backslash: ['#', '\'', '', '', 0, 'VK_OEM_2'], + Semicolon: ['ö', 'Ö', '', '', 0, 'VK_OEM_3'], + Quote: ['ä', 'Ä', '', '', 0, 'VK_OEM_7'], + Backquote: ['^', '°', '', '', 0, 'VK_OEM_5'], + Comma: [',', ';', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', ':', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '|', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/dk.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/dk.win.ts new file mode 100644 index 00000000000..b774622699c --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/dk.win.ts @@ -0,0 +1,170 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000406', id: '', text: 'Danish' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', 'µ', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '"', '@', '', 0, 'VK_2'], + Digit3: ['3', '#', '£', '', 0, 'VK_3'], + Digit4: ['4', '¤', '$', '', 0, 'VK_4'], + Digit5: ['5', '%', '€', '', 0, 'VK_5'], + Digit6: ['6', '&', '', '', 0, 'VK_6'], + Digit7: ['7', '/', '{', '', 0, 'VK_7'], + Digit8: ['8', '(', '[', '', 0, 'VK_8'], + Digit9: ['9', ')', ']', '', 0, 'VK_9'], + Digit0: ['0', '=', '}', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['+', '?', '', '', 0, 'VK_OEM_PLUS'], + Equal: ['´', '`', '|', '', 0, 'VK_OEM_4'], + BracketLeft: ['å', 'Å', '', '', 0, 'VK_OEM_6'], + BracketRight: ['¨', '^', '~', '', 0, 'VK_OEM_1'], + Backslash: ['\'', '*', '', '', 0, 'VK_OEM_2'], + Semicolon: ['æ', 'Æ', '', '', 0, 'VK_OEM_3'], + Quote: ['ø', 'Ø', '', '', 0, 'VK_OEM_7'], + Backquote: ['½', '§', '', '', 0, 'VK_OEM_5'], + Comma: [',', ';', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', ':', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '\\', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } + +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-belgian.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-belgian.win.ts new file mode 100644 index 00000000000..89e3a27892a --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-belgian.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000813', id: '', text: 'Belgian (Period)' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: [',', '?', '', '', 0, 'VK_OEM_COMMA'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['a', 'A', '', '', 0, 'VK_A'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['z', 'Z', '', '', 0, 'VK_Z'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['w', 'W', '', '', 0, 'VK_W'], + Digit1: ['&', '1', '|', '', 0, 'VK_1'], + Digit2: ['é', '2', '@', '', 0, 'VK_2'], + Digit3: ['"', '3', '#', '', 0, 'VK_3'], + Digit4: ['\'', '4', '{', '', 0, 'VK_4'], + Digit5: ['(', '5', '[', '', 0, 'VK_5'], + Digit6: ['§', '6', '^', '', 0, 'VK_6'], + Digit7: ['è', '7', '', '', 0, 'VK_7'], + Digit8: ['!', '8', '', '', 0, 'VK_8'], + Digit9: ['ç', '9', '{', '', 0, 'VK_9'], + Digit0: ['à', '0', '}', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: [')', '°', '', '', 0, 'VK_OEM_4'], + Equal: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + BracketLeft: ['^', '¨', '[', '', 0, 'VK_OEM_6'], + BracketRight: ['$', '*', ']', '', 0, 'VK_OEM_1'], + Backslash: ['µ', '£', '`', '`', 0, 'VK_OEM_5'], + Semicolon: ['m', 'M', '', '', 0, 'VK_M'], + Quote: ['ù', '%', '´', '´', 0, 'VK_OEM_3'], + Backquote: ['²', '³', '', '', 0, 'VK_OEM_7'], + Comma: [';', '.', '', '', 0, 'VK_OEM_PERIOD'], + Period: [':', '/', '', '', 0, 'VK_OEM_2'], + Slash: ['=', '+', '~', '~', 0, 'VK_OEM_PLUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '\\', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-ext.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-ext.darwin.ts new file mode 100644 index 00000000000..a12a2338bff --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-ext.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.USExtended', lang: 'en', localizedName: 'ABC - Extended' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', '¯', '̄', 4], + KeyB: ['b', 'B', '˘', '̆', 4], + KeyC: ['c', 'C', '¸', '̧', 4], + KeyD: ['d', 'D', 'ð', 'Ð', 0], + KeyE: ['e', 'E', '´', '́', 4], + KeyF: ['f', 'F', 'ƒ', '', 0], + KeyG: ['g', 'G', '©', '‸', 8], + KeyH: ['h', 'H', 'ˍ', '̱', 4], + KeyI: ['i', 'I', 'ʼ', '̛', 4], + KeyJ: ['j', 'J', '˝', '̋', 4], + KeyK: ['k', 'K', '˚', '̊', 4], + KeyL: ['l', 'L', '-', '̵', 4], + KeyM: ['m', 'M', '˛', '̨', 4], + KeyN: ['n', 'N', '˜', '̃', 4], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', ',', '̦', 4], + KeyQ: ['q', 'Q', 'œ', 'Œ', 0], + KeyR: ['r', 'R', '®', '‰', 0], + KeyS: ['s', 'S', 'ß', '', 0], + KeyT: ['t', 'T', 'þ', 'Þ', 0], + KeyU: ['u', 'U', '¨', '̈', 4], + KeyV: ['v', 'V', 'ˇ', '̌', 4], + KeyW: ['w', 'W', '˙', '̇', 4], + KeyX: ['x', 'X', '.', '̣', 4], + KeyY: ['y', 'Y', '¥', '', 0], + KeyZ: ['z', 'Z', 'ˀ', '̉', 4], + Digit1: ['1', '!', '¡', '⁄', 0], + Digit2: ['2', '@', '™', '€', 0], + Digit3: ['3', '#', '£', '‹', 0], + Digit4: ['4', '$', '¢', '›', 0], + Digit5: ['5', '%', '§', '†', 0], + Digit6: ['6', '^', 'ˆ', '̂', 4], + Digit7: ['7', '&', '¶', '‡', 0], + Digit8: ['8', '*', '•', '°', 0], + Digit9: ['9', '(', 'ª', '·', 0], + Digit0: ['0', ')', 'º', '‚', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '–', '—', 0], + Equal: ['=', '+', '≠', '±', 0], + BracketLeft: ['[', '{', '“', '”', 0], + BracketRight: [']', '}', '‘', '’', 0], + Backslash: ['\\', '|', '«', '»', 0], + Semicolon: [';', ':', '…', '№', 8], + Quote: ['\'', '"', 'æ', 'Æ', 0], + Backquote: ['`', '~', '`', '̀', 4], + Comma: [',', '<', '≤', '„', 0], + Period: ['.', '>', '≥', 'ʔ', 8], + Slash: ['/', '?', '÷', '¿', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: ['.', '.', '.', '.', 0], + IntlBackslash: ['§', '±', '§', '±', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-in.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-in.win.ts new file mode 100644 index 00000000000..a1786f42061 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-in.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00004009', id: '', text: 'India' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', 'ā', 'Ā', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', 'ḍ', 'Ḍ', 0, 'VK_D'], + KeyE: ['e', 'E', 'ē', 'Ē', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', 'ṅ', 'Ṅ', 0, 'VK_G'], + KeyH: ['h', 'H', 'ḥ', 'Ḥ', 0, 'VK_H'], + KeyI: ['i', 'I', 'ī', 'Ī', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', 'l̥', 'L̥', 0, 'VK_L'], + KeyM: ['m', 'M', 'ṁ', 'Ṁ', 0, 'VK_M'], + KeyN: ['n', 'N', 'ṇ', 'Ṇ', 0, 'VK_N'], + KeyO: ['o', 'O', 'ō', 'Ō', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', 'æ', 'Æ', 0, 'VK_Q'], + KeyR: ['r', 'R', 'r̥', 'R̥', 0, 'VK_R'], + KeyS: ['s', 'S', 'ś', 'Ś', 0, 'VK_S'], + KeyT: ['t', 'T', 'ṭ', 'Ṭ', 0, 'VK_T'], + KeyU: ['u', 'U', 'ū', 'Ū', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', 'ṣ', 'Ṣ', 0, 'VK_X'], + KeyY: ['y', 'Y', 'ñ', 'Ñ', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '@', '', '', 0, 'VK_2'], + Digit3: ['3', '#', '', '', 0, 'VK_3'], + Digit4: ['4', '$', '₹', '', 0, 'VK_4'], + Digit5: ['5', '%', '', '', 0, 'VK_5'], + Digit6: ['6', '^', '', 'ˆ', 0, 'VK_6'], + Digit7: ['7', '&', '', '', 0, 'VK_7'], + Digit8: ['8', '*', '', '', 0, 'VK_8'], + Digit9: ['9', '(', '', '˘', 0, 'VK_9'], + Digit0: ['0', ')', '', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['-', '_', '-', 'ˍ', 0, 'VK_OEM_MINUS'], + Equal: ['=', '+', '', '', 0, 'VK_OEM_PLUS'], + BracketLeft: ['[', '{', '', '', 0, 'VK_OEM_4'], + BracketRight: [']', '}', '', '', 0, 'VK_OEM_6'], + Backslash: ['\\', '|', '', '', 0, 'VK_OEM_5'], + Semicolon: [';', ':', '', '', 0, 'VK_OEM_1'], + Quote: ['\'', '"', '', '', 0, 'VK_OEM_7'], + Backquote: ['`', '~', '', '~', 0, 'VK_OEM_3'], + Comma: [',', '<', ',', '<', 0, 'VK_OEM_COMMA'], + Period: ['.', '>', '.', '', 0, 'VK_OEM_PERIOD'], + Slash: ['/', '?', '', '', 0, 'VK_OEM_2'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['\\', '|', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-intl.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-intl.darwin.ts new file mode 100644 index 00000000000..ceb7b67a878 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-intl.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.USInternational-PC', lang: 'en', localizedName: 'U.S. International - PC' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', 'å', 'Å', 0], + KeyB: ['b', 'B', '∫', 'ı', 0], + KeyC: ['c', 'C', 'ç', 'Ç', 0], + KeyD: ['d', 'D', '∂', 'Î', 0], + KeyE: ['e', 'E', '´', '´', 4], + KeyF: ['f', 'F', 'ƒ', 'Ï', 0], + KeyG: ['g', 'G', '©', '˝', 0], + KeyH: ['h', 'H', '˙', 'Ó', 0], + KeyI: ['i', 'I', 'ˆ', 'ˆ', 4], + KeyJ: ['j', 'J', '∆', 'Ô', 0], + KeyK: ['k', 'K', '˚', '', 0], + KeyL: ['l', 'L', '¬', 'Ò', 0], + KeyM: ['m', 'M', 'µ', 'Â', 0], + KeyN: ['n', 'N', '˜', '˜', 4], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'π', '∏', 0], + KeyQ: ['q', 'Q', 'œ', 'Œ', 0], + KeyR: ['r', 'R', '®', '‰', 0], + KeyS: ['s', 'S', 'ß', 'Í', 0], + KeyT: ['t', 'T', '†', 'ˇ', 0], + KeyU: ['u', 'U', '¨', '¨', 4], + KeyV: ['v', 'V', '√', '◊', 0], + KeyW: ['w', 'W', '∑', '„', 0], + KeyX: ['x', 'X', '≈', '˛', 0], + KeyY: ['y', 'Y', '¥', 'Á', 0], + KeyZ: ['z', 'Z', 'Ω', '¸', 0], + Digit1: ['1', '!', '¡', '⁄', 0], + Digit2: ['2', '@', '™', '€', 0], + Digit3: ['3', '#', '£', '‹', 0], + Digit4: ['4', '$', '¢', '›', 0], + Digit5: ['5', '%', '∞', 'fi', 0], + Digit6: ['6', 'ˆ', '§', 'fl', 2], + Digit7: ['7', '&', '¶', '‡', 0], + Digit8: ['8', '*', '•', '°', 0], + Digit9: ['9', '(', 'ª', '·', 0], + Digit0: ['0', ')', 'º', '‚', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '–', '—', 0], + Equal: ['=', '+', '≠', '±', 0], + BracketLeft: ['[', '{', '“', '”', 0], + BracketRight: [']', '}', '‘', '’', 0], + Backslash: ['\\', '|', '«', '»', 0], + Semicolon: [';', ':', '…', 'Ú', 0], + Quote: ['\'', '"', 'æ', 'Æ', 3], + Backquote: ['`', '˜', '`', '`', 7], + Comma: [',', '<', '≤', '¯', 0], + Period: ['.', '>', '≥', '˘', 0], + Slash: ['/', '?', '÷', '¿', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: ['.', '.', '.', '.', 0], + IntlBackslash: ['§', '±', '§', '±', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-intl.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-intl.win.ts new file mode 100644 index 00000000000..75a2a40b805 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-intl.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00020409', id: '0001', text: 'United States-International' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', 'á', 'Á', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '©', '¢', 0, 'VK_C'], + KeyD: ['d', 'D', 'ð', 'Ð', 0, 'VK_D'], + KeyE: ['e', 'E', 'é', 'É', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', 'í', 'Í', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', 'ø', 'Ø', 0, 'VK_L'], + KeyM: ['m', 'M', 'µ', '', 0, 'VK_M'], + KeyN: ['n', 'N', 'ñ', 'Ñ', 0, 'VK_N'], + KeyO: ['o', 'O', 'ó', 'Ó', 0, 'VK_O'], + KeyP: ['p', 'P', 'ö', 'Ö', 0, 'VK_P'], + KeyQ: ['q', 'Q', 'ä', 'Ä', 0, 'VK_Q'], + KeyR: ['r', 'R', '®', '', 0, 'VK_R'], + KeyS: ['s', 'S', 'ß', '§', 0, 'VK_S'], + KeyT: ['t', 'T', 'þ', 'Þ', 0, 'VK_T'], + KeyU: ['u', 'U', 'ú', 'Ú', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', 'å', 'Å', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', 'ü', 'Ü', 0, 'VK_Y'], + KeyZ: ['z', 'Z', 'æ', 'Æ', 0, 'VK_Z'], + Digit1: ['1', '!', '¡', '¹', 0, 'VK_1'], + Digit2: ['2', '@', '²', '', 0, 'VK_2'], + Digit3: ['3', '#', '³', '', 0, 'VK_3'], + Digit4: ['4', '$', '¤', '£', 0, 'VK_4'], + Digit5: ['5', '%', '€', '', 0, 'VK_5'], + Digit6: ['6', '^', '¼', '', 0, 'VK_6'], + Digit7: ['7', '&', '½', '', 0, 'VK_7'], + Digit8: ['8', '*', '¾', '', 0, 'VK_8'], + Digit9: ['9', '(', '‘', '', 0, 'VK_9'], + Digit0: ['0', ')', '’', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['-', '_', '¥', '', 0, 'VK_OEM_MINUS'], + Equal: ['=', '+', '×', '÷', 0, 'VK_OEM_PLUS'], + BracketLeft: ['[', '{', '«', '', 0, 'VK_OEM_4'], + BracketRight: [']', '}', '»', '', 0, 'VK_OEM_6'], + Backslash: ['\\', '|', '¬', '¦', 0, 'VK_OEM_5'], + Semicolon: [';', ':', '¶', '°', 0, 'VK_OEM_1'], + Quote: ['\'', '"', '´', '¨', 0, 'VK_OEM_7'], + Backquote: ['`', '~', '', '', 0, 'VK_OEM_3'], + Comma: [',', '<', 'ç', 'Ç', 0, 'VK_OEM_COMMA'], + Period: ['.', '>', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['/', '?', '¿', '', 0, 'VK_OEM_2'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['\\', '|', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-uk.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-uk.darwin.ts new file mode 100644 index 00000000000..89236337707 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-uk.darwin.ts @@ -0,0 +1,131 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.British', lang: 'en', localizedName: 'British' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', 'å', 'Å', 0], + KeyB: ['b', 'B', '∫', 'ı', 0], + KeyC: ['c', 'C', 'ç', 'Ç', 0], + KeyD: ['d', 'D', '∂', 'Î', 0], + KeyE: ['e', 'E', '´', '‰', 4], + KeyF: ['f', 'F', 'ƒ', 'Ï', 0], + KeyG: ['g', 'G', '©', 'Ì', 0], + KeyH: ['h', 'H', '˙', 'Ó', 0], + KeyI: ['i', 'I', '^', 'È', 4], + KeyJ: ['j', 'J', '∆', 'Ô', 0], + KeyK: ['k', 'K', '˚', '', 0], + KeyL: ['l', 'L', '¬', 'Ò', 0], + KeyM: ['m', 'M', 'µ', '˜', 0], + KeyN: ['n', 'N', '~', 'ˆ', 4], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'π', '∏', 0], + KeyQ: ['q', 'Q', 'œ', 'Œ', 0], + KeyR: ['r', 'R', '®', 'Â', 0], + KeyS: ['s', 'S', 'ß', 'Í', 0], + KeyT: ['t', 'T', '†', 'Ê', 0], + KeyU: ['u', 'U', '¨', 'Ë', 4], + KeyV: ['v', 'V', '√', '◊', 0], + KeyW: ['w', 'W', '∑', '„', 0], + KeyX: ['x', 'X', '≈', 'Ù', 0], + KeyY: ['y', 'Y', '¥', 'Á', 0], + KeyZ: ['z', 'Z', 'Ω', 'Û', 0], + Digit1: ['1', '!', '¡', '⁄', 0], + Digit2: ['2', '@', '€', '™', 0], + Digit3: ['3', '£', '#', '‹', 0], + Digit4: ['4', '$', '¢', '›', 0], + Digit5: ['5', '%', '∞', 'fi', 0], + Digit6: ['6', '^', '§', 'fl', 0], + Digit7: ['7', '&', '¶', '‡', 0], + Digit8: ['8', '*', '•', '°', 0], + Digit9: ['9', '(', 'ª', '·', 0], + Digit0: ['0', ')', 'º', '‚', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '–', '—', 0], + Equal: ['=', '+', '≠', '±', 0], + BracketLeft: ['[', '{', '“', '”', 0], + BracketRight: [']', '}', '‘', '’', 0], + Backslash: ['\\', '|', '«', '»', 0], + Semicolon: [';', ':', '…', 'Ú', 0], + Quote: ['\'', '"', 'æ', 'Æ', 0], + Backquote: ['`', '~', '`', 'Ÿ', 4], + Comma: [',', '<', '≤', '¯', 0], + Period: ['.', '>', '≥', '˘', 0], + Slash: ['/', '?', '÷', '¿', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: ['.', '.', '.', '.', 0], + IntlBackslash: ['§', '±', '', '', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-uk.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-uk.win.ts new file mode 100644 index 00000000000..0b07025fa88 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en-uk.win.ts @@ -0,0 +1,170 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000809', id: '', text: 'United Kingdom' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', 'á', 'Á', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', 'é', 'É', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', 'í', 'Í', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', '', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', 'ó', 'Ó', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', 'ú', 'Ú', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '"', '', '', 0, 'VK_2'], + Digit3: ['3', '£', '', '', 0, 'VK_3'], + Digit4: ['4', '$', '€', '', 0, 'VK_4'], + Digit5: ['5', '%', '', '', 0, 'VK_5'], + Digit6: ['6', '^', '', '', 0, 'VK_6'], + Digit7: ['7', '&', '', '', 0, 'VK_7'], + Digit8: ['8', '*', '', '', 0, 'VK_8'], + Digit9: ['9', '(', '', '', 0, 'VK_9'], + Digit0: ['0', ')', '', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + Equal: ['=', '+', '', '', 0, 'VK_OEM_PLUS'], + BracketLeft: ['[', '{', '', '', 0, 'VK_OEM_4'], + BracketRight: [']', '}', '', '', 0, 'VK_OEM_6'], + Backslash: ['#', '~', '\\', '|', 0, 'VK_OEM_7'], + Semicolon: [';', ':', '', '', 0, 'VK_OEM_1'], + Quote: ['\'', '@', '', '', 0, 'VK_OEM_3'], + Backquote: ['`', '¬', '¦', '', 0, 'VK_OEM_8'], + Comma: [',', '<', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', '>', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['/', '?', '', '', 0, 'VK_OEM_2'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['\\', '|', '', '', 0, 'VK_OEM_5'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } + +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en.darwin.ts new file mode 100644 index 00000000000..f0fca4a9d74 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en.darwin.ts @@ -0,0 +1,140 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.US', lang: 'en', localizedName: 'U.S.', isUSStandard: true }, + secondaryLayouts: [ + { id: 'com.apple.keylayout.ABC', lang: 'en', localizedName: 'ABC' }, + { id: 'com.sogou.inputmethod.sogou.pinyin', lang: 'zh-Hans', localizedName: 'Pinyin - Simplified' }, + { id: 'com.apple.inputmethod.Kotoeri.Roman', lang: 'en', localizedName: 'Romaji' }, + { id: 'com.apple.inputmethod.Kotoeri.Japanese', lang: 'ja', localizedName: 'Hiragana' }, + { id: 'com.apple.keylayout.Australian', lang: 'en', localizedName: 'Australian' }, + { id: 'com.apple.keylayout.Canadian', lang: 'en', localizedName: 'Canadian English' }, + { id: 'com.apple.keylayout.Brazilian', lang: 'pt', localizedName: 'Brazilian' }, + ], + mapping: { + KeyA: ['a', 'A', 'å', 'Å', 0], + KeyB: ['b', 'B', '∫', 'ı', 0], + KeyC: ['c', 'C', 'ç', 'Ç', 0], + KeyD: ['d', 'D', '∂', 'Î', 0], + KeyE: ['e', 'E', '´', '´', 4], + KeyF: ['f', 'F', 'ƒ', 'Ï', 0], + KeyG: ['g', 'G', '©', '˝', 0], + KeyH: ['h', 'H', '˙', 'Ó', 0], + KeyI: ['i', 'I', 'ˆ', 'ˆ', 4], + KeyJ: ['j', 'J', '∆', 'Ô', 0], + KeyK: ['k', 'K', '˚', '', 0], + KeyL: ['l', 'L', '¬', 'Ò', 0], + KeyM: ['m', 'M', 'µ', 'Â', 0], + KeyN: ['n', 'N', '˜', '˜', 4], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'π', '∏', 0], + KeyQ: ['q', 'Q', 'œ', 'Œ', 0], + KeyR: ['r', 'R', '®', '‰', 0], + KeyS: ['s', 'S', 'ß', 'Í', 0], + KeyT: ['t', 'T', '†', 'ˇ', 0], + KeyU: ['u', 'U', '¨', '¨', 4], + KeyV: ['v', 'V', '√', '◊', 0], + KeyW: ['w', 'W', '∑', '„', 0], + KeyX: ['x', 'X', '≈', '˛', 0], + KeyY: ['y', 'Y', '¥', 'Á', 0], + KeyZ: ['z', 'Z', 'Ω', '¸', 0], + Digit1: ['1', '!', '¡', '⁄', 0], + Digit2: ['2', '@', '™', '€', 0], + Digit3: ['3', '#', '£', '‹', 0], + Digit4: ['4', '$', '¢', '›', 0], + Digit5: ['5', '%', '∞', 'fi', 0], + Digit6: ['6', '^', '§', 'fl', 0], + Digit7: ['7', '&', '¶', '‡', 0], + Digit8: ['8', '*', '•', '°', 0], + Digit9: ['9', '(', 'ª', '·', 0], + Digit0: ['0', ')', 'º', '‚', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '–', '—', 0], + Equal: ['=', '+', '≠', '±', 0], + BracketLeft: ['[', '{', '“', '”', 0], + BracketRight: [']', '}', '‘', '’', 0], + Backslash: ['\\', '|', '«', '»', 0], + Semicolon: [';', ':', '…', 'Ú', 0], + Quote: ['\'', '"', 'æ', 'Æ', 0], + Backquote: ['`', '~', '`', '`', 4], + Comma: [',', '<', '≤', '¯', 0], + Period: ['.', '>', '≥', '˘', 0], + Slash: ['/', '?', '÷', '¿', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: ['.', '.', '.', '.', 0], + IntlBackslash: ['§', '±', '§', '±', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en.linux.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en.linux.ts new file mode 100644 index 00000000000..571e9164e15 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en.linux.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 { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { model: 'pc105', layout: 'us', variant: '', options: '', rules: 'evdev', isUSStandard: true }, + secondaryLayouts: [ + { model: 'pc105', layout: 'cn', variant: '', options: '', rules: 'evdev' }, + ], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', 'a', 'A', 0], + KeyB: ['b', 'B', 'b', 'B', 0], + KeyC: ['c', 'C', 'c', 'C', 0], + KeyD: ['d', 'D', 'd', 'D', 0], + KeyE: ['e', 'E', 'e', 'E', 0], + KeyF: ['f', 'F', 'f', 'F', 0], + KeyG: ['g', 'G', 'g', 'G', 0], + KeyH: ['h', 'H', 'h', 'H', 0], + KeyI: ['i', 'I', 'i', 'I', 0], + KeyJ: ['j', 'J', 'j', 'J', 0], + KeyK: ['k', 'K', 'k', 'K', 0], + KeyL: ['l', 'L', 'l', 'L', 0], + KeyM: ['m', 'M', 'm', 'M', 0], + KeyN: ['n', 'N', 'n', 'N', 0], + KeyO: ['o', 'O', 'o', 'O', 0], + KeyP: ['p', 'P', 'p', 'P', 0], + KeyQ: ['q', 'Q', 'q', 'Q', 0], + KeyR: ['r', 'R', 'r', 'R', 0], + KeyS: ['s', 'S', 's', 'S', 0], + KeyT: ['t', 'T', 't', 'T', 0], + KeyU: ['u', 'U', 'u', 'U', 0], + KeyV: ['v', 'V', 'v', 'V', 0], + KeyW: ['w', 'W', 'w', 'W', 0], + KeyX: ['x', 'X', 'x', 'X', 0], + KeyY: ['y', 'Y', 'y', 'Y', 0], + KeyZ: ['z', 'Z', 'z', 'Z', 0], + Digit1: ['1', '!', '1', '!', 0], + Digit2: ['2', '@', '2', '@', 0], + Digit3: ['3', '#', '3', '#', 0], + Digit4: ['4', '$', '4', '$', 0], + Digit5: ['5', '%', '5', '%', 0], + Digit6: ['6', '^', '6', '^', 0], + Digit7: ['7', '&', '7', '&', 0], + Digit8: ['8', '*', '8', '*', 0], + Digit9: ['9', '(', '9', '(', 0], + Digit0: ['0', ')', '0', ')', 0], + Enter: ['\r', '\r', '\r', '\r', 0], + Escape: ['\u001b', '\u001b', '\u001b', '\u001b', 0], + Backspace: ['\b', '\b', '\b', '\b', 0], + Tab: ['\t', '', '\t', '', 0], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '-', '_', 0], + Equal: ['=', '+', '=', '+', 0], + BracketLeft: ['[', '{', '[', '{', 0], + BracketRight: [']', '}', ']', '}', 0], + Backslash: ['\\', '|', '\\', '|', 0], + Semicolon: [';', ':', ';', ':', 0], + Quote: ['\'', '"', '\'', '"', 0], + Backquote: ['`', '~', '`', '~', 0], + Comma: [',', '<', ',', '<', 0], + Period: ['.', '>', '.', '>', 0], + Slash: ['/', '?', '/', '?', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: ['', '', '', '', 0], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: ['\r', '\r', '\r', '\r', 0], + Numpad1: ['', '1', '', '1', 0], + Numpad2: ['', '2', '', '2', 0], + Numpad3: ['', '3', '', '3', 0], + Numpad4: ['', '4', '', '4', 0], + Numpad5: ['', '5', '', '5', 0], + Numpad6: ['', '6', '', '6', 0], + Numpad7: ['', '7', '', '7', 0], + Numpad8: ['', '8', '', '8', 0], + Numpad9: ['', '9', '', '9', 0], + Numpad0: ['', '0', '', '0', 0], + NumpadDecimal: ['', '.', '', '.', 0], + IntlBackslash: ['<', '>', '|', '¦', 0], + ContextMenu: [], + Power: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Open: [], + Help: [], + Select: [], + Again: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + Find: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: ['.', '.', '.', '.', 0], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + Lang5: [], + NumpadParenLeft: ['(', '(', '(', '(', 0], + NumpadParenRight: [')', ')', ')', ')', 0], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + BrightnessUp: [], + BrightnessDown: [], + MediaPlay: [], + MediaRecord: [], + MediaFastForward: [], + MediaRewind: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + SelectTask: [], + LaunchScreenSaver: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [], + MailReply: [], + MailForward: [], + MailSend: [] + } + +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en.win.ts new file mode 100644 index 00000000000..3d6845dc053 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/en.win.ts @@ -0,0 +1,174 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000409', id: '', text: 'US', isUSStandard: true }, + secondaryLayouts: [ + { name: '00000804', id: '', text: 'Chinese (Simplified) - US Keyboard' }, + { name: '00000411', id: '', text: 'Japanese' }, + { name: '00000412', id: '', text: 'Korean' }, + { name: '00000404', id: '', text: 'Chinese (Traditional) - US Keyboard' } + ], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', '', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '@', '', '', 0, 'VK_2'], + Digit3: ['3', '#', '', '', 0, 'VK_3'], + Digit4: ['4', '$', '', '', 0, 'VK_4'], + Digit5: ['5', '%', '', '', 0, 'VK_5'], + Digit6: ['6', '^', '', '', 0, 'VK_6'], + Digit7: ['7', '&', '', '', 0, 'VK_7'], + Digit8: ['8', '*', '', '', 0, 'VK_8'], + Digit9: ['9', '(', '', '', 0, 'VK_9'], + Digit0: ['0', ')', '', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + Equal: ['=', '+', '', '', 0, 'VK_OEM_PLUS'], + BracketLeft: ['[', '{', '', '', 0, 'VK_OEM_4'], + BracketRight: [']', '}', '', '', 0, 'VK_OEM_6'], + Backslash: ['\\', '|', '', '', 0, 'VK_OEM_5'], + Semicolon: [';', ':', '', '', 0, 'VK_OEM_1'], + Quote: ['\'', '"', '', '', 0, 'VK_OEM_7'], + Backquote: ['`', '~', '', '', 0, 'VK_OEM_3'], + Comma: [',', '<', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', '>', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['/', '?', '', '', 0, 'VK_OEM_2'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['\\', '|', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es-latin.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es-latin.win.ts new file mode 100644 index 00000000000..16531eda212 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es-latin.win.ts @@ -0,0 +1,170 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '0000080A', id: '', text: 'Latin American' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', '', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '@', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '"', '', '', 0, 'VK_2'], + Digit3: ['3', '#', '', '', 0, 'VK_3'], + Digit4: ['4', '$', '', '', 0, 'VK_4'], + Digit5: ['5', '%', '', '', 0, 'VK_5'], + Digit6: ['6', '&', '', '', 0, 'VK_6'], + Digit7: ['7', '/', '', '', 0, 'VK_7'], + Digit8: ['8', '(', '', '', 0, 'VK_8'], + Digit9: ['9', ')', '', '', 0, 'VK_9'], + Digit0: ['0', '=', '', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['\'', '?', '\\', '', 0, 'VK_OEM_4'], + Equal: ['¿', '¡', '', '', 0, 'VK_OEM_6'], + BracketLeft: ['´', '¨', '', '', 0, 'VK_OEM_1'], + BracketRight: ['+', '*', '~', '', 0, 'VK_OEM_PLUS'], + Backslash: ['}', ']', '`', '', 0, 'VK_OEM_2'], + Semicolon: ['ñ', 'Ñ', '', '', 0, 'VK_OEM_3'], + Quote: ['{', '[', '^', '', 0, 'VK_OEM_7'], + Backquote: ['|', '°', '¬', '', 0, 'VK_OEM_5'], + Comma: [',', ';', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', ':', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } + +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es.darwin.ts new file mode 100644 index 00000000000..679dfb36d08 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.Spanish-ISO', lang: 'es', localizedName: 'Spanish - ISO' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', 'å', 'Å', 0], + KeyB: ['b', 'B', 'ß', '', 0], + KeyC: ['c', 'C', '©', ' ', 0], + KeyD: ['d', 'D', '∂', '∆', 0], + KeyE: ['e', 'E', '€', '€', 0], + KeyF: ['f', 'F', 'ƒ', 'fi', 0], + KeyG: ['g', 'G', '', 'fl', 0], + KeyH: ['h', 'H', '™', ' ', 0], + KeyI: ['i', 'I', ' ', ' ', 0], + KeyJ: ['j', 'J', '¶', '¯', 0], + KeyK: ['k', 'K', '§', 'ˇ', 0], + KeyL: ['l', 'L', ' ', '˘', 0], + KeyM: ['m', 'M', 'µ', '˚', 0], + KeyN: ['n', 'N', ' ', '˙', 0], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'π', '∏', 0], + KeyQ: ['q', 'Q', 'œ', 'Œ', 0], + KeyR: ['r', 'R', '®', ' ', 0], + KeyS: ['s', 'S', '∫', ' ', 0], + KeyT: ['t', 'T', '†', '‡', 0], + KeyU: ['u', 'U', ' ', ' ', 0], + KeyV: ['v', 'V', '√', '◊', 0], + KeyW: ['w', 'W', 'æ', 'Æ', 0], + KeyX: ['x', 'X', '∑', '›', 0], + KeyY: ['y', 'Y', '¥', ' ', 0], + KeyZ: ['z', 'Z', 'Ω', '‹', 0], + Digit1: ['1', '!', '|', 'ı', 0], + Digit2: ['2', '"', '@', '˝', 0], + Digit3: ['3', '·', '#', '•', 0], + Digit4: ['4', '$', '¢', '£', 0], + Digit5: ['5', '%', '∞', '‰', 0], + Digit6: ['6', '&', '¬', ' ', 0], + Digit7: ['7', '/', '÷', '⁄', 0], + Digit8: ['8', '(', '“', '‘', 0], + Digit9: ['9', ')', '”', '’', 0], + Digit0: ['0', '=', '≠', '≈', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['\'', '?', '´', '¸', 0], + Equal: ['¡', '¿', '‚', '˛', 0], + BracketLeft: ['`', '^', '[', 'ˆ', 3], + BracketRight: ['+', '*', ']', '±', 0], + Backslash: ['ç', 'Ç', '}', '»', 0], + Semicolon: ['ñ', 'Ñ', '~', '˜', 4], + Quote: ['´', '¨', '{', '«', 3], + Backquote: ['<', '>', '≤', '≥', 0], + Comma: [',', ';', '„', '', 0], + Period: ['.', ':', '…', '…', 0], + Slash: ['-', '_', '–', '—', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: [',', ',', ',', ',', 0], + IntlBackslash: ['º', 'ª', '\\', '°', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es.linux.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es.linux.ts new file mode 100644 index 00000000000..8fcff46f8d6 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es.linux.ts @@ -0,0 +1,187 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { model: 'pc105', layout: 'es', variant: '', options: '', rules: 'evdev' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', 'æ', 'Æ', 0], + KeyB: ['b', 'B', '”', '’', 0], + KeyC: ['c', 'C', '¢', '©', 0], + KeyD: ['d', 'D', 'ð', 'Ð', 0], + KeyE: ['e', 'E', '€', '¢', 0], + KeyF: ['f', 'F', 'đ', 'ª', 0], + KeyG: ['g', 'G', 'ŋ', 'Ŋ', 0], + KeyH: ['h', 'H', 'ħ', 'Ħ', 0], + KeyI: ['i', 'I', '→', 'ı', 0], + KeyJ: ['j', 'J', '̉', '̛', 0], + KeyK: ['k', 'K', 'ĸ', '&', 0], + KeyL: ['l', 'L', 'ł', 'Ł', 0], + KeyM: ['m', 'M', 'µ', 'º', 0], + KeyN: ['n', 'N', 'n', 'N', 0], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'þ', 'Þ', 0], + KeyQ: ['q', 'Q', '@', 'Ω', 0], + KeyR: ['r', 'R', '¶', '®', 0], + KeyS: ['s', 'S', 'ß', '§', 0], + KeyT: ['t', 'T', 'ŧ', 'Ŧ', 0], + KeyU: ['u', 'U', '↓', '↑', 0], + KeyV: ['v', 'V', '“', '‘', 0], + KeyW: ['w', 'W', 'ł', 'Ł', 0], + KeyX: ['x', 'X', '»', '>', 0], + KeyY: ['y', 'Y', '←', '¥', 0], + KeyZ: ['z', 'Z', '«', '<', 0], + Digit1: ['1', '!', '|', '¡', 0], + Digit2: ['2', '"', '@', '⅛', 0], + Digit3: ['3', '·', '#', '£', 0], + Digit4: ['4', '$', '~', '$', 0], + Digit5: ['5', '%', '½', '⅜', 0], + Digit6: ['6', '&', '¬', '⅝', 0], + Digit7: ['7', '/', '{', '⅞', 0], + Digit8: ['8', '(', '[', '™', 0], + Digit9: ['9', ')', ']', '±', 0], + Digit0: ['0', '=', '}', '°', 0], + Enter: ['\r', '\r', '\r', '\r', 0], + Escape: ['\u001b', '\u001b', '\u001b', '\u001b', 0], + Backspace: ['\b', '\b', '\b', '\b', 0], + Tab: ['\t', '', '\t', '', 0], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['\'', '?', '\\', '¿', 0], + Equal: ['¡', '¿', '̃', '~', 0], + BracketLeft: ['̀', '̂', '[', '̊', 0], + BracketRight: ['+', '*', ']', '̄', 0], + Backslash: ['ç', 'Ç', '}', '̆', 0], + Semicolon: ['ñ', 'Ñ', '~', '̋', 0], + Quote: ['́', '̈', '{', '{', 0], + Backquote: ['º', 'ª', '\\', '\\', 0], + Comma: [',', ';', '─', '×', 0], + Period: ['.', ':', '·', '÷', 0], + Slash: ['-', '_', '̣', '̇', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: ['', '', '', '', 0], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: ['\r', '\r', '\r', '\r', 0], + Numpad1: ['', '1', '', '1', 0], + Numpad2: ['', '2', '', '2', 0], + Numpad3: ['', '3', '', '3', 0], + Numpad4: ['', '4', '', '4', 0], + Numpad5: ['', '5', '', '5', 0], + Numpad6: ['', '6', '', '6', 0], + Numpad7: ['', '7', '', '7', 0], + Numpad8: ['', '8', '', '8', 0], + Numpad9: ['', '9', '', '9', 0], + Numpad0: ['', '0', '', '0', 0], + NumpadDecimal: ['', '.', '', '.', 0], + IntlBackslash: ['<', '>', '|', '¦', 0], + ContextMenu: [], + Power: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Open: [], + Help: [], + Select: [], + Again: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + Find: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: ['.', '.', '.', '.', 0], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + Lang5: [], + NumpadParenLeft: ['(', '(', '(', '(', 0], + NumpadParenRight: [')', ')', ')', ')', 0], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + BrightnessUp: [], + BrightnessDown: [], + MediaPlay: [], + MediaRecord: [], + MediaFastForward: [], + MediaRewind: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + SelectTask: [], + LaunchScreenSaver: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [], + MailReply: [], + MailForward: [], + MailSend: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es.win.ts new file mode 100644 index 00000000000..3ac96a5dc59 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/es.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '0000040A', id: '', text: 'Spanish' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', '', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '|', '', 0, 'VK_1'], + Digit2: ['2', '"', '@', '', 0, 'VK_2'], + Digit3: ['3', '·', '#', '', 0, 'VK_3'], + Digit4: ['4', '$', '~', '', 0, 'VK_4'], + Digit5: ['5', '%', '€', '', 0, 'VK_5'], + Digit6: ['6', '&', '¬', '', 0, 'VK_6'], + Digit7: ['7', '/', '', '', 0, 'VK_7'], + Digit8: ['8', '(', '', '', 0, 'VK_8'], + Digit9: ['9', ')', '', '', 0, 'VK_9'], + Digit0: ['0', '=', '', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['\'', '?', '', '', 0, 'VK_OEM_4'], + Equal: ['¡', '¿', '', '', 0, 'VK_OEM_6'], + BracketLeft: ['`', '^', '[', '', 0, 'VK_OEM_1'], + BracketRight: ['+', '*', ']', '', 0, 'VK_OEM_PLUS'], + Backslash: ['ç', 'Ç', '}', '', 0, 'VK_OEM_2'], + Semicolon: ['ñ', 'Ñ', '', '', 0, 'VK_OEM_3'], + Quote: ['´', '¨', '{', '', 0, 'VK_OEM_7'], + Backquote: ['º', 'ª', '\\', '', 0, 'VK_OEM_5'], + Comma: [',', ';', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', ':', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/fr.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/fr.darwin.ts new file mode 100644 index 00000000000..fa9198b64a0 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/fr.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.French', lang: 'fr', localizedName: 'French' }, + secondaryLayouts: [], + mapping: { + KeyA: ['q', 'Q', '‡', 'Ω', 0], + KeyB: ['b', 'B', 'ß', '∫', 0], + KeyC: ['c', 'C', '©', '¢', 0], + KeyD: ['d', 'D', '∂', '∆', 0], + KeyE: ['e', 'E', 'ê', 'Ê', 0], + KeyF: ['f', 'F', 'ƒ', '·', 0], + KeyG: ['g', 'G', 'fi', 'fl', 0], + KeyH: ['h', 'H', 'Ì', 'Î', 0], + KeyI: ['i', 'I', 'î', 'ï', 0], + KeyJ: ['j', 'J', 'Ï', 'Í', 0], + KeyK: ['k', 'K', 'È', 'Ë', 0], + KeyL: ['l', 'L', '¬', '|', 0], + KeyM: [',', '?', '∞', '¿', 0], + KeyN: ['n', 'N', '~', 'ı', 4], + KeyO: ['o', 'O', 'œ', 'Œ', 0], + KeyP: ['p', 'P', 'π', '∏', 0], + KeyQ: ['a', 'A', 'æ', 'Æ', 0], + KeyR: ['r', 'R', '®', '‚', 0], + KeyS: ['s', 'S', 'Ò', '∑', 0], + KeyT: ['t', 'T', '†', '™', 0], + KeyU: ['u', 'U', 'º', 'ª', 0], + KeyV: ['v', 'V', '◊', '√', 0], + KeyW: ['z', 'Z', 'Â', 'Å', 0], + KeyX: ['x', 'X', '≈', '⁄', 0], + KeyY: ['y', 'Y', 'Ú', 'Ÿ', 0], + KeyZ: ['w', 'W', '‹', '›', 0], + Digit1: ['&', '1', '', '´', 8], + Digit2: ['é', '2', 'ë', '„', 0], + Digit3: ['"', '3', '“', '”', 0], + Digit4: ['\'', '4', '‘', '’', 0], + Digit5: ['(', '5', '{', '[', 0], + Digit6: ['§', '6', '¶', 'å', 0], + Digit7: ['è', '7', '«', '»', 0], + Digit8: ['!', '8', '¡', 'Û', 0], + Digit9: ['ç', '9', 'Ç', 'Á', 0], + Digit0: ['à', '0', 'ø', 'Ø', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: [')', '°', '}', ']', 0], + Equal: ['-', '_', '—', '–', 0], + BracketLeft: ['^', '¨', 'ô', 'Ô', 3], + BracketRight: ['$', '*', '€', '¥', 0], + Backslash: ['`', '£', '@', '#', 1], + Semicolon: ['m', 'M', 'µ', 'Ó', 0], + Quote: ['ù', '%', 'Ù', '‰', 0], + Backquote: ['<', '>', '≤', '≥', 0], + Comma: [';', '.', '…', '•', 0], + Period: [':', '/', '÷', '\\', 0], + Slash: ['=', '+', '≠', '±', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: [',', '.', ',', '.', 0], + IntlBackslash: ['@', '#', '•', 'Ÿ', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/fr.linux.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/fr.linux.ts new file mode 100644 index 00000000000..dc9488f28c3 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/fr.linux.ts @@ -0,0 +1,187 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { model: 'pc104', layout: 'fr', variant: '', options: '', rules: 'base' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['q', 'Q', '@', 'Ω', 0], + KeyB: ['b', 'B', '”', '’', 0], + KeyC: ['c', 'C', '¢', '©', 0], + KeyD: ['d', 'D', 'ð', 'Ð', 0], + KeyE: ['e', 'E', '€', '¢', 0], + KeyF: ['f', 'F', 'đ', 'ª', 0], + KeyG: ['g', 'G', 'ŋ', 'Ŋ', 0], + KeyH: ['h', 'H', 'ħ', 'Ħ', 0], + KeyI: ['i', 'I', '→', 'ı', 0], + KeyJ: ['j', 'J', '̉', '̛', 0], + KeyK: ['k', 'K', 'ĸ', '&', 0], + KeyL: ['l', 'L', 'ł', 'Ł', 0], + KeyM: [',', '?', '́', '̋', 0], + KeyN: ['n', 'N', 'n', 'N', 0], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'þ', 'Þ', 0], + KeyQ: ['a', 'A', 'æ', 'Æ', 0], + KeyR: ['r', 'R', '¶', '®', 0], + KeyS: ['s', 'S', 'ß', '§', 0], + KeyT: ['t', 'T', 'ŧ', 'Ŧ', 0], + KeyU: ['u', 'U', '↓', '↑', 0], + KeyV: ['v', 'V', '“', '‘', 0], + KeyW: ['z', 'Z', '«', '<', 0], + KeyX: ['x', 'X', '»', '>', 0], + KeyY: ['y', 'Y', '←', '¥', 0], + KeyZ: ['w', 'W', 'ł', 'Ł', 0], + Digit1: ['&', '1', '¹', '¡', 0], + Digit2: ['é', '2', '~', '⅛', 0], + Digit3: ['"', '3', '#', '£', 0], + Digit4: ['\'', '4', '{', '$', 0], + Digit5: ['(', '5', '[', '⅜', 0], + Digit6: ['-', '6', '|', '⅝', 0], + Digit7: ['è', '7', '`', '⅞', 0], + Digit8: ['_', '8', '\\', '™', 0], + Digit9: ['ç', '9', '^', '±', 0], + Digit0: ['à', '0', '@', '°', 0], + Enter: ['\r', '\r', '\r', '\r', 0], + Escape: ['\u001b', '\u001b', '\u001b', '\u001b', 0], + Backspace: ['\b', '\b', '\b', '\b', 0], + Tab: ['\t', '', '\t', '', 0], + Space: [' ', ' ', ' ', ' ', 0], + Minus: [')', '°', ']', '¿', 0], + Equal: ['=', '+', '}', '̨', 0], + BracketLeft: ['̂', '̈', '̈', '̊', 0], + BracketRight: ['$', '£', '¤', '̄', 0], + Backslash: ['*', 'µ', '̀', '̆', 0], + Semicolon: ['m', 'M', 'µ', 'º', 0], + Quote: ['ù', '%', '̂', '̌', 0], + Backquote: ['²', '~', '¬', '¬', 0], + Comma: [';', '.', '─', '×', 0], + Period: [':', '/', '·', '÷', 0], + Slash: ['!', '§', '̣', '̇', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: ['', '', '', '', 0], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: ['/', '/', '/', '/', 0], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: [], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['', '1', '', '1', 0], + Numpad2: ['', '2', '', '2', 0], + Numpad3: ['', '3', '', '3', 0], + Numpad4: ['', '4', '', '4', 0], + Numpad5: ['', '5', '', '5', 0], + Numpad6: ['', '6', '', '6', 0], + Numpad7: ['', '7', '', '7', 0], + Numpad8: ['', '8', '', '8', 0], + Numpad9: ['', '9', '', '9', 0], + Numpad0: ['', '0', '', '0', 0], + NumpadDecimal: ['', '.', '', '.', 0], + IntlBackslash: ['<', '>', '|', '¦', 0], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Open: [], + Help: [], + Select: [], + Again: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + Find: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + Lang5: [], + NumpadParenLeft: [], + NumpadParenRight: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: ['\r', '\r', '\r', '\r', 0], + MetaRight: ['.', '.', '.', '.', 0], + BrightnessUp: [], + BrightnessDown: [], + MediaPlay: [], + MediaRecord: [], + MediaFastForward: [], + MediaRewind: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + SelectTask: [], + LaunchScreenSaver: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [], + MailReply: [], + MailForward: [], + MailSend: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/fr.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/fr.win.ts new file mode 100644 index 00000000000..c9f032d7bee --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/fr.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '0000040C', id: '', text: 'French' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: [',', '?', '', '', 0, 'VK_OEM_COMMA'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['a', 'A', '', '', 0, 'VK_A'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['z', 'Z', '', '', 0, 'VK_Z'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['w', 'W', '', '', 0, 'VK_W'], + Digit1: ['&', '1', '', '', 0, 'VK_1'], + Digit2: ['é', '2', '~', '', 0, 'VK_2'], + Digit3: ['"', '3', '#', '', 0, 'VK_3'], + Digit4: ['\'', '4', '{', '', 0, 'VK_4'], + Digit5: ['(', '5', '[', '', 0, 'VK_5'], + Digit6: ['-', '6', '|', '', 0, 'VK_6'], + Digit7: ['è', '7', '`', '', 0, 'VK_7'], + Digit8: ['_', '8', '\\', '', 0, 'VK_8'], + Digit9: ['ç', '9', '^', '', 0, 'VK_9'], + Digit0: ['à', '0', '@', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: [')', '°', ']', '', 0, 'VK_OEM_4'], + Equal: ['=', '+', '}', '', 0, 'VK_OEM_PLUS'], + BracketLeft: ['^', '¨', '', '', 0, 'VK_OEM_6'], + BracketRight: ['$', '£', '¤', '', 0, 'VK_OEM_1'], + Backslash: ['*', 'µ', '', '', 0, 'VK_OEM_5'], + Semicolon: ['m', 'M', '', '', 0, 'VK_M'], + Quote: ['ù', '%', '', '', 0, 'VK_OEM_3'], + Backquote: ['²', '', '', '', 0, 'VK_OEM_7'], + Comma: [';', '.', '', '', 0, 'VK_OEM_PERIOD'], + Period: [':', '/', '', '', 0, 'VK_OEM_2'], + Slash: ['!', '§', '', '', 0, 'VK_OEM_8'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/hu.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/hu.win.ts new file mode 100644 index 00000000000..d6b5a4dac06 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/hu.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '0000040E', id: '', text: 'Hungarian' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', 'ä', '', 0, 'VK_A'], + KeyB: ['b', 'B', '{', '', 0, 'VK_B'], + KeyC: ['c', 'C', '&', '', 0, 'VK_C'], + KeyD: ['d', 'D', 'Đ', '', 0, 'VK_D'], + KeyE: ['e', 'E', 'Ä', '', 0, 'VK_E'], + KeyF: ['f', 'F', '[', '', 0, 'VK_F'], + KeyG: ['g', 'G', ']', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', 'Í', '', 0, 'VK_I'], + KeyJ: ['j', 'J', 'í', '', 0, 'VK_J'], + KeyK: ['k', 'K', 'ł', '', 0, 'VK_K'], + KeyL: ['l', 'L', 'Ł', '', 0, 'VK_L'], + KeyM: ['m', 'M', '<', '', 0, 'VK_M'], + KeyN: ['n', 'N', '}', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '\\', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', 'đ', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '€', '', 0, 'VK_U'], + KeyV: ['v', 'V', '@', '', 0, 'VK_V'], + KeyW: ['w', 'W', '|', '', 0, 'VK_W'], + KeyX: ['x', 'X', '#', '', 0, 'VK_X'], + KeyY: ['z', 'Z', '', '', 0, 'VK_Z'], + KeyZ: ['y', 'Y', '>', '', 0, 'VK_Y'], + Digit1: ['1', '\'', '~', '', 0, 'VK_1'], + Digit2: ['2', '"', 'ˇ', '', 0, 'VK_2'], + Digit3: ['3', '+', '^', '', 0, 'VK_3'], + Digit4: ['4', '!', '˘', '', 0, 'VK_4'], + Digit5: ['5', '%', '°', '', 0, 'VK_5'], + Digit6: ['6', '/', '˛', '', 0, 'VK_6'], + Digit7: ['7', '=', '`', '', 0, 'VK_7'], + Digit8: ['8', '(', '˙', '', 0, 'VK_8'], + Digit9: ['9', ')', '´', '', 0, 'VK_9'], + Digit0: ['ö', 'Ö', '˝', '', 0, 'VK_OEM_3'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['ü', 'Ü', '¨', '', 0, 'VK_OEM_2'], + Equal: ['ó', 'Ó', '¸', '', 0, 'VK_OEM_PLUS'], + BracketLeft: ['ő', 'Ő', '÷', '', 0, 'VK_OEM_4'], + BracketRight: ['ú', 'Ú', '×', '', 0, 'VK_OEM_6'], + Backslash: ['ű', 'Ű', '¤', '', 0, 'VK_OEM_5'], + Semicolon: ['é', 'É', '$', '', 0, 'VK_OEM_1'], + Quote: ['á', 'Á', 'ß', '', 0, 'VK_OEM_7'], + Backquote: ['0', '§', '', '', 0, 'VK_0'], + Comma: [',', '?', ';', '', 0, 'VK_OEM_COMMA'], + Period: ['.', ':', '>', '', 0, 'VK_OEM_PERIOD'], + Slash: ['-', '_', '*', '', 0, 'VK_OEM_MINUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['í', 'Í', '<', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/it.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/it.darwin.ts new file mode 100644 index 00000000000..1dc97d9903f --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/it.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.Italian-Pro', lang: 'it', localizedName: 'Italian' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', 'å', 'Å', 0], + KeyB: ['b', 'B', '∫', 'Í', 0], + KeyC: ['c', 'C', '©', 'Á', 0], + KeyD: ['d', 'D', '∂', '˘', 0], + KeyE: ['e', 'E', '€', 'È', 0], + KeyF: ['f', 'F', 'ƒ', '˙', 0], + KeyG: ['g', 'G', '∞', '˚', 0], + KeyH: ['h', 'H', '∆', '¸', 0], + KeyI: ['i', 'I', 'œ', 'Œ', 0], + KeyJ: ['j', 'J', 'ª', '˝', 0], + KeyK: ['k', 'K', 'º', '˛', 0], + KeyL: ['l', 'L', '¬', 'ˇ', 0], + KeyM: ['m', 'M', 'µ', 'Ú', 0], + KeyN: ['n', 'N', '˜', 'Ó', 4], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'π', '∏', 0], + KeyQ: ['q', 'Q', '„', '‚', 0], + KeyR: ['r', 'R', '®', 'Ì', 0], + KeyS: ['s', 'S', 'ß', '¯', 0], + KeyT: ['t', 'T', '™', 'Ò', 0], + KeyU: ['u', 'U', '¨', 'Ù', 4], + KeyV: ['v', 'V', '√', 'É', 0], + KeyW: ['w', 'W', 'Ω', 'À', 0], + KeyX: ['x', 'X', '†', '‡', 0], + KeyY: ['y', 'Y', 'æ', 'Æ', 0], + KeyZ: ['z', 'Z', '∑', ' ', 0], + Digit1: ['1', '!', '«', '»', 0], + Digit2: ['2', '"', '“', '”', 0], + Digit3: ['3', '£', '‘', '’', 0], + Digit4: ['4', '$', '¥', '¢', 0], + Digit5: ['5', '%', '~', '‰', 0], + Digit6: ['6', '&', '‹', '›', 0], + Digit7: ['7', '/', '÷', '⁄', 0], + Digit8: ['8', '(', '´', '', 4], + Digit9: ['9', ')', '`', ' ', 4], + Digit0: ['0', '=', '≠', '≈', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['\'', '?', '¡', '¿', 0], + Equal: ['ì', '^', 'ˆ', '±', 4], + BracketLeft: ['è', 'é', '[', '{', 0], + BracketRight: ['+', '*', ']', '}', 0], + Backslash: ['ù', '§', '¶', '◊', 0], + Semicolon: ['ò', 'ç', '@', 'Ç', 0], + Quote: ['à', '°', '#', '∞', 0], + Backquote: ['<', '>', '≤', '≥', 0], + Comma: [',', ';', '…', ' ', 0], + Period: ['.', ':', '•', '·', 0], + Slash: ['-', '_', '–', '—', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: [',', '.', ',', '.', 0], + IntlBackslash: ['\\', '|', '`', 'ı', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/it.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/it.win.ts new file mode 100644 index 00000000000..573b7b0c6c3 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/it.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000410', id: '', text: 'Italian' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', '', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '"', '', '', 0, 'VK_2'], + Digit3: ['3', '£', '', '', 0, 'VK_3'], + Digit4: ['4', '$', '', '', 0, 'VK_4'], + Digit5: ['5', '%', '€', '', 0, 'VK_5'], + Digit6: ['6', '&', '', '', 0, 'VK_6'], + Digit7: ['7', '/', '', '', 0, 'VK_7'], + Digit8: ['8', '(', '', '', 0, 'VK_8'], + Digit9: ['9', ')', '', '', 0, 'VK_9'], + Digit0: ['0', '=', '', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['\'', '?', '', '', 0, 'VK_OEM_4'], + Equal: ['ì', '^', '', '', 0, 'VK_OEM_6'], + BracketLeft: ['è', 'é', '[', '{', 0, 'VK_OEM_1'], + BracketRight: ['+', '*', ']', '}', 0, 'VK_OEM_PLUS'], + Backslash: ['ù', '§', '', '', 0, 'VK_OEM_2'], + Semicolon: ['ò', 'ç', '@', '', 0, 'VK_OEM_3'], + Quote: ['à', '°', '#', '', 0, 'VK_OEM_7'], + Backquote: ['\\', '|', '', '', 0, 'VK_OEM_5'], + Comma: [',', ';', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', ':', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/jp-roman.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/jp-roman.darwin.ts new file mode 100644 index 00000000000..27328f3d87b --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/jp-roman.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.google.inputmethod.Japanese.Roman', lang: 'en', localizedName: 'Alphanumeric (Google)' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', '¯', '̄', 4], + KeyB: ['b', 'B', '˘', '̆', 4], + KeyC: ['c', 'C', '¸', '̧', 4], + KeyD: ['d', 'D', 'ð', 'Ð', 0], + KeyE: ['e', 'E', '´', '́', 4], + KeyF: ['f', 'F', 'ƒ', '', 0], + KeyG: ['g', 'G', '©', '‸', 8], + KeyH: ['h', 'H', 'ˍ', '̱', 4], + KeyI: ['i', 'I', 'ʼ', '̛', 4], + KeyJ: ['j', 'J', '˝', '̋', 4], + KeyK: ['k', 'K', '˚', '̊', 4], + KeyL: ['l', 'L', '-', '̵', 4], + KeyM: ['m', 'M', '˛', '̨', 4], + KeyN: ['n', 'N', '˜', '̃', 4], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', ',', '̦', 4], + KeyQ: ['q', 'Q', 'œ', 'Œ', 0], + KeyR: ['r', 'R', '®', '‰', 0], + KeyS: ['s', 'S', 'ß', '', 0], + KeyT: ['t', 'T', 'þ', 'Þ', 0], + KeyU: ['u', 'U', '¨', '̈', 4], + KeyV: ['v', 'V', 'ˇ', '̌', 4], + KeyW: ['w', 'W', '˙', '̇', 4], + KeyX: ['x', 'X', '.', '̣', 4], + KeyY: ['y', 'Y', '¥', '', 0], + KeyZ: ['z', 'Z', 'ˀ', '̉', 4], + Digit1: ['1', '!', '¡', '⁄', 0], + Digit2: ['2', '@', '™', '€', 0], + Digit3: ['3', '#', '£', '‹', 0], + Digit4: ['4', '$', '¢', '›', 0], + Digit5: ['5', '%', '§', '†', 0], + Digit6: ['6', '^', 'ˆ', '̂', 4], + Digit7: ['7', '&', '¶', '‡', 0], + Digit8: ['8', '*', '•', '°', 0], + Digit9: ['9', '(', 'ª', '·', 0], + Digit0: ['0', ')', 'º', '‚', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '–', '—', 0], + Equal: ['=', '+', '≠', '±', 0], + BracketLeft: ['[', '{', '“', '”', 0], + BracketRight: [']', '}', '‘', '’', 0], + Backslash: ['\\', '|', '«', '»', 0], + Semicolon: [';', ':', '…', '№', 8], + Quote: ['\'', '"', 'æ', 'Æ', 0], + Backquote: ['`', '~', '`', '̀', 4], + Comma: [',', '<', '≤', '„', 0], + Period: ['.', '>', '≥', 'ʔ', 8], + Slash: ['/', '?', '÷', '¿', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: ['.', '.', '.', '.', 0], + IntlBackslash: ['§', '±', '§', '±', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/jp.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/jp.darwin.ts new file mode 100644 index 00000000000..819f96ba5ca --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/jp.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.inputmethod.Kotoeri.Japanese', lang: 'ja', localizedName: 'Hiragana' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', 'å', 'Å', 0], + KeyB: ['b', 'B', '∫', 'ı', 0], + KeyC: ['c', 'C', 'ç', 'Ç', 0], + KeyD: ['d', 'D', '∂', 'Î', 0], + KeyE: ['e', 'E', '´', '´', 4], + KeyF: ['f', 'F', 'ƒ', 'Ï', 0], + KeyG: ['g', 'G', '©', '˝', 0], + KeyH: ['h', 'H', '˙', 'Ó', 0], + KeyI: ['i', 'I', 'ˆ', 'ˆ', 4], + KeyJ: ['j', 'J', '∆', 'Ô', 0], + KeyK: ['k', 'K', '˚', '', 0], + KeyL: ['l', 'L', '¬', 'Ò', 0], + KeyM: ['m', 'M', 'µ', 'Â', 0], + KeyN: ['n', 'N', '˜', '˜', 4], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'π', '∏', 0], + KeyQ: ['q', 'Q', 'œ', 'Œ', 0], + KeyR: ['r', 'R', '®', '‰', 0], + KeyS: ['s', 'S', 'ß', 'Í', 0], + KeyT: ['t', 'T', '†', 'ˇ', 0], + KeyU: ['u', 'U', '¨', '¨', 4], + KeyV: ['v', 'V', '√', '◊', 0], + KeyW: ['w', 'W', '∑', '„', 0], + KeyX: ['x', 'X', '≈', '˛', 0], + KeyY: ['y', 'Y', '¥', 'Á', 0], + KeyZ: ['z', 'Z', 'Ω', '¸', 0], + Digit1: ['1', '!', '¡', '⁄', 0], + Digit2: ['2', '@', '™', '€', 0], + Digit3: ['3', '#', '£', '‹', 0], + Digit4: ['4', '$', '¢', '›', 0], + Digit5: ['5', '%', '∞', 'fi', 0], + Digit6: ['6', '^', '§', 'fl', 0], + Digit7: ['7', '&', '¶', '‡', 0], + Digit8: ['8', '*', '•', '°', 0], + Digit9: ['9', '(', 'ª', '·', 0], + Digit0: ['0', ')', 'º', '‚', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '–', '—', 0], + Equal: ['=', '+', '≠', '±', 0], + BracketLeft: ['[', '{', '“', '”', 0], + BracketRight: [']', '}', '‘', '’', 0], + Backslash: ['\\', '|', '«', '»', 0], + Semicolon: [';', ':', '…', 'Ú', 0], + Quote: ['\'', '"', 'æ', 'Æ', 0], + Backquote: ['`', '~', '`', '`', 4], + Comma: [',', '<', '≤', '¯', 0], + Period: ['.', '>', '≥', '˘', 0], + Slash: ['/', '?', '÷', '¿', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: ['.', '.', '.', '.', 0], + IntlBackslash: ['§', '±', '§', '±', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ko.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ko.darwin.ts new file mode 100644 index 00000000000..4219a7bd62f --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ko.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.inputmethod.Korean.2SetKorean', lang: 'ko', localizedName: '2-Set Korean' }, + secondaryLayouts: [], + mapping: { + KeyA: ['ㅁ', 'ㅁ', 'a', 'A', 0], + KeyB: ['ㅠ', 'ㅠ', 'b', 'B', 0], + KeyC: ['ㅊ', 'ㅊ', 'c', 'C', 0], + KeyD: ['ㅇ', 'ㅇ', 'd', 'D', 0], + KeyE: ['ㄷ', 'ㄸ', 'e', 'E', 0], + KeyF: ['ㄹ', 'ㄹ', 'f', 'F', 0], + KeyG: ['ㅎ', 'ㅎ', 'g', 'G', 0], + KeyH: ['ㅗ', 'ㅗ', 'h', 'H', 0], + KeyI: ['ㅑ', 'ㅑ', 'i', 'I', 0], + KeyJ: ['ㅓ', 'ㅓ', 'j', 'J', 0], + KeyK: ['ㅏ', 'ㅏ', 'k', 'K', 0], + KeyL: ['ㅣ', 'ㅣ', 'l', 'L', 0], + KeyM: ['ㅡ', 'ㅡ', 'm', 'M', 0], + KeyN: ['ㅜ', 'ㅜ', 'n', 'N', 0], + KeyO: ['ㅐ', 'ㅒ', 'o', 'O', 0], + KeyP: ['ㅔ', 'ㅖ', 'p', 'P', 0], + KeyQ: ['ㅂ', 'ㅃ', 'q', 'Q', 0], + KeyR: ['ㄱ', 'ㄲ', 'r', 'R', 0], + KeyS: ['ㄴ', 'ㄴ', 's', 'S', 0], + KeyT: ['ㅅ', 'ㅆ', 't', 'T', 0], + KeyU: ['ㅕ', 'ㅕ', 'u', 'U', 0], + KeyV: ['ㅍ', 'ㅍ', 'v', 'V', 0], + KeyW: ['ㅈ', 'ㅉ', 'w', 'W', 0], + KeyX: ['ㅌ', 'ㅌ', 'x', 'X', 0], + KeyY: ['ㅛ', 'ㅛ', 'y', 'Y', 0], + KeyZ: ['ㅋ', 'ㅋ', 'z', 'Z', 0], + Digit1: ['1', '!', '1', '!', 0], + Digit2: ['2', '@', '2', '@', 0], + Digit3: ['3', '#', '3', '#', 0], + Digit4: ['4', '$', '4', '$', 0], + Digit5: ['5', '%', '5', '%', 0], + Digit6: ['6', '^', '6', '^', 0], + Digit7: ['7', '&', '7', '&', 0], + Digit8: ['8', '*', '8', '*', 0], + Digit9: ['9', '(', '9', '(', 0], + Digit0: ['0', ')', '0', ')', 0], + Enter: [], + Escape: ['', '', '', '‌', 0], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '-', '_', 0], + Equal: ['=', '+', '=', '+', 0], + BracketLeft: ['[', '{', '[', '{', 0], + BracketRight: [']', '}', ']', '}', 0], + Backslash: ['\\', '|', '\\', '|', 0], + Semicolon: [';', ':', ';', ':', 0], + Quote: ['\'', '"', '\'', '"', 0], + Backquote: ['₩', '~', '`', '~', 0], + Comma: [',', '<', ',', '<', 0], + Period: ['.', '>', '.', '>', 0], + Slash: ['/', '?', '/', '?', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: ['.', '.', '.', '.', 0], + IntlBackslash: ['§', '±', '§', '±', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.darwin.ts new file mode 100644 index 00000000000..8de0fa205a3 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.darwin.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/en.darwin'; // 15% +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/zh-hans.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/en-uk.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/es.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/jp-roman.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/de.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/en-intl.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/en-ext.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/fr.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/jp.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/pl.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/it.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/ru.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/pt.darwin'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/ko.darwin'; + +export { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.linux.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.linux.ts new file mode 100644 index 00000000000..6561501fce4 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.linux.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 'vs/workbench/services/keybinding/browser/keyboardLayouts/en.linux'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/es.linux'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/de.linux'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/fr.linux'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/ru.linux'; + +export { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.win.ts new file mode 100644 index 00000000000..bb85b252f90 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.win.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/en.win'; // 40% +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/es-latin.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/en-in.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/de.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/en-uk.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/fr.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/pt-br.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/es.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/en-intl.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/ru.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/pl.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/it.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/sv.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/tr.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/pt.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/dk.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/no.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/thai.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/hu.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/de-swiss.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/en-belgian.win'; +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/cz.win'; + +export { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/no.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/no.win.ts new file mode 100644 index 00000000000..2c415e8ebff --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/no.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000414', id: '', text: 'Norwegian' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', 'µ', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '"', '@', '', 0, 'VK_2'], + Digit3: ['3', '#', '£', '', 0, 'VK_3'], + Digit4: ['4', '¤', '$', '', 0, 'VK_4'], + Digit5: ['5', '%', '€', '', 0, 'VK_5'], + Digit6: ['6', '&', '', '', 0, 'VK_6'], + Digit7: ['7', '/', '{', '', 0, 'VK_7'], + Digit8: ['8', '(', '[', '', 0, 'VK_8'], + Digit9: ['9', ')', ']', '', 0, 'VK_9'], + Digit0: ['0', '=', '}', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['+', '?', '', '', 0, 'VK_OEM_PLUS'], + Equal: ['\\', '`', '´', '', 0, 'VK_OEM_4'], + BracketLeft: ['å', 'Å', '', '', 0, 'VK_OEM_6'], + BracketRight: ['¨', '^', '~', '', 0, 'VK_OEM_1'], + Backslash: ['\'', '*', '', '', 0, 'VK_OEM_2'], + Semicolon: ['ø', 'Ø', '', '', 0, 'VK_OEM_3'], + Quote: ['æ', 'Æ', '', '', 0, 'VK_OEM_7'], + Backquote: ['|', '§', '', '', 0, 'VK_OEM_5'], + Comma: [',', ';', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', ':', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pl.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pl.darwin.ts new file mode 100644 index 00000000000..57577ba513c --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pl.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.PolishPro', lang: 'pl', localizedName: 'Polish - Pro' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', 'ą', 'Ą', 0], + KeyB: ['b', 'B', 'ļ', 'ű', 0], + KeyC: ['c', 'C', 'ć', 'Ć', 0], + KeyD: ['d', 'D', '∂', 'Ž', 0], + KeyE: ['e', 'E', 'ę', 'Ę', 0], + KeyF: ['f', 'F', 'ń', 'ž', 0], + KeyG: ['g', 'G', '©', 'Ū', 0], + KeyH: ['h', 'H', 'ķ', 'Ó', 0], + KeyI: ['i', 'I', '^', 'ť', 4], + KeyJ: ['j', 'J', '∆', 'Ô', 0], + KeyK: ['k', 'K', 'Ż', 'ū', 0], + KeyL: ['l', 'L', 'ł', 'Ł', 0], + KeyM: ['m', 'M', 'Ķ', 'ų', 0], + KeyN: ['n', 'N', 'ń', 'Ń', 0], + KeyO: ['o', 'O', 'ó', 'Ó', 0], + KeyP: ['p', 'P', 'Ļ', 'ł', 0], + KeyQ: ['q', 'Q', 'Ō', 'ő', 0], + KeyR: ['r', 'R', '®', '£', 0], + KeyS: ['s', 'S', 'ś', 'Ś', 0], + KeyT: ['t', 'T', '†', 'ś', 0], + KeyU: ['u', 'U', '¨', 'Ť', 4], + KeyV: ['v', 'V', '√', '◊', 0], + KeyW: ['w', 'W', '∑', '„', 0], + KeyX: ['x', 'X', 'ź', 'Ź', 0], + KeyY: ['y', 'Y', 'ī', 'Á', 0], + KeyZ: ['z', 'Z', 'ż', 'Ż', 0], + Digit1: ['1', '!', 'Ń', 'ŕ', 0], + Digit2: ['2', '@', '™', 'Ř', 0], + Digit3: ['3', '#', '€', '‹', 0], + Digit4: ['4', '$', 'ß', '›', 0], + Digit5: ['5', '%', 'į', 'ř', 0], + Digit6: ['6', '^', '§', 'Ŗ', 0], + Digit7: ['7', '&', '¶', 'ŗ', 0], + Digit8: ['8', '*', '•', '°', 0], + Digit9: ['9', '(', 'Ľ', 'Š', 0], + Digit0: ['0', ')', 'ľ', '‚', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '–', '—', 0], + Equal: ['=', '+', '≠', 'Ī', 0], + BracketLeft: ['[', '{', '„', '”', 0], + BracketRight: [']', '}', '‚', '’', 0], + Backslash: ['\\', '|', '«', '»', 0], + Semicolon: [';', ':', '…', 'Ú', 0], + Quote: ['\'', '"', 'ĺ', 'ģ', 0], + Backquote: ['`', '~', '`', 'Ŕ', 4], + Comma: [',', '<', '≤', 'Ý', 0], + Period: ['.', '>', '≥', 'ý', 0], + Slash: ['/', '?', '÷', 'ņ', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: ['.', '.', '.', '.', 0], + IntlBackslash: ['§', '£', '¬', '¬', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pl.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pl.win.ts new file mode 100644 index 00000000000..a110111a83f --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pl.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000415', id: '', text: 'Polish (Programmers)' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', 'ą', 'Ą', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', 'ć', 'Ć', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', 'ę', 'Ę', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', 'ł', 'Ł', 0, 'VK_L'], + KeyM: ['m', 'M', '', '', 0, 'VK_M'], + KeyN: ['n', 'N', 'ń', 'Ń', 0, 'VK_N'], + KeyO: ['o', 'O', 'ó', 'Ó', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', 'ś', 'Ś', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '€', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', 'ź', 'Ź', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', 'ż', 'Ż', 0, 'VK_Z'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '@', '', '', 0, 'VK_2'], + Digit3: ['3', '#', '', '', 0, 'VK_3'], + Digit4: ['4', '$', '', '', 0, 'VK_4'], + Digit5: ['5', '%', '', '', 0, 'VK_5'], + Digit6: ['6', '^', '', '', 0, 'VK_6'], + Digit7: ['7', '&', '', '', 0, 'VK_7'], + Digit8: ['8', '*', '', '', 0, 'VK_8'], + Digit9: ['9', '(', '', '', 0, 'VK_9'], + Digit0: ['0', ')', '', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + Equal: ['=', '+', '', '', 0, 'VK_OEM_PLUS'], + BracketLeft: ['[', '{', '', '', 0, 'VK_OEM_4'], + BracketRight: [']', '}', '', '', 0, 'VK_OEM_6'], + Backslash: ['\\', '|', '', '', 0, 'VK_OEM_5'], + Semicolon: [';', ':', '', '', 0, 'VK_OEM_1'], + Quote: ['\'', '"', '', '', 0, 'VK_OEM_7'], + Backquote: ['`', '~', '', '', 0, 'VK_OEM_3'], + Comma: [',', '<', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', '>', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['/', '?', '', '', 0, 'VK_OEM_2'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['\\', '|', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pt-br.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pt-br.win.ts new file mode 100644 index 00000000000..9bb82448a7c --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pt-br.win.ts @@ -0,0 +1,170 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000416', id: '', text: 'Portuguese (Brazilian ABNT)' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '₢', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '°', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', '', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '/', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '?', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '¹', '', 0, 'VK_1'], + Digit2: ['2', '@', '²', '', 0, 'VK_2'], + Digit3: ['3', '#', '³', '', 0, 'VK_3'], + Digit4: ['4', '$', '£', '', 0, 'VK_4'], + Digit5: ['5', '%', '¢', '', 0, 'VK_5'], + Digit6: ['6', '¨', '¬', '', 0, 'VK_6'], + Digit7: ['7', '&', '', '', 0, 'VK_7'], + Digit8: ['8', '*', '', '', 0, 'VK_8'], + Digit9: ['9', '(', '', '', 0, 'VK_9'], + Digit0: ['0', ')', '', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + Equal: ['=', '+', '§', '', 0, 'VK_OEM_PLUS'], + BracketLeft: ['´', '`', '', '', 0, 'VK_OEM_4'], + BracketRight: ['[', '{', 'ª', '', 0, 'VK_OEM_6'], + Backslash: [']', '}', 'º', '', 0, 'VK_OEM_5'], + Semicolon: ['ç', 'Ç', '', '', 0, 'VK_OEM_1'], + Quote: ['~', '^', '', '', 0, 'VK_OEM_7'], + Backquote: ['\'', '"', '', '', 0, 'VK_OEM_3'], + Comma: [',', '<', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', '>', '', '', 0, 'VK_OEM_PERIOD'], + Slash: [';', ':', '', '', 0, 'VK_OEM_2'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['\\', '|', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: ['.', '.', '', '', 0, 'VK_ABNT_C2'], + IntlRo: ['/', '?', '°', '', 0, 'VK_ABNT_C1'], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pt.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pt.darwin.ts new file mode 100644 index 00000000000..87435fdc0ea --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pt.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.Brazilian-Pro', lang: 'pt' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', 'å', 'Å', 0], + KeyB: ['b', 'B', '∫', 'ı', 0], + KeyC: ['c', 'C', 'ç', 'Ç', 0], + KeyD: ['d', 'D', '∂', 'Î', 0], + KeyE: ['e', 'E', '´', '´', 4], + KeyF: ['f', 'F', 'ƒ', 'Ï', 0], + KeyG: ['g', 'G', '©', '˝', 0], + KeyH: ['h', 'H', '˙', 'Ó', 0], + KeyI: ['i', 'I', 'ˆ', 'ˆ', 4], + KeyJ: ['j', 'J', '∆', 'Ô', 0], + KeyK: ['k', 'K', '˚', '', 0], + KeyL: ['l', 'L', '¬', 'Ò', 0], + KeyM: ['m', 'M', 'µ', 'Â', 0], + KeyN: ['n', 'N', '˜', '˜', 4], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'π', '∏', 0], + KeyQ: ['q', 'Q', 'œ', 'Œ', 0], + KeyR: ['r', 'R', '®', '‰', 0], + KeyS: ['s', 'S', 'ß', 'Í', 0], + KeyT: ['t', 'T', '†', 'ˇ', 0], + KeyU: ['u', 'U', '¨', '¨', 4], + KeyV: ['v', 'V', '√', '◊', 0], + KeyW: ['w', 'W', '∑', '„', 0], + KeyX: ['x', 'X', '≈', '˛', 0], + KeyY: ['y', 'Y', '¥', 'Á', 0], + KeyZ: ['z', 'Z', 'Ω', '¸', 0], + Digit1: ['1', '!', '¡', '⁄', 0], + Digit2: ['2', '@', '™', '€', 0], + Digit3: ['3', '#', '£', '‹', 0], + Digit4: ['4', '$', '¢', '›', 0], + Digit5: ['5', '%', '∞', 'fi', 0], + Digit6: ['6', 'ˆ', '§', 'fl', 2], + Digit7: ['7', '&', '¶', '‡', 0], + Digit8: ['8', '*', '•', '°', 0], + Digit9: ['9', '(', 'ª', '·', 0], + Digit0: ['0', ')', 'º', '‚', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '–', '—', 0], + Equal: ['=', '+', '≠', '±', 0], + BracketLeft: ['[', '{', '“', '”', 0], + BracketRight: [']', '}', '‘', '’', 0], + Backslash: ['\\', '|', '«', '»', 0], + Semicolon: [';', ':', '…', 'Ú', 0], + Quote: ['\'', '"', 'æ', 'Æ', 3], + Backquote: ['`', '˜', '`', '`', 7], + Comma: [',', '<', '≤', '¯', 0], + Period: ['.', '>', '≥', '˘', 0], + Slash: ['/', '?', '÷', '¿', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: ['.', '.', '.', '.', 0], + IntlBackslash: ['§', '±', '§', '±', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pt.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pt.win.ts new file mode 100644 index 00000000000..456a537654b --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/pt.win.ts @@ -0,0 +1,170 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000816', id: '', text: 'Portuguese' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', '', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '"', '@', '', 0, 'VK_2'], + Digit3: ['3', '#', '£', '', 0, 'VK_3'], + Digit4: ['4', '$', '§', '', 0, 'VK_4'], + Digit5: ['5', '%', '€', '', 0, 'VK_5'], + Digit6: ['6', '&', '', '', 0, 'VK_6'], + Digit7: ['7', '/', '{', '', 0, 'VK_7'], + Digit8: ['8', '(', '[', '', 0, 'VK_8'], + Digit9: ['9', ')', ']', '', 0, 'VK_9'], + Digit0: ['0', '=', '}', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['\'', '?', '', '', 0, 'VK_OEM_4'], + Equal: ['«', '»', '', '', 0, 'VK_OEM_6'], + BracketLeft: ['+', '*', '¨', '', 0, 'VK_OEM_PLUS'], + BracketRight: ['´', '`', ']', '', 0, 'VK_OEM_1'], + Backslash: ['~', '^', '', '', 0, 'VK_OEM_2'], + Semicolon: ['ç', 'Ç', '', '', 0, 'VK_OEM_3'], + Quote: ['º', 'ª', '', '', 0, 'VK_OEM_7'], + Backquote: ['\\', '|', '', '', 0, 'VK_OEM_5'], + Comma: [',', ';', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', ':', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } + +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ru.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ru.darwin.ts new file mode 100644 index 00000000000..5eea5ac38f5 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ru.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.Russian', lang: 'ru', localizedName: 'Russian' }, + secondaryLayouts: [], + mapping: { + KeyA: ['ф', 'Ф', 'ƒ', 'ƒ', 0], + KeyB: ['и', 'И', 'и', 'И', 0], + KeyC: ['с', 'С', '≠', '≠', 0], + KeyD: ['в', 'В', 'ћ', 'Ћ', 0], + KeyE: ['у', 'У', 'ќ', 'Ќ', 0], + KeyF: ['а', 'А', '÷', '÷', 0], + KeyG: ['п', 'П', '©', '©', 0], + KeyH: ['р', 'Р', '₽', '₽', 0], + KeyI: ['ш', 'Ш', 'ѕ', 'Ѕ', 0], + KeyJ: ['о', 'О', '°', '•', 0], + KeyK: ['л', 'Л', 'љ', 'Љ', 0], + KeyL: ['д', 'Д', '∆', '∆', 0], + KeyM: ['ь', 'Ь', '~', '~', 0], + KeyN: ['т', 'Т', '™', '™', 0], + KeyO: ['щ', 'Щ', 'ў', 'Ў', 0], + KeyP: ['з', 'З', '‘', '’', 0], + KeyQ: ['й', 'Й', 'ј', 'Ј', 0], + KeyR: ['к', 'К', '®', '®', 0], + KeyS: ['ы', 'Ы', 'ы', 'Ы', 0], + KeyT: ['е', 'Е', '†', '†', 0], + KeyU: ['г', 'Г', 'ѓ', 'Ѓ', 0], + KeyV: ['м', 'М', 'µ', 'µ', 0], + KeyW: ['ц', 'Ц', 'џ', 'Џ', 0], + KeyX: ['ч', 'Ч', '≈', '≈', 0], + KeyY: ['н', 'Н', 'њ', 'Њ', 0], + KeyZ: ['я', 'Я', 'ђ', 'Ђ', 0], + Digit1: ['1', '!', '!', '|', 0], + Digit2: ['2', '"', '@', '"', 0], + Digit3: ['3', '№', '#', '£', 0], + Digit4: ['4', '%', '$', '€', 0], + Digit5: ['5', ':', '%', '∞', 0], + Digit6: ['6', ',', '^', '¬', 0], + Digit7: ['7', '.', '&', '¶', 0], + Digit8: ['8', ';', '*', '√', 0], + Digit9: ['9', '(', '{', '\'', 0], + Digit0: ['0', ')', '}', '`', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '–', '—', 0], + Equal: ['=', '+', '»', '«', 0], + BracketLeft: ['х', 'Х', '“', '”', 0], + BracketRight: ['ъ', 'Ъ', 'ъ', 'Ъ', 0], + Backslash: ['ё', 'Ё', 'ё', 'Ё', 0], + Semicolon: ['ж', 'Ж', '…', '…', 0], + Quote: ['э', 'Э', 'э', 'Э', 0], + Backquote: [']', '[', ']', '[', 0], + Comma: ['б', 'Б', '≤', '<', 0], + Period: ['ю', 'Ю', '≥', '>', 0], + Slash: ['/', '?', '“', '„', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: [',', '.', ',', ',', 0], + IntlBackslash: ['>', '<', '§', '±', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ru.linux.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ru.linux.ts new file mode 100644 index 00000000000..b13adb0d9ac --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ru.linux.ts @@ -0,0 +1,187 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { model: 'pc104', layout: 'ru', variant: ',', options: '', rules: 'base' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['ф', 'Ф', 'ф', 'Ф', 0], + KeyB: ['и', 'И', 'и', 'И', 0], + KeyC: ['с', 'С', 'с', 'С', 0], + KeyD: ['в', 'В', 'в', 'В', 0], + KeyE: ['у', 'У', 'у', 'У', 0], + KeyF: ['а', 'А', 'а', 'А', 0], + KeyG: ['п', 'П', 'п', 'П', 0], + KeyH: ['р', 'Р', 'р', 'Р', 0], + KeyI: ['ш', 'Ш', 'ш', 'Ш', 0], + KeyJ: ['о', 'О', 'о', 'О', 0], + KeyK: ['л', 'Л', 'л', 'Л', 0], + KeyL: ['д', 'Д', 'д', 'Д', 0], + KeyM: ['ь', 'Ь', 'ь', 'Ь', 0], + KeyN: ['т', 'Т', 'т', 'Т', 0], + KeyO: ['щ', 'Щ', 'щ', 'Щ', 0], + KeyP: ['з', 'З', 'з', 'З', 0], + KeyQ: ['й', 'Й', 'й', 'Й', 0], + KeyR: ['к', 'К', 'к', 'К', 0], + KeyS: ['ы', 'Ы', 'ы', 'Ы', 0], + KeyT: ['е', 'Е', 'е', 'Е', 0], + KeyU: ['г', 'Г', 'г', 'Г', 0], + KeyV: ['м', 'М', 'м', 'М', 0], + KeyW: ['ц', 'Ц', 'ц', 'Ц', 0], + KeyX: ['ч', 'Ч', 'ч', 'Ч', 0], + KeyY: ['н', 'Н', 'н', 'Н', 0], + KeyZ: ['я', 'Я', 'я', 'Я', 0], + Digit1: ['1', '!', '1', '!', 0], + Digit2: ['2', '"', '2', '"', 0], + Digit3: ['3', '№', '3', '№', 0], + Digit4: ['4', ';', '4', ';', 0], + Digit5: ['5', '%', '5', '%', 0], + Digit6: ['6', ':', '6', ':', 0], + Digit7: ['7', '?', '7', '?', 0], + Digit8: ['8', '*', '₽', '', 0], + Digit9: ['9', '(', '9', '(', 0], + Digit0: ['0', ')', '0', ')', 0], + Enter: ['\r', '\r', '\r', '\r', 0], + Escape: ['\u001b', '\u001b', '\u001b', '\u001b', 0], + Backspace: ['\b', '\b', '\b', '\b', 0], + Tab: ['\t', '', '\t', '', 0], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '_', '-', '_', 0], + Equal: ['=', '+', '=', '+', 0], + BracketLeft: ['х', 'Х', 'х', 'Х', 0], + BracketRight: ['ъ', 'Ъ', 'ъ', 'Ъ', 0], + Backslash: ['\\', '/', '\\', '/', 0], + Semicolon: ['ж', 'Ж', 'ж', 'Ж', 0], + Quote: ['э', 'Э', 'э', 'Э', 0], + Backquote: ['ё', 'Ё', 'ё', 'Ё', 0], + Comma: ['б', 'Б', 'б', 'Б', 0], + Period: ['ю', 'Ю', 'ю', 'Ю', 0], + Slash: ['.', ',', '.', ',', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: ['', '', '', '', 0], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: ['/', '/', '/', '/', 0], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: [], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['', '1', '', '1', 0], + Numpad2: ['', '2', '', '2', 0], + Numpad3: ['', '3', '', '3', 0], + Numpad4: ['', '4', '', '4', 0], + Numpad5: ['', '5', '', '5', 0], + Numpad6: ['', '6', '', '6', 0], + Numpad7: ['', '7', '', '7', 0], + Numpad8: ['', '8', '', '8', 0], + Numpad9: ['', '9', '', '9', 0], + Numpad0: ['', '0', '', '0', 0], + NumpadDecimal: ['', ',', '', ',', 0], + IntlBackslash: ['/', '|', '|', '¦', 0], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Open: [], + Help: [], + Select: [], + Again: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + Find: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + Lang5: [], + NumpadParenLeft: [], + NumpadParenRight: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: ['\r', '\r', '\r', '\r', 0], + MetaRight: ['.', '.', '.', '.', 0], + BrightnessUp: [], + BrightnessDown: [], + MediaPlay: [], + MediaRecord: [], + MediaFastForward: [], + MediaRewind: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + SelectTask: [], + LaunchScreenSaver: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [], + MailReply: [], + MailForward: [], + MailSend: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ru.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ru.win.ts new file mode 100644 index 00000000000..0da492a10ac --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/ru.win.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '00000419', id: '', text: 'Russian' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['ф', 'Ф', '', '', 0, 'VK_A'], + KeyB: ['и', 'И', '', '', 0, 'VK_B'], + KeyC: ['с', 'С', '', '', 0, 'VK_C'], + KeyD: ['в', 'В', '', '', 0, 'VK_D'], + KeyE: ['у', 'У', '', '', 0, 'VK_E'], + KeyF: ['а', 'А', '', '', 0, 'VK_F'], + KeyG: ['п', 'П', '', '', 0, 'VK_G'], + KeyH: ['р', 'Р', '', '', 0, 'VK_H'], + KeyI: ['ш', 'Ш', '', '', 0, 'VK_I'], + KeyJ: ['о', 'О', '', '', 0, 'VK_J'], + KeyK: ['л', 'Л', '', '', 0, 'VK_K'], + KeyL: ['д', 'Д', '', '', 0, 'VK_L'], + KeyM: ['ь', 'Ь', '', '', 0, 'VK_M'], + KeyN: ['т', 'Т', '', '', 0, 'VK_N'], + KeyO: ['щ', 'Щ', '', '', 0, 'VK_O'], + KeyP: ['з', 'З', '', '', 0, 'VK_P'], + KeyQ: ['й', 'Й', '', '', 0, 'VK_Q'], + KeyR: ['к', 'К', '', '', 0, 'VK_R'], + KeyS: ['ы', 'Ы', '', '', 0, 'VK_S'], + KeyT: ['е', 'Е', '', '', 0, 'VK_T'], + KeyU: ['г', 'Г', '', '', 0, 'VK_U'], + KeyV: ['м', 'М', '', '', 0, 'VK_V'], + KeyW: ['ц', 'Ц', '', '', 0, 'VK_W'], + KeyX: ['ч', 'Ч', '', '', 0, 'VK_X'], + KeyY: ['н', 'Н', '', '', 0, 'VK_Y'], + KeyZ: ['я', 'Я', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '"', '', '', 0, 'VK_2'], + Digit3: ['3', '№', '', '', 0, 'VK_3'], + Digit4: ['4', ';', '', '', 0, 'VK_4'], + Digit5: ['5', '%', '', '', 0, 'VK_5'], + Digit6: ['6', ':', '', '', 0, 'VK_6'], + Digit7: ['7', '?', '', '', 0, 'VK_7'], + Digit8: ['8', '*', '₽', '', 0, 'VK_8'], + Digit9: ['9', '(', '', '', 0, 'VK_9'], + Digit0: ['0', ')', '', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + Equal: ['=', '+', '', '', 0, 'VK_OEM_PLUS'], + BracketLeft: ['х', 'Х', '', '', 0, 'VK_OEM_4'], + BracketRight: ['ъ', 'Ъ', '', '', 0, 'VK_OEM_6'], + Backslash: ['\\', '/', '', '', 0, 'VK_OEM_5'], + Semicolon: ['ж', 'Ж', '', '', 0, 'VK_OEM_1'], + Quote: ['э', 'Э', '', '', 0, 'VK_OEM_7'], + Backquote: ['ё', 'Ё', '', '', 0, 'VK_OEM_3'], + Comma: ['б', 'Б', '', '', 0, 'VK_OEM_COMMA'], + Period: ['ю', 'Ю', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['.', ',', '', '', 0, 'VK_OEM_2'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['\\', '/', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/sv.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/sv.darwin.ts new file mode 100644 index 00000000000..6d80477a689 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/sv.darwin.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.keylayout.Swedish-Pro', lang: 'sv', localizedName: 'Swedish - Pro' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', '', '◊', 0], + KeyB: ['b', 'B', '›', '»', 0], + KeyC: ['c', 'C', 'ç', 'Ç', 0], + KeyD: ['d', 'D', '∂', '∆', 0], + KeyE: ['e', 'E', 'é', 'É', 0], + KeyF: ['f', 'F', 'ƒ', '∫', 0], + KeyG: ['g', 'G', '¸', '¯', 0], + KeyH: ['h', 'H', '˛', '˘', 0], + KeyI: ['i', 'I', 'ı', 'ˆ', 0], + KeyJ: ['j', 'J', '√', '¬', 0], + KeyK: ['k', 'K', 'ª', 'º', 0], + KeyL: ['l', 'L', 'fi', 'fl', 0], + KeyM: ['m', 'M', '’', '”', 0], + KeyN: ['n', 'N', '‘', '“', 0], + KeyO: ['o', 'O', 'œ', 'Œ', 0], + KeyP: ['p', 'P', 'π', '∏', 0], + KeyQ: ['q', 'Q', '•', '°', 0], + KeyR: ['r', 'R', '®', '√', 0], + KeyS: ['s', 'S', 'ß', '∑', 0], + KeyT: ['t', 'T', '†', '‡', 0], + KeyU: ['u', 'U', 'ü', 'Ü', 0], + KeyV: ['v', 'V', '‹', '«', 0], + KeyW: ['w', 'W', 'Ω', '˝', 0], + KeyX: ['x', 'X', '≈', 'ˇ', 0], + KeyY: ['y', 'Y', 'µ', '˜', 0], + KeyZ: ['z', 'Z', '÷', '⁄', 0], + Digit1: ['1', '!', '©', '¡', 0], + Digit2: ['2', '"', '@', '”', 0], + Digit3: ['3', '#', '£', '¥', 0], + Digit4: ['4', '€', '$', '¢', 0], + Digit5: ['5', '%', '∞', '‰', 0], + Digit6: ['6', '&', '§', '¶', 0], + Digit7: ['7', '/', '|', '\\', 0], + Digit8: ['8', '(', '[', '{', 0], + Digit9: ['9', ')', ']', '}', 0], + Digit0: ['0', '=', '≈', '≠', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['+', '?', '±', '¿', 0], + Equal: ['´', '`', '´', '`', 3], + BracketLeft: ['å', 'Å', '˙', '˚', 0], + BracketRight: ['¨', '^', '~', '^', 7], + Backslash: ['\'', '*', '™', '’', 0], + Semicolon: ['ö', 'Ö', 'ø', 'Ø', 0], + Quote: ['ä', 'Ä', 'æ', 'Æ', 0], + Backquote: ['<', '>', '≤', '≥', 0], + Comma: [',', ';', '‚', '„', 0], + Period: ['.', ':', '…', '·', 0], + Slash: ['-', '_', '–', '—', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: [',', '.', ',', '.', 0], + IntlBackslash: ['§', '°', '¶', '•', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/sv.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/sv.win.ts new file mode 100644 index 00000000000..c7128b5c929 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/sv.win.ts @@ -0,0 +1,171 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '0000041D', id: '', text: 'Swedish' }, + secondaryLayouts: [ + { name: '0000040B', id: '', text: 'Finnish' } + ], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', '', '', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['i', 'I', '', '', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', 'µ', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', '', '', 0, 'VK_S'], + KeyT: ['t', 'T', '', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '', '', 0, 'VK_1'], + Digit2: ['2', '"', '@', '', 0, 'VK_2'], + Digit3: ['3', '#', '£', '', 0, 'VK_3'], + Digit4: ['4', '¤', '$', '', 0, 'VK_4'], + Digit5: ['5', '%', '€', '', 0, 'VK_5'], + Digit6: ['6', '&', '', '', 0, 'VK_6'], + Digit7: ['7', '/', '{', '', 0, 'VK_7'], + Digit8: ['8', '(', '[', '', 0, 'VK_8'], + Digit9: ['9', ')', ']', '', 0, 'VK_9'], + Digit0: ['0', '=', '}', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['+', '?', '\\', '', 0, 'VK_OEM_PLUS'], + Equal: ['´', '`', '', '', 0, 'VK_OEM_4'], + BracketLeft: ['å', 'Å', '', '', 0, 'VK_OEM_6'], + BracketRight: ['¨', '^', '~', '', 0, 'VK_OEM_1'], + Backslash: ['\'', '*', '', '', 0, 'VK_OEM_2'], + Semicolon: ['ö', 'Ö', '', '', 0, 'VK_OEM_3'], + Quote: ['ä', 'Ä', '', '', 0, 'VK_OEM_7'], + Backquote: ['§', '½', '', '', 0, 'VK_OEM_5'], + Comma: [',', ';', '', '', 0, 'VK_OEM_COMMA'], + Period: ['.', ':', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['-', '_', '', '', 0, 'VK_OEM_MINUS'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '|', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/thai.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/thai.win.ts new file mode 100644 index 00000000000..be85bfedd9e --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/thai.win.ts @@ -0,0 +1,168 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '0000041E', id: '', text: 'Thai Kedmanee' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['ฟ', 'ฤ', '', '', 0, 'VK_A'], + KeyB: ['ิ', 'ฺ', '', '', 0, 'VK_B'], + KeyC: ['แ', 'ฉ', '', '', 0, 'VK_C'], + KeyD: ['ก', 'ฏ', '', '', 0, 'VK_D'], + KeyE: ['ำ', 'ฎ', '', '', 0, 'VK_E'], + KeyF: ['ด', 'โ', '', '', 0, 'VK_F'], + KeyG: ['เ', 'ฌ', '', '', 0, 'VK_G'], + KeyH: ['้', '็', '', '', 0, 'VK_H'], + KeyI: ['ร', 'ณ', '', '', 0, 'VK_I'], + KeyJ: ['่', '๋', '', '', 0, 'VK_J'], + KeyK: ['า', 'ษ', '', '', 0, 'VK_K'], + KeyL: ['ส', 'ศ', '', '', 0, 'VK_L'], + KeyM: ['ท', '?', '', '', 0, 'VK_M'], + KeyN: ['ื', '์', '', '', 0, 'VK_N'], + KeyO: ['น', 'ฯ', '', '', 0, 'VK_O'], + KeyP: ['ย', 'ญ', '', '', 0, 'VK_P'], + KeyQ: ['ๆ', '๐', '', '', 0, 'VK_Q'], + KeyR: ['พ', 'ฑ', '', '', 0, 'VK_R'], + KeyS: ['ห', 'ฆ', '', '', 0, 'VK_S'], + KeyT: ['ะ', 'ธ', '', '', 0, 'VK_T'], + KeyU: ['ี', '๊', '', '', 0, 'VK_U'], + KeyV: ['อ', 'ฮ', '', '', 0, 'VK_V'], + KeyW: ['ไ', '"', '', '', 0, 'VK_W'], + KeyX: ['ป', ')', '', '', 0, 'VK_X'], + KeyY: ['ั', 'ํ', '', '', 0, 'VK_Y'], + KeyZ: ['ผ', '(', '', '', 0, 'VK_Z'], + Digit1: ['ๅ', '+', '', '', 0, 'VK_1'], + Digit2: ['/', '๑', '', '', 0, 'VK_2'], + Digit3: ['-', '๒', '', '', 0, 'VK_3'], + Digit4: ['ภ', '๓', '', '', 0, 'VK_4'], + Digit5: ['ถ', '๔', '', '', 0, 'VK_5'], + Digit6: ['ุ', 'ู', '', '', 0, 'VK_6'], + Digit7: ['ึ', '฿', '', '', 0, 'VK_7'], + Digit8: ['ค', '๕', '', '', 0, 'VK_8'], + Digit9: ['ต', '๖', '', '', 0, 'VK_9'], + Digit0: ['จ', '๗', '', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['ข', '๘', '', '', 0, 'VK_OEM_MINUS'], + Equal: ['ช', '๙', '', '', 0, 'VK_OEM_PLUS'], + BracketLeft: ['บ', 'ฐ', '', '', 0, 'VK_OEM_4'], + BracketRight: ['ล', ',', '', '', 0, 'VK_OEM_6'], + Backslash: ['ฃ', 'ฅ', '', '', 0, 'VK_OEM_5'], + Semicolon: ['ว', 'ซ', '', '', 0, 'VK_OEM_1'], + Quote: ['ง', '.', '', '', 0, 'VK_OEM_7'], + Backquote: ['_', '%', '', '', 0, 'VK_OEM_3'], + Comma: ['ม', 'ฒ', '', '', 0, 'VK_OEM_COMMA'], + Period: ['ใ', 'ฬ', '', '', 0, 'VK_OEM_PERIOD'], + Slash: ['ฝ', 'ฦ', '', '', 0, 'VK_OEM_2'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['ฃ', 'ฅ', '', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/tr.win.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/tr.win.ts new file mode 100644 index 00000000000..955b03f5fc0 --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/tr.win.ts @@ -0,0 +1,168 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { name: '0000041F', id: '', text: 'Turkish Q' }, + secondaryLayouts: [], + mapping: { + Sleep: [], + WakeUp: [], + KeyA: ['a', 'A', 'æ', 'Æ', 0, 'VK_A'], + KeyB: ['b', 'B', '', '', 0, 'VK_B'], + KeyC: ['c', 'C', '', '', 0, 'VK_C'], + KeyD: ['d', 'D', '', '', 0, 'VK_D'], + KeyE: ['e', 'E', '€', '', 0, 'VK_E'], + KeyF: ['f', 'F', '', '', 0, 'VK_F'], + KeyG: ['g', 'G', '', '', 0, 'VK_G'], + KeyH: ['h', 'H', '', '', 0, 'VK_H'], + KeyI: ['ı', 'I', 'i', 'İ', 0, 'VK_I'], + KeyJ: ['j', 'J', '', '', 0, 'VK_J'], + KeyK: ['k', 'K', '', '', 0, 'VK_K'], + KeyL: ['l', 'L', '', '', 0, 'VK_L'], + KeyM: ['m', 'M', '', '', 0, 'VK_M'], + KeyN: ['n', 'N', '', '', 0, 'VK_N'], + KeyO: ['o', 'O', '', '', 0, 'VK_O'], + KeyP: ['p', 'P', '', '', 0, 'VK_P'], + KeyQ: ['q', 'Q', '@', '', 0, 'VK_Q'], + KeyR: ['r', 'R', '', '', 0, 'VK_R'], + KeyS: ['s', 'S', 'ß', '', 0, 'VK_S'], + KeyT: ['t', 'T', '₺', '', 0, 'VK_T'], + KeyU: ['u', 'U', '', '', 0, 'VK_U'], + KeyV: ['v', 'V', '', '', 0, 'VK_V'], + KeyW: ['w', 'W', '', '', 0, 'VK_W'], + KeyX: ['x', 'X', '', '', 0, 'VK_X'], + KeyY: ['y', 'Y', '', '', 0, 'VK_Y'], + KeyZ: ['z', 'Z', '', '', 0, 'VK_Z'], + Digit1: ['1', '!', '>', '', 0, 'VK_1'], + Digit2: ['2', '\'', '£', '', 0, 'VK_2'], + Digit3: ['3', '^', '#', '', 0, 'VK_3'], + Digit4: ['4', '+', '$', '', 0, 'VK_4'], + Digit5: ['5', '%', '½', '', 0, 'VK_5'], + Digit6: ['6', '&', '', '', 0, 'VK_6'], + Digit7: ['7', '/', '{', '', 0, 'VK_7'], + Digit8: ['8', '(', '[', '', 0, 'VK_8'], + Digit9: ['9', ')', ']', '', 0, 'VK_9'], + Digit0: ['0', '=', '}', '', 0, 'VK_0'], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', '', '', 0, 'VK_SPACE'], + Minus: ['*', '?', '\\', '', 0, 'VK_OEM_8'], + Equal: ['-', '_', '|', '', 0, 'VK_OEM_MINUS'], + BracketLeft: ['ğ', 'Ğ', '¨', '', 0, 'VK_OEM_4'], + BracketRight: ['ü', 'Ü', '~', '', 0, 'VK_OEM_6'], + Backslash: [',', ';', '`', '', 0, 'VK_OEM_COMMA'], + Semicolon: ['ş', 'Ş', '´', '', 0, 'VK_OEM_1'], + Quote: ['i', 'İ', '', '', 0, 'VK_OEM_7'], + Backquote: ['"', 'é', '<', '', 0, 'VK_OEM_3'], + Comma: ['ö', 'Ö', '', '', 0, 'VK_OEM_2'], + Period: ['ç', 'Ç', '', '', 0, 'VK_OEM_5'], + Slash: ['.', ':', '', '', 0, 'VK_OEM_PERIOD'], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + PrintScreen: [], + ScrollLock: [], + Pause: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '', '', 0, 'VK_DIVIDE'], + NumpadMultiply: ['*', '*', '', '', 0, 'VK_MULTIPLY'], + NumpadSubtract: ['-', '-', '', '', 0, 'VK_SUBTRACT'], + NumpadAdd: ['+', '+', '', '', 0, 'VK_ADD'], + NumpadEnter: [], + Numpad1: [], + Numpad2: [], + Numpad3: [], + Numpad4: [], + Numpad5: [], + Numpad6: [], + Numpad7: [], + Numpad8: [], + Numpad9: [], + Numpad0: [], + NumpadDecimal: [], + IntlBackslash: ['<', '>', '|', '', 0, 'VK_OEM_102'], + ContextMenu: [], + Power: [], + NumpadEqual: [], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + F21: [], + F22: [], + F23: [], + F24: [], + Help: [], + Undo: [], + Cut: [], + Copy: [], + Paste: [], + AudioVolumeMute: [], + AudioVolumeUp: [], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + Convert: [], + NonConvert: [], + Lang1: [], + Lang2: [], + Lang3: [], + Lang4: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [], + MediaTrackNext: [], + MediaTrackPrevious: [], + MediaStop: [], + Eject: [], + MediaPlayPause: [], + MediaSelect: [], + LaunchMail: [], + LaunchApp2: [], + LaunchApp1: [], + BrowserSearch: [], + BrowserHome: [], + BrowserBack: [], + BrowserForward: [], + BrowserStop: [], + BrowserRefresh: [], + BrowserFavorites: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keyboardLayouts/zh-hans.darwin.ts b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/zh-hans.darwin.ts new file mode 100644 index 00000000000..49d1f60ae0d --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keyboardLayouts/zh-hans.darwin.ts @@ -0,0 +1,131 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; + +KeyboardLayoutContribution.INSTANCE.registerKeyboardLayout({ + layout: { id: 'com.apple.inputmethod.SCIM.ITABC', lang: 'zh-Hans', localizedName: '搜狗拼音' }, + secondaryLayouts: [], + mapping: { + KeyA: ['a', 'A', 'å', 'Å', 0], + KeyB: ['b', 'B', '∫', 'ı', 0], + KeyC: ['c', 'C', 'ç', 'Ç', 0], + KeyD: ['d', 'D', '∂', 'Î', 0], + KeyE: ['e', 'E', '´', '´', 4], + KeyF: ['f', 'F', 'ƒ', 'Ï', 0], + KeyG: ['g', 'G', '©', '˝', 0], + KeyH: ['h', 'H', '˙', 'Ó', 0], + KeyI: ['i', 'I', 'ˆ', 'ˆ', 4], + KeyJ: ['j', 'J', '∆', 'Ô', 0], + KeyK: ['k', 'K', '˚', '', 0], + KeyL: ['l', 'L', '¬', 'Ò', 0], + KeyM: ['m', 'M', 'µ', 'Â', 0], + KeyN: ['n', 'N', '˜', '˜', 4], + KeyO: ['o', 'O', 'ø', 'Ø', 0], + KeyP: ['p', 'P', 'π', '∏', 0], + KeyQ: ['q', 'Q', 'œ', 'Œ', 0], + KeyR: ['r', 'R', '®', '‰', 0], + KeyS: ['s', 'S', 'ß', 'Í', 0], + KeyT: ['t', 'T', '†', 'ˇ', 0], + KeyU: ['u', 'U', '¨', '¨', 4], + KeyV: ['v', 'V', '√', '◊', 0], + KeyW: ['w', 'W', '∑', '„', 0], + KeyX: ['x', 'X', '≈', '˛', 0], + KeyY: ['y', 'Y', '¥', 'Á', 0], + KeyZ: ['z', 'Z', 'Ω', '¸', 0], + Digit1: ['1', '!', '¡', '⁄', 0], + Digit2: ['2', '@', '™', '€', 0], + Digit3: ['3', '#', '£', '‹', 0], + Digit4: ['4', '¥', '¢', '›', 0], + Digit5: ['5', '%', '∞', 'fi', 0], + Digit6: ['6', '', '§', 'fl', 0], + Digit7: ['7', '&', '¶', '‡', 0], + Digit8: ['8', '*', '•', '°', 0], + Digit9: ['9', '(', 'ª', '·', 0], + Digit0: ['0', ')', 'º', '‚', 0], + Enter: [], + Escape: [], + Backspace: [], + Tab: [], + Space: [' ', ' ', ' ', ' ', 0], + Minus: ['-', '', '–', '—', 0], + Equal: ['=', '+', '≠', '±', 0], + BracketLeft: ['【', '「', '“', '”', 0], + BracketRight: ['】', '」', '‘', '’', 0], + Backslash: ['、', '|', '«', '»', 0], + Semicolon: [';', ':', '…', 'Ú', 0], + Quote: ['\'', '"', 'æ', 'Æ', 0], + Backquote: ['·', '~', '`', '`', 4], + Comma: [',', '《', '≤', '¯', 0], + Period: ['。', '》', '≥', '˘', 0], + Slash: ['/', '?', '÷', '¿', 0], + CapsLock: [], + F1: [], + F2: [], + F3: [], + F4: [], + F5: [], + F6: [], + F7: [], + F8: [], + F9: [], + F10: [], + F11: [], + F12: [], + Insert: [], + Home: [], + PageUp: [], + Delete: [], + End: [], + PageDown: [], + ArrowRight: [], + ArrowLeft: [], + ArrowDown: [], + ArrowUp: [], + NumLock: [], + NumpadDivide: ['/', '/', '/', '/', 0], + NumpadMultiply: ['*', '*', '*', '*', 0], + NumpadSubtract: ['-', '-', '-', '-', 0], + NumpadAdd: ['+', '+', '+', '+', 0], + NumpadEnter: [], + Numpad1: ['1', '1', '1', '1', 0], + Numpad2: ['2', '2', '2', '2', 0], + Numpad3: ['3', '3', '3', '3', 0], + Numpad4: ['4', '4', '4', '4', 0], + Numpad5: ['5', '5', '5', '5', 0], + Numpad6: ['6', '6', '6', '6', 0], + Numpad7: ['7', '7', '7', '7', 0], + Numpad8: ['8', '8', '8', '8', 0], + Numpad9: ['9', '9', '9', '9', 0], + Numpad0: ['0', '0', '0', '0', 0], + NumpadDecimal: ['.', '.', '.', '.', 0], + IntlBackslash: ['§', '±', '§', '±', 0], + ContextMenu: [], + NumpadEqual: ['=', '=', '=', '=', 0], + F13: [], + F14: [], + F15: [], + F16: [], + F17: [], + F18: [], + F19: [], + F20: [], + AudioVolumeMute: [], + AudioVolumeUp: ['', '=', '', '=', 0], + AudioVolumeDown: [], + NumpadComma: [], + IntlRo: [], + KanaMode: [], + IntlYen: [], + ControlLeft: [], + ShiftLeft: [], + AltLeft: [], + MetaLeft: [], + ControlRight: [], + ShiftRight: [], + AltRight: [], + MetaRight: [] + } +}); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/browser/keymapService.ts b/src/vs/workbench/services/keybinding/browser/keymapService.ts new file mode 100644 index 00000000000..cff794267bf --- /dev/null +++ b/src/vs/workbench/services/keybinding/browser/keymapService.ts @@ -0,0 +1,634 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { Emitter, Event } from 'vs/base/common/event'; +import { Disposable, toDisposable, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { IKeymapService, IKeyboardLayoutInfo, IKeyboardMapping, IWindowsKeyboardMapping, KeymapInfo, IRawMixedKeyboardMapping, getKeyboardLayoutId, IKeymapInfo } from 'vs/workbench/services/keybinding/common/keymapInfo'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { DispatchConfig } from 'vs/workbench/services/keybinding/common/dispatchConfig'; +import { IKeyboardMapper, CachedKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; +import { OS, OperatingSystem, isMacintosh, isWindows } from 'vs/base/common/platform'; +import { WindowsKeyboardMapper } from 'vs/workbench/services/keybinding/common/windowsKeyboardMapper'; +import { MacLinuxFallbackKeyboardMapper } from 'vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper'; +import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; +import { IMacLinuxKeyboardMapping, MacLinuxKeyboardMapper } from 'vs/workbench/services/keybinding/common/macLinuxKeyboardMapper'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { URI } from 'vs/base/common/uri'; +import { IFileService, FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { dirname, isEqual } from 'vs/base/common/resources'; +import { parse } from 'vs/base/common/json'; +import * as objects from 'vs/base/common/objects'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as ConfigExtensions, IConfigurationRegistry, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { INavigatorWithKeyboard } from 'vs/workbench/services/keybinding/common/navigatorKeyboard'; + +export class BrowserKeyboardMapperFactoryBase { + // keyboard mapper + protected _initialized: boolean; + protected _keyboardMapper: IKeyboardMapper | null; + private readonly _onDidChangeKeyboardMapper = new Emitter(); + public readonly onDidChangeKeyboardMapper: Event = this._onDidChangeKeyboardMapper.event; + + // keymap infos + protected _keymapInfos: KeymapInfo[]; + protected _mru: KeymapInfo[]; + private _activeKeymapInfo: KeymapInfo | null; + + get activeKeymap(): KeymapInfo | null { + return this._activeKeymapInfo; + } + + get keymapInfos(): KeymapInfo[] { + return this._keymapInfos; + } + + get activeKeyboardLayout(): IKeyboardLayoutInfo | null { + if (!this._initialized) { + return null; + } + + return this._activeKeymapInfo && this._activeKeymapInfo.layout; + } + + get activeKeyMapping(): IKeyboardMapping | null { + if (!this._initialized) { + return null; + } + + return this._activeKeymapInfo && this._activeKeymapInfo.mapping; + } + + get keyboardLayouts(): IKeyboardLayoutInfo[] { + return this._keymapInfos.map(keymapInfo => keymapInfo.layout); + } + + protected constructor() { + this._keyboardMapper = null; + this._initialized = false; + this._keymapInfos = []; + this._mru = []; + this._activeKeymapInfo = null; + + if ((navigator).keyboard && (navigator).keyboard.addEventListener) { + (navigator).keyboard.addEventListener!('layoutchange', () => { + // Update user keyboard map settings + this._getBrowserKeyMapping().then((mapping: IKeyboardMapping | null) => { + if (this.isKeyMappingActive(mapping)) { + return; + } + + this.onKeyboardLayoutChanged(); + }); + }); + } + } + + registerKeyboardLayout(layout: KeymapInfo) { + this._keymapInfos.push(layout); + this._mru = this._keymapInfos; + } + + removeKeyboardLayout(layout: KeymapInfo): void { + let index = this._mru.indexOf(layout); + this._mru.splice(index, 1); + index = this._keymapInfos.indexOf(layout); + this._keymapInfos.splice(index, 1); + } + + getMatchedKeymapInfo(keyMapping: IKeyboardMapping | null): KeymapInfo | null { + if (!keyMapping) { + return null; + } + + let usStandard = this.getUSStandardLayout(); + + if (usStandard) { + let maxScore = usStandard.getScore(keyMapping); + if (maxScore === 0) { + return usStandard; + } + + let result = usStandard; + for (let i = 0; i < this._mru.length; i++) { + let score = this._mru[i].getScore(keyMapping); + if (score > maxScore) { + if (score === 0) { + return this._mru[i]; + } + + maxScore = score; + result = this._mru[i]; + } + } + + return result; + } + + for (let i = 0; i < this._mru.length; i++) { + if (this._mru[i].fuzzyEqual(keyMapping)) { + return this._mru[i]; + } + } + + return null; + } + + getUSStandardLayout() { + const usStandardLayouts = this._mru.filter(layout => layout.layout.isUSStandard); + + if (usStandardLayouts.length) { + return usStandardLayouts[0]; + } + + return null; + } + + isKeyMappingActive(keymap: IKeyboardMapping | null) { + return this._activeKeymapInfo && keymap && this._activeKeymapInfo.fuzzyEqual(keymap); + } + + setUSKeyboardLayout() { + this._activeKeymapInfo = this.getUSStandardLayout(); + } + + setActiveKeyMapping(keymap: IKeyboardMapping | null) { + let matchedKeyboardLayout = this.getMatchedKeymapInfo(keymap); + if (matchedKeyboardLayout) { + if (!this._activeKeymapInfo) { + this._activeKeymapInfo = matchedKeyboardLayout; + } else if (keymap) { + if (matchedKeyboardLayout.getScore(keymap) > this._activeKeymapInfo.getScore(keymap)) { + this._activeKeymapInfo = matchedKeyboardLayout; + } + } + } + + if (!this._activeKeymapInfo) { + this._activeKeymapInfo = this.getUSStandardLayout(); + } + + if (!this._activeKeymapInfo) { + return; + } + + const index = this._mru.indexOf(this._activeKeymapInfo); + + this._mru.splice(index, 1); + this._mru.unshift(this._activeKeymapInfo); + + this._setKeyboardData(this._activeKeymapInfo); + } + + setActiveKeymapInfo(keymapInfo: KeymapInfo) { + this._activeKeymapInfo = keymapInfo; + + const index = this._mru.indexOf(this._activeKeymapInfo); + + if (index === 0) { + return; + } + + this._mru.splice(index, 1); + this._mru.unshift(this._activeKeymapInfo); + + this._setKeyboardData(this._activeKeymapInfo); + } + + public onKeyboardLayoutChanged(): void { + this._updateKeyboardLayoutAsync(this._initialized); + } + + private _updateKeyboardLayoutAsync(initialized: boolean, keyboardEvent?: IKeyboardEvent) { + if (!initialized) { + return; + } + + this._getBrowserKeyMapping(keyboardEvent).then(keyMap => { + // might be false positive + if (this.isKeyMappingActive(keyMap)) { + return; + } + this.setActiveKeyMapping(keyMap); + }); + } + + public getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper { + if (!this._initialized) { + return new MacLinuxFallbackKeyboardMapper(OS); + } + if (dispatchConfig === DispatchConfig.KeyCode) { + // Forcefully set to use keyCode + return new MacLinuxFallbackKeyboardMapper(OS); + } + return this._keyboardMapper!; + } + + public validateCurrentKeyboardMapping(keyboardEvent: IKeyboardEvent): void { + if (!this._initialized) { + return; + } + + let isCurrentKeyboard = this._validateCurrentKeyboardMapping(keyboardEvent); + + if (isCurrentKeyboard) { + return; + } + + this._updateKeyboardLayoutAsync(true, keyboardEvent); + } + + public setKeyboardLayout(layoutName: string) { + let matchedLayouts: KeymapInfo[] = this.keymapInfos.filter(keymapInfo => getKeyboardLayoutId(keymapInfo.layout) === layoutName); + + if (matchedLayouts.length > 0) { + this.setActiveKeymapInfo(matchedLayouts[0]); + } + } + + private _setKeyboardData(keymapInfo: KeymapInfo): void { + this._initialized = true; + + this._keyboardMapper = new CachedKeyboardMapper(BrowserKeyboardMapperFactory._createKeyboardMapper(keymapInfo)); + this._onDidChangeKeyboardMapper.fire(); + } + + private static _createKeyboardMapper(keymapInfo: KeymapInfo): IKeyboardMapper { + let rawMapping = keymapInfo.mapping; + const isUSStandard = !!keymapInfo.layout.isUSStandard; + if (OS === OperatingSystem.Windows) { + return new WindowsKeyboardMapper(isUSStandard, rawMapping); + } + if (Object.keys(rawMapping).length === 0) { + // Looks like reading the mappings failed (most likely Mac + Japanese/Chinese keyboard layouts) + return new MacLinuxFallbackKeyboardMapper(OS); + } + + return new MacLinuxKeyboardMapper(isUSStandard, rawMapping, OS); + } + + //#region Browser API + private _validateCurrentKeyboardMapping(keyboardEvent: IKeyboardEvent): boolean { + if (!this._initialized) { + return true; + } + + const standardKeyboardEvent = keyboardEvent as StandardKeyboardEvent; + const currentKeymap = this._activeKeymapInfo; + if (!currentKeymap) { + return true; + } + + const mapping = currentKeymap.mapping[standardKeyboardEvent.code]; + + if (!mapping) { + return false; + } + + if (mapping.value === '') { + // we don't undetstand + if (keyboardEvent.ctrlKey || keyboardEvent.metaKey) { + setTimeout(() => { + this._getBrowserKeyMapping().then((keymap: IKeyboardMapping) => { + if (this.isKeyMappingActive(keymap)) { + return; + } + + this.onKeyboardLayoutChanged(); + }); + }, 350); + } + return true; + } + + const expectedValue = standardKeyboardEvent.altKey && standardKeyboardEvent.shiftKey ? mapping.withShiftAltGr : + standardKeyboardEvent.altKey ? mapping.withAltGr : + standardKeyboardEvent.shiftKey ? mapping.withShift : mapping.value; + + const isDead = (standardKeyboardEvent.altKey && standardKeyboardEvent.shiftKey && mapping.withShiftAltGrIsDeadKey) || + (standardKeyboardEvent.altKey && mapping.withAltGrIsDeadKey) || + (standardKeyboardEvent.shiftKey && mapping.withShiftIsDeadKey) || + mapping.valueIsDeadKey; + + if (isDead && standardKeyboardEvent.browserEvent.key !== 'Dead') { + return false; + } + + // TODO, this assumption is wrong as `browserEvent.key` doesn't necessarily equal expectedValue from real keymap + if (!isDead && standardKeyboardEvent.browserEvent.key !== expectedValue) { + return false; + } + + return true; + } + + private async _getBrowserKeyMapping(keyboardEvent?: IKeyboardEvent): Promise { + if ((navigator as any).keyboard) { + try { + return (navigator as any).keyboard.getLayoutMap().then((e: any) => { + let ret: IKeyboardMapping = {}; + for (let key of e) { + ret[key[0]] = { + 'value': key[1], + 'withShift': '', + 'withAltGr': '', + 'withShiftAltGr': '' + }; + } + + const matchedKeyboardLayout = this.getMatchedKeymapInfo(ret); + + if (matchedKeyboardLayout) { + return matchedKeyboardLayout.mapping; + } + + return null; + }); + } catch { + // getLayoutMap can throw if invoked from a nested browsing context + } + } else if (keyboardEvent && !keyboardEvent.shiftKey && !keyboardEvent.altKey && !keyboardEvent.metaKey && !keyboardEvent.metaKey) { + let ret: IKeyboardMapping = {}; + const standardKeyboardEvent = keyboardEvent as StandardKeyboardEvent; + ret[standardKeyboardEvent.browserEvent.code] = { + 'value': standardKeyboardEvent.browserEvent.key, + 'withShift': '', + 'withAltGr': '', + 'withShiftAltGr': '' + }; + + const matchedKeyboardLayout = this.getMatchedKeymapInfo(ret); + + if (matchedKeyboardLayout) { + return ret; + } + + return null; + } + + return null; + } + + //#endregion +} + +export class BrowserKeyboardMapperFactory extends BrowserKeyboardMapperFactoryBase { + public static readonly INSTANCE = new BrowserKeyboardMapperFactory(); + // keyboard mapper + + private constructor() { + super(); + + const platform = isWindows ? 'win' : isMacintosh ? 'darwin' : 'linux'; + + import('vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.' + platform).then((m) => { + let keymapInfos: IKeymapInfo[] = m.KeyboardLayoutContribution.INSTANCE.layoutInfos; + this._keymapInfos.push(...keymapInfos.map(info => (new KeymapInfo(info.layout, info.secondaryLayouts, info.mapping, info.isUserKeyboardLayout)))); + this._mru = this._keymapInfos; + this._initialized = true; + this.onKeyboardLayoutChanged(); + }); + } +} + +class UserKeyboardLayout extends Disposable { + private readonly reloadConfigurationScheduler: RunOnceScheduler; + protected readonly _onDidChange: Emitter = this._register(new Emitter()); + readonly onDidChange: Event = this._onDidChange.event; + + private fileWatcherDisposable: IDisposable = Disposable.None; + private directoryWatcherDisposable: IDisposable = Disposable.None; + + private _keyboardLayout: KeymapInfo | null; + get keyboardLayout(): KeymapInfo | null { return this._keyboardLayout; } + + constructor( + private readonly keyboardLayoutResource: URI, + private readonly fileService: IFileService + ) { + super(); + + this._keyboardLayout = null; + + this._register(fileService.onFileChanges(e => this.handleFileEvents(e))); + this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(changed => { + if (changed) { + this._onDidChange.fire(); + } + }), 50)); + + this._register(toDisposable(() => { + this.stopWatchingResource(); + this.stopWatchingDirectory(); + })); + } + + async initialize(): Promise { + const exists = await this.fileService.exists(this.keyboardLayoutResource); + this.onResourceExists(exists); + await this.reload(); + } + + private async reload(): Promise { + const existing = this._keyboardLayout; + try { + const content = await this.fileService.readFile(this.keyboardLayoutResource); + const value = parse(content.value.toString()); + const layoutInfo = value.layout; + const mappings = value.rawMapping; + this._keyboardLayout = KeymapInfo.createKeyboardLayoutFromDebugInfo(layoutInfo, mappings, true); + } catch (e) { + this._keyboardLayout = null; + } + + return existing ? !objects.equals(existing, this._keyboardLayout) : true; + } + + private watchResource(): void { + this.fileWatcherDisposable = this.fileService.watch(this.keyboardLayoutResource); + } + + private watchDirectory(): void { + const directory = dirname(this.keyboardLayoutResource); + this.directoryWatcherDisposable = this.fileService.watch(directory); + } + + private stopWatchingResource(): void { + this.fileWatcherDisposable.dispose(); + this.fileWatcherDisposable = Disposable.None; + } + + private stopWatchingDirectory(): void { + this.directoryWatcherDisposable.dispose(); + this.directoryWatcherDisposable = Disposable.None; + } + + private async handleFileEvents(event: FileChangesEvent): Promise { + const events = event.changes; + + let affectedByChanges = false; + + // Find changes that affect the resource + for (const event of events) { + affectedByChanges = isEqual(this.keyboardLayoutResource, event.resource); + if (affectedByChanges) { + if (event.type === FileChangeType.ADDED) { + this.onResourceExists(true); + } else if (event.type === FileChangeType.DELETED) { + this.onResourceExists(false); + } + break; + } + } + + if (affectedByChanges) { + this.reloadConfigurationScheduler.schedule(); + } + } + + private onResourceExists(exists: boolean): void { + if (exists) { + this.stopWatchingDirectory(); + this.watchResource(); + } else { + this.stopWatchingResource(); + this.watchDirectory(); + } + } +} + +class BrowserKeymapService extends Disposable implements IKeymapService { + public _serviceBrand: any; + + private readonly _onDidChangeKeyboardMapper = new Emitter(); + public readonly onDidChangeKeyboardMapper: Event = this._onDidChangeKeyboardMapper.event; + + private _userKeyboardLayout: UserKeyboardLayout; + + private readonly layoutChangeListener = this._register(new MutableDisposable()); + + constructor( + @IEnvironmentService environmentService: IEnvironmentService, + @IFileService fileService: IFileService, + @IConfigurationService private configurationService: IConfigurationService, + ) { + super(); + const keyboardConfig = configurationService.getValue<{ layout: string }>('keyboard'); + const layout = keyboardConfig.layout; + + this.registerKeyboardListener(); + + if (layout && layout !== 'autodetect') { + // set keyboard layout + BrowserKeyboardMapperFactory.INSTANCE.setKeyboardLayout(layout); + } + + this._register(configurationService.onDidChangeConfiguration(e => { + if (e.affectedKeys.indexOf('keyboard.layout') >= 0) { + const keyboardConfig = configurationService.getValue<{ layout: string }>('keyboard'); + const layout = keyboardConfig.layout; + + if (layout === 'autodetect') { + this.registerKeyboardListener(); + BrowserKeyboardMapperFactory.INSTANCE.onKeyboardLayoutChanged(); + } else { + BrowserKeyboardMapperFactory.INSTANCE.setKeyboardLayout(layout); + this.layoutChangeListener.clear(); + } + } + })); + + this._userKeyboardLayout = new UserKeyboardLayout(environmentService.keyboardLayoutResource, fileService); + this._userKeyboardLayout.initialize().then(() => { + if (this._userKeyboardLayout.keyboardLayout) { + BrowserKeyboardMapperFactory.INSTANCE.registerKeyboardLayout(this._userKeyboardLayout.keyboardLayout); + + this.setUserKeyboardLayoutIfMatched(); + } + }); + + this._register(this._userKeyboardLayout.onDidChange(() => { + let userKeyboardLayouts = BrowserKeyboardMapperFactory.INSTANCE.keymapInfos.filter(layout => layout.isUserKeyboardLayout); + + if (userKeyboardLayouts.length) { + if (this._userKeyboardLayout.keyboardLayout) { + userKeyboardLayouts[0].update(this._userKeyboardLayout.keyboardLayout); + } else { + BrowserKeyboardMapperFactory.INSTANCE.removeKeyboardLayout(userKeyboardLayouts[0]); + } + } else { + if (this._userKeyboardLayout.keyboardLayout) { + BrowserKeyboardMapperFactory.INSTANCE.registerKeyboardLayout(this._userKeyboardLayout.keyboardLayout); + } + } + + this.setUserKeyboardLayoutIfMatched(); + })); + } + + setUserKeyboardLayoutIfMatched() { + const keyboardConfig = this.configurationService.getValue<{ layout: string }>('keyboard'); + const layout = keyboardConfig.layout; + + if (layout && this._userKeyboardLayout.keyboardLayout) { + if (getKeyboardLayoutId(this._userKeyboardLayout.keyboardLayout.layout) === layout && BrowserKeyboardMapperFactory.INSTANCE.activeKeymap) { + + if (!this._userKeyboardLayout.keyboardLayout.equal(BrowserKeyboardMapperFactory.INSTANCE.activeKeymap)) { + BrowserKeyboardMapperFactory.INSTANCE.setActiveKeymapInfo(this._userKeyboardLayout.keyboardLayout); + } + } + } + } + + registerKeyboardListener() { + this.layoutChangeListener.value = BrowserKeyboardMapperFactory.INSTANCE.onDidChangeKeyboardMapper(() => { + this._onDidChangeKeyboardMapper.fire(); + }); + } + + getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper { + return BrowserKeyboardMapperFactory.INSTANCE.getKeyboardMapper(dispatchConfig); + } + + public getCurrentKeyboardLayout(): IKeyboardLayoutInfo | null { + return BrowserKeyboardMapperFactory.INSTANCE.activeKeyboardLayout; + } + + public getAllKeyboardLayouts(): IKeyboardLayoutInfo[] { + return BrowserKeyboardMapperFactory.INSTANCE.keyboardLayouts; + } + + public getRawKeyboardMapping(): IKeyboardMapping | null { + return BrowserKeyboardMapperFactory.INSTANCE.activeKeyMapping; + } + + public validateCurrentKeyboardMapping(keyboardEvent: IKeyboardEvent): void { + BrowserKeyboardMapperFactory.INSTANCE.validateCurrentKeyboardMapping(keyboardEvent); + } +} + +registerSingleton(IKeymapService, BrowserKeymapService, true); + +// Configuration +const configurationRegistry = Registry.as(ConfigExtensions.Configuration); +const keyboardConfiguration: IConfigurationNode = { + 'id': 'keyboard', + 'order': 15, + 'type': 'object', + 'title': nls.localize('keyboardConfigurationTitle', "Keyboard"), + 'overridable': true, + 'properties': { + 'keyboard.layout': { + 'type': 'string', + 'default': 'autodetect', + 'description': nls.localize('keyboard.layout.config', "Control the keyboard layout used in web.") + } + } +}; + +configurationRegistry.registerConfiguration(keyboardConfiguration); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/common/dispatchConfig.ts b/src/vs/workbench/services/keybinding/common/dispatchConfig.ts new file mode 100644 index 00000000000..a92871c7e96 --- /dev/null +++ b/src/vs/workbench/services/keybinding/common/dispatchConfig.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + +export const enum DispatchConfig { + Code, + KeyCode +} + +export function getDispatchConfig(configurationService: IConfigurationService): DispatchConfig { + const keyboard = configurationService.getValue('keyboard'); + const r = (keyboard ? (keyboard).dispatch : null); + return (r === 'keyCode' ? DispatchConfig.KeyCode : DispatchConfig.Code); +} \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts index ffc63601409..49b53c2d234 100644 --- a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts +++ b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts @@ -44,7 +44,7 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding public _serviceBrand: any; private queue: Queue; - private resource: URI = URI.file(this.environmentService.appKeybindingsPath); + private resource: URI = this.environmentService.keybindingsResource; constructor( @ITextModelService private readonly textModelResolverService: ITextModelService, @@ -210,7 +210,7 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding private resolveModelReference(): Promise> { return this.fileService.exists(this.resource) .then(exists => { - const EOL = this.configurationService.getValue('files', { overrideIdentifier: 'json' })['eol']; + const EOL = this.configurationService.getValue<{}>('files', { overrideIdentifier: 'json' })['eol']; const result: Promise = exists ? Promise.resolve(null) : this.textFileService.write(this.resource, this.getEmptyContent(EOL), { encoding: 'utf8' }); return result.then(() => this.textModelResolverService.createModelReference(this.resource)); }); diff --git a/src/vs/workbench/services/keybinding/common/keymapInfo.ts b/src/vs/workbench/services/keybinding/common/keymapInfo.ts new file mode 100644 index 00000000000..edea5fd675c --- /dev/null +++ b/src/vs/workbench/services/keybinding/common/keymapInfo.ts @@ -0,0 +1,342 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from 'vs/base/common/event'; +import { isWindows, isLinux } from 'vs/base/common/platform'; +import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { DispatchConfig } from 'vs/workbench/services/keybinding/common/dispatchConfig'; +import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; +import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; + + +export interface IWindowsKeyMapping { + vkey: string; + value: string; + withShift: string; + withAltGr: string; + withShiftAltGr: string; +} +export interface IWindowsKeyboardMapping { + [code: string]: IWindowsKeyMapping; +} +export interface ILinuxKeyMapping { + value: string; + withShift: string; + withAltGr: string; + withShiftAltGr: string; +} +export interface ILinuxKeyboardMapping { + [code: string]: ILinuxKeyMapping; +} +export interface IMacKeyMapping { + value: string; + withShift: string; + withAltGr: string; + withShiftAltGr: string; + valueIsDeadKey: boolean; + withShiftIsDeadKey: boolean; + withAltGrIsDeadKey: boolean; + withShiftAltGrIsDeadKey: boolean; +} +export interface IMacKeyboardMapping { + [code: string]: IMacKeyMapping; +} + +export type IKeyboardMapping = IWindowsKeyboardMapping | ILinuxKeyboardMapping | IMacKeyboardMapping; + +/* __GDPR__FRAGMENT__ + "IKeyboardLayoutInfo" : { + "name" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "id": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "text": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } +*/ +export interface IWindowsKeyboardLayoutInfo { + name: string; + id: string; + text: string; +} + +/* __GDPR__FRAGMENT__ + "IKeyboardLayoutInfo" : { + "model" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "layout": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "variant": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "options": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "rules": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } +*/ +export interface ILinuxKeyboardLayoutInfo { + model: string; + layout: string; + variant: string; + options: string; + rules: string; +} + +/* __GDPR__FRAGMENT__ + "IKeyboardLayoutInfo" : { + "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "lang": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "localizedName": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } +*/ +export interface IMacKeyboardLayoutInfo { + id: string; + lang: string; + localizedName?: string; +} + +export type IKeyboardLayoutInfo = (IWindowsKeyboardLayoutInfo | ILinuxKeyboardLayoutInfo | IMacKeyboardLayoutInfo) & { isUserKeyboardLayout?: boolean; isUSStandard?: true }; + +export const IKeymapService = createDecorator('keymapService'); + +export interface IKeymapService { + _serviceBrand: ServiceIdentifier; + onDidChangeKeyboardMapper: Event; + getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper; + getCurrentKeyboardLayout(): IKeyboardLayoutInfo | null; + getAllKeyboardLayouts(): IKeyboardLayoutInfo[]; + getRawKeyboardMapping(): IKeyboardMapping | null; + validateCurrentKeyboardMapping(keyboardEvent: IKeyboardEvent): void; +} + +export function areKeyboardLayoutsEqual(a: IKeyboardLayoutInfo | null, b: IKeyboardLayoutInfo | null): boolean { + if (!a || !b) { + return false; + } + + if ((a).name && (b).name && (a).name === (b).name) { + return true; + } + + if ((a).id && (b).id && (a).id === (b).id) { + return true; + } + + if ((a).model && + (b).model && + (a).model === (b).model && + (a).layout === (b).layout + ) { + return true; + } + + return false; +} + +export function parseKeyboardLayoutDescription(layout: IKeyboardLayoutInfo | null): { label: string, description: string } { + if (!layout) { + return { label: '', description: '' }; + } + + if ((layout).name) { + // windows + let windowsLayout = layout; + return { + label: windowsLayout.text, + description: '' + }; + } + + if ((layout).id) { + let macLayout = layout; + if (macLayout.localizedName) { + return { + label: macLayout.localizedName, + description: '' + }; + } + + if (/^com\.apple\.keylayout\./.test(macLayout.id)) { + return { + label: macLayout.id.replace(/^com\.apple\.keylayout\./, '').replace(/-/, ' '), + description: '' + }; + } + if (/^.*inputmethod\./.test(macLayout.id)) { + return { + label: macLayout.id.replace(/^.*inputmethod\./, '').replace(/[-\.]/, ' '), + description: `Input Method (${macLayout.lang})` + }; + } + + return { + label: macLayout.lang, + description: '' + }; + } + + let linuxLayout = layout; + + return { + label: linuxLayout.layout, + description: '' + }; +} + +export function getKeyboardLayoutId(layout: IKeyboardLayoutInfo): string { + if ((layout).name) { + return (layout).name; + } + + if ((layout).id) { + return (layout).id; + } + + return (layout).layout; +} + +function deserializeMapping(serializedMapping: ISerializedMapping) { + let mapping = serializedMapping; + + let ret = {}; + for (let key in mapping) { + let result: (string | number)[] = mapping[key]; + if (result.length) { + let value = result[0]; + let withShift = result[1]; + let withAltGr = result[2]; + let withShiftAltGr = result[3]; + let mask = Number(result[4]); + let vkey = result.length === 6 ? result[5] : undefined; + ret[key] = { + 'value': value, + 'vkey': vkey, + 'withShift': withShift, + 'withAltGr': withAltGr, + 'withShiftAltGr': withShiftAltGr, + 'valueIsDeadKey': (mask & 1) > 0, + 'withShiftIsDeadKey': (mask & 2) > 0, + 'withAltGrIsDeadKey': (mask & 4) > 0, + 'withShiftAltGrIsDeadKey': (mask & 8) > 0 + }; + } else { + ret[key] = { + 'value': '', + 'valueIsDeadKey': false, + 'withShift': '', + 'withShiftIsDeadKey': false, + 'withAltGr': '', + 'withAltGrIsDeadKey': false, + 'withShiftAltGr': '', + 'withShiftAltGrIsDeadKey': false + }; + } + } + + return ret; +} + +export interface IRawMixedKeyboardMapping { + [key: string]: { + value: string, + withShift: string; + withAltGr: string; + withShiftAltGr: string; + valueIsDeadKey?: boolean; + withShiftIsDeadKey?: boolean; + withAltGrIsDeadKey?: boolean; + withShiftAltGrIsDeadKey?: boolean; + + }; +} + +interface ISerializedMapping { + [key: string]: (string | number)[]; +} + +export interface IKeymapInfo { + layout: IKeyboardLayoutInfo; + secondaryLayouts: IKeyboardLayoutInfo[]; + mapping: ISerializedMapping; + isUserKeyboardLayout?: boolean; +} + +export class KeymapInfo { + mapping: IRawMixedKeyboardMapping; + isUserKeyboardLayout: boolean; + + constructor(public layout: IKeyboardLayoutInfo, public secondaryLayouts: IKeyboardLayoutInfo[], keyboardMapping: ISerializedMapping, isUserKeyboardLayout?: boolean) { + this.mapping = deserializeMapping(keyboardMapping); + this.isUserKeyboardLayout = !!isUserKeyboardLayout; + this.layout.isUserKeyboardLayout = !!isUserKeyboardLayout; + } + + static createKeyboardLayoutFromDebugInfo(layout: IKeyboardLayoutInfo, value: IRawMixedKeyboardMapping, isUserKeyboardLayout?: boolean): KeymapInfo { + let keyboardLayoutInfo = new KeymapInfo(layout, [], {}, true); + keyboardLayoutInfo.mapping = value; + return keyboardLayoutInfo; + } + + update(other: KeymapInfo) { + this.layout = other.layout; + this.secondaryLayouts = other.secondaryLayouts; + this.mapping = other.mapping; + this.isUserKeyboardLayout = other.isUserKeyboardLayout; + this.layout.isUserKeyboardLayout = other.isUserKeyboardLayout; + } + + getScore(other: IRawMixedKeyboardMapping): number { + let score = 0; + for (let key in other) { + if (isWindows && (key === 'Backslash' || key === 'KeyQ')) { + // keymap from Chromium is probably wrong. + continue; + } + + if (isLinux && (key === 'Backspace' || key === 'Escape')) { + // native keymap doesn't align with keyboard event + continue; + } + + if (this.mapping[key] === undefined) { + score -= 1; + } + + let currentMapping = this.mapping[key]; + let otherMapping = other[key]; + + if (currentMapping.value !== otherMapping.value) { + score -= 1; + } + } + + return score; + } + + equal(other: KeymapInfo): boolean { + if (this.isUserKeyboardLayout !== other.isUserKeyboardLayout) { + return false; + } + + if (getKeyboardLayoutId(this.layout) !== getKeyboardLayoutId(other.layout)) { + return false; + } + + return this.fuzzyEqual(other.mapping); + } + + fuzzyEqual(other: IRawMixedKeyboardMapping): boolean { + for (let key in other) { + if (isWindows && (key === 'Backslash' || key === 'KeyQ')) { + // keymap from Chromium is probably wrong. + continue; + } + if (this.mapping[key] === undefined) { + return false; + } + + let currentMapping = this.mapping[key]; + let otherMapping = other[key]; + + if (currentMapping.value !== otherMapping.value) { + return false; + } + } + + return true; + } +} diff --git a/src/vs/workbench/services/keybinding/common/navigatorKeyboard.ts b/src/vs/workbench/services/keybinding/common/navigatorKeyboard.ts new file mode 100644 index 00000000000..51e8de9f790 --- /dev/null +++ b/src/vs/workbench/services/keybinding/common/navigatorKeyboard.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export interface IKeyboard { + getLayoutMap(): Promise; + lock(keyCodes?: string[]): Promise; + unlock(): void; + addEventListener?(type: string, listener: () => void): void; + +} +export type INavigatorWithKeyboard = Navigator & { + keyboard: IKeyboard +}; \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybinding.contribution.ts b/src/vs/workbench/services/keybinding/electron-browser/keybinding.contribution.ts new file mode 100644 index 00000000000..eec3ac01744 --- /dev/null +++ b/src/vs/workbench/services/keybinding/electron-browser/keybinding.contribution.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { release } from 'os'; +import { OS, OperatingSystem } from 'vs/base/common/platform'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as ConfigExtensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; + +const configurationRegistry = Registry.as(ConfigExtensions.Configuration); +const keyboardConfiguration: IConfigurationNode = { + 'id': 'keyboard', + 'order': 15, + 'type': 'object', + 'title': nls.localize('keyboardConfigurationTitle', "Keyboard"), + 'overridable': true, + 'properties': { + 'keyboard.touchbar.enabled': { + 'type': 'boolean', + 'default': true, + 'description': nls.localize('touchbar.enabled', "Enables the macOS touchbar buttons on the keyboard if available."), + 'included': OS === OperatingSystem.Macintosh && parseFloat(release()) >= 16 // Minimum: macOS Sierra (10.12.x = darwin 16.x) + } + } +}; + +configurationRegistry.registerConfiguration(keyboardConfiguration); \ No newline at end of file diff --git a/src/vs/workbench/services/keybinding/electron-browser/nativeKeymapService.ts b/src/vs/workbench/services/keybinding/electron-browser/nativeKeymapService.ts new file mode 100644 index 00000000000..475c881af9f --- /dev/null +++ b/src/vs/workbench/services/keybinding/electron-browser/nativeKeymapService.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nativeKeymap from 'native-keymap'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IKeymapService, IKeyboardLayoutInfo, IKeyboardMapping } from 'vs/workbench/services/keybinding/common/keymapInfo'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IKeyboardMapper, CachedKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper'; +import { Emitter, Event } from 'vs/base/common/event'; +import { DispatchConfig } from 'vs/workbench/services/keybinding/common/dispatchConfig'; +import { MacLinuxFallbackKeyboardMapper } from 'vs/workbench/services/keybinding/common/macLinuxFallbackKeyboardMapper'; +import { OS, OperatingSystem } from 'vs/base/common/platform'; +import { WindowsKeyboardMapper, windowsKeyboardMappingEquals } from 'vs/workbench/services/keybinding/common/windowsKeyboardMapper'; +import { MacLinuxKeyboardMapper, macLinuxKeyboardMappingEquals, IMacLinuxKeyboardMapping } from 'vs/workbench/services/keybinding/common/macLinuxKeyboardMapper'; +import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; + +export class KeyboardMapperFactory { + public static readonly INSTANCE = new KeyboardMapperFactory(); + + private _layoutInfo: nativeKeymap.IKeyboardLayoutInfo | null; + private _rawMapping: nativeKeymap.IKeyboardMapping | null; + private _keyboardMapper: IKeyboardMapper | null; + private _initialized: boolean; + + private readonly _onDidChangeKeyboardMapper = new Emitter(); + public readonly onDidChangeKeyboardMapper: Event = this._onDidChangeKeyboardMapper.event; + + private constructor() { + this._layoutInfo = null; + this._rawMapping = null; + this._keyboardMapper = null; + this._initialized = false; + } + + public _onKeyboardLayoutChanged(): void { + if (this._initialized) { + this._setKeyboardData(nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); + } + } + + public getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper { + if (!this._initialized) { + this._setKeyboardData(nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); + } + if (dispatchConfig === DispatchConfig.KeyCode) { + // Forcefully set to use keyCode + return new MacLinuxFallbackKeyboardMapper(OS); + } + return this._keyboardMapper!; + } + + public getCurrentKeyboardLayout(): nativeKeymap.IKeyboardLayoutInfo | null { + if (!this._initialized) { + this._setKeyboardData(nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); + } + return this._layoutInfo; + } + + private static _isUSStandard(_kbInfo: nativeKeymap.IKeyboardLayoutInfo): boolean { + if (OS === OperatingSystem.Linux) { + const kbInfo = _kbInfo; + return (kbInfo && kbInfo.layout === 'us'); + } + + if (OS === OperatingSystem.Macintosh) { + const kbInfo = _kbInfo; + return (kbInfo && kbInfo.id === 'com.apple.keylayout.US'); + } + + if (OS === OperatingSystem.Windows) { + const kbInfo = _kbInfo; + return (kbInfo && kbInfo.name === '00000409'); + } + + return false; + } + + public getRawKeyboardMapping(): nativeKeymap.IKeyboardMapping | null { + if (!this._initialized) { + this._setKeyboardData(nativeKeymap.getCurrentKeyboardLayout(), nativeKeymap.getKeyMap()); + } + return this._rawMapping; + } + + private _setKeyboardData(layoutInfo: nativeKeymap.IKeyboardLayoutInfo, rawMapping: nativeKeymap.IKeyboardMapping): void { + this._layoutInfo = layoutInfo; + + if (this._initialized && KeyboardMapperFactory._equals(this._rawMapping, rawMapping)) { + // nothing to do... + return; + } + + this._initialized = true; + this._rawMapping = rawMapping; + this._keyboardMapper = new CachedKeyboardMapper( + KeyboardMapperFactory._createKeyboardMapper(this._layoutInfo, this._rawMapping) + ); + this._onDidChangeKeyboardMapper.fire(); + } + + private static _createKeyboardMapper(layoutInfo: nativeKeymap.IKeyboardLayoutInfo, rawMapping: nativeKeymap.IKeyboardMapping): IKeyboardMapper { + const isUSStandard = KeyboardMapperFactory._isUSStandard(layoutInfo); + if (OS === OperatingSystem.Windows) { + return new WindowsKeyboardMapper(isUSStandard, rawMapping); + } + + if (Object.keys(rawMapping).length === 0) { + // Looks like reading the mappings failed (most likely Mac + Japanese/Chinese keyboard layouts) + return new MacLinuxFallbackKeyboardMapper(OS); + } + + if (OS === OperatingSystem.Macintosh) { + const kbInfo = layoutInfo; + if (kbInfo.id === 'com.apple.keylayout.DVORAK-QWERTYCMD') { + // Use keyCode based dispatching for DVORAK - QWERTY ⌘ + return new MacLinuxFallbackKeyboardMapper(OS); + } + } + + return new MacLinuxKeyboardMapper(isUSStandard, rawMapping, OS); + } + + private static _equals(a: nativeKeymap.IKeyboardMapping | null, b: nativeKeymap.IKeyboardMapping | null): boolean { + if (OS === OperatingSystem.Windows) { + return windowsKeyboardMappingEquals(a, b); + } + + return macLinuxKeyboardMappingEquals(a, b); + } +} + +class NativeKeymapService extends Disposable implements IKeymapService { + public _serviceBrand: any; + + private readonly _onDidChangeKeyboardMapper = new Emitter(); + public readonly onDidChangeKeyboardMapper: Event = this._onDidChangeKeyboardMapper.event; + + constructor() { + super(); + + this._register(KeyboardMapperFactory.INSTANCE.onDidChangeKeyboardMapper(() => { + this._onDidChangeKeyboardMapper.fire(); + })); + } + + getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper { + return KeyboardMapperFactory.INSTANCE.getKeyboardMapper(dispatchConfig); + } + + public getCurrentKeyboardLayout(): IKeyboardLayoutInfo | null { + return KeyboardMapperFactory.INSTANCE.getCurrentKeyboardLayout(); + } + + getAllKeyboardLayouts(): IKeyboardLayoutInfo[] { + return []; + } + + public getRawKeyboardMapping(): IKeyboardMapping | null { + return KeyboardMapperFactory.INSTANCE.getRawKeyboardMapping(); + } + + public validateCurrentKeyboardMapping(keyboardEvent: IKeyboardEvent): void { + return; + } +} + +registerSingleton(IKeymapService, NativeKeymapService, true); diff --git a/src/vs/workbench/services/keybinding/test/browserKeyboardMapper.test.ts b/src/vs/workbench/services/keybinding/test/browserKeyboardMapper.test.ts new file mode 100644 index 00000000000..8c06d950aed --- /dev/null +++ b/src/vs/workbench/services/keybinding/test/browserKeyboardMapper.test.ts @@ -0,0 +1,135 @@ +/*--------------------------------------------------------------------------------------------- + * 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 'vs/workbench/services/keybinding/browser/keyboardLayouts/en.darwin'; // 15% +import 'vs/workbench/services/keybinding/browser/keyboardLayouts/de.darwin'; +import { KeyboardLayoutContribution } from 'vs/workbench/services/keybinding/browser/keyboardLayouts/_.contribution'; +import { BrowserKeyboardMapperFactoryBase } from '../browser/keymapService'; +import { KeymapInfo, IKeymapInfo } from '../common/keymapInfo'; + +class TestKeyboardMapperFactory extends BrowserKeyboardMapperFactoryBase { + constructor() { + super(); + + let keymapInfos: IKeymapInfo[] = KeyboardLayoutContribution.INSTANCE.layoutInfos; + this._keymapInfos.push(...keymapInfos.map(info => (new KeymapInfo(info.layout, info.secondaryLayouts, info.mapping, info.isUserKeyboardLayout)))); + this._mru = this._keymapInfos; + this._initialized = true; + this.onKeyboardLayoutChanged(); + } +} + + +suite('keyboard layout loader', () => { + const instance: TestKeyboardMapperFactory = new TestKeyboardMapperFactory(); + + test('load default US keyboard layout', () => { + assert.notEqual(instance.activeKeyboardLayout, null); + assert.equal(instance.activeKeyboardLayout!.isUSStandard, true); + }); + + test('isKeyMappingActive', () => { + assert.equal(instance.isKeyMappingActive({ + KeyA: { + value: 'a', + valueIsDeadKey: false, + withShift: 'A', + withShiftIsDeadKey: false, + withAltGr: 'å', + withAltGrIsDeadKey: false, + withShiftAltGr: 'Å', + withShiftAltGrIsDeadKey: false + } + }), true); + + assert.equal(instance.isKeyMappingActive({ + KeyA: { + value: 'a', + valueIsDeadKey: false, + withShift: 'A', + withShiftIsDeadKey: false, + withAltGr: 'å', + withAltGrIsDeadKey: false, + withShiftAltGr: 'Å', + withShiftAltGrIsDeadKey: false + }, + KeyZ: { + value: 'z', + valueIsDeadKey: false, + withShift: 'Z', + withShiftIsDeadKey: false, + withAltGr: 'Ω', + withAltGrIsDeadKey: false, + withShiftAltGr: '¸', + withShiftAltGrIsDeadKey: false + } + }), true); + + assert.equal(instance.isKeyMappingActive({ + KeyZ: { + value: 'y', + valueIsDeadKey: false, + withShift: 'Y', + withShiftIsDeadKey: false, + withAltGr: '¥', + withAltGrIsDeadKey: false, + withShiftAltGr: 'Ÿ', + withShiftAltGrIsDeadKey: false + }, + }), false); + + }); + + test('Switch keymapping', () => { + instance.setActiveKeyMapping({ + KeyZ: { + value: 'y', + valueIsDeadKey: false, + withShift: 'Y', + withShiftIsDeadKey: false, + withAltGr: '¥', + withAltGrIsDeadKey: false, + withShiftAltGr: 'Ÿ', + withShiftAltGrIsDeadKey: false + } + }); + assert.equal(!!instance.activeKeyboardLayout!.isUSStandard, false); + assert.equal(instance.isKeyMappingActive({ + KeyZ: { + value: 'y', + valueIsDeadKey: false, + withShift: 'Y', + withShiftIsDeadKey: false, + withAltGr: '¥', + withAltGrIsDeadKey: false, + withShiftAltGr: 'Ÿ', + withShiftAltGrIsDeadKey: false + }, + }), true); + + instance.setUSKeyboardLayout(); + assert.equal(instance.activeKeyboardLayout!.isUSStandard, true); + }); + + test('Switch keyboard layout info', () => { + instance.setKeyboardLayout('com.apple.keylayout.German'); + assert.equal(!!instance.activeKeyboardLayout!.isUSStandard, false); + assert.equal(instance.isKeyMappingActive({ + KeyZ: { + value: 'y', + valueIsDeadKey: false, + withShift: 'Y', + withShiftIsDeadKey: false, + withAltGr: '¥', + withAltGrIsDeadKey: false, + withShiftAltGr: 'Ÿ', + withShiftAltGrIsDeadKey: false + }, + }), true); + + instance.setUSKeyboardLayout(); + assert.equal(instance.activeKeyboardLayout!.isUSStandard, true); + }); +}); \ No newline at end of file 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 c9327d4205b..255ec9727fc 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 @@ -40,10 +40,11 @@ import { KeybindingsEditingService } from 'vs/workbench/services/keybinding/comm import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; -import { TestBackupFileService, TestContextService, TestEditorGroupsService, TestEditorService, TestLifecycleService, TestLogService, TestTextFileService, TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices'; +import { TestBackupFileService, TestContextService, TestEditorGroupsService, TestEditorService, TestLifecycleService, TestTextFileService, TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices'; import { FileService } from 'vs/workbench/services/files/common/fileService'; import { Schemas } from 'vs/base/common/network'; import { DiskFileSystemProvider } from 'vs/workbench/services/files/node/diskFileSystemProvider'; +import { URI } from 'vs/base/common/uri'; interface Modifiers { metaKey?: boolean; @@ -65,7 +66,7 @@ suite('KeybindingsEditing', () => { instantiationService = new TestInstantiationService(); - instantiationService.stub(IEnvironmentService, { appKeybindingsPath: keybindingsFile, appSettingsPath: path.join(testDir, 'settings.json') }); + instantiationService.stub(IEnvironmentService, { keybindingsResource: URI.file(keybindingsFile), settingsResource: URI.file(path.join(testDir, 'settings.json')) }); instantiationService.stub(IConfigurationService, ConfigurationService); instantiationService.stub(IConfigurationService, 'getValue', { 'eol': '\n' }); instantiationService.stub(IConfigurationService, 'onDidUpdateConfiguration', () => { }); @@ -78,7 +79,7 @@ suite('KeybindingsEditing', () => { instantiationService.stub(IEditorService, new TestEditorService()); instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(IModeService, ModeServiceImpl); - instantiationService.stub(ILogService, new TestLogService()); + instantiationService.stub(ILogService, new NullLogService()); instantiationService.stub(ITextResourcePropertiesService, new TestTextResourcePropertiesService(instantiationService.get(IConfigurationService))); instantiationService.stub(IModelService, instantiationService.createInstance(ModelServiceImpl)); const fileService = new FileService(new NullLogService()); @@ -145,7 +146,7 @@ suite('KeybindingsEditing', () => { test('edit a default keybinding to a non existing keybindings file', () => { keybindingsFile = path.join(testDir, 'nonExistingFile.json'); - instantiationService.get(IEnvironmentService).appKeybindingsPath = keybindingsFile; + instantiationService.get(IEnvironmentService).keybindingsResource = URI.file(keybindingsFile); testObject = instantiationService.createInstance(KeybindingsEditingService); const expected: IUserFriendlyKeybinding[] = [{ key: 'alt+c', command: 'a' }, { key: 'escape', command: '-a' }]; @@ -274,7 +275,7 @@ suite('KeybindingsEditing', () => { parts.push(aSimpleKeybinding(chordPart)); } } - let keybinding = parts.length > 0 ? new USLayoutResolvedKeybinding(new ChordKeybinding(parts), OS) : null; + const keybinding = parts.length > 0 ? new USLayoutResolvedKeybinding(new ChordKeybinding(parts), OS) : undefined; return new ResolvedKeybindingItem(keybinding, command || 'some command', null, when ? ContextKeyExpr.deserialize(when) : undefined, isDefault === undefined ? true : isDefault); } diff --git a/src/vs/workbench/services/layout/browser/layoutService.ts b/src/vs/workbench/services/layout/browser/layoutService.ts index 815af5edeb2..c89bbef87c1 100644 --- a/src/vs/workbench/services/layout/browser/layoutService.ts +++ b/src/vs/workbench/services/layout/browser/layoutService.ts @@ -45,6 +45,21 @@ export interface IWorkbenchLayoutService extends ILayoutService { */ readonly onZenModeChange: Event; + /** + * Emits when fullscreen is enabled or disabled. + */ + readonly onFullscreenChange: Event; + + /** + * Emits when centered layout is enabled or disabled. + */ + readonly onCenteredLayoutChange: Event; + + /** + * Emit when panel position changes. + */ + readonly onPanelPositionChange: Event; + /** * Asks the part service if all parts have been fully restored. For editor part * this means that the contents of editors have loaded. diff --git a/src/vs/workbench/services/notification/common/notificationService.ts b/src/vs/workbench/services/notification/common/notificationService.ts index 0861420e86f..78280e20a22 100644 --- a/src/vs/workbench/services/notification/common/notificationService.ts +++ b/src/vs/workbench/services/notification/common/notificationService.ts @@ -3,15 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { INotificationService, INotification, INotificationHandle, Severity, NotificationMessage, INotificationActions, IPromptChoice, IPromptOptions } from 'vs/platform/notification/common/notification'; +import { INotificationService, INotification, INotificationHandle, Severity, NotificationMessage, INotificationActions, IPromptChoice, IPromptOptions, IStatusMessageOptions } from 'vs/platform/notification/common/notification'; import { INotificationsModel, NotificationsModel, ChoiceAction } from 'vs/workbench/common/notifications'; -import { dispose, Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { IAction } from 'vs/base/common/actions'; export class NotificationService extends Disposable implements INotificationService { - _serviceBrand: any; + _serviceBrand: ServiceIdentifier; private _model: INotificationsModel = this._register(new NotificationsModel()); @@ -26,7 +28,7 @@ export class NotificationService extends Disposable implements INotificationServ return; } - this.model.notify({ severity: Severity.Info, message }); + this.model.addNotification({ severity: Severity.Info, message }); } warn(message: NotificationMessage | NotificationMessage[]): void { @@ -36,7 +38,7 @@ export class NotificationService extends Disposable implements INotificationServ return; } - this.model.notify({ severity: Severity.Warning, message }); + this.model.addNotification({ severity: Severity.Warning, message }); } error(message: NotificationMessage | NotificationMessage[]): void { @@ -46,37 +48,32 @@ export class NotificationService extends Disposable implements INotificationServ return; } - this.model.notify({ severity: Severity.Error, message }); + this.model.addNotification({ severity: Severity.Error, message }); } notify(notification: INotification): INotificationHandle { - return this.model.notify(notification); + return this.model.addNotification(notification); } prompt(severity: Severity, message: string, choices: IPromptChoice[], options?: IPromptOptions): INotificationHandle { - const toDispose: IDisposable[] = []; + const toDispose = new DisposableStore(); let choiceClicked = false; let handle: INotificationHandle; // Convert choices into primary/secondary actions - const actions: INotificationActions = { primary: [], secondary: [] }; + const primaryActions: IAction[] = []; + const secondaryActions: IAction[] = []; 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); + primaryActions.push(action); } else { - if (!actions.secondary) { - actions.secondary = []; - } - actions.secondary.push(action); + secondaryActions.push(action); } // React to action being clicked - toDispose.push(action.onDidRun(() => { + toDispose.add(action.onDidRun(() => { choiceClicked = true; // Close notification unless we are told to keep open @@ -85,16 +82,17 @@ export class NotificationService extends Disposable implements INotificationServ } })); - toDispose.push(action); + toDispose.add(action); }); // Show notification with actions + const actions: INotificationActions = { primary: primaryActions, secondary: secondaryActions }; handle = this.notify({ severity, message, actions, sticky: options && options.sticky, silent: options && options.silent }); Event.once(handle.onDidClose)(() => { // Cleanup when notification gets disposed - dispose(toDispose); + toDispose.dispose(); // Indicate cancellation to the outside if no action was executed if (options && typeof options.onCancel === 'function' && !choiceClicked) { @@ -104,6 +102,10 @@ export class NotificationService extends Disposable implements INotificationServ return handle; } + + status(message: NotificationMessage, options?: IStatusMessageOptions): IDisposable { + return this.model.showStatusMessage(message, options); + } } registerSingleton(INotificationService, NotificationService, true); \ No newline at end of file diff --git a/src/vs/workbench/services/output/common/outputChannelModel.ts b/src/vs/workbench/services/output/common/outputChannelModel.ts index 007ead80d15..be87bf1aecc 100644 --- a/src/vs/workbench/services/output/common/outputChannelModel.ts +++ b/src/vs/workbench/services/output/common/outputChannelModel.ts @@ -51,10 +51,10 @@ export abstract class AsbtractOutputChannelModelService { export abstract class AbstractFileOutputChannelModel extends Disposable implements IOutputChannelModel { - protected _onDidAppendedContent = new Emitter(); + protected readonly _onDidAppendedContent = this._register(new Emitter()); readonly onDidAppendedContent: Event = this._onDidAppendedContent.event; - protected _onDispose = new Emitter(); + protected readonly _onDispose = this._register(new Emitter()); readonly onDispose: Event = this._onDispose.event; protected modelUpdater: RunOnceScheduler; @@ -96,12 +96,11 @@ export abstract class AbstractFileOutputChannelModel extends Disposable implemen } else { 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(() => { + const disposable = this.model.onWillDispose(() => { this.onModelWillDispose(this.model); this.model = null; - dispose(disposables); - })); + dispose(disposable); + }); } return this.model; } @@ -340,11 +339,10 @@ export class BufferredOutputChannel extends Disposable implements IOutputChannel private createModel(content: string): ITextModel { const model = this.modelService.createModel(content, this.modeService.create(this.mimeType), this.modelUri); - const disposables: IDisposable[] = []; - disposables.push(model.onWillDispose(() => { + const disposable = model.onWillDispose(() => { this.model = null; - dispose(disposables); - })); + dispose(disposable); + }); return model; } diff --git a/src/vs/workbench/services/panel/common/panelService.ts b/src/vs/workbench/services/panel/common/panelService.ts index 4609cb5d2bd..9e957d61747 100644 --- a/src/vs/workbench/services/panel/common/panelService.ts +++ b/src/vs/workbench/services/panel/common/panelService.ts @@ -8,6 +8,7 @@ import { IPanel } from 'vs/workbench/common/panel'; import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { IBadge } from 'vs/workbench/services/activity/common/activity'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { IProgressIndicator } from 'vs/platform/progress/common/progress'; export const IPanelService = createDecorator('panelService'); @@ -18,11 +19,11 @@ export interface IPanelIdentifier { } export interface IPanelService { + _serviceBrand: ServiceIdentifier; - onDidPanelOpen: Event<{ panel: IPanel, focus: boolean }>; - - onDidPanelClose: Event; + readonly onDidPanelOpen: Event<{ panel: IPanel, focus: boolean }>; + readonly onDidPanelClose: Event; /** * Opens a panel with the given identifier and pass keyboard focus to it if specified. @@ -35,7 +36,12 @@ export interface IPanelService { getActivePanel(): IPanel | null; /** - * Returns all built-in panels following the default order (Problems - Output - Debug Console - Terminal) + * Returns the panel by id. + */ + getPanel(id: string): IPanelIdentifier | undefined; + + /** + * Returns all built-in panels following the default order */ getPanels(): IPanelIdentifier[]; @@ -44,6 +50,11 @@ export interface IPanelService { */ getPinnedPanels(): IPanelIdentifier[]; + /** + * Returns the progress indicator for the panel bar. + */ + getProgressIndicator(id: string): IProgressIndicator | null; + /** * Show an activity in a panel. */ diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index 0ca6f7d17f4..9f6330fcd37 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -273,7 +273,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 override the defaults") + '\n[\n]'; - const editableKeybindings = URI.file(this.environmentService.appKeybindingsPath); + const editableKeybindings = this.environmentService.keybindingsResource; const openDefaultKeybindings = !!this.configurationService.getValue('workbench.settings.openDefaultKeybindings'); // Create as needed and open in editor @@ -524,9 +524,9 @@ export class PreferencesService extends Disposable implements IPreferencesServic switch (configurationTarget) { case ConfigurationTarget.USER: case ConfigurationTarget.USER_LOCAL: - return URI.file(this.environmentService.appSettingsPath); + return this.environmentService.settingsResource; case ConfigurationTarget.USER_REMOTE: - return URI.file(this.environmentService.appSettingsPath); + return this.environmentService.settingsResource; case ConfigurationTarget.WORKSPACE: if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { return null; diff --git a/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts b/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts index fed784141bf..0f80e25b1d2 100644 --- a/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts +++ b/src/vs/workbench/services/preferences/common/keybindingsEditorModel.ts @@ -174,7 +174,7 @@ export class KeybindingsEditorModel extends EditorModel { const commandsWithDefaultKeybindings = this.keybindingsService.getDefaultKeybindings().map(keybinding => keybinding.command); for (const command of KeybindingResolver.getAllUnboundCommands(boundCommands)) { - const keybindingItem = new ResolvedKeybindingItem(null, command, null, undefined, commandsWithDefaultKeybindings.indexOf(command) === -1); + const keybindingItem = new ResolvedKeybindingItem(undefined, command, null, undefined, commandsWithDefaultKeybindings.indexOf(command) === -1); this._keybindingItemsSortedByPrecedence.push(KeybindingsEditorModel.toKeybindingEntry(command, keybindingItem, workbenchActionsRegistry, editorActionsLabels)); } this._keybindingItems = this._keybindingItemsSortedByPrecedence.slice(0).sort((a, b) => KeybindingsEditorModel.compareKeybindingData(a, b)); diff --git a/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts b/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts index 002a49d799c..ee813e14fe5 100644 --- a/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts +++ b/src/vs/workbench/services/preferences/common/preferencesEditorInput.ts @@ -31,7 +31,7 @@ export class DefaultPreferencesEditorInput extends ResourceEditorInput { constructor(defaultSettingsResource: URI, @ITextModelService textModelResolverService: ITextModelService ) { - super(nls.localize('settingsEditorName', "Default Settings"), '', defaultSettingsResource, textModelResolverService); + super(nls.localize('settingsEditorName', "Default Settings"), '', defaultSettingsResource, undefined, textModelResolverService); } getTypeId(): string { 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 4c256284b39..c70d6d94927 100644 --- a/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts +++ b/src/vs/workbench/services/preferences/test/common/keybindingsEditorModel.test.ts @@ -617,7 +617,7 @@ suite('KeybindingsEditorModel test', () => { parts.push(aSimpleKeybinding(chordPart)); } } - let keybinding = parts.length > 0 ? new USLayoutResolvedKeybinding(new ChordKeybinding(parts), OS) : null; + const keybinding = parts.length > 0 ? new USLayoutResolvedKeybinding(new ChordKeybinding(parts), OS) : undefined; return new ResolvedKeybindingItem(keybinding, command || 'some command', null, when ? ContextKeyExpr.deserialize(when) : undefined, isDefault === undefined ? true : isDefault); } diff --git a/src/vs/workbench/services/progress/browser/editorProgressService.ts b/src/vs/workbench/services/progress/browser/editorProgressService.ts new file mode 100644 index 00000000000..7ea3daae18e --- /dev/null +++ b/src/vs/workbench/services/progress/browser/editorProgressService.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IEditorProgressService } from 'vs/platform/progress/common/progress'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { ProgressBarIndicator } from 'vs/workbench/services/progress/browser/progressIndicator'; + +export class EditorProgressService extends ProgressBarIndicator { + + _serviceBrand: ServiceIdentifier; +} diff --git a/src/vs/workbench/services/progress/browser/media/progressService2.css b/src/vs/workbench/services/progress/browser/media/progressService.css similarity index 85% rename from src/vs/workbench/services/progress/browser/media/progressService2.css rename to src/vs/workbench/services/progress/browser/media/progressService.css index 850fdf2e3c5..e9d6a1574e8 100644 --- a/src/vs/workbench/services/progress/browser/media/progressService2.css +++ b/src/vs/workbench/services/progress/browser/media/progressService.css @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench .part.statusbar > .statusbar-item.progress { +.monaco-workbench .part.statusbar > .items-container > .statusbar-item.progress { padding-left: 5px; } -.monaco-workbench .part.statusbar > .statusbar-item.progress .spinner-container { +.monaco-workbench .part.statusbar > .items-container > .statusbar-item.progress .spinner-container { padding-right: 5px; } diff --git a/src/vs/workbench/services/progress/browser/progressIndicator.ts b/src/vs/workbench/services/progress/browser/progressIndicator.ts new file mode 100644 index 00000000000..1d11c084f7c --- /dev/null +++ b/src/vs/workbench/services/progress/browser/progressIndicator.ts @@ -0,0 +1,300 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from 'vs/base/common/lifecycle'; +import { isUndefinedOrNull } from 'vs/base/common/types'; +import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { IProgressRunner, IProgressIndicator } from 'vs/platform/progress/common/progress'; + +export class ProgressBarIndicator implements IProgressIndicator { + + constructor(private progressbar: ProgressBar) { } + + show(infinite: true, delay?: number): IProgressRunner; + show(total: number, delay?: number): IProgressRunner; + show(infiniteOrTotal: true | number, delay?: number): IProgressRunner { + if (typeof infiniteOrTotal === 'boolean') { + this.progressbar.infinite().show(delay); + } else { + this.progressbar.total(infiniteOrTotal).show(delay); + } + + return { + total: (total: number) => { + this.progressbar.total(total); + }, + + worked: (worked: number) => { + if (this.progressbar.hasTotal()) { + this.progressbar.worked(worked); + } else { + this.progressbar.infinite().show(); + } + }, + + done: () => { + this.progressbar.stop().hide(); + } + }; + } + + async showWhile(promise: Promise, delay?: number): Promise { + try { + this.progressbar.infinite().show(delay); + + await promise; + } catch (error) { + // ignore + } finally { + this.progressbar.stop().hide(); + } + } +} + +namespace ProgressIndicatorState { + + export const enum Type { + None, + Done, + Infinite, + While, + Work + } + + export const None = new class { readonly type = Type.None; }; + export const Done = new class { readonly type = Type.Done; }; + export const Infinite = new class { readonly type = Type.Infinite; }; + + export class While { + readonly type = Type.While; + + constructor( + readonly whilePromise: Promise, + readonly whileStart: number, + readonly whileDelay: number, + ) { } + } + + export class Work { + readonly type = Type.Work; + + constructor( + readonly total: number | undefined, + readonly worked: number | undefined + ) { } + } + + export type State = + typeof None + | typeof Done + | typeof Infinite + | While + | Work; +} + +export abstract class CompositeScope extends Disposable { + + constructor( + private viewletService: IViewletService, + private panelService: IPanelService, + private scopeId: string + ) { + super(); + + this.registerListeners(); + } + + registerListeners(): void { + this._register(this.viewletService.onDidViewletOpen(viewlet => this.onScopeOpened(viewlet.getId()))); + this._register(this.panelService.onDidPanelOpen(({ panel }) => this.onScopeOpened(panel.getId()))); + + this._register(this.viewletService.onDidViewletClose(viewlet => this.onScopeClosed(viewlet.getId()))); + this._register(this.panelService.onDidPanelClose(panel => this.onScopeClosed(panel.getId()))); + } + + private onScopeClosed(scopeId: string) { + if (scopeId === this.scopeId) { + this.onScopeDeactivated(); + } + } + + private onScopeOpened(scopeId: string) { + if (scopeId === this.scopeId) { + this.onScopeActivated(); + } + } + + abstract onScopeActivated(): void; + + abstract onScopeDeactivated(): void; +} + +export class CompositeProgressIndicator extends CompositeScope implements IProgressIndicator { + private isActive: boolean; + private progressbar: ProgressBar; + private progressState: ProgressIndicatorState.State = ProgressIndicatorState.None; + + constructor( + progressbar: ProgressBar, + scopeId: string, + isActive: boolean, + @IViewletService viewletService: IViewletService, + @IPanelService panelService: IPanelService + ) { + super(viewletService, panelService, scopeId); + + this.progressbar = progressbar; + this.isActive = isActive || isUndefinedOrNull(scopeId); // If service is unscoped, enable by default + } + + onScopeDeactivated(): void { + this.isActive = false; + } + + onScopeActivated(): void { + this.isActive = true; + + // Return early if progress state indicates that progress is done + if (this.progressState.type === ProgressIndicatorState.Done.type) { + return; + } + + // Replay Infinite Progress from Promise + if (this.progressState.type === ProgressIndicatorState.Type.While) { + let delay: number | undefined; + if (this.progressState.whileDelay > 0) { + const remainingDelay = this.progressState.whileDelay - (Date.now() - this.progressState.whileStart); + if (remainingDelay > 0) { + delay = remainingDelay; + } + } + + this.doShowWhile(delay); + } + + // Replay Infinite Progress + else if (this.progressState.type === ProgressIndicatorState.Type.Infinite) { + this.progressbar.infinite().show(); + } + + // Replay Finite Progress (Total & Worked) + else if (this.progressState.type === ProgressIndicatorState.Type.Work) { + if (this.progressState.total) { + this.progressbar.total(this.progressState.total).show(); + } + + if (this.progressState.worked) { + this.progressbar.worked(this.progressState.worked).show(); + } + } + } + + show(infinite: true, delay?: number): IProgressRunner; + show(total: number, delay?: number): IProgressRunner; + show(infiniteOrTotal: true | number, delay?: number): IProgressRunner { + + // Sort out Arguments + if (typeof infiniteOrTotal === 'boolean') { + this.progressState = ProgressIndicatorState.Infinite; + } else { + this.progressState = new ProgressIndicatorState.Work(infiniteOrTotal, undefined); + } + + // Active: Show Progress + if (this.isActive) { + + // Infinite: Start Progressbar and Show after Delay + if (this.progressState.type === ProgressIndicatorState.Type.Infinite) { + this.progressbar.infinite().show(delay); + } + + // Finite: Start Progressbar and Show after Delay + else if (this.progressState.type === ProgressIndicatorState.Type.Work && typeof this.progressState.total === 'number') { + this.progressbar.total(this.progressState.total).show(delay); + } + } + + return { + total: (total: number) => { + this.progressState = new ProgressIndicatorState.Work( + total, + this.progressState.type === ProgressIndicatorState.Type.Work ? this.progressState.worked : undefined); + + if (this.isActive) { + this.progressbar.total(total); + } + }, + + worked: (worked: number) => { + + // Verify first that we are either not active or the progressbar has a total set + if (!this.isActive || this.progressbar.hasTotal()) { + this.progressState = new ProgressIndicatorState.Work( + this.progressState.type === ProgressIndicatorState.Type.Work ? this.progressState.total : undefined, + this.progressState.type === ProgressIndicatorState.Type.Work && typeof this.progressState.worked === 'number' ? this.progressState.worked + worked : worked); + + if (this.isActive) { + this.progressbar.worked(worked); + } + } + + // Otherwise the progress bar does not support worked(), we fallback to infinite() progress + else { + this.progressState = ProgressIndicatorState.Infinite; + this.progressbar.infinite().show(); + } + }, + + done: () => { + this.progressState = ProgressIndicatorState.Done; + + if (this.isActive) { + this.progressbar.stop().hide(); + } + } + }; + } + + async showWhile(promise: Promise, delay?: number): Promise { + + // Join with existing running promise to ensure progress is accurate + if (this.progressState.type === ProgressIndicatorState.Type.While) { + promise = Promise.all([promise, this.progressState.whilePromise]); + } + + // Keep Promise in State + this.progressState = new ProgressIndicatorState.While(promise, delay || 0, Date.now()); + + try { + this.doShowWhile(delay); + + await promise; + } catch (error) { + // ignore + } finally { + + // If this is not the last promise in the list of joined promises, skip this + if (this.progressState.type !== ProgressIndicatorState.Type.While || this.progressState.whilePromise === promise) { + + // The while promise is either null or equal the promise we last hooked on + this.progressState = ProgressIndicatorState.None; + + if (this.isActive) { + this.progressbar.stop().hide(); + } + } + } + } + + private doShowWhile(delay?: number): void { + + // Show Progress when active + if (this.isActive) { + this.progressbar.infinite().show(delay); + } + } +} diff --git a/src/vs/workbench/services/progress/browser/progressService.ts b/src/vs/workbench/services/progress/browser/progressService.ts index b3d3a517941..d7bb38745a1 100644 --- a/src/vs/workbench/services/progress/browser/progressService.ts +++ b/src/vs/workbench/services/progress/browser/progressService.ts @@ -3,290 +3,381 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vs/base/common/lifecycle'; -import * as types from 'vs/base/common/types'; -import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; +import 'vs/css!./media/progressService'; + +import { localize } from 'vs/nls'; +import { IDisposable, dispose, DisposableStore, MutableDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { IProgressService, IProgressOptions, IProgressStep, ProgressLocation, IProgress, Progress, IProgressCompositeOptions, IProgressNotificationOptions, IProgressRunner, IProgressIndicator } from 'vs/platform/progress/common/progress'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { StatusbarAlignment, IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; +import { timeout } from 'vs/base/common/async'; +import { ProgressBadge, IActivityService } from 'vs/workbench/services/activity/common/activity'; +import { INotificationService, Severity, INotificationHandle, INotificationActions } from 'vs/platform/notification/common/notification'; +import { Action } from 'vs/base/common/actions'; +import { Event } from 'vs/base/common/event'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; +import { Dialog } from 'vs/base/browser/ui/dialog/dialog'; +import { attachDialogStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { EventHelper } from 'vs/base/browser/dom'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { IProgressService, IProgressRunner } from 'vs/platform/progress/common/progress'; +export class ProgressService extends Disposable implements IProgressService { -namespace ProgressState { - export const enum Type { - None, - Done, - Infinite, - While, - Work - } + _serviceBrand: ServiceIdentifier; - export const None = new class { readonly type = Type.None; }; - export const Done = new class { readonly type = Type.Done; }; - export const Infinite = new class { readonly type = Type.Infinite; }; - - export class While { - public readonly type = Type.While; - constructor( - public readonly whilePromise: Promise, - public readonly whileStart: number, - public readonly whileDelay: number, - ) { } - } - - export class Work { - public readonly type = Type.Work; - constructor( - public readonly total: number | undefined, - public readonly worked: number | undefined - ) { } - } - - export type State = - typeof None - | typeof Done - | typeof Infinite - | While - | Work; -} - -export abstract class ScopedService extends Disposable { - - constructor(private viewletService: IViewletService, private panelService: IPanelService, private scopeId: string) { - super(); - - this.registerListeners(); - } - - registerListeners(): void { - this._register(this.viewletService.onDidViewletOpen(viewlet => this.onScopeOpened(viewlet.getId()))); - this._register(this.panelService.onDidPanelOpen(({ panel }) => this.onScopeOpened(panel.getId()))); - - this._register(this.viewletService.onDidViewletClose(viewlet => this.onScopeClosed(viewlet.getId()))); - this._register(this.panelService.onDidPanelClose(panel => this.onScopeClosed(panel.getId()))); - } - - private onScopeClosed(scopeId: string) { - if (scopeId === this.scopeId) { - this.onScopeDeactivated(); - } - } - - private onScopeOpened(scopeId: string) { - if (scopeId === this.scopeId) { - this.onScopeActivated(); - } - } - - abstract onScopeActivated(): void; - - abstract onScopeDeactivated(): void; -} - -export class ScopedProgressService extends ScopedService implements IProgressService { - _serviceBrand: any; - private isActive: boolean; - private progressbar: ProgressBar; - private progressState: ProgressState.State = ProgressState.None; + private readonly stack: [IProgressOptions, Progress][] = []; + private readonly globalStatusEntry = this._register(new MutableDisposable()); constructor( - progressbar: ProgressBar, - scopeId: string, - isActive: boolean, - @IViewletService viewletService: IViewletService, - @IPanelService panelService: IPanelService + @IActivityService private readonly activityService: IActivityService, + @IViewletService private readonly viewletService: IViewletService, + @IPanelService private readonly panelService: IPanelService, + @INotificationService private readonly notificationService: INotificationService, + @IStatusbarService private readonly statusbarService: IStatusbarService, + @ILayoutService private readonly layoutService: ILayoutService, + @IThemeService private readonly themeService: IThemeService, + @IKeybindingService private readonly keybindingService: IKeybindingService ) { - super(viewletService, panelService, scopeId); - - this.progressbar = progressbar; - this.isActive = isActive || types.isUndefinedOrNull(scopeId); // If service is unscoped, enable by default + super(); } - onScopeDeactivated(): void { - this.isActive = false; - } - - onScopeActivated(): void { - this.isActive = true; - - // Return early if progress state indicates that progress is done - if (this.progressState.type === ProgressState.Done.type) { - return; - } - - // Replay Infinite Progress from Promise - if (this.progressState.type === ProgressState.Type.While) { - let delay: number | undefined; - if (this.progressState.whileDelay > 0) { - const remainingDelay = this.progressState.whileDelay - (Date.now() - this.progressState.whileStart); - if (remainingDelay > 0) { - delay = remainingDelay; - } + withProgress(options: IProgressOptions, task: (progress: IProgress) => Promise, onDidCancel?: () => void): Promise { + const { location } = options; + if (typeof location === 'string') { + if (this.viewletService.getProgressIndicator(location)) { + return this.withViewletProgress(location, task, { ...options, location }); } - this.doShowWhile(delay); - } - - // Replay Infinite Progress - else if (this.progressState.type === ProgressState.Type.Infinite) { - this.progressbar.infinite().show(); - } - - // Replay Finite Progress (Total & Worked) - else if (this.progressState.type === ProgressState.Type.Work) { - if (this.progressState.total) { - this.progressbar.total(this.progressState.total).show(); + if (this.panelService.getProgressIndicator(location)) { + return this.withPanelProgress(location, task, { ...options, location }); } - if (this.progressState.worked) { - this.progressbar.worked(this.progressState.worked).show(); - } + return Promise.reject(new Error(`Bad progress location: ${location}`)); + } + + switch (location) { + case ProgressLocation.Notification: + return this.withNotificationProgress({ ...options, location }, task, onDidCancel); + case ProgressLocation.Window: + return this.withWindowProgress(options, task); + case ProgressLocation.Explorer: + return this.withViewletProgress('workbench.view.explorer', task, { ...options, location }); + case ProgressLocation.Scm: + return this.withViewletProgress('workbench.view.scm', task, { ...options, location }); + case ProgressLocation.Extensions: + return this.withViewletProgress('workbench.view.extensions', task, { ...options, location }); + case ProgressLocation.Dialog: + return this.withDialogProgress(options, task, onDidCancel); + default: + return Promise.reject(new Error(`Bad progress location: ${location}`)); } } - show(infinite: true, delay?: number): IProgressRunner; - show(total: number, delay?: number): IProgressRunner; - show(infiniteOrTotal: true | number, delay?: number): IProgressRunner { - // Sort out Arguments - if (typeof infiniteOrTotal === 'boolean') { - this.progressState = ProgressState.Infinite; - } else { - this.progressState = new ProgressState.Work(infiniteOrTotal, undefined); - } + private withWindowProgress(options: IProgressOptions, callback: (progress: IProgress<{ message?: string }>) => Promise): Promise { + const task: [IProgressOptions, Progress] = [options, new Progress(() => this.updateWindowProgress())]; - // Active: Show Progress - if (this.isActive) { + const promise = callback(task[1]); - // Infinite: Start Progressbar and Show after Delay - if (this.progressState.type === ProgressState.Type.Infinite) { - this.progressbar.infinite().show(delay); - } + let delayHandle: any = setTimeout(() => { + delayHandle = undefined; + this.stack.unshift(task); + this.updateWindowProgress(); - // Finite: Start Progressbar and Show after Delay - else if (this.progressState.type === ProgressState.Type.Work && typeof this.progressState.total === 'number') { - this.progressbar.total(this.progressState.total).show(delay); - } - } + // show progress for at least 150ms + Promise.all([ + timeout(150), + promise + ]).finally(() => { + const idx = this.stack.indexOf(task); + this.stack.splice(idx, 1); + this.updateWindowProgress(); + }); + }, 150); - return { - total: (total: number) => { - this.progressState = new ProgressState.Work( - total, - this.progressState.type === ProgressState.Type.Work ? this.progressState.worked : undefined); - - if (this.isActive) { - this.progressbar.total(total); - } - }, - - worked: (worked: number) => { - - // Verify first that we are either not active or the progressbar has a total set - if (!this.isActive || this.progressbar.hasTotal()) { - this.progressState = new ProgressState.Work( - this.progressState.type === ProgressState.Type.Work ? this.progressState.total : undefined, - this.progressState.type === ProgressState.Type.Work && typeof this.progressState.worked === 'number' ? this.progressState.worked + worked : worked); - - if (this.isActive) { - this.progressbar.worked(worked); - } - } - - // Otherwise the progress bar does not support worked(), we fallback to infinite() progress - else { - this.progressState = ProgressState.Infinite; - this.progressbar.infinite().show(); - } - }, - - done: () => { - this.progressState = ProgressState.Done; - - if (this.isActive) { - this.progressbar.stop().hide(); - } - } - }; + // cancel delay if promise finishes below 150ms + return promise.finally(() => clearTimeout(delayHandle)); } - showWhile(promise: Promise, delay?: number): Promise { - // Join with existing running promise to ensure progress is accurate - if (this.progressState.type === ProgressState.Type.While) { - promise = Promise.all([promise, this.progressState.whilePromise]); - } + private updateWindowProgress(idx: number = 0) { + this.globalStatusEntry.clear(); - // Keep Promise in State - this.progressState = new ProgressState.While(promise, delay || 0, Date.now()); + if (idx < this.stack.length) { + const [options, progress] = this.stack[idx]; - let stop = () => { + let progressTitle = options.title; + let progressMessage = progress.value && progress.value.message; + let text: string; + let title: string; - // If this is not the last promise in the list of joined promises, return early - if (this.progressState.type === ProgressState.Type.While && this.progressState.whilePromise !== promise) { + if (progressTitle && progressMessage) { + // : <message> + text = localize('progress.text2', "{0}: {1}", progressTitle, progressMessage); + title = options.source ? localize('progress.title3', "[{0}] {1}: {2}", options.source, progressTitle, progressMessage) : text; + + } else if (progressTitle) { + // <title> + text = progressTitle; + title = options.source ? localize('progress.title2', "[{0}]: {1}", options.source, progressTitle) : text; + + } else if (progressMessage) { + // <message> + text = progressMessage; + title = options.source ? localize('progress.title2', "[{0}]: {1}", options.source, progressMessage) : text; + + } else { + // no title, no message -> no progress. try with next on stack + this.updateWindowProgress(idx + 1); return; } - // The while promise is either null or equal the promise we last hooked on - this.progressState = ProgressState.None; + this.globalStatusEntry.value = this.statusbarService.addEntry({ + text: `$(sync~spin) ${text}`, + tooltip: title + }, 'status.progress', localize('status.progress', "Progress Message"), StatusbarAlignment.LEFT); + } + } - if (this.isActive) { - this.progressbar.stop().hide(); + private withNotificationProgress<P extends Promise<R>, R = unknown>(options: IProgressNotificationOptions, callback: (progress: IProgress<{ message?: string, increment?: number }>) => P, onDidCancel?: () => void): P { + const toDispose = new DisposableStore(); + + const createNotification = (message: string | undefined, increment?: number): INotificationHandle | undefined => { + if (!message) { + return undefined; // we need a message at least + } + + const primaryActions = options.primaryActions ? Array.from(options.primaryActions) : []; + const secondaryActions = options.secondaryActions ? Array.from(options.secondaryActions) : []; + if (options.cancellable) { + const cancelAction = new class extends Action { + constructor() { + super('progress.cancel', localize('cancel', "Cancel"), undefined, true); + } + + run(): Promise<any> { + if (typeof onDidCancel === 'function') { + onDidCancel(); + } + + return Promise.resolve(undefined); + } + }; + toDispose.add(cancelAction); + + primaryActions.push(cancelAction); + } + + const actions: INotificationActions = { primary: primaryActions, secondary: secondaryActions }; + const handle = this.notificationService.notify({ + severity: Severity.Info, + message, + source: options.source, + actions + }); + + updateProgress(handle, increment); + + Event.once(handle.onDidClose)(() => { + toDispose.dispose(); + }); + + return handle; + }; + + const updateProgress = (notification: INotificationHandle, increment?: number): void => { + if (typeof increment === 'number' && increment >= 0) { + notification.progress.total(100); // always percentage based + notification.progress.worked(increment); + } else { + notification.progress.infinite(); } }; - this.doShowWhile(delay); + let handle: INotificationHandle | undefined; + const updateNotification = (message?: string, increment?: number): void => { + if (!handle) { + handle = createNotification(message, increment); + } else { + if (typeof message === 'string') { + let newMessage: string; + if (typeof options.title === 'string') { + newMessage = `${options.title}: ${message}`; // always prefix with overall title if we have it (https://github.com/Microsoft/vscode/issues/50932) + } else { + newMessage = message; + } - return promise.then(stop, stop); - } - - private doShowWhile(delay?: number): void { - - // Show Progress when active - if (this.isActive) { - this.progressbar.infinite().show(delay); - } - } -} - -export class ProgressService implements IProgressService { - - _serviceBrand: any; - - constructor(private progressbar: ProgressBar) { } - - show(infinite: true, delay?: number): IProgressRunner; - show(total: number, delay?: number): IProgressRunner; - show(infiniteOrTotal: true | number, delay?: number): IProgressRunner { - if (typeof infiniteOrTotal === 'boolean') { - this.progressbar.infinite().show(delay); - } else { - this.progressbar.total(infiniteOrTotal).show(delay); - } - - return { - total: (total: number) => { - this.progressbar.total(total); - }, - - worked: (worked: number) => { - if (this.progressbar.hasTotal()) { - this.progressbar.worked(worked); - } else { - this.progressbar.infinite().show(); + handle.updateMessage(newMessage); } - }, - done: () => { - this.progressbar.stop().hide(); + if (typeof increment === 'number') { + updateProgress(handle, increment); + } } }; + + // Show initially + updateNotification(options.title); + + // Update based on progress + const promise = callback({ + report: progress => { + updateNotification(progress.message, progress.increment); + } + }); + + // Show progress for at least 800ms and then hide once done or canceled + Promise.all([timeout(800), promise]).finally(() => { + if (handle) { + handle.close(); + } + }); + + return promise; } - showWhile(promise: Promise<any>, delay?: number): Promise<void> { - const stop = () => { - this.progressbar.stop().hide(); + private withViewletProgress<P extends Promise<R>, R = unknown>(viewletId: string, task: (progress: IProgress<IProgressStep>) => P, options: IProgressCompositeOptions): P { + + // show in viewlet + const promise = this.withCompositeProgress(this.viewletService.getProgressIndicator(viewletId), task, options); + + // show activity bar + let activityProgress: IDisposable; + let delayHandle: any = setTimeout(() => { + delayHandle = undefined; + + const handle = this.activityService.showActivity( + viewletId, + new ProgressBadge(() => ''), + 'progress-badge', + 100 + ); + + const startTimeVisible = Date.now(); + const minTimeVisible = 300; + activityProgress = { + dispose() { + const d = Date.now() - startTimeVisible; + if (d < minTimeVisible) { + // should at least show for Nms + setTimeout(() => handle.dispose(), minTimeVisible - d); + } else { + // shown long enough + handle.dispose(); + } + } + }; + }, options.delay || 300); + + promise.finally(() => { + clearTimeout(delayHandle); + dispose(activityProgress); + }); + + return promise; + } + + private withPanelProgress<P extends Promise<R>, R = unknown>(panelid: string, task: (progress: IProgress<IProgressStep>) => P, options: IProgressCompositeOptions): P { + + // show in panel + return this.withCompositeProgress(this.panelService.getProgressIndicator(panelid), task, options); + } + + private withCompositeProgress<P extends Promise<R>, R = unknown>(progressIndicator: IProgressIndicator | null, task: (progress: IProgress<IProgressStep>) => P, options: IProgressCompositeOptions): P { + let progressRunner: IProgressRunner | undefined = undefined; + + const promise = task({ + report: progress => { + if (!progressRunner) { + return; + } + + if (typeof progress.increment === 'number') { + progressRunner.worked(progress.increment); + } + + if (typeof progress.total === 'number') { + progressRunner.total(progress.total); + } + } + }); + + if (progressIndicator) { + if (typeof options.total === 'number') { + progressRunner = progressIndicator.show(options.total, options.delay); + promise.catch(() => undefined /* ignore */).finally(() => progressRunner ? progressRunner.done() : undefined); + } else { + progressIndicator.showWhile(promise, options.delay); + } + } + + return promise; + } + + private withDialogProgress<P extends Promise<R>, R = unknown>(options: IProgressOptions, task: (progress: IProgress<IProgressStep>) => P, onDidCancel?: () => void): P { + const disposables = new DisposableStore(); + const allowableCommands = [ + 'workbench.action.quit', + 'workbench.action.reloadWindow' + ]; + + let dialog: Dialog; + + const createDialog = (message: string) => { + dialog = new Dialog( + this.layoutService.container, + message, + [options.cancellable ? localize('cancel', "Cancel") : localize('dismiss', "Dismiss")], + { + type: 'pending', + keyEventProcessor: (event: StandardKeyboardEvent) => { + const resolved = this.keybindingService.softDispatch(event, this.layoutService.container); + if (resolved && resolved.commandId) { + if (allowableCommands.indexOf(resolved.commandId) === -1) { + EventHelper.stop(event, true); + } + } + } + } + ); + + disposables.add(dialog); + disposables.add(attachDialogStyler(dialog, this.themeService)); + + dialog.show().then(() => { + if (typeof onDidCancel === 'function') { + onDidCancel(); + } + + dispose(dialog); + }); + + return dialog; }; - this.progressbar.infinite().show(delay); + const updateDialog = (message?: string) => { + if (message && !dialog) { + dialog = createDialog(message); + } else if (message) { + dialog.updateMessage(message); + } + }; - return promise.then(stop, stop); + const promise = task({ + report: progress => { + updateDialog(progress.message); + } + }); + + promise.finally(() => { + dispose(disposables); + }); + + return promise; } } + +registerSingleton(IProgressService, ProgressService, true); diff --git a/src/vs/workbench/services/progress/browser/progressService2.ts b/src/vs/workbench/services/progress/browser/progressService2.ts deleted file mode 100644 index d2e4eee1263..00000000000 --- a/src/vs/workbench/services/progress/browser/progressService2.ts +++ /dev/null @@ -1,344 +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 'vs/css!./media/progressService2'; - -import { localize } from 'vs/nls'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IProgressService2, IProgressOptions, IProgressStep, ProgressLocation, IProgress, emptyProgress, Progress, IProgressNotificationOptions } from 'vs/platform/progress/common/progress'; -import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { StatusbarAlignment, IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; -import { timeout } from 'vs/base/common/async'; -import { ProgressBadge, IActivityService } from 'vs/workbench/services/activity/common/activity'; -import { INotificationService, Severity, INotificationHandle, INotificationActions } from 'vs/platform/notification/common/notification'; -import { Action } from 'vs/base/common/actions'; -import { Event } from 'vs/base/common/event'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; -import { Dialog } from 'vs/base/browser/ui/dialog/dialog'; -import { attachDialogStyler } from 'vs/platform/theme/common/styler'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { EventHelper } from 'vs/base/browser/dom'; - -export class ProgressService2 implements IProgressService2 { - - _serviceBrand: any; - - private readonly _stack: [IProgressOptions, Progress<IProgressStep>][] = []; - private _globalStatusEntry: IDisposable; - - constructor( - @IActivityService private readonly _activityBar: IActivityService, - @IViewletService private readonly _viewletService: IViewletService, - @INotificationService private readonly _notificationService: INotificationService, - @IStatusbarService private readonly _statusbarService: IStatusbarService, - @ILayoutService private readonly _layoutService: ILayoutService, - @IThemeService private readonly _themeService: IThemeService, - @IKeybindingService private readonly _keybindingService: IKeybindingService - ) { } - - withProgress<R = unknown>(options: IProgressOptions, task: (progress: IProgress<IProgressStep>) => Promise<R>, onDidCancel?: () => void): Promise<R> { - - const { location } = options; - if (typeof location === 'string') { - const viewlet = this._viewletService.getViewlet(location); - if (viewlet) { - return this._withViewletProgress(location, task); - } - return Promise.reject(new Error(`Bad progress location: ${location}`)); - } - - switch (location) { - case ProgressLocation.Notification: - return this._withNotificationProgress({ ...options, location: ProgressLocation.Notification }, task, onDidCancel); - case ProgressLocation.Window: - return this._withWindowProgress(options, task); - case ProgressLocation.Explorer: - return this._withViewletProgress('workbench.view.explorer', task); - case ProgressLocation.Scm: - return this._withViewletProgress('workbench.view.scm', task); - case ProgressLocation.Extensions: - return this._withViewletProgress('workbench.view.extensions', task); - case ProgressLocation.Dialog: - return this._withDialogProgress(options, task, onDidCancel); - default: - return Promise.reject(new Error(`Bad progress location: ${location}`)); - } - } - - private _withWindowProgress<R = unknown>(options: IProgressOptions, callback: (progress: IProgress<{ message?: string }>) => Promise<R>): Promise<R> { - - const task: [IProgressOptions, Progress<IProgressStep>] = [options, new Progress<IProgressStep>(() => this._updateWindowProgress())]; - - const promise = callback(task[1]); - - let delayHandle: any = setTimeout(() => { - delayHandle = undefined; - this._stack.unshift(task); - this._updateWindowProgress(); - - // show progress for at least 150ms - Promise.all([ - timeout(150), - promise - ]).finally(() => { - const idx = this._stack.indexOf(task); - this._stack.splice(idx, 1); - this._updateWindowProgress(); - }); - - }, 150); - - // cancel delay if promise finishes below 150ms - return promise.finally(() => clearTimeout(delayHandle)); - } - - private _updateWindowProgress(idx: number = 0) { - - dispose(this._globalStatusEntry); - - if (idx < this._stack.length) { - - const [options, progress] = this._stack[idx]; - - let progressTitle = options.title; - let progressMessage = progress.value && progress.value.message; - let text: string; - let title: string; - - if (progressTitle && progressMessage) { - // <title>: <message> - text = localize('progress.text2', "{0}: {1}", progressTitle, progressMessage); - title = options.source ? localize('progress.title3', "[{0}] {1}: {2}", options.source, progressTitle, progressMessage) : text; - - } else if (progressTitle) { - // <title> - text = progressTitle; - title = options.source ? localize('progress.title2', "[{0}]: {1}", options.source, progressTitle) : text; - - } else if (progressMessage) { - // <message> - text = progressMessage; - title = options.source ? localize('progress.title2', "[{0}]: {1}", options.source, progressMessage) : text; - - } else { - // no title, no message -> no progress. try with next on stack - this._updateWindowProgress(idx + 1); - return; - } - - this._globalStatusEntry = this._statusbarService.addEntry({ - text: `$(sync~spin) ${text}`, - tooltip: title - }, StatusbarAlignment.LEFT); - } - } - - private _withNotificationProgress<P extends Promise<R>, R = unknown>(options: IProgressNotificationOptions, callback: (progress: IProgress<{ message?: string, increment?: number }>) => P, onDidCancel?: () => void): P { - const toDispose: IDisposable[] = []; - - const createNotification = (message: string | undefined, increment?: number): INotificationHandle | undefined => { - if (!message) { - return undefined; // we need a message at least - } - - const actions: INotificationActions = { primary: options.primaryActions || [], secondary: options.secondaryActions || [] }; - if (options.cancellable) { - const cancelAction = new class extends Action { - constructor() { - super('progress.cancel', localize('cancel', "Cancel"), undefined, true); - } - - run(): Promise<any> { - if (typeof onDidCancel === 'function') { - onDidCancel(); - } - - return Promise.resolve(undefined); - } - }; - toDispose.push(cancelAction); - - actions.primary!.push(cancelAction); - } - - const handle = this._notificationService.notify({ - severity: Severity.Info, - message, - source: options.source, - actions - }); - - updateProgress(handle, increment); - - Event.once(handle.onDidClose)(() => { - dispose(toDispose); - }); - - return handle; - }; - - const updateProgress = (notification: INotificationHandle, increment?: number): void => { - if (typeof increment === 'number' && increment >= 0) { - notification.progress.total(100); // always percentage based - notification.progress.worked(increment); - } else { - notification.progress.infinite(); - } - }; - - let handle: INotificationHandle | undefined; - const updateNotification = (message?: string, increment?: number): void => { - if (!handle) { - handle = createNotification(message, increment); - } else { - if (typeof message === 'string') { - let newMessage: string; - if (typeof options.title === 'string') { - newMessage = `${options.title}: ${message}`; // always prefix with overall title if we have it (https://github.com/Microsoft/vscode/issues/50932) - } else { - newMessage = message; - } - - handle.updateMessage(newMessage); - } - - if (typeof increment === 'number') { - updateProgress(handle, increment); - } - } - }; - - // Show initially - updateNotification(options.title); - - // Update based on progress - const p = callback({ - report: progress => { - updateNotification(progress.message, progress.increment); - } - }); - - // Show progress for at least 800ms and then hide once done or canceled - Promise.all([timeout(800), p]).finally(() => { - if (handle) { - handle.close(); - } - }); - - return p; - } - - private _withViewletProgress<P extends Promise<R>, R = unknown>(viewletId: string, task: (progress: IProgress<{ message?: string }>) => P): P { - - const promise = task(emptyProgress); - - // show in viewlet - const viewletProgress = this._viewletService.getProgressIndicator(viewletId); - if (viewletProgress) { - viewletProgress.showWhile(promise); - } - - // show activity bar - let activityProgress: IDisposable; - let delayHandle: any = setTimeout(() => { - delayHandle = undefined; - const handle = this._activityBar.showActivity( - viewletId, - new ProgressBadge(() => ''), - 'progress-badge', - 100 - ); - const startTimeVisible = Date.now(); - const minTimeVisible = 300; - activityProgress = { - dispose() { - const d = Date.now() - startTimeVisible; - if (d < minTimeVisible) { - // should at least show for Nms - setTimeout(() => handle.dispose(), minTimeVisible - d); - } else { - // shown long enough - handle.dispose(); - } - } - }; - }, 300); - - const onDone = () => { - clearTimeout(delayHandle); - dispose(activityProgress); - }; - - promise.then(onDone, onDone); - return promise; - } - - private _withDialogProgress<P extends Promise<R>, R = unknown>(options: IProgressOptions, task: (progress: IProgress<{ message?: string, increment?: number }>) => P, onDidCancel?: () => void): P { - const disposables: IDisposable[] = []; - const allowableCommands = [ - 'workbench.action.quit', - 'workbench.action.reloadWindow' - ]; - - let dialog: Dialog; - - const createDialog = (message: string) => { - dialog = new Dialog( - this._layoutService.container, - message, - [options.cancellable ? localize('cancel', "Cancel") : localize('dismiss', "Dismiss")], - { - type: 'pending', - keyEventProcessor: (event: StandardKeyboardEvent) => { - const resolved = this._keybindingService.softDispatch(event, this._layoutService.container); - if (resolved && resolved.commandId) { - if (allowableCommands.indexOf(resolved.commandId) === -1) { - EventHelper.stop(event, true); - } - } - } - } - ); - - disposables.push(dialog); - disposables.push(attachDialogStyler(dialog, this._themeService)); - - dialog.show().then(() => { - if (typeof onDidCancel === 'function') { - onDidCancel(); - } - - dispose(dialog); - }); - - return dialog; - }; - - const updateDialog = (message?: string) => { - if (message && !dialog) { - dialog = createDialog(message); - } else if (message) { - dialog.updateMessage(message); - } - }; - - const p = task({ - report: progress => { - updateDialog(progress.message); - } - }); - - p.finally(() => { - dispose(disposables); - }); - - return p; - } -} - -registerSingleton(IProgressService2, ProgressService2, true); diff --git a/src/vs/workbench/services/progress/test/progressService.test.ts b/src/vs/workbench/services/progress/test/progressIndicator.test.ts similarity index 65% rename from src/vs/workbench/services/progress/test/progressService.test.ts rename to src/vs/workbench/services/progress/test/progressIndicator.test.ts index 58ac7214e7a..a8d2b740d0b 100644 --- a/src/vs/workbench/services/progress/test/progressService.test.ts +++ b/src/vs/workbench/services/progress/test/progressIndicator.test.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IAction, IActionItem } from 'vs/base/common/actions'; +import { IAction, IActionViewItem } from 'vs/base/common/actions'; import { IEditorControl } from 'vs/workbench/common/editor'; -import { ScopedProgressService, ScopedService } from 'vs/workbench/services/progress/browser/progressService'; +import { CompositeScope, CompositeProgressIndicator } from 'vs/workbench/services/progress/browser/progressIndicator'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IViewlet } from 'vs/workbench/common/viewlet'; @@ -16,106 +16,55 @@ class TestViewlet implements IViewlet { constructor(private id: string) { } - getId(): string { - return this.id; - } - - /** - * Returns the name of this composite to show in the title area. - */ - getTitle(): string { - return this.id; - } - - /** - * Returns the primary actions of the composite. - */ - getActions(): IAction[] { - return []; - } - - /** - * Returns the secondary actions of the composite. - */ - getSecondaryActions(): IAction[] { - return []; - } - - /** - * Returns an array of actions to show in the context menu of the composite - */ - public getContextMenuActions(): IAction[] { - return []; - } - - /** - * Returns the action item for a specific action. - */ - getActionItem(action: IAction): IActionItem { - return null!; - } - - /** - * Returns the underlying control of this composite. - */ - getControl(): IEditorControl { - return null!; - } - - /** - * Asks the underlying control to focus. - */ - focus(): void { - } - - getOptimalWidth(): number { - return 10; - } + getId(): string { return this.id; } + getTitle(): string { return this.id; } + getActions(): IAction[] { return []; } + getSecondaryActions(): IAction[] { return []; } + getContextMenuActions(): IAction[] { return []; } + getActionViewItem(action: IAction): IActionViewItem { return null!; } + getControl(): IEditorControl { return null!; } + focus(): void { } + getOptimalWidth(): number { return 10; } } -class TestScopedService extends ScopedService { - public isActive: boolean; +class TestCompositeScope extends CompositeScope { + isActive: boolean; constructor(viewletService: IViewletService, panelService: IPanelService, scopeId: string) { super(viewletService, panelService, scopeId); } - public onScopeActivated() { - this.isActive = true; - } - public onScopeDeactivated() { - this.isActive = false; - } + onScopeActivated() { this.isActive = true; } + onScopeDeactivated() { this.isActive = false; } } class TestProgressBar { - public fTotal: number; - public fWorked: number; - public fInfinite: boolean; - public fDone: boolean; + fTotal: number; + fWorked: number; + fInfinite: boolean; + fDone: boolean; - constructor() { - } + constructor() { } - public infinite() { + infinite() { this.fDone = null!; this.fInfinite = true; return this; } - public total(total: number) { + total(total: number) { this.fDone = null!; this.fTotal = total; return this; } - public hasTotal() { + hasTotal() { return !!this.fTotal; } - public worked(worked: number) { + worked(worked: number) { this.fDone = null!; if (this.fWorked) { @@ -127,7 +76,7 @@ class TestProgressBar { return this; } - public done() { + done() { this.fDone = true; this.fInfinite = null!; @@ -137,25 +86,21 @@ class TestProgressBar { return this; } - public stop() { + stop() { return this.done(); } - public show(): void { + show(): void { } - } - - public hide(): void { - - } + hide(): void { } } -suite('Progress Service', () => { +suite('Progress Indicator', () => { - test('ScopedService', () => { + test('CompositeScope', () => { let viewletService = new TestViewletService(); let panelService = new TestPanelService(); - let service = new TestScopedService(viewletService, panelService, 'test.scopeId'); + let service = new TestCompositeScope(viewletService, panelService, 'test.scopeId'); const testViewlet = new TestViewlet('test.scopeId'); assert(!service.isActive); @@ -167,11 +112,11 @@ suite('Progress Service', () => { }); - test('WorkbenchProgressService', async () => { + test('CompositeProgressIndicator', async () => { let testProgressBar = new TestProgressBar(); let viewletService = new TestViewletService(); let panelService = new TestPanelService(); - let service = new ScopedProgressService((<any>testProgressBar), 'test.scopeId', true, viewletService, panelService); + let service = new CompositeProgressIndicator((<any>testProgressBar), 'test.scopeId', true, viewletService, panelService); // Active: Show (Infinite) let fn = service.show(true); diff --git a/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts b/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts index c2e838965b1..ee8b3c9b9d2 100644 --- a/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts +++ b/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts @@ -3,23 +3,30 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IRemoteAgentConnection } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { AbstractRemoteAgentService } from 'vs/workbench/services/remote/common/abstractRemoteAgentService'; +import { AbstractRemoteAgentService, RemoteAgentConnection } from 'vs/workbench/services/remote/common/abstractRemoteAgentService'; import { IProductService } from 'vs/platform/product/common/product'; +import { browserWebSocketFactory } from 'vs/platform/remote/browser/browserWebSocketFactory'; +import { ISignService } from 'vs/platform/sign/common/sign'; export class RemoteAgentService extends AbstractRemoteAgentService { + private readonly _connection: IRemoteAgentConnection | null = null; + constructor( - @IEnvironmentService environmentService: IEnvironmentService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @IProductService productService: IProductService, - @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService + @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService, + @ISignService signService: ISignService ) { super(environmentService); + + this._connection = this._register(new RemoteAgentConnection(environmentService.configuration.remoteAuthority!, productService.commit, browserWebSocketFactory, environmentService, remoteAuthorityResolverService, signService)); } getConnection(): IRemoteAgentConnection | null { - return null; + return this._connection; } } diff --git a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts index 79b10c15b65..c5ece27cfb2 100644 --- a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts @@ -19,6 +19,7 @@ import { RemoteExtensionEnvironmentChannelClient } from 'vs/workbench/services/r import { INotificationService } from 'vs/platform/notification/common/notification'; import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnosticsService'; import { Emitter } from 'vs/base/common/event'; +import { ISignService } from 'vs/platform/sign/common/sign'; export abstract class AbstractRemoteAgentService extends Disposable implements IRemoteAgentService { @@ -84,7 +85,8 @@ export class RemoteAgentConnection extends Disposable implements IRemoteAgentCon private readonly _commit: string | undefined, private readonly _webSocketFactory: IWebSocketFactory, private readonly _environmentService: IEnvironmentService, - private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService + private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService, + private readonly _signService: ISignService ) { super(); this.remoteAuthority = remoteAuthority; @@ -122,7 +124,8 @@ export class RemoteAgentConnection extends Disposable implements IRemoteAgentCon const { host, port } = await this._remoteAuthorityResolverService.resolveAuthority(this.remoteAuthority); return { host, port }; } - } + }, + signService: this._signService }; const connection = this._register(await connectRemoteAgentManagement(options, this.remoteAuthority, `renderer`)); this._register(connection.onDidStateChange(e => this._onDidStateChange.fire(e))); diff --git a/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts b/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts index 09a5f340302..ae87e384bb4 100644 --- a/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts +++ b/src/vs/workbench/services/remote/common/remoteAgentEnvironmentChannel.ts @@ -34,28 +34,28 @@ export class RemoteExtensionEnvironmentChannelClient { constructor(private channel: IChannel) { } - getEnvironmentData(remoteAuthority: string, extensionDevelopmentPath?: URI[]): Promise<IRemoteAgentEnvironment> { + async getEnvironmentData(remoteAuthority: string, extensionDevelopmentPath?: URI[]): Promise<IRemoteAgentEnvironment> { const args: IGetEnvironmentDataArguments = { language: platform.language, remoteAuthority, extensionDevelopmentPath }; - return this.channel.call<IRemoteAgentEnvironmentDTO>('getEnvironmentData', args) - .then((data: IRemoteAgentEnvironmentDTO): IRemoteAgentEnvironment => { - return { - pid: data.pid, - appRoot: URI.revive(data.appRoot), - appSettingsHome: URI.revive(data.appSettingsHome), - settingsPath: URI.revive(data.settingsPath), - logsPath: URI.revive(data.logsPath), - extensionsPath: URI.revive(data.extensionsPath), - extensionHostLogsPath: URI.revive(data.extensionHostLogsPath), - globalStorageHome: URI.revive(data.globalStorageHome), - userHome: URI.revive(data.userHome), - extensions: data.extensions.map(ext => { (<any>ext).extensionLocation = URI.revive(ext.extensionLocation); return ext; }), - os: data.os - }; - }); + + const data = await this.channel.call<IRemoteAgentEnvironmentDTO>('getEnvironmentData', args); + + return { + pid: data.pid, + appRoot: URI.revive(data.appRoot), + appSettingsHome: URI.revive(data.appSettingsHome), + settingsPath: URI.revive(data.settingsPath), + logsPath: URI.revive(data.logsPath), + extensionsPath: URI.revive(data.extensionsPath), + extensionHostLogsPath: URI.revive(data.extensionHostLogsPath), + globalStorageHome: URI.revive(data.globalStorageHome), + userHome: URI.revive(data.userHome), + extensions: data.extensions.map(ext => { (<any>ext).extensionLocation = URI.revive(ext.extensionLocation); return ext; }), + os: data.os + }; } getDiagnosticInfo(options: IDiagnosticInfoOptions): Promise<IDiagnosticInfo> { diff --git a/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts b/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts index 4415f1cf118..d101f0c57dc 100644 --- a/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts +++ b/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts @@ -10,6 +10,7 @@ import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remot import product from 'vs/platform/product/node/product'; import { nodeWebSocketFactory } from 'vs/platform/remote/node/nodeWebSocketFactory'; import { AbstractRemoteAgentService, RemoteAgentConnection } from 'vs/workbench/services/remote/common/abstractRemoteAgentService'; +import { ISignService } from 'vs/platform/sign/common/sign'; export class RemoteAgentService extends AbstractRemoteAgentService { @@ -17,11 +18,12 @@ export class RemoteAgentService extends AbstractRemoteAgentService { constructor({ remoteAuthority }: IWindowConfiguration, @IEnvironmentService environmentService: IEnvironmentService, - @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService + @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService, + @ISignService signService: ISignService ) { super(environmentService); if (remoteAuthority) { - this._connection = this._register(new RemoteAgentConnection(remoteAuthority, product.commit, nodeWebSocketFactory, environmentService, remoteAuthorityResolverService)); + this._connection = this._register(new RemoteAgentConnection(remoteAuthority, product.commit, nodeWebSocketFactory, environmentService, remoteAuthorityResolverService, signService)); } } diff --git a/src/vs/workbench/services/remote/node/tunnelService.ts b/src/vs/workbench/services/remote/node/tunnelService.ts index 498b12773bb..b8e30ea867b 100644 --- a/src/vs/workbench/services/remote/node/tunnelService.ts +++ b/src/vs/workbench/services/remote/node/tunnelService.ts @@ -3,16 +3,114 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as net from 'net'; +import { Barrier } from 'vs/base/common/async'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import product from 'vs/platform/product/node/product'; +import { connectRemoteAgentTunnel, IConnectionOptions } from 'vs/platform/remote/common/remoteAgentConnection'; +import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; +import { nodeWebSocketFactory } from 'vs/platform/remote/node/nodeWebSocketFactory'; +import { ISignService } from 'vs/platform/sign/common/sign'; + +export async function createRemoteTunnel(options: IConnectionOptions, tunnelRemotePort: number): Promise<RemoteTunnel> { + const tunnel = new NodeRemoteTunnel(options, tunnelRemotePort); + return tunnel.waitForReady(); +} + +class NodeRemoteTunnel extends Disposable implements RemoteTunnel { + + public readonly tunnelRemotePort: number; + public readonly tunnelLocalPort: number; + + private readonly _options: IConnectionOptions; + private readonly _server: net.Server; + private readonly _barrier: Barrier; + + private readonly _listeningListener: () => void; + private readonly _connectionListener: (socket: net.Socket) => void; + + constructor(options: IConnectionOptions, tunnelRemotePort: number) { + super(); + this._options = options; + this._server = net.createServer(); + this._barrier = new Barrier(); + + this._listeningListener = () => this._barrier.open(); + this._server.on('listening', this._listeningListener); + + this._connectionListener = (socket) => this._onConnection(socket); + this._server.on('connection', this._connectionListener); + + this.tunnelRemotePort = tunnelRemotePort; + this.tunnelLocalPort = (<net.AddressInfo>this._server.listen(0).address()).port; + } + + public dispose(): void { + super.dispose(); + this._server.removeListener('listening', this._listeningListener); + this._server.removeListener('connection', this._connectionListener); + this._server.close(); + } + + public async waitForReady(): Promise<this> { + await this._barrier.wait(); + return this; + } + + private async _onConnection(localSocket: net.Socket): Promise<void> { + // pause reading on the socket until we have a chance to forward its data + localSocket.pause(); + + const protocol = await connectRemoteAgentTunnel(this._options, this.tunnelRemotePort); + const remoteSocket = (<NodeSocket>protocol.getSocket()).socket; + const dataChunk = protocol.readEntireBuffer(); + protocol.dispose(); + + if (dataChunk.byteLength > 0) { + localSocket.write(dataChunk.buffer); + } + + localSocket.on('end', () => remoteSocket.end()); + localSocket.on('close', () => remoteSocket.end()); + remoteSocket.on('end', () => localSocket.end()); + remoteSocket.on('close', () => localSocket.end()); + + localSocket.pipe(remoteSocket); + remoteSocket.pipe(localSocket); + } +} export class TunnelService implements ITunnelService { _serviceBrand: any; public constructor( + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, + @IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService, + @ISignService private readonly signService: ISignService ) { } openTunnel(remotePort: number): Promise<RemoteTunnel> | undefined { - return undefined; + const remoteAuthority = this.environmentService.configuration.remoteAuthority; + if (!remoteAuthority) { + return undefined; + } + + const options: IConnectionOptions = { + isBuilt: this.environmentService.isBuilt, + commit: product.commit, + webSocketFactory: nodeWebSocketFactory, + addressProvider: { + getAddress: async () => { + const { host, port } = await this.remoteAuthorityResolverService.resolveAuthority(remoteAuthority); + return { host, port }; + } + }, + signService: this.signService + }; + return createRemoteTunnel(options, remotePort); } } diff --git a/src/vs/workbench/services/search/common/searchService.ts b/src/vs/workbench/services/search/common/searchService.ts new file mode 100644 index 00000000000..8c793ca7a87 --- /dev/null +++ b/src/vs/workbench/services/search/common/searchService.ts @@ -0,0 +1,425 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as arrays from 'vs/base/common/arrays'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { canceled } from 'vs/base/common/errors'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { keys, ResourceMap, values } from 'vs/base/common/map'; +import { Schemas } from 'vs/base/common/network'; +import { StopWatch } from 'vs/base/common/stopwatch'; +import { URI as uri } from 'vs/base/common/uri'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IFileService } from 'vs/platform/files/common/files'; +import { ILogService } from 'vs/platform/log/common/log'; +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 { deserializeSearchError, FileMatch, ICachedSearchStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, IProgressMessage, ISearchComplete, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, ITextQuery, pathIncludedInQuery, QueryType, SearchError, SearchErrorCode, SearchProviderType, isFileMatch, isProgressMessage } from 'vs/workbench/services/search/common/search'; +import { addContextToEditorMatches, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; +import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; + +export class SearchService extends Disposable implements ISearchService { + _serviceBrand: any; + + protected diskSearch: ISearchResultProvider; + private readonly fileSearchProviders = new Map<string, ISearchResultProvider>(); + private readonly textSearchProviders = new Map<string, ISearchResultProvider>(); + + constructor( + private readonly modelService: IModelService, + private readonly untitledEditorService: IUntitledEditorService, + private readonly editorService: IEditorService, + private readonly telemetryService: ITelemetryService, + private readonly logService: ILogService, + private readonly extensionService: IExtensionService, + private readonly fileService: IFileService + ) { + super(); + } + + registerSearchResultProvider(scheme: string, type: SearchProviderType, provider: ISearchResultProvider): IDisposable { + let list: Map<string, ISearchResultProvider>; + if (type === SearchProviderType.file) { + list = this.fileSearchProviders; + } else if (type === SearchProviderType.text) { + list = this.textSearchProviders; + } else { + throw new Error('Unknown SearchProviderType'); + } + + list.set(scheme, provider); + + return toDisposable(() => { + list.delete(scheme); + }); + } + + textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): Promise<ISearchComplete> { + // Get local results from dirty/untitled + const localResults = this.getLocalResults(query); + + if (onProgress) { + arrays.coalesce(localResults.values()).forEach(onProgress); + } + + const onProviderProgress = (progress: ISearchProgressItem) => { + if (isFileMatch(progress)) { + // Match + if (!localResults.has(progress.resource) && onProgress) { // don't override local results + onProgress(progress); + } + } else if (onProgress) { + // Progress + onProgress(<IProgressMessage>progress); + } + + if (isProgressMessage(progress)) { + this.logService.debug('SearchService#search', progress.message); + } + }; + + return this.doSearch(query, token, onProviderProgress); + } + + fileSearch(query: IFileQuery, token?: CancellationToken): Promise<ISearchComplete> { + return this.doSearch(query, token); + } + + private doSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): Promise<ISearchComplete> { + this.logService.trace('SearchService#search', JSON.stringify(query)); + + const schemesInQuery = this.getSchemesInQuery(query); + + const providerActivations: Promise<any>[] = [Promise.resolve(null)]; + schemesInQuery.forEach(scheme => providerActivations.push(this.extensionService.activateByEvent(`onSearch:${scheme}`))); + providerActivations.push(this.extensionService.activateByEvent('onSearch:file')); + + 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 Promise.reject(canceled()); + } + + const progressCallback = (item: ISearchProgressItem) => { + if (token && token.isCancellationRequested) { + return; + } + + if (onProgress) { + onProgress(item); + } + }; + + return this.searchWithProviders(query, progressCallback, token); + }) + .then(completes => { + completes = arrays.coalesce(completes); + if (!completes.length) { + return { + limitHit: false, + results: [] + }; + } + + return <ISearchComplete>{ + limitHit: completes[0] && completes[0].limitHit, + stats: completes[0].stats, + results: arrays.flatten(completes.map((c: ISearchComplete) => c.results)) + }; + }); + + return new Promise((resolve, reject) => { + if (token) { + token.onCancellationRequested(() => { + reject(canceled()); + }); + } + + providerPromise.then(resolve, reject); + }); + } + + private getSchemesInQuery(query: ISearchQuery): Set<string> { + const schemes = new Set<string>(); + if (query.folderQueries) { + query.folderQueries.forEach(fq => schemes.add(fq.folder.scheme)); + } + + if (query.extraFileResources) { + query.extraFileResources.forEach(extraFile => schemes.add(extraFile.scheme)); + } + + return schemes; + } + + private searchWithProviders(query: ISearchQuery, onProviderProgress: (progress: ISearchProgressItem) => void, token?: CancellationToken) { + const e2eSW = StopWatch.create(false); + + const diskSearchQueries: IFolderQuery[] = []; + const searchPs: Promise<ISearchComplete>[] = []; + + const fqs = this.groupFolderQueriesByScheme(query); + keys(fqs).forEach(scheme => { + const schemeFQs = fqs.get(scheme)!; + const provider = query.type === QueryType.File ? + this.fileSearchProviders.get(scheme) : + this.textSearchProviders.get(scheme); + + if (!provider && scheme === 'file') { + diskSearchQueries.push(...schemeFQs); + } else if (!provider) { + console.warn('No search provider registered for scheme: ' + scheme); + } else { + const oneSchemeQuery: ISearchQuery = { + ...query, + ...{ + folderQueries: schemeFQs + } + }; + + searchPs.push(query.type === QueryType.File ? + provider.fileSearch(<IFileQuery>oneSchemeQuery, token) : + provider.textSearch(<ITextQuery>oneSchemeQuery, onProviderProgress, token)); + } + }); + + const diskSearchExtraFileResources = query.extraFileResources && query.extraFileResources.filter(res => res.scheme === Schemas.file); + + if (diskSearchQueries.length || diskSearchExtraFileResources) { + const diskSearchQuery: ISearchQuery = { + ...query, + ...{ + folderQueries: diskSearchQueries + }, + extraFileResources: diskSearchExtraFileResources + }; + + + if (this.diskSearch) { + searchPs.push(diskSearchQuery.type === QueryType.File ? + this.diskSearch.fileSearch(diskSearchQuery, token) : + this.diskSearch.textSearch(diskSearchQuery, onProviderProgress, token)); + } + } + + 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; + }, err => { + const endToEndTime = e2eSW.elapsed(); + this.logService.trace(`SearchService#search: ${endToEndTime}ms`); + const searchError = deserializeSearchError(err.message); + this.sendTelemetry(query, endToEndTime, undefined, searchError); + + throw searchError; + }); + } + + private groupFolderQueriesByScheme(query: ISearchQuery): Map<string, IFolderQuery[]> { + const queries = new Map<string, IFolderQuery[]>(); + + query.folderQueries.forEach(fq => { + const schemeFQs = queries.get(fq.folder.scheme) || []; + schemeFQs.push(fq); + + queries.set(fq.folder.scheme, schemeFQs); + }); + + return queries; + } + + 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' : + otherSchemeOnly ? 'other' : + 'mixed'; + + if (query.type === QueryType.File && complete && complete.stats) { + const fileSearchStats = complete.stats as IFileSearchStats; + if (fileSearchStats.fromCache) { + const cacheStats: ICachedSearchStats = fileSearchStats.detailStats as ICachedSearchStats; + + /* __GDPR__ + "cachedSearchComplete" : { + "reason" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "resultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "workspaceFolderCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "endToEndTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "sortingTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "cacheWasResolved" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "cacheLookupTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "cacheFilterTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "cacheEntryCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "scheme" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } + } + */ + this.telemetryService.publicLog('cachedSearchComplete', { + reason: query._reason, + resultCount: fileSearchStats.resultCount, + workspaceFolderCount: query.folderQueries.length, + type: fileSearchStats.type, + endToEndTime: endToEndTime, + sortingTime: fileSearchStats.sortingTime, + cacheWasResolved: cacheStats.cacheWasResolved, + cacheLookupTime: cacheStats.cacheLookupTime, + cacheFilterTime: cacheStats.cacheFilterTime, + cacheEntryCount: cacheStats.cacheEntryCount, + scheme + }); + } else { + const searchEngineStats: ISearchEngineStats = fileSearchStats.detailStats as ISearchEngineStats; + + /* __GDPR__ + "searchComplete" : { + "reason" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "resultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "workspaceFolderCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "endToEndTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "sortingTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "fileWalkTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "directoriesWalked" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "filesWalked" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "cmdTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "cmdResultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "scheme" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } + } + */ + this.telemetryService.publicLog('searchComplete', { + reason: query._reason, + resultCount: fileSearchStats.resultCount, + workspaceFolderCount: query.folderQueries.length, + type: fileSearchStats.type, + endToEndTime: endToEndTime, + sortingTime: fileSearchStats.sortingTime, + fileWalkTime: searchEngineStats.fileWalkTime, + directoriesWalked: searchEngineStats.directoriesWalked, + filesWalked: searchEngineStats.filesWalked, + cmdTime: searchEngineStats.cmdTime, + cmdResultCount: searchEngineStats.cmdResultCount, + scheme + }); + } + } else if (query.type === QueryType.Text) { + let errorType: string | undefined; + if (err) { + 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__ + "textSearchComplete" : { + "reason" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "workspaceFolderCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "endToEndTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, + "scheme" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "error" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "useRipgrep" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, + "usePCRE2" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } + } + */ + this.telemetryService.publicLog('textSearchComplete', { + reason: query._reason, + workspaceFolderCount: query.folderQueries.length, + endToEndTime: endToEndTime, + scheme, + error: errorType, + usePCRE2: !!query.usePCRE2 + }); + } + } + + private getLocalResults(query: ITextQuery): ResourceMap<IFileMatch | null> { + const localResults = new ResourceMap<IFileMatch | null>(); + + if (query.type === QueryType.Text) { + const models = this.modelService.getModels(); + models.forEach((model) => { + const resource = model.uri; + if (!resource) { + return; + } + + if (!this.editorService.isOpen({ resource })) { + return; + } + + // Support untitled files + if (resource.scheme === Schemas.untitled) { + if (!this.untitledEditorService.exists(resource)) { + return; + } + } + + // Block walkthrough, webview, etc. + else if (!this.fileService.canHandleResource(resource)) { + return; + } + + if (!this.matches(resource, query)) { + return; // respect user filters + } + + // Use editor API to find matches + const matches = model.findMatches(query.contentPattern.pattern, false, !!query.contentPattern.isRegExp, !!query.contentPattern.isCaseSensitive, query.contentPattern.isWordMatch ? query.contentPattern.wordSeparators! : null, false, query.maxResults); + if (matches.length) { + const fileMatch = new FileMatch(resource); + localResults.set(resource, fileMatch); + + const textSearchResults = editorMatchesToTextSearchResults(matches, model, query.previewOptions); + fileMatch.results = addContextToEditorMatches(textSearchResults, model, query); + } else { + localResults.set(resource, null); + } + }); + } + + return localResults; + } + + private matches(resource: uri, query: ITextQuery): boolean { + return pathIncludedInQuery(query, resource.fsPath); + } + + clearCache(cacheKey: string): Promise<void> { + const clearPs = [ + this.diskSearch, + ...values(this.fileSearchProviders) + ].map(provider => provider && provider.clearCache(cacheKey)); + + return Promise.all(clearPs) + .then(() => { }); + } +} + +export class RemoteSearchService extends SearchService { + constructor( + @IModelService modelService: IModelService, + @IUntitledEditorService untitledEditorService: IUntitledEditorService, + @IEditorService editorService: IEditorService, + @ITelemetryService telemetryService: ITelemetryService, + @ILogService logService: ILogService, + @IExtensionService extensionService: IExtensionService, + @IFileService fileService: IFileService + ) { + super(modelService, untitledEditorService, editorService, telemetryService, logService, extensionService, fileService); + } +} + +registerSingleton(ISearchService, RemoteSearchService, true); diff --git a/src/vs/workbench/services/search/node/fileSearch.ts b/src/vs/workbench/services/search/node/fileSearch.ts index 612bd746dfb..5ade08b60ca 100644 --- a/src/vs/workbench/services/search/node/fileSearch.ts +++ b/src/vs/workbench/services/search/node/fileSearch.ts @@ -22,6 +22,7 @@ import { URI } from 'vs/base/common/uri'; import { readdir } from 'vs/base/node/pfs'; import { IFileQuery, IFolderQuery, IProgressMessage, ISearchEngineStats, IRawFileMatch, ISearchEngine, ISearchEngineSuccess } from 'vs/workbench/services/search/common/search'; import { spawnRipgrepCmd } from './ripgrepFileSearch'; +import { prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer'; interface IDirectoryEntry { base: string; @@ -76,7 +77,7 @@ export class FileWalker { this.errors = []; if (this.filePattern) { - this.normalizedFilePatternLowercase = strings.stripWildcards(this.filePattern).toLowerCase(); + this.normalizedFilePatternLowercase = prepareQuery(this.filePattern).lowercase; } this.globalExcludePattern = config.excludePattern && glob.parse(config.excludePattern); diff --git a/src/vs/workbench/services/search/node/rawSearchService.ts b/src/vs/workbench/services/search/node/rawSearchService.ts index 1d5ed2ca55e..2a27d205aac 100644 --- a/src/vs/workbench/services/search/node/rawSearchService.ts +++ b/src/vs/workbench/services/search/node/rawSearchService.ts @@ -16,7 +16,7 @@ import { StopWatch } from 'vs/base/common/stopwatch'; import * as strings from 'vs/base/common/strings'; import { URI, UriComponents } from 'vs/base/common/uri'; import { compareItemsByScore, IItemAccessor, prepareQuery, ScorerCache } from 'vs/base/parts/quickopen/common/quickOpenScorer'; -import { MAX_FILE_SIZE } from 'vs/platform/files/node/fileConstants'; +import { MAX_FILE_SIZE } from 'vs/base/node/pfs'; import { ICachedSearchStats, IFileQuery, IFileSearchStats, IFolderQuery, IProgressMessage, IRawFileQuery, IRawQuery, IRawTextQuery, ITextQuery, IFileSearchProgressItem, IRawFileMatch, IRawSearchService, ISearchEngine, ISearchEngineSuccess, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedSearchSuccess } from 'vs/workbench/services/search/common/search'; import { Engine as FileSearchEngine } from 'vs/workbench/services/search/node/fileSearch'; import { TextSearchEngineAdapter } from 'vs/workbench/services/search/node/textSearchAdapter'; @@ -312,7 +312,7 @@ export class SearchService implements IRawSearchService { // Pattern match on results const results: IRawFileMatch[] = []; - const normalizedSearchValueLowercase = strings.stripWildcards(searchValue).toLowerCase(); + const normalizedSearchValueLowercase = prepareQuery(searchValue).lowercase; for (const entry of cachedEntries) { // Check if this entry is a match for the search value diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index fcef06a15fb..53de732e011 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -4,416 +4,44 @@ *--------------------------------------------------------------------------------------------*/ import { getPathFromAmdModule } from 'vs/base/common/amd'; -import * as arrays from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { canceled } from 'vs/base/common/errors'; import { Event } from 'vs/base/common/event'; -import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { keys, ResourceMap, values } from 'vs/base/common/map'; -import { Schemas } from 'vs/base/common/network'; -import { StopWatch } from 'vs/base/common/stopwatch'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { URI as uri } from 'vs/base/common/uri'; -import * as pfs from 'vs/base/node/pfs'; import { getNextTickChannel } from 'vs/base/parts/ipc/common/ipc'; import { Client, IIPCOptions } from 'vs/base/parts/ipc/node/ipc.cp'; -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 { IFileService } from 'vs/platform/files/common/files'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; -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 { deserializeSearchError, FileMatch, ICachedSearchStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, IProgressMessage, IRawSearchService, ISearchComplete, ISearchConfiguration, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess, ITextQuery, pathIncludedInQuery, QueryType, SearchError, SearchErrorCode, SearchProviderType, isFileMatch, isProgressMessage } from 'vs/workbench/services/search/common/search'; -import { addContextToEditorMatches, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; -import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { FileMatch, IFileMatch, IFileQuery, IProgressMessage, IRawSearchService, ISearchComplete, ISearchConfiguration, ISearchProgressItem, ISearchResultProvider, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess, ITextQuery, ISearchService } from 'vs/workbench/services/search/common/search'; import { SearchChannelClient } from './searchIpc'; +import { SearchService } from 'vs/workbench/services/search/common/searchService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -export class SearchService extends Disposable implements ISearchService { - _serviceBrand: any; - - private diskSearch: DiskSearch; - private readonly fileSearchProviders = new Map<string, ISearchResultProvider>(); - private readonly textSearchProviders = new Map<string, ISearchResultProvider>(); - +export class LocalSearchService extends SearchService { constructor( - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IModelService private readonly modelService: IModelService, - @IUntitledEditorService private readonly untitledEditorService: IUntitledEditorService, - @IEditorService private readonly editorService: IEditorService, - @IEnvironmentService environmentService: IEnvironmentService, - @ITelemetryService private readonly telemetryService: ITelemetryService, - @ILogService private readonly logService: ILogService, - @IExtensionService private readonly extensionService: IExtensionService, - @IFileService private readonly fileService: IFileService + @IModelService modelService: IModelService, + @IUntitledEditorService untitledEditorService: IUntitledEditorService, + @IEditorService editorService: IEditorService, + @ITelemetryService telemetryService: ITelemetryService, + @ILogService logService: ILogService, + @IExtensionService extensionService: IExtensionService, + @IFileService fileService: IFileService, + @IEnvironmentService readonly environmentService: IEnvironmentService, + @IInstantiationService readonly instantiationService: IInstantiationService ) { - super(); - this.diskSearch = this.instantiationService.createInstance(DiskSearch, !environmentService.isBuilt || environmentService.verbose, environmentService.debugSearch); - } + super(modelService, untitledEditorService, editorService, telemetryService, logService, extensionService, fileService); - registerSearchResultProvider(scheme: string, type: SearchProviderType, provider: ISearchResultProvider): IDisposable { - let list: Map<string, ISearchResultProvider>; - if (type === SearchProviderType.file) { - list = this.fileSearchProviders; - } else if (type === SearchProviderType.text) { - list = this.textSearchProviders; - } else { - throw new Error('Unknown SearchProviderType'); - } - list.set(scheme, provider); - - return toDisposable(() => { - list.delete(scheme); - }); - } - - textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): Promise<ISearchComplete> { - // Get local results from dirty/untitled - const localResults = this.getLocalResults(query); - - if (onProgress) { - arrays.coalesce(localResults.values()).forEach(onProgress); - } - - const onProviderProgress = (progress: ISearchProgressItem) => { - if (isFileMatch(progress)) { - // Match - if (!localResults.has(progress.resource) && onProgress) { // don't override local results - onProgress(progress); - } - } else if (onProgress) { - // Progress - onProgress(<IProgressMessage>progress); - } - - if (isProgressMessage(progress)) { - this.logService.debug('SearchService#search', progress.message); - } - }; - - return this.doSearch(query, token, onProviderProgress); - } - - fileSearch(query: IFileQuery, token?: CancellationToken): Promise<ISearchComplete> { - return this.doSearch(query, token); - } - - private doSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): Promise<ISearchComplete> { - this.logService.trace('SearchService#search', JSON.stringify(query)); - - const schemesInQuery = this.getSchemesInQuery(query); - - const providerActivations: Promise<any>[] = [Promise.resolve(null)]; - schemesInQuery.forEach(scheme => providerActivations.push(this.extensionService.activateByEvent(`onSearch:${scheme}`))); - providerActivations.push(this.extensionService.activateByEvent('onSearch:file')); - - 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 Promise.reject(canceled()); - } - - const progressCallback = (item: ISearchProgressItem) => { - if (token && token.isCancellationRequested) { - return; - } - - if (onProgress) { - onProgress(item); - } - }; - - return this.searchWithProviders(query, progressCallback, token); - }) - .then(completes => { - completes = arrays.coalesce(completes); - if (!completes.length) { - return { - limitHit: false, - results: [] - }; - } - - return <ISearchComplete>{ - limitHit: completes[0] && completes[0].limitHit, - stats: completes[0].stats, - results: arrays.flatten(completes.map((c: ISearchComplete) => c.results)) - }; - }); - - return new Promise((resolve, reject) => { - if (token) { - token.onCancellationRequested(() => { - reject(canceled()); - }); - } - - providerPromise.then(resolve, reject); - }); - } - - private getSchemesInQuery(query: ISearchQuery): Set<string> { - const schemes = new Set<string>(); - if (query.folderQueries) { - query.folderQueries.forEach(fq => schemes.add(fq.folder.scheme)); - } - - if (query.extraFileResources) { - query.extraFileResources.forEach(extraFile => schemes.add(extraFile.scheme)); - } - - return schemes; - } - - private searchWithProviders(query: ISearchQuery, onProviderProgress: (progress: ISearchProgressItem) => void, token?: CancellationToken) { - const e2eSW = StopWatch.create(false); - - const diskSearchQueries: IFolderQuery[] = []; - const searchPs: Promise<ISearchComplete>[] = []; - - const fqs = this.groupFolderQueriesByScheme(query); - keys(fqs).forEach(scheme => { - const schemeFQs = fqs.get(scheme)!; - const provider = query.type === QueryType.File ? - this.fileSearchProviders.get(scheme) : - this.textSearchProviders.get(scheme); - - if (!provider && scheme === 'file') { - diskSearchQueries.push(...schemeFQs); - } else if (!provider) { - console.warn('No search provider registered for scheme: ' + scheme); - } else { - const oneSchemeQuery: ISearchQuery = { - ...query, - ...{ - folderQueries: schemeFQs - } - }; - - searchPs.push(query.type === QueryType.File ? - provider.fileSearch(<IFileQuery>oneSchemeQuery, token) : - provider.textSearch(<ITextQuery>oneSchemeQuery, onProviderProgress, token)); - } - }); - - const diskSearchExtraFileResources = query.extraFileResources && query.extraFileResources.filter(res => res.scheme === Schemas.file); - - if (diskSearchQueries.length || diskSearchExtraFileResources) { - const diskSearchQuery: ISearchQuery = { - ...query, - ...{ - folderQueries: diskSearchQueries - }, - extraFileResources: diskSearchExtraFileResources - }; - - searchPs.push(diskSearchQuery.type === QueryType.File ? - this.diskSearch.fileSearch(diskSearchQuery, token) : - this.diskSearch.textSearch(diskSearchQuery, onProviderProgress, token)); - } - - 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; - }, err => { - const endToEndTime = e2eSW.elapsed(); - this.logService.trace(`SearchService#search: ${endToEndTime}ms`); - const searchError = deserializeSearchError(err.message); - this.sendTelemetry(query, endToEndTime, undefined, searchError); - - throw searchError; - }); - } - - private groupFolderQueriesByScheme(query: ISearchQuery): Map<string, IFolderQuery[]> { - const queries = new Map<string, IFolderQuery[]>(); - - query.folderQueries.forEach(fq => { - const schemeFQs = queries.get(fq.folder.scheme) || []; - schemeFQs.push(fq); - - queries.set(fq.folder.scheme, schemeFQs); - }); - - return queries; - } - - 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' : - otherSchemeOnly ? 'other' : - 'mixed'; - - if (query.type === QueryType.File && complete && complete.stats) { - const fileSearchStats = complete.stats as IFileSearchStats; - if (fileSearchStats.fromCache) { - const cacheStats: ICachedSearchStats = fileSearchStats.detailStats as ICachedSearchStats; - - /* __GDPR__ - "cachedSearchComplete" : { - "reason" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "resultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "workspaceFolderCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "endToEndTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "sortingTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "cacheWasResolved" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "cacheLookupTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "cacheFilterTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "cacheEntryCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "scheme" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } - } - */ - this.telemetryService.publicLog('cachedSearchComplete', { - reason: query._reason, - resultCount: fileSearchStats.resultCount, - workspaceFolderCount: query.folderQueries.length, - type: fileSearchStats.type, - endToEndTime: endToEndTime, - sortingTime: fileSearchStats.sortingTime, - cacheWasResolved: cacheStats.cacheWasResolved, - cacheLookupTime: cacheStats.cacheLookupTime, - cacheFilterTime: cacheStats.cacheFilterTime, - cacheEntryCount: cacheStats.cacheEntryCount, - scheme - }); - } else { - const searchEngineStats: ISearchEngineStats = fileSearchStats.detailStats as ISearchEngineStats; - - /* __GDPR__ - "searchComplete" : { - "reason" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "resultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "workspaceFolderCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "endToEndTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "sortingTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "fileWalkTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "directoriesWalked" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "filesWalked" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "cmdTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "cmdResultCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "scheme" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } - } - */ - this.telemetryService.publicLog('searchComplete', { - reason: query._reason, - resultCount: fileSearchStats.resultCount, - workspaceFolderCount: query.folderQueries.length, - type: fileSearchStats.type, - endToEndTime: endToEndTime, - sortingTime: fileSearchStats.sortingTime, - fileWalkTime: searchEngineStats.fileWalkTime, - directoriesWalked: searchEngineStats.directoriesWalked, - filesWalked: searchEngineStats.filesWalked, - cmdTime: searchEngineStats.cmdTime, - cmdResultCount: searchEngineStats.cmdResultCount, - scheme - }); - } - } else if (query.type === QueryType.Text) { - let errorType: string | undefined; - if (err) { - 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__ - "textSearchComplete" : { - "reason" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "workspaceFolderCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "endToEndTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "scheme" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "error" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "useRipgrep" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }, - "usePCRE2" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } - } - */ - this.telemetryService.publicLog('textSearchComplete', { - reason: query._reason, - workspaceFolderCount: query.folderQueries.length, - endToEndTime: endToEndTime, - scheme, - error: errorType, - usePCRE2: !!query.usePCRE2 - }); - } - } - - private getLocalResults(query: ITextQuery): ResourceMap<IFileMatch | null> { - const localResults = new ResourceMap<IFileMatch | null>(); - - if (query.type === QueryType.Text) { - const models = this.modelService.getModels(); - models.forEach((model) => { - const resource = model.uri; - if (!resource) { - return; - } - - if (!this.editorService.isOpen({ resource })) { - return; - } - - // Support untitled files - if (resource.scheme === Schemas.untitled) { - if (!this.untitledEditorService.exists(resource)) { - return; - } - } - - // Block walkthrough, webview, etc. - else if (!this.fileService.canHandleResource(resource)) { - return; - } - - if (!this.matches(resource, query)) { - return; // respect user filters - } - - // Use editor API to find matches - const matches = model.findMatches(query.contentPattern.pattern, false, !!query.contentPattern.isRegExp, !!query.contentPattern.isCaseSensitive, query.contentPattern.isWordMatch ? query.contentPattern.wordSeparators! : null, false, query.maxResults); - if (matches.length) { - const fileMatch = new FileMatch(resource); - localResults.set(resource, fileMatch); - - const textSearchResults = editorMatchesToTextSearchResults(matches, model, query.previewOptions); - fileMatch.results = addContextToEditorMatches(textSearchResults, model, query); - } else { - localResults.set(resource, null); - } - }); - } - - return localResults; - } - - private matches(resource: uri, query: ITextQuery): boolean { - return pathIncludedInQuery(query, resource.fsPath); - } - - clearCache(cacheKey: string): Promise<void> { - const clearPs = [ - this.diskSearch, - ...values(this.fileSearchProviders) - ].map(provider => provider && provider.clearCache(cacheKey)); - - return Promise.all(clearPs) - .then(() => { }); + this.diskSearch = instantiationService.createInstance(DiskSearch, !environmentService.isBuilt || environmentService.verbose, environmentService.debugSearch); } } @@ -425,6 +53,7 @@ export class DiskSearch implements ISearchResultProvider { searchDebug: IDebugParams | undefined, @ILogService private readonly logService: ILogService, @IConfigurationService private readonly configService: IConfigurationService, + @IFileService private readonly fileService: IFileService ) { const timeout = this.configService.getValue<ISearchConfiguration>().search.maintainFileSearchCache ? Number.MAX_VALUE : @@ -465,7 +94,7 @@ export class DiskSearch implements ISearchResultProvider { textSearch(query: ITextQuery, onProgress?: (p: ISearchProgressItem) => void, token?: CancellationToken): Promise<ISearchComplete> { const folderQueries = query.folderQueries || []; - return Promise.all(folderQueries.map(q => q.folder.scheme === Schemas.file && pfs.exists(q.folder.fsPath))) + return Promise.all(folderQueries.map(q => this.fileService.exists(q.folder))) .then(exists => { if (token && token.isCancellationRequested) { throw canceled(); @@ -480,7 +109,7 @@ export class DiskSearch implements ISearchResultProvider { fileSearch(query: IFileQuery, token?: CancellationToken): Promise<ISearchComplete> { const folderQueries = query.folderQueries || []; - return Promise.all(folderQueries.map(q => q.folder.scheme === Schemas.file && pfs.exists(q.folder.fsPath))) + return Promise.all(folderQueries.map(q => this.fileService.exists(q.folder))) .then(exists => { if (token && token.isCancellationRequested) { throw canceled(); @@ -575,4 +204,4 @@ export class DiskSearch implements ISearchResultProvider { } } -registerSingleton(ISearchService, SearchService, true); \ No newline at end of file +registerSingleton(ISearchService, LocalSearchService, true); \ No newline at end of file diff --git a/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts b/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts index d905178fccf..5583a243cb2 100644 --- a/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts @@ -16,6 +16,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { resolveWorkbenchCommonProperties } from 'vs/platform/telemetry/node/workbenchCommonProperties'; import { TelemetryService as BaseTelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings'; export class TelemetryService extends Disposable implements ITelemetryService { @@ -37,7 +38,7 @@ export class TelemetryService extends Disposable implements ITelemetryService { const channel = sharedProcessService.getChannel('telemetryAppender'); const config: ITelemetryServiceConfig = { appender: combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(logService)), - commonProperties: resolveWorkbenchCommonProperties(storageService, productService.commit, productService.version, environmentService.configuration.machineId, environmentService.installSourcePath), + commonProperties: resolveWorkbenchCommonProperties(storageService, productService.commit, productService.version, environmentService.configuration.machineId, environmentService.installSourcePath, environmentService.configuration.remoteAuthority), piiPaths: [environmentService.appRoot, environmentService.extensionsPath] }; @@ -59,6 +60,10 @@ export class TelemetryService extends Disposable implements ITelemetryService { return this.impl.publicLog(eventName, data, anonymizeFilePaths); } + publicLog2<E extends ClassifiedEvent<T> = never, T extends GDPRClassification<T> = never>(eventName: string, data?: StrictPropertyCheck<T, E>, anonymizeFilePaths?: boolean) { + return this.publicLog(eventName, data as ITelemetryData, anonymizeFilePaths); + } + getTelemetryInfo(): Promise<ITelemetryInfo> { return this.impl.getTelemetryInfo(); } diff --git a/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts b/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts new file mode 100644 index 00000000000..e25b10d9696 --- /dev/null +++ b/src/vs/workbench/services/textMate/browser/abstractTextMateService.ts @@ -0,0 +1,524 @@ +/*--------------------------------------------------------------------------------------------- + * 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 * as dom from 'vs/base/browser/dom'; +import { Color } from 'vs/base/common/color'; +import { onUnexpectedError } from 'vs/base/common/errors'; +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 { 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'; +import { generateTokensCSSForColorMap } from 'vs/editor/common/modes/supports/tokenization'; +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 { ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { IEmbeddedLanguagesMap, ITMSyntaxExtensionPoint, TokenTypesContribution, grammarsExtPoint } from 'vs/workbench/services/textMate/common/TMGrammars'; +import { ITextMateService } from 'vs/workbench/services/textMate/common/textMateService'; +import { ITokenColorizationRule, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IEmbeddedLanguagesMap as IEmbeddedLanguagesMap2, IGrammar, ITokenTypeMap, Registry, StackElement, StandardTokenType, RegistryOptions, IRawGrammar } from 'vscode-textmate'; +import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + +export class TMScopeRegistry extends Disposable { + + private _scopeNameToLanguageRegistration: { [scopeName: string]: TMLanguageRegistration; }; + private _encounteredLanguages: boolean[]; + + private readonly _onDidEncounterLanguage = this._register(new Emitter<LanguageId>()); + public readonly onDidEncounterLanguage: Event<LanguageId> = this._onDidEncounterLanguage.event; + + constructor() { + super(); + this.reset(); + } + + public reset(): void { + this._scopeNameToLanguageRegistration = Object.create(null); + this._encounteredLanguages = []; + } + + public register(scopeName: string, grammarLocation: URI, embeddedLanguages?: IEmbeddedLanguagesMap, tokenTypes?: TokenTypesContribution): void { + if (this._scopeNameToLanguageRegistration[scopeName]) { + const existingRegistration = this._scopeNameToLanguageRegistration[scopeName]; + if (!resources.isEqual(existingRegistration.grammarLocation, grammarLocation)) { + console.warn( + `Overwriting grammar scope name to file mapping for scope ${scopeName}.\n` + + `Old grammar file: ${existingRegistration.grammarLocation.toString()}.\n` + + `New grammar file: ${grammarLocation.toString()}` + ); + } + } + this._scopeNameToLanguageRegistration[scopeName] = new TMLanguageRegistration(scopeName, grammarLocation, embeddedLanguages, tokenTypes); + } + + public getLanguageRegistration(scopeName: string): TMLanguageRegistration { + return this._scopeNameToLanguageRegistration[scopeName] || null; + } + + public getGrammarLocation(scopeName: string): URI | null { + let data = this.getLanguageRegistration(scopeName); + return data ? data.grammarLocation : null; + } + + /** + * To be called when tokenization found/hit an embedded language. + */ + public onEncounteredLanguage(languageId: LanguageId): void { + if (!this._encounteredLanguages[languageId]) { + this._encounteredLanguages[languageId] = true; + this._onDidEncounterLanguage.fire(languageId); + } + } +} + +export class TMLanguageRegistration { + _topLevelScopeNameDataBrand: void; + + readonly scopeName: string; + readonly grammarLocation: URI; + readonly embeddedLanguages: IEmbeddedLanguagesMap; + readonly tokenTypes: ITokenTypeMap; + + constructor(scopeName: string, grammarLocation: URI, embeddedLanguages: IEmbeddedLanguagesMap | undefined, tokenTypes: TokenTypesContribution | undefined) { + this.scopeName = scopeName; + this.grammarLocation = grammarLocation; + + // embeddedLanguages handling + this.embeddedLanguages = Object.create(null); + + if (embeddedLanguages) { + // If embeddedLanguages are configured, fill in `this._embeddedLanguages` + let scopes = Object.keys(embeddedLanguages); + for (let i = 0, len = scopes.length; i < len; i++) { + let scope = scopes[i]; + let language = embeddedLanguages[scope]; + if (typeof language !== 'string') { + // never hurts to be too careful + continue; + } + this.embeddedLanguages[scope] = language; + } + } + + this.tokenTypes = Object.create(null); + if (tokenTypes) { + // If tokenTypes is configured, fill in `this._tokenTypes` + const scopes = Object.keys(tokenTypes); + for (const scope of scopes) { + const tokenType = tokenTypes[scope]; + switch (tokenType) { + case 'string': + this.tokenTypes[scope] = StandardTokenType.String; + break; + case 'other': + this.tokenTypes[scope] = StandardTokenType.Other; + break; + case 'comment': + this.tokenTypes[scope] = StandardTokenType.Comment; + break; + } + } + } + } +} + +interface ICreateGrammarResult { + languageId: LanguageId; + grammar: IGrammar; + initialState: StackElement; + containsEmbeddedLanguages: boolean; +} + +export abstract class AbstractTextMateService extends Disposable implements ITextMateService { + public _serviceBrand: any; + + private readonly _onDidEncounterLanguage: Emitter<LanguageId> = this._register(new Emitter<LanguageId>()); + public readonly onDidEncounterLanguage: Event<LanguageId> = this._onDidEncounterLanguage.event; + + private readonly _styleElement: HTMLStyleElement; + private readonly _createdModes: string[]; + + protected _scopeRegistry: TMScopeRegistry; + private _injections: { [scopeName: string]: string[]; }; + private _injectedEmbeddedLanguages: { [scopeName: string]: IEmbeddedLanguagesMap[]; }; + protected _languageToScope: Map<string, string>; + private _grammarRegistry: Promise<[Registry, StackElement]> | null; + private _tokenizersRegistrations: IDisposable[]; + private _currentTokenColors: ITokenColorizationRule[] | null; + private _themeListener: IDisposable | null; + + constructor( + @IModeService private readonly _modeService: IModeService, + @IWorkbenchThemeService private readonly _themeService: IWorkbenchThemeService, + @IFileService private readonly _fileService: IFileService, + @INotificationService private readonly _notificationService: INotificationService, + @ILogService private readonly _logService: ILogService, + @IConfigurationService private readonly _configurationService: IConfigurationService + ) { + super(); + this._styleElement = dom.createStyleSheet(); + this._styleElement.className = 'vscode-tokens-styles'; + this._createdModes = []; + this._scopeRegistry = new TMScopeRegistry(); + this._scopeRegistry.onDidEncounterLanguage((language) => this._onDidEncounterLanguage.fire(language)); + this._injections = {}; + this._injectedEmbeddedLanguages = {}; + this._languageToScope = new Map<string, string>(); + this._grammarRegistry = null; + this._tokenizersRegistrations = []; + this._currentTokenColors = null; + this._themeListener = null; + + grammarsExtPoint.setHandler((extensions) => { + this._scopeRegistry.reset(); + this._injections = {}; + this._injectedEmbeddedLanguages = {}; + this._languageToScope = new Map<string, string>(); + this._grammarRegistry = null; + this._tokenizersRegistrations = dispose(this._tokenizersRegistrations); + this._currentTokenColors = null; + if (this._themeListener) { + this._themeListener.dispose(); + this._themeListener = null; + } + + for (const extension of extensions) { + let grammars = extension.value; + for (const grammar of grammars) { + this._handleGrammarExtensionPointUser(extension.description.extensionLocation, grammar, extension.collector); + } + } + + for (const createMode of this._createdModes) { + this._registerDefinitionIfAvailable(createMode); + } + }); + + // Generate some color map until the grammar registry is loaded + let colorTheme = this._themeService.getColorTheme(); + let defaultForeground: Color = Color.transparent; + let defaultBackground: Color = Color.transparent; + for (let i = 0, len = colorTheme.tokenColors.length; i < len; i++) { + let rule = colorTheme.tokenColors[i]; + if (!rule.scope && rule.settings) { + if (rule.settings.foreground) { + defaultForeground = Color.fromHex(rule.settings.foreground); + } + if (rule.settings.background) { + defaultBackground = Color.fromHex(rule.settings.background); + } + } + } + TokenizationRegistry.setColorMap([null!, defaultForeground, defaultBackground]); + + this._modeService.onDidCreateMode((mode) => { + let modeId = mode.getId(); + this._createdModes.push(modeId); + this._registerDefinitionIfAvailable(modeId); + }); + } + + private _registerDefinitionIfAvailable(modeId: string): void { + if (this._languageToScope.has(modeId)) { + const promise = this._createGrammar(modeId).then((r) => { + return new TMTokenization(this._scopeRegistry, r.languageId, r.grammar, r.initialState, r.containsEmbeddedLanguages, this._notificationService, this._configurationService); + }, e => { + onUnexpectedError(e); + return null; + }); + this._tokenizersRegistrations.push(TokenizationRegistry.registerPromise(modeId, promise)); + } + } + + protected _getRegistryOptions(parseRawGrammar: (content: string, filePath: string) => IRawGrammar): RegistryOptions { + return { + loadGrammar: async (scopeName: string) => { + const location = this._scopeRegistry.getGrammarLocation(scopeName); + if (!location) { + this._logService.trace(`No grammar found for scope ${scopeName}`); + return null; + } + try { + const content = await this._fileService.readFile(location); + return parseRawGrammar(content.value.toString(), location.path); + } catch (e) { + this._logService.error(`Unable to load and parse grammar for scope ${scopeName} from ${location}`, e); + return null; + } + }, + getInjections: (scopeName: string) => { + 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; + } + }; + } + + private async _createGrammarRegistry(): Promise<[Registry, StackElement]> { + const { Registry, INITIAL, parseRawGrammar } = await this._loadVSCodeTextmate(); + const grammarRegistry = new Registry(this._getRegistryOptions(parseRawGrammar)); + this._updateTheme(grammarRegistry); + this._themeListener = this._themeService.onDidColorThemeChange((e) => this._updateTheme(grammarRegistry)); + return <[Registry, StackElement]>[grammarRegistry, INITIAL]; + } + + private _getOrCreateGrammarRegistry(): Promise<[Registry, StackElement]> { + if (!this._grammarRegistry) { + this._grammarRegistry = this._createGrammarRegistry(); + } + return this._grammarRegistry; + } + + private static _toColorMap(colorMap: string[]): Color[] { + let result: Color[] = [null!]; + for (let i = 1, len = colorMap.length; i < len; i++) { + result[i] = Color.fromHex(colorMap[i]); + } + return result; + } + + private _updateTheme(grammarRegistry: Registry): void { + let colorTheme = this._themeService.getColorTheme(); + if (!this.compareTokenRules(colorTheme.tokenColors)) { + return; + } + grammarRegistry.setTheme({ name: colorTheme.label, settings: colorTheme.tokenColors }); + let colorMap = AbstractTextMateService._toColorMap(grammarRegistry.getColorMap()); + let cssRules = generateTokensCSSForColorMap(colorMap); + this._styleElement.innerHTML = cssRules; + TokenizationRegistry.setColorMap(colorMap); + } + + private compareTokenRules(newRules: ITokenColorizationRule[]): boolean { + let currRules = this._currentTokenColors; + this._currentTokenColors = newRules; + if (!newRules || !currRules || newRules.length !== currRules.length) { + return true; + } + for (let i = newRules.length - 1; i >= 0; i--) { + let r1 = newRules[i]; + let r2 = currRules[i]; + if (r1.scope !== r2.scope) { + return true; + } + let s1 = r1.settings; + let s2 = r2.settings; + if (s1 && s2) { + if (s1.fontStyle !== s2.fontStyle || s1.foreground !== s2.foreground || s1.background !== s2.background) { + return true; + } + } else if (!s1 || !s2) { + return true; + } + } + return false; + } + + private _handleGrammarExtensionPointUser(extensionLocation: URI, syntax: ITMSyntaxExtensionPoint, collector: ExtensionMessageCollector): void { + if (syntax.language && ((typeof syntax.language !== 'string') || !this._modeService.isRegisteredMode(syntax.language))) { + collector.error(nls.localize('invalid.language', "Unknown language in `contributes.{0}.language`. Provided value: {1}", grammarsExtPoint.name, String(syntax.language))); + return; + } + if (!syntax.scopeName || (typeof syntax.scopeName !== 'string')) { + collector.error(nls.localize('invalid.scopeName', "Expected string in `contributes.{0}.scopeName`. Provided value: {1}", grammarsExtPoint.name, String(syntax.scopeName))); + return; + } + if (!syntax.path || (typeof syntax.path !== 'string')) { + collector.error(nls.localize('invalid.path.0', "Expected string in `contributes.{0}.path`. Provided value: {1}", grammarsExtPoint.name, String(syntax.path))); + return; + } + if (syntax.injectTo && (!Array.isArray(syntax.injectTo) || syntax.injectTo.some(scope => typeof scope !== 'string'))) { + collector.error(nls.localize('invalid.injectTo', "Invalid value in `contributes.{0}.injectTo`. Must be an array of language scope names. Provided value: {1}", grammarsExtPoint.name, JSON.stringify(syntax.injectTo))); + return; + } + if (syntax.embeddedLanguages && !types.isObject(syntax.embeddedLanguages)) { + collector.error(nls.localize('invalid.embeddedLanguages', "Invalid value in `contributes.{0}.embeddedLanguages`. Must be an object map from scope name to language. Provided value: {1}", grammarsExtPoint.name, JSON.stringify(syntax.embeddedLanguages))); + return; + } + + if (syntax.tokenTypes && !types.isObject(syntax.tokenTypes)) { + collector.error(nls.localize('invalid.tokenTypes', "Invalid value in `contributes.{0}.tokenTypes`. Must be an object map from scope name to token type. Provided value: {1}", grammarsExtPoint.name, JSON.stringify(syntax.tokenTypes))); + return; + } + + const grammarLocation = resources.joinPath(extensionLocation, syntax.path); + if (!resources.isEqualOrParent(grammarLocation, extensionLocation)) { + collector.warn(nls.localize('invalid.path.1', "Expected `contributes.{0}.path` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable.", grammarsExtPoint.name, grammarLocation.path, extensionLocation.path)); + } + + this._scopeRegistry.register(syntax.scopeName, grammarLocation, syntax.embeddedLanguages, syntax.tokenTypes); + + if (syntax.injectTo) { + for (let injectScope of syntax.injectTo) { + let injections = this._injections[injectScope]; + if (!injections) { + this._injections[injectScope] = injections = []; + } + injections.push(syntax.scopeName); + } + + if (syntax.embeddedLanguages) { + for (let injectScope of syntax.injectTo) { + let injectedEmbeddedLanguages = this._injectedEmbeddedLanguages[injectScope]; + if (!injectedEmbeddedLanguages) { + this._injectedEmbeddedLanguages[injectScope] = injectedEmbeddedLanguages = []; + } + injectedEmbeddedLanguages.push(syntax.embeddedLanguages); + } + } + } + + let modeId = syntax.language; + if (modeId) { + this._languageToScope.set(modeId, syntax.scopeName); + } + } + + private _resolveEmbeddedLanguages(embeddedLanguages: IEmbeddedLanguagesMap): IEmbeddedLanguagesMap2 { + let scopes = Object.keys(embeddedLanguages); + let result: IEmbeddedLanguagesMap2 = Object.create(null); + for (let i = 0, len = scopes.length; i < len; i++) { + let scope = scopes[i]; + let language = embeddedLanguages[scope]; + let languageIdentifier = this._modeService.getLanguageIdentifier(language); + if (languageIdentifier) { + result[scope] = languageIdentifier.id; + } + } + return result; + } + + public async createGrammar(modeId: string): Promise<IGrammar> { + const { grammar } = await this._createGrammar(modeId); + return grammar; + } + + private async _createGrammar(modeId: string): Promise<ICreateGrammarResult> { + const scopeName = this._languageToScope.get(modeId); + if (typeof scopeName !== 'string') { + // No TM grammar defined + return Promise.reject(new Error(nls.localize('no-tm-grammar', "No TM Grammar registered for this language."))); + } + const languageRegistration = this._scopeRegistry.getLanguageRegistration(scopeName); + if (!languageRegistration) { + // No TM grammar defined + 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]; + if (rawInjectedEmbeddedLanguages) { + let injectedEmbeddedLanguages: IEmbeddedLanguagesMap2[] = rawInjectedEmbeddedLanguages.map(this._resolveEmbeddedLanguages.bind(this)); + for (const injected of injectedEmbeddedLanguages) { + for (const scope of Object.keys(injected)) { + embeddedLanguages[scope] = injected[scope]; + } + } + } + + let languageId = this._modeService.getLanguageIdentifier(modeId)!.id; + let containsEmbeddedLanguages = (Object.keys(embeddedLanguages).length > 0); + + const [grammarRegistry, initialState] = await this._getOrCreateGrammarRegistry(); + const grammar = await grammarRegistry.loadGrammarWithConfiguration(scopeName, languageId, { embeddedLanguages, tokenTypes: languageRegistration.tokenTypes }); + return { + languageId: languageId, + grammar: grammar, + initialState: initialState, + containsEmbeddedLanguages: containsEmbeddedLanguages + }; + } + + protected abstract _loadVSCodeTextmate(): Promise<typeof import('vscode-textmate')>; +} + +class TMTokenization implements ITokenizationSupport { + + private readonly _scopeRegistry: TMScopeRegistry; + private readonly _languageId: LanguageId; + private readonly _grammar: IGrammar; + private readonly _containsEmbeddedLanguages: boolean; + private readonly _seenLanguages: boolean[]; + private readonly _initialState: StackElement; + private _maxTokenizationLineLength: number; + private _tokenizationWarningAlreadyShown: boolean; + + constructor(scopeRegistry: TMScopeRegistry, languageId: LanguageId, grammar: IGrammar, initialState: StackElement, containsEmbeddedLanguages: boolean, @INotificationService private readonly notificationService: INotificationService, @IConfigurationService readonly configurationService: IConfigurationService) { + this._scopeRegistry = scopeRegistry; + this._languageId = languageId; + this._grammar = grammar; + this._initialState = initialState; + this._containsEmbeddedLanguages = containsEmbeddedLanguages; + this._seenLanguages = []; + this._maxTokenizationLineLength = configurationService.getValue<number>('editor.maxTokenizationLineLength'); + configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('editor.maxTokenizationLineLength')) { + this._maxTokenizationLineLength = configurationService.getValue<number>('editor.maxTokenizationLineLength'); + } + }); + } + + public getInitialState(): IState { + return this._initialState; + } + + public tokenize(line: string, state: IState, offsetDelta: number): TokenizationResult { + throw new Error('Not supported!'); + } + + public tokenize2(line: string, state: StackElement, offsetDelta: number): TokenizationResult2 { + if (offsetDelta !== 0) { + throw new Error('Unexpected: offsetDelta should be 0.'); + } + + // Do not attempt to tokenize if a line is too long + if (line.length >= this._maxTokenizationLineLength) { + if (!this._tokenizationWarningAlreadyShown) { + this._tokenizationWarningAlreadyShown = true; + this.notificationService.warn(nls.localize('too many characters', "Tokenization is skipped for long lines for performance reasons. The length of a long line can be configured via `editor.maxTokenizationLineLength`.")); + } + console.log(`Line (${line.substr(0, 15)}...): longer than ${this._maxTokenizationLineLength} characters, tokenization skipped.`); + return nullTokenize2(this._languageId, line, state, offsetDelta); + } + + let textMateResult = this._grammar.tokenizeLine2(line, state); + + if (this._containsEmbeddedLanguages) { + let seenLanguages = this._seenLanguages; + let tokens = textMateResult.tokens; + + // Must check if any of the embedded languages was hit + for (let i = 0, len = (tokens.length >>> 1); i < len; i++) { + let metadata = tokens[(i << 1) + 1]; + let languageId = TokenMetadata.getLanguageId(metadata); + + if (!seenLanguages[languageId]) { + seenLanguages[languageId] = true; + this._scopeRegistry.onEncounteredLanguage(languageId); + } + } + } + + let endState: StackElement; + // try to save an object if possible + if (state.equals(textMateResult.ruleStack)) { + endState = state; + } else { + endState = textMateResult.ruleStack; + + } + + return new TokenizationResult2(textMateResult.tokens, endState); + } +} diff --git a/src/vs/workbench/services/textMate/browser/textMateService.ts b/src/vs/workbench/services/textMate/browser/textMateService.ts new file mode 100644 index 00000000000..c126bf74d25 --- /dev/null +++ b/src/vs/workbench/services/textMate/browser/textMateService.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ITextMateService } from 'vs/workbench/services/textMate/common/textMateService'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { AbstractTextMateService } from 'vs/workbench/services/textMate/browser/abstractTextMateService'; +import * as vscodeTextmate from 'vscode-textmate'; +import * as onigasm from 'onigasm-umd'; +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 { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + +export class TextMateService extends AbstractTextMateService { + + constructor( + @IModeService modeService: IModeService, + @IWorkbenchThemeService themeService: IWorkbenchThemeService, + @IFileService fileService: IFileService, + @INotificationService notificationService: INotificationService, + @ILogService logService: ILogService, + @IConfigurationService configurationService: IConfigurationService + ) { + super(modeService, themeService, fileService, notificationService, logService, configurationService); + } + + protected _loadVSCodeTextmate(): Promise<typeof import('vscode-textmate')> { + return import('vscode-textmate'); + } + + protected _getRegistryOptions(parseRawGrammar: (content: string, filePath: string) => vscodeTextmate.IRawGrammar): vscodeTextmate.RegistryOptions { + const result = super._getRegistryOptions(parseRawGrammar); + result.getOnigLib = () => loadOnigasm(); + return result; + } +} + +let onigasmPromise: Promise<vscodeTextmate.IOnigLib> | null = null; +async function loadOnigasm(): Promise<vscodeTextmate.IOnigLib> { + if (!onigasmPromise) { + onigasmPromise = doLoadOnigasm(); + } + return onigasmPromise; +} + +async function doLoadOnigasm(): Promise<vscodeTextmate.IOnigLib> { + const wasmBytes = await loadOnigasmWASM(); + await onigasm.loadWASM(wasmBytes); + return { + createOnigScanner(patterns: string[]) { return new onigasm.OnigScanner(patterns); }, + createOnigString(s: string) { return new onigasm.OnigString(s); } + }; +} + +async function loadOnigasmWASM(): Promise<ArrayBuffer> { + const wasmPath = require.toUrl('onigasm-umd/../onigasm.wasm'); + const response = await fetch(wasmPath); + const bytes = await response.arrayBuffer(); + return bytes; +} + +registerSingleton(ITextMateService, TextMateService); diff --git a/src/vs/workbench/services/textMate/electron-browser/cgmanifest.json b/src/vs/workbench/services/textMate/common/cgmanifest.json similarity index 100% rename from src/vs/workbench/services/textMate/electron-browser/cgmanifest.json rename to src/vs/workbench/services/textMate/common/cgmanifest.json diff --git a/src/vs/workbench/services/textMate/electron-browser/textMateService.ts b/src/vs/workbench/services/textMate/electron-browser/textMateService.ts index 94e175490fa..4b5ec093cd9 100644 --- a/src/vs/workbench/services/textMate/electron-browser/textMateService.ts +++ b/src/vs/workbench/services/textMate/electron-browser/textMateService.ts @@ -3,512 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; -import * as dom from 'vs/base/browser/dom'; -import { Color } from 'vs/base/common/color'; -import { onUnexpectedError } from 'vs/base/common/errors'; -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 { 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'; -import { generateTokensCSSForColorMap } from 'vs/editor/common/modes/supports/tokenization'; -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 { ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { IEmbeddedLanguagesMap, ITMSyntaxExtensionPoint, TokenTypesContribution, grammarsExtPoint } from 'vs/workbench/services/textMate/common/TMGrammars'; import { ITextMateService } from 'vs/workbench/services/textMate/common/textMateService'; -import { ITokenColorizationRule, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; -import { IEmbeddedLanguagesMap as IEmbeddedLanguagesMap2, IGrammar, ITokenTypeMap, Registry, StackElement, StandardTokenType } from 'vscode-textmate'; -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { AbstractTextMateService } from 'vs/workbench/services/textMate/browser/abstractTextMateService'; -export class TMScopeRegistry { +export class TextMateService extends AbstractTextMateService { - private _scopeNameToLanguageRegistration: { [scopeName: string]: TMLanguageRegistration; }; - private _encounteredLanguages: boolean[]; - - private readonly _onDidEncounterLanguage = new Emitter<LanguageId>(); - public readonly onDidEncounterLanguage: Event<LanguageId> = this._onDidEncounterLanguage.event; - - constructor() { - this.reset(); - } - - public reset(): void { - this._scopeNameToLanguageRegistration = Object.create(null); - this._encounteredLanguages = []; - } - - public register(scopeName: string, grammarLocation: URI, embeddedLanguages?: IEmbeddedLanguagesMap, tokenTypes?: TokenTypesContribution): void { - if (this._scopeNameToLanguageRegistration[scopeName]) { - const existingRegistration = this._scopeNameToLanguageRegistration[scopeName]; - if (!resources.isEqual(existingRegistration.grammarLocation, grammarLocation)) { - console.warn( - `Overwriting grammar scope name to file mapping for scope ${scopeName}.\n` + - `Old grammar file: ${existingRegistration.grammarLocation.toString()}.\n` + - `New grammar file: ${grammarLocation.toString()}` - ); - } - } - this._scopeNameToLanguageRegistration[scopeName] = new TMLanguageRegistration(scopeName, grammarLocation, embeddedLanguages, tokenTypes); - } - - public getLanguageRegistration(scopeName: string): TMLanguageRegistration { - return this._scopeNameToLanguageRegistration[scopeName] || null; - } - - public getGrammarLocation(scopeName: string): URI | null { - let data = this.getLanguageRegistration(scopeName); - return data ? data.grammarLocation : null; - } - - /** - * To be called when tokenization found/hit an embedded language. - */ - public onEncounteredLanguage(languageId: LanguageId): void { - if (!this._encounteredLanguages[languageId]) { - this._encounteredLanguages[languageId] = true; - this._onDidEncounterLanguage.fire(languageId); - } - } -} - -export class TMLanguageRegistration { - _topLevelScopeNameDataBrand: void; - - readonly scopeName: string; - readonly grammarLocation: URI; - readonly embeddedLanguages: IEmbeddedLanguagesMap; - readonly tokenTypes: ITokenTypeMap; - - constructor(scopeName: string, grammarLocation: URI, embeddedLanguages: IEmbeddedLanguagesMap | undefined, tokenTypes: TokenTypesContribution | undefined) { - this.scopeName = scopeName; - this.grammarLocation = grammarLocation; - - // embeddedLanguages handling - this.embeddedLanguages = Object.create(null); - - if (embeddedLanguages) { - // If embeddedLanguages are configured, fill in `this._embeddedLanguages` - let scopes = Object.keys(embeddedLanguages); - for (let i = 0, len = scopes.length; i < len; i++) { - let scope = scopes[i]; - let language = embeddedLanguages[scope]; - if (typeof language !== 'string') { - // never hurts to be too careful - continue; - } - this.embeddedLanguages[scope] = language; - } - } - - this.tokenTypes = Object.create(null); - if (tokenTypes) { - // If tokenTypes is configured, fill in `this._tokenTypes` - const scopes = Object.keys(tokenTypes); - for (const scope of scopes) { - const tokenType = tokenTypes[scope]; - switch (tokenType) { - case 'string': - this.tokenTypes[scope] = StandardTokenType.String; - break; - case 'other': - this.tokenTypes[scope] = StandardTokenType.Other; - break; - case 'comment': - this.tokenTypes[scope] = StandardTokenType.Comment; - break; - } - } - } - } -} - -interface ICreateGrammarResult { - languageId: LanguageId; - grammar: IGrammar; - initialState: StackElement; - containsEmbeddedLanguages: boolean; -} - -export class TextMateService extends Disposable implements ITextMateService { - public _serviceBrand: any; - - private readonly _onDidEncounterLanguage: Emitter<LanguageId> = this._register(new Emitter<LanguageId>()); - public readonly onDidEncounterLanguage: Event<LanguageId> = this._onDidEncounterLanguage.event; - - private readonly _styleElement: HTMLStyleElement; - private readonly _createdModes: string[]; - - private _scopeRegistry: TMScopeRegistry; - private _injections: { [scopeName: string]: string[]; }; - private _injectedEmbeddedLanguages: { [scopeName: string]: IEmbeddedLanguagesMap[]; }; - private _languageToScope: Map<string, string>; - private _grammarRegistry: Promise<[Registry, StackElement]> | null; - private _tokenizersRegistrations: IDisposable[]; - private _currentTokenColors: ITokenColorizationRule[] | null; - private _themeListener: IDisposable | null; - - constructor( - @IModeService private readonly _modeService: IModeService, - @IWorkbenchThemeService private readonly _themeService: IWorkbenchThemeService, - @IFileService private readonly _fileService: IFileService, - @INotificationService private readonly _notificationService: INotificationService, - @ILogService private readonly _logService: ILogService, - @IConfigurationService private readonly _configurationService: IConfigurationService - ) { - super(); - this._styleElement = dom.createStyleSheet(); - this._styleElement.className = 'vscode-tokens-styles'; - this._createdModes = []; - this._scopeRegistry = new TMScopeRegistry(); - this._scopeRegistry.onDidEncounterLanguage((language) => this._onDidEncounterLanguage.fire(language)); - this._injections = {}; - this._injectedEmbeddedLanguages = {}; - this._languageToScope = new Map<string, string>(); - this._grammarRegistry = null; - this._tokenizersRegistrations = []; - this._currentTokenColors = null; - this._themeListener = null; - - grammarsExtPoint.setHandler((extensions) => { - this._scopeRegistry.reset(); - this._injections = {}; - this._injectedEmbeddedLanguages = {}; - this._languageToScope = new Map<string, string>(); - this._grammarRegistry = null; - this._tokenizersRegistrations = dispose(this._tokenizersRegistrations); - this._currentTokenColors = null; - if (this._themeListener) { - this._themeListener.dispose(); - this._themeListener = null; - } - - for (const extension of extensions) { - let grammars = extension.value; - for (const grammar of grammars) { - this._handleGrammarExtensionPointUser(extension.description.extensionLocation, grammar, extension.collector); - } - } - - for (const createMode of this._createdModes) { - this._registerDefinitionIfAvailable(createMode); - } - }); - - // Generate some color map until the grammar registry is loaded - let colorTheme = this._themeService.getColorTheme(); - let defaultForeground: Color = Color.transparent; - let defaultBackground: Color = Color.transparent; - for (let i = 0, len = colorTheme.tokenColors.length; i < len; i++) { - let rule = colorTheme.tokenColors[i]; - if (!rule.scope && rule.settings) { - if (rule.settings.foreground) { - defaultForeground = Color.fromHex(rule.settings.foreground); - } - if (rule.settings.background) { - defaultBackground = Color.fromHex(rule.settings.background); - } - } - } - TokenizationRegistry.setColorMap([null!, defaultForeground, defaultBackground]); - - this._modeService.onDidCreateMode((mode) => { - let modeId = mode.getId(); - this._createdModes.push(modeId); - this._registerDefinitionIfAvailable(modeId); - }); - } - - private _registerDefinitionIfAvailable(modeId: string): void { - if (this._languageToScope.has(modeId)) { - const promise = this._createGrammar(modeId).then((r) => { - return new TMTokenization(this._scopeRegistry, r.languageId, r.grammar, r.initialState, r.containsEmbeddedLanguages, this._notificationService, this._configurationService); - }, e => { - onUnexpectedError(e); - return null; - }); - this._tokenizersRegistrations.push(TokenizationRegistry.registerPromise(modeId, promise)); - } - } - - private async _createGrammarRegistry(): Promise<[Registry, StackElement]> { - const { Registry, INITIAL, parseRawGrammar } = await import('vscode-textmate'); - const grammarRegistry = new Registry({ - loadGrammar: async (scopeName: string) => { - const location = this._scopeRegistry.getGrammarLocation(scopeName); - if (!location) { - this._logService.trace(`No grammar found for scope ${scopeName}`); - return null; - } - try { - const content = await this._fileService.readFile(location); - return parseRawGrammar(content.value.toString(), location.path); - } catch (e) { - this._logService.error(`Unable to load and parse grammar for scope ${scopeName} from ${location}`, e); - return null; - } - }, - getInjections: (scopeName: string) => { - 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); - this._themeListener = this._themeService.onDidColorThemeChange((e) => this._updateTheme(grammarRegistry)); - return <[Registry, StackElement]>[grammarRegistry, INITIAL]; - } - - private _getOrCreateGrammarRegistry(): Promise<[Registry, StackElement]> { - if (!this._grammarRegistry) { - this._grammarRegistry = this._createGrammarRegistry(); - } - return this._grammarRegistry; - } - - private static _toColorMap(colorMap: string[]): Color[] { - let result: Color[] = [null!]; - for (let i = 1, len = colorMap.length; i < len; i++) { - result[i] = Color.fromHex(colorMap[i]); - } - return result; - } - - private _updateTheme(grammarRegistry: Registry): void { - let colorTheme = this._themeService.getColorTheme(); - if (!this.compareTokenRules(colorTheme.tokenColors)) { - return; - } - grammarRegistry.setTheme({ name: colorTheme.label, settings: colorTheme.tokenColors }); - let colorMap = TextMateService._toColorMap(grammarRegistry.getColorMap()); - let cssRules = generateTokensCSSForColorMap(colorMap); - this._styleElement.innerHTML = cssRules; - TokenizationRegistry.setColorMap(colorMap); - } - - private compareTokenRules(newRules: ITokenColorizationRule[]): boolean { - let currRules = this._currentTokenColors; - this._currentTokenColors = newRules; - if (!newRules || !currRules || newRules.length !== currRules.length) { - return true; - } - for (let i = newRules.length - 1; i >= 0; i--) { - let r1 = newRules[i]; - let r2 = currRules[i]; - if (r1.scope !== r2.scope) { - return true; - } - let s1 = r1.settings; - let s2 = r2.settings; - if (s1 && s2) { - if (s1.fontStyle !== s2.fontStyle || s1.foreground !== s2.foreground || s1.background !== s2.background) { - return true; - } - } else if (!s1 || !s2) { - return true; - } - } - return false; - } - - private _handleGrammarExtensionPointUser(extensionLocation: URI, syntax: ITMSyntaxExtensionPoint, collector: ExtensionMessageCollector): void { - if (syntax.language && ((typeof syntax.language !== 'string') || !this._modeService.isRegisteredMode(syntax.language))) { - collector.error(nls.localize('invalid.language', "Unknown language in `contributes.{0}.language`. Provided value: {1}", grammarsExtPoint.name, String(syntax.language))); - return; - } - if (!syntax.scopeName || (typeof syntax.scopeName !== 'string')) { - collector.error(nls.localize('invalid.scopeName', "Expected string in `contributes.{0}.scopeName`. Provided value: {1}", grammarsExtPoint.name, String(syntax.scopeName))); - return; - } - if (!syntax.path || (typeof syntax.path !== 'string')) { - collector.error(nls.localize('invalid.path.0', "Expected string in `contributes.{0}.path`. Provided value: {1}", grammarsExtPoint.name, String(syntax.path))); - return; - } - if (syntax.injectTo && (!Array.isArray(syntax.injectTo) || syntax.injectTo.some(scope => typeof scope !== 'string'))) { - collector.error(nls.localize('invalid.injectTo', "Invalid value in `contributes.{0}.injectTo`. Must be an array of language scope names. Provided value: {1}", grammarsExtPoint.name, JSON.stringify(syntax.injectTo))); - return; - } - if (syntax.embeddedLanguages && !types.isObject(syntax.embeddedLanguages)) { - collector.error(nls.localize('invalid.embeddedLanguages', "Invalid value in `contributes.{0}.embeddedLanguages`. Must be an object map from scope name to language. Provided value: {1}", grammarsExtPoint.name, JSON.stringify(syntax.embeddedLanguages))); - return; - } - - if (syntax.tokenTypes && !types.isObject(syntax.tokenTypes)) { - collector.error(nls.localize('invalid.tokenTypes', "Invalid value in `contributes.{0}.tokenTypes`. Must be an object map from scope name to token type. Provided value: {1}", grammarsExtPoint.name, JSON.stringify(syntax.tokenTypes))); - return; - } - - const grammarLocation = resources.joinPath(extensionLocation, syntax.path); - if (!resources.isEqualOrParent(grammarLocation, extensionLocation)) { - collector.warn(nls.localize('invalid.path.1', "Expected `contributes.{0}.path` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable.", grammarsExtPoint.name, grammarLocation.path, extensionLocation.path)); - } - - this._scopeRegistry.register(syntax.scopeName, grammarLocation, syntax.embeddedLanguages, syntax.tokenTypes); - - if (syntax.injectTo) { - for (let injectScope of syntax.injectTo) { - let injections = this._injections[injectScope]; - if (!injections) { - this._injections[injectScope] = injections = []; - } - injections.push(syntax.scopeName); - } - - if (syntax.embeddedLanguages) { - for (let injectScope of syntax.injectTo) { - let injectedEmbeddedLanguages = this._injectedEmbeddedLanguages[injectScope]; - if (!injectedEmbeddedLanguages) { - this._injectedEmbeddedLanguages[injectScope] = injectedEmbeddedLanguages = []; - } - injectedEmbeddedLanguages.push(syntax.embeddedLanguages); - } - } - } - - let modeId = syntax.language; - if (modeId) { - this._languageToScope.set(modeId, syntax.scopeName); - } - } - - private _resolveEmbeddedLanguages(embeddedLanguages: IEmbeddedLanguagesMap): IEmbeddedLanguagesMap2 { - let scopes = Object.keys(embeddedLanguages); - let result: IEmbeddedLanguagesMap2 = Object.create(null); - for (let i = 0, len = scopes.length; i < len; i++) { - let scope = scopes[i]; - let language = embeddedLanguages[scope]; - let languageIdentifier = this._modeService.getLanguageIdentifier(language); - if (languageIdentifier) { - result[scope] = languageIdentifier.id; - } - } - return result; - } - - public async createGrammar(modeId: string): Promise<IGrammar> { - const { grammar } = await this._createGrammar(modeId); - return grammar; - } - - private async _createGrammar(modeId: string): Promise<ICreateGrammarResult> { - const scopeName = this._languageToScope.get(modeId); - if (typeof scopeName !== 'string') { - // No TM grammar defined - return Promise.reject(new Error(nls.localize('no-tm-grammar', "No TM Grammar registered for this language."))); - } - const languageRegistration = this._scopeRegistry.getLanguageRegistration(scopeName); - if (!languageRegistration) { - // No TM grammar defined - 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]; - if (rawInjectedEmbeddedLanguages) { - let injectedEmbeddedLanguages: IEmbeddedLanguagesMap2[] = rawInjectedEmbeddedLanguages.map(this._resolveEmbeddedLanguages.bind(this)); - for (const injected of injectedEmbeddedLanguages) { - for (const scope of Object.keys(injected)) { - embeddedLanguages[scope] = injected[scope]; - } - } - } - - let languageId = this._modeService.getLanguageIdentifier(modeId)!.id; - let containsEmbeddedLanguages = (Object.keys(embeddedLanguages).length > 0); - - const [grammarRegistry, initialState] = await this._getOrCreateGrammarRegistry(); - const grammar = await grammarRegistry.loadGrammarWithConfiguration(scopeName, languageId, { embeddedLanguages, tokenTypes: languageRegistration.tokenTypes }); - return { - languageId: languageId, - grammar: grammar, - initialState: initialState, - containsEmbeddedLanguages: containsEmbeddedLanguages - }; - } -} - -class TMTokenization implements ITokenizationSupport { - - private readonly _scopeRegistry: TMScopeRegistry; - private readonly _languageId: LanguageId; - private readonly _grammar: IGrammar; - private readonly _containsEmbeddedLanguages: boolean; - private readonly _seenLanguages: boolean[]; - private readonly _initialState: StackElement; - private _maxTokenizationLineLength: number; - private _tokenizationWarningAlreadyShown: boolean; - - constructor(scopeRegistry: TMScopeRegistry, languageId: LanguageId, grammar: IGrammar, initialState: StackElement, containsEmbeddedLanguages: boolean, @INotificationService private readonly notificationService: INotificationService, @IConfigurationService readonly configurationService: IConfigurationService) { - this._scopeRegistry = scopeRegistry; - this._languageId = languageId; - this._grammar = grammar; - this._initialState = initialState; - this._containsEmbeddedLanguages = containsEmbeddedLanguages; - this._seenLanguages = []; - this._maxTokenizationLineLength = configurationService.getValue<number>('editor.maxTokenizationLineLength'); - } - - public getInitialState(): IState { - return this._initialState; - } - - public tokenize(line: string, state: IState, offsetDelta: number): TokenizationResult { - throw new Error('Not supported!'); - } - - public tokenize2(line: string, state: StackElement, offsetDelta: number): TokenizationResult2 { - if (offsetDelta !== 0) { - throw new Error('Unexpected: offsetDelta should be 0.'); - } - - // Do not attempt to tokenize if a line is too long - if (line.length >= this._maxTokenizationLineLength) { - if (!this._tokenizationWarningAlreadyShown) { - this._tokenizationWarningAlreadyShown = true; - this.notificationService.warn(nls.localize('too many characters', "Tokenization is skipped for long lines for performance reasons. The length of a long line can be configured via `editor.maxTokenizationLineLength`.")); - } - console.log(`Line (${line.substr(0, 15)}...): longer than ${this._maxTokenizationLineLength} characters, tokenization skipped.`); - return nullTokenize2(this._languageId, line, state, offsetDelta); - } - - let textMateResult = this._grammar.tokenizeLine2(line, state); - - if (this._containsEmbeddedLanguages) { - let seenLanguages = this._seenLanguages; - let tokens = textMateResult.tokens; - - // Must check if any of the embedded languages was hit - for (let i = 0, len = (tokens.length >>> 1); i < len; i++) { - let metadata = tokens[(i << 1) + 1]; - let languageId = TokenMetadata.getLanguageId(metadata); - - if (!seenLanguages[languageId]) { - seenLanguages[languageId] = true; - this._scopeRegistry.onEncounteredLanguage(languageId); - } - } - } - - let endState: StackElement; - // try to save an object if possible - if (state.equals(textMateResult.ruleStack)) { - endState = state; - } else { - endState = textMateResult.ruleStack; - - } - - return new TokenizationResult2(textMateResult.tokens, endState); + protected _loadVSCodeTextmate(): Promise<typeof import('vscode-textmate')> { + return import('vscode-textmate'); } } diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index d15549e21cc..8d653165119 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -6,6 +6,7 @@ import { TextFileService } from 'vs/workbench/services/textfile/common/textFileService'; import { ITextFileService, IResourceEncodings, IResourceEncoding } from 'vs/workbench/services/textfile/common/textfiles'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle'; export class BrowserTextFileService extends TextFileService { @@ -14,6 +15,19 @@ export class BrowserTextFileService extends TextFileService { return { encoding: 'utf8', hasBOM: false }; } }; + + protected beforeShutdown(reason: ShutdownReason): boolean | Promise<boolean> { + const veto = super.beforeShutdown(reason); + + // Web: there is no support for long running unload handlers. As such + // we need to return a direct boolean veto when we detect that there + // are dirty files around. + if (veto instanceof Promise) { + return this.getDirty().length > 0; + } + + return veto; + } } registerSingleton(ITextFileService, BrowserTextFileService); \ No newline at end of file diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index ecb2e2bab4a..9a70fd94b6a 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -3,13 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { join } from 'vs/base/common/path'; import * as nls from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; import { guessMimeTypes } from 'vs/base/common/mime'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { URI } from 'vs/base/common/uri'; -import { isUndefinedOrNull, withUndefinedAsNull } from 'vs/base/common/types'; +import { isUndefinedOrNull } from 'vs/base/common/types'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ITextFileService, IAutoSaveConfiguration, ModelState, ITextFileEditorModel, ISaveOptions, ISaveErrorHandler, ISaveParticipant, StateChange, SaveReason, ITextFileStreamContent, ILoadOptions, LoadReason, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; @@ -18,21 +17,27 @@ import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel' import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { IFileService, FileOperationError, FileOperationResult, CONTENT_CHANGE_EVENT_BUFFER_DELAY, FileChangesEvent, FileChangeType, IFileStatWithMetadata, ETAG_DISABLED } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; +import { IModeService } 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'; import { ITextBufferFactory } from 'vs/editor/common/model'; import { hash } from 'vs/base/common/hash'; -import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { isLinux } from 'vs/base/common/platform'; -import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { toDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; -import { isEqual, isEqualOrParent, extname, basename } from 'vs/base/common/resources'; +import { isEqual, isEqualOrParent, extname, basename, joinPath } from 'vs/base/common/resources'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Schemas } from 'vs/base/common/network'; +export interface IBackupMetaData { + mtime: number; + size: number; + etag: string; + orphaned: boolean; +} + /** * 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. */ @@ -57,20 +62,20 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private resource: URI; - private contentEncoding: string; // encoding as reported from disk - private preferredEncoding: string; // encoding as chosen by the user + private contentEncoding: string; // encoding as reported from disk + private preferredEncoding: string; // encoding as chosen by the user + + private preferredMode: string; // mode as chosen by the user private versionId: number; private bufferSavedVersionId: number; private blockModelContentChange: boolean; - private createTextEditorModelPromise: Promise<TextFileEditorModel> | null; - - private lastResolvedDiskStat: IFileStatWithMetadata; + private lastResolvedFileStat: IFileStatWithMetadata; private autoSaveAfterMillies?: number; private autoSaveAfterMilliesEnabled: boolean; - private autoSaveDisposable?: IDisposable; + private readonly autoSaveDisposable = this._register(new MutableDisposable()); private saveSequentializer: SaveSequentializer; private lastSaveAttemptTime: number; @@ -88,6 +93,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil constructor( resource: URI, preferredEncoding: string, + preferredMode: string, @INotificationService private readonly notificationService: INotificationService, @IModeService modeService: IModeService, @IModelService modelService: IModelService, @@ -104,6 +110,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.resource = resource; this.preferredEncoding = preferredEncoding; + this.preferredMode = preferredMode; this.inOrphanMode = false; this.dirty = false; this.versionId = 0; @@ -136,7 +143,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } } - private onFileChanges(e: FileChangesEvent): void { + private async onFileChanges(e: FileChangesEvent): Promise<void> { let fileEventImpactsModel = false; let newInOrphanModeGuess: boolean | undefined; @@ -159,28 +166,25 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } if (fileEventImpactsModel && this.inOrphanMode !== newInOrphanModeGuess) { - let checkOrphanedPromise: Promise<boolean>; + let newInOrphanModeValidated: boolean = false; if (newInOrphanModeGuess) { // We have received reports of users seeing delete events even though the file still // exists (network shares issue: https://github.com/Microsoft/vscode/issues/13665). // Since we do not want to mark the model as orphaned, we have to check if the // file is really gone and not just a faulty file event. - checkOrphanedPromise = timeout(100).then(() => { - if (this.disposed) { - return true; - } + await timeout(100); - return this.fileService.exists(this.resource).then(exists => !exists); - }); - } else { - checkOrphanedPromise = Promise.resolve(false); + if (this.disposed) { + newInOrphanModeValidated = true; + } else { + const exists = await this.fileService.exists(this.resource); + newInOrphanModeValidated = !exists; + } } - checkOrphanedPromise.then(newInOrphanModeValidated => { - if (this.inOrphanMode !== newInOrphanModeValidated && !this.disposed) { - this.setOrphaned(newInOrphanModeValidated); - } - }); + if (this.inOrphanMode !== newInOrphanModeValidated && !this.disposed) { + this.setOrphaned(newInOrphanModeValidated); + } } } @@ -199,53 +203,69 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } private onFilesAssociationChange(): void { - if (!this.textEditorModel) { + if (!this.isResolved()) { return; } const firstLineText = this.getFirstLineText(this.textEditorModel); - const languageSelection = this.getOrCreateMode(this.modeService, undefined, firstLineText); + const languageSelection = this.getOrCreateMode(this.resource, this.modeService, this.preferredMode, firstLineText); this.modelService.setMode(this.textEditorModel, languageSelection); } - getVersionId(): number { - return this.versionId; + setMode(mode: string): void { + super.setMode(mode); + + this.preferredMode = mode; + } + + async backup(target = this.resource): Promise<void> { + if (this.isResolved()) { + + // Only fill in model metadata if resource matches + let meta: IBackupMetaData | undefined = undefined; + if (isEqual(target, this.resource) && this.lastResolvedFileStat) { + meta = { + mtime: this.lastResolvedFileStat.mtime, + size: this.lastResolvedFileStat.size, + etag: this.lastResolvedFileStat.etag, + orphaned: this.inOrphanMode + }; + } + + return this.backupFileService.backupResource<IBackupMetaData>(target, this.createSnapshot(), this.versionId, meta); + } } async revert(soft?: boolean): Promise<void> { if (!this.isResolved()) { - return Promise.resolve(undefined); + return; } // Cancel any running auto-save - this.cancelPendingAutoSave(); + this.autoSaveDisposable.clear(); // Unset flags const undo = this.setDirty(false); - let loadPromise: Promise<unknown>; - if (soft) { - loadPromise = Promise.resolve(); - } else { - loadPromise = this.load({ forceReadFromDisk: true }); + // Force read from disk unless reverting soft + if (!soft) { + try { + await this.load({ forceReadFromDisk: true }); + } catch (error) { + + // Set flags back to previous values, we are still dirty if revert failed + undo(); + + throw error; + } } - try { - await loadPromise; - - // Emit file change event - this._onDidStateChange.fire(StateChange.REVERTED); - } catch (error) { - - // Set flags back to previous values, we are still dirty if revert failed - undo(); - - return Promise.reject(error); - } + // Emit file change event + this._onDidStateChange.fire(StateChange.REVERTED); } - load(options?: ILoadOptions): Promise<ITextFileEditorModel> { + async load(options?: ILoadOptions): Promise<ITextFileEditorModel> { this.logService.trace('load() - enter', this.resource); // It is very important to not reload the model when the model is dirty. @@ -254,44 +274,57 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil if (this.dirty || this.saveSequentializer.hasPendingSave()) { this.logService.trace('load() - exit - without loading because model is dirty or being saved', this.resource); - return Promise.resolve(this); + return this; } // Only for new models we support to load from backup - if (!this.textEditorModel && !this.createTextEditorModelPromise) { - return this.loadFromBackup(options); + if (!this.isResolved()) { + const backup = await this.backupFileService.loadBackupResource(this.resource); + + if (this.isResolved()) { + return this; // Make sure meanwhile someone else did not suceed in loading + } + + if (backup) { + try { + return await this.loadFromBackup(backup, options); + } catch (error) { + // ignore error and continue to load as file below + } + } } // Otherwise load from file resource return this.loadFromFile(options); } - private async loadFromBackup(options?: ILoadOptions): Promise<TextFileEditorModel> { - const backup = await this.backupFileService.loadBackupResource(this.resource); + private async loadFromBackup(backup: URI, options?: ILoadOptions): Promise<TextFileEditorModel> { - // Make sure meanwhile someone else did not suceed or start loading - if (this.createTextEditorModelPromise || this.textEditorModel) { - return this.createTextEditorModelPromise || this; + // Resolve actual backup contents + const resolvedBackup = await this.backupFileService.resolveBackupContent<IBackupMetaData>(backup); + + if (this.isResolved()) { + return this; // Make sure meanwhile someone else did not suceed in loading } - // If we have a backup, continue loading with it - if (!!backup) { - const content: ITextFileStreamContent = { - resource: this.resource, - name: basename(this.resource), - mtime: Date.now(), - size: 0, - etag: ETAG_DISABLED, // always allow to save content restored from a backup (see https://github.com/Microsoft/vscode/issues/72343) - value: createTextBufferFactory(''), // will be filled later from backup - encoding: this.textFileService.encoding.getPreferredWriteEncoding(this.resource, this.preferredEncoding).encoding, - isReadonly: false - }; + // Load with backup + this.loadFromContent({ + resource: this.resource, + name: basename(this.resource), + mtime: resolvedBackup.meta ? resolvedBackup.meta.mtime : Date.now(), + size: resolvedBackup.meta ? resolvedBackup.meta.size : 0, + etag: resolvedBackup.meta ? resolvedBackup.meta.etag : ETAG_DISABLED, // etag disabled if unknown! + value: resolvedBackup.value, + encoding: this.textFileService.encoding.getPreferredWriteEncoding(this.resource, this.preferredEncoding).encoding, + isReadonly: false + }, options, true /* from backup */); - return this.loadWithContent(content, options, backup); + // Restore orphaned flag based on state + if (resolvedBackup.meta && resolvedBackup.meta.orphaned) { + this.setOrphaned(true); } - // Otherwise load from file - return this.loadFromFile(options); + return this; } private async loadFromFile(options?: ILoadOptions): Promise<TextFileEditorModel> { @@ -302,8 +335,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil let etag: string | undefined; if (forceReadFromDisk) { etag = ETAG_DISABLED; // disable ETag if we enforce to read from disk - } else if (this.lastResolvedDiskStat) { - etag = this.lastResolvedDiskStat.etag; // otherwise respect etag to support caching + } else if (this.lastResolvedFileStat) { + etag = this.lastResolvedFileStat.etag; // otherwise respect etag to support caching } // Ensure to track the versionId before doing a long running operation @@ -321,12 +354,11 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Clear orphaned state when loading was successful this.setOrphaned(false); - // Guard against the model having changed in the meantime - if (currentVersionId === this.versionId) { - return this.loadWithContent(content, options); + if (currentVersionId !== this.versionId) { + return this; // Make sure meanwhile someone else did not suceed loading } - return this; + return this.loadFromContent(content, options); } catch (error) { const result = error.fileOperationResult; @@ -356,37 +388,11 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } } - private async loadWithContent(content: ITextFileStreamContent, options?: ILoadOptions, backup?: URI): Promise<TextFileEditorModel> { - const model = await this.doLoadWithContent(content, backup); - - // Telemetry: We log the fileGet telemetry event after the model has been loaded to ensure a good mimetype - const settingsType = this.getTypeIfSettings(); - if (settingsType) { - /* __GDPR__ - "settingsRead" : { - "settingsType": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('settingsRead', { settingsType }); // Do not log read to user settings.json and .vscode folder as a fileGet event as it ruins our JSON usage data - } else { - /* __GDPR__ - "fileGet" : { - "${include}": [ - "${FileTelemetryData}" - ] - } - */ - this.telemetryService.publicLog('fileGet', this.getTelemetryData(options && options.reason ? options.reason : LoadReason.OTHER)); - } - - return model; - } - - private doLoadWithContent(content: ITextFileStreamContent, backup?: URI): Promise<TextFileEditorModel> { + private loadFromContent(content: ITextFileStreamContent, options?: ILoadOptions, fromBackup?: boolean): TextFileEditorModel { this.logService.trace('load() - resolved content', this.resource); // Update our resolved disk stat model - this.updateLastResolvedDiskStat({ + this.updateLastResolvedFileStat({ resource: this.resource, name: content.name, mtime: content.mtime, @@ -409,21 +415,61 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } // Update Existing Model - if (this.textEditorModel) { + if (this.isResolved()) { this.doUpdateTextModel(content.value); - - return Promise.resolve(this); - } - - // Join an existing request to create the editor model to avoid race conditions - else if (this.createTextEditorModelPromise) { - this.logService.trace('load() - join existing text editor model promise', this.resource); - - return this.createTextEditorModelPromise; } // Create New Model - return this.doCreateTextModel(content.resource, content.value, backup); + else { + this.doCreateTextModel(content.resource, content.value, !!fromBackup); + } + + // Telemetry: We log the fileGet telemetry event after the model has been loaded to ensure a good mimetype + const settingsType = this.getTypeIfSettings(); + if (settingsType) { + /* __GDPR__ + "settingsRead" : { + "settingsType": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog('settingsRead', { settingsType }); // Do not log read to user settings.json and .vscode folder as a fileGet event as it ruins our JSON usage data + } else { + /* __GDPR__ + "fileGet" : { + "${include}": [ + "${FileTelemetryData}" + ] + } + */ + this.telemetryService.publicLog('fileGet', this.getTelemetryData(options && options.reason ? options.reason : LoadReason.OTHER)); + } + + return this; + } + + private doCreateTextModel(resource: URI, value: ITextBufferFactory, fromBackup: boolean): void { + this.logService.trace('load() - created text editor model', this.resource); + + // Create model + this.createTextEditorModel(value, resource, this.preferredMode); + + // We restored a backup so we have to set the model as being dirty + // We also want to trigger auto save if it is enabled to simulate the exact same behaviour + // you would get if manually making the model dirty (fixes https://github.com/Microsoft/vscode/issues/16977) + if (fromBackup) { + this.makeDirty(); + if (this.autoSaveAfterMilliesEnabled) { + this.doAutoSave(this.versionId); + } + } + + // Ensure we are not tracking a stale state + else { + this.setDirty(false); + } + + // Model Listeners + this.installModelListeners(); } private doUpdateTextModel(value: ITextBufferFactory): void { @@ -435,7 +481,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Update model value in a block that ignores model content change events this.blockModelContentChange = true; try { - this.updateTextEditorModel(value); + this.updateTextEditorModel(value, this.preferredMode); } finally { this.blockModelContentChange = false; } @@ -444,44 +490,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.updateSavedVersionId(); } - private doCreateTextModel(resource: URI, value: ITextBufferFactory, backup: URI | undefined): Promise<TextFileEditorModel> { - this.logService.trace('load() - created text editor model', this.resource); - - this.createTextEditorModelPromise = this.doLoadBackup(backup).then(backupContent => { - this.createTextEditorModelPromise = null; - - // Create model - const hasBackupContent = !!backupContent; - this.createTextEditorModel(backupContent ? backupContent : value, resource); - - // We restored a backup so we have to set the model as being dirty - // We also want to trigger auto save if it is enabled to simulate the exact same behaviour - // you would get if manually making the model dirty (fixes https://github.com/Microsoft/vscode/issues/16977) - if (hasBackupContent) { - this.makeDirty(); - if (this.autoSaveAfterMilliesEnabled) { - this.doAutoSave(this.versionId); - } - } - - // Ensure we are not tracking a stale state - else { - this.setDirty(false); - } - - // Model Listeners - this.installModelListeners(); - - return this; - }, error => { - this.createTextEditorModelPromise = null; - - return Promise.reject<TextFileEditorModel>(error); - }); - - return this.createTextEditorModelPromise; - } - private installModelListeners(): void { // See https://github.com/Microsoft/vscode/issues/30189 @@ -489,27 +497,11 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // where `value` was captured in the content change listener closure scope. // Content Change - if (this.textEditorModel) { + if (this.isResolved()) { this._register(this.textEditorModel.onDidChangeContent(() => this.onModelContentChanged())); } } - private async doLoadBackup(backup: URI | undefined): Promise<ITextBufferFactory | null> { - if (!backup) { - return null; - } - - try { - return withUndefinedAsNull(await this.backupFileService.resolveBackupContent(backup)); - } catch (error) { - return null; // ignore errors - } - } - - protected getOrCreateMode(modeService: IModeService, preferredModeIds: string | undefined, firstLineText?: string): ILanguageSelection { - return modeService.createByFilepathOrFirstLine(this.resource.fsPath, firstLineText); - } - private onModelContentChanged(): void { this.logService.trace(`onModelContentChanged() - enter`, this.resource); @@ -526,7 +518,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // In this case we clear the dirty flag and emit a SAVED event to indicate this state. // Note: we currently only do this check when auto-save is turned off because there you see // a dirty indicator that you want to get rid of when undoing to the saved version. - if (!this.autoSaveAfterMilliesEnabled && this.textEditorModel && this.textEditorModel.getAlternativeVersionId() === this.bufferSavedVersionId) { + if (!this.autoSaveAfterMilliesEnabled && this.isResolved() && this.textEditorModel.getAlternativeVersionId() === this.bufferSavedVersionId) { this.logService.trace('onModelContentChanged() - model content changed back to last saved version', this.resource); // Clear flags @@ -575,7 +567,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.logService.trace(`doAutoSave() - enter for versionId ${versionId}`, this.resource); // Cancel any currently running auto saves to make this the one that succeeds - this.cancelPendingAutoSave(); + this.autoSaveDisposable.clear(); // Create new save timer and store it for disposal as needed const handle = setTimeout(() => { @@ -586,25 +578,18 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } }, this.autoSaveAfterMillies); - this.autoSaveDisposable = toDisposable(() => clearTimeout(handle)); + this.autoSaveDisposable.value = toDisposable(() => clearTimeout(handle)); } - private cancelPendingAutoSave(): void { - if (this.autoSaveDisposable) { - this.autoSaveDisposable.dispose(); - this.autoSaveDisposable = undefined; - } - } - - save(options: ISaveOptions = Object.create(null)): Promise<void> { + async save(options: ISaveOptions = Object.create(null)): Promise<void> { if (!this.isResolved()) { - return Promise.resolve(undefined); + return; } this.logService.trace('save() - enter', this.resource); // Cancel any currently running auto saves to make this the one that succeeds - this.cancelPendingAutoSave(); + this.autoSaveDisposable.clear(); return this.doSave(this.versionId, options); } @@ -624,7 +609,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil if (this.saveSequentializer.hasPendingSave(versionId)) { this.logService.trace(`doSave(${versionId}) - exit - found a pending save for versionId ${versionId}`, this.resource); - return this.saveSequentializer.pendingSave || Promise.resolve(undefined); + return this.saveSequentializer.pendingSave || Promise.resolve(); } // Return early if not dirty (unless forced) or version changed meanwhile @@ -637,7 +622,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil if ((!options.force && !this.dirty) || versionId !== this.versionId) { this.logService.trace(`doSave(${versionId}) - exit - because not dirty and/or versionId is different (this.isDirty: ${this.dirty}, this.versionId: ${this.versionId})`, this.resource); - return Promise.resolve(undefined); + return Promise.resolve(); } // Return if currently saving by storing this save request as the next save that should happen. @@ -657,7 +642,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Push all edit operations to the undo stack so that the user has a chance to // Ctrl+Z back to the saved version. We only do this when auto-save is turned off - if (!this.autoSaveAfterMilliesEnabled && this.textEditorModel) { + if (!this.autoSaveAfterMilliesEnabled && this.isResolved()) { this.textEditorModel.pushStackElement(); } @@ -687,7 +672,12 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // saving contents to disk that are stale (see https://github.com/Microsoft/vscode/issues/50942). // To fix this issue, we will not store the contents to disk when we got disposed. if (this.disposed) { - return undefined; + return; + } + + // We require a resolved model from this point on, since we are about to write data to disk. + if (!this.isResolved()) { + return; } // Under certain conditions we do a short-cut of flushing contents to disk when we can assume that @@ -713,16 +703,12 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Save to Disk // mark the save operation as currently pending with the versionId (it might have changed from a save participant triggering) this.logService.trace(`doSave(${versionId}) - before write()`, this.resource); - const snapshot = this.createSnapshot(); - if (!snapshot) { - throw new Error('Invalid snapshot'); - } - return this.saveSequentializer.setPending(newVersionId, this.textFileService.write(this.lastResolvedDiskStat.resource, snapshot, { + return this.saveSequentializer.setPending(newVersionId, this.textFileService.write(this.lastResolvedFileStat.resource, this.createSnapshot(), { overwriteReadonly: options.overwriteReadonly, overwriteEncoding: options.overwriteEncoding, - mtime: this.lastResolvedDiskStat.mtime, + mtime: this.lastResolvedFileStat.mtime, encoding: this.getEncoding(), - etag: this.lastResolvedDiskStat.etag, + etag: this.lastResolvedFileStat.etag, writeElevated: options.writeElevated }).then(stat => { this.logService.trace(`doSave(${versionId}) - after write()`, this.resource); @@ -736,7 +722,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } // Updated resolved stat with updated stat - this.updateLastResolvedDiskStat(stat); + this.updateLastResolvedFileStat(stat); // Cancel any content change event promises as they are no longer valid this.contentChangeEventScheduler.cancel(); @@ -789,22 +775,22 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } // Check for global settings file - if (isEqual(this.resource, URI.file(this.environmentService.appSettingsPath), !isLinux)) { + if (isEqual(this.resource, this.environmentService.settingsResource, !isLinux)) { return 'global-settings'; } // Check for keybindings file - if (isEqual(this.resource, URI.file(this.environmentService.appKeybindingsPath), !isLinux)) { + if (isEqual(this.resource, this.environmentService.keybindingsResource, !isLinux)) { return 'keybindings'; } // Check for locale file - if (isEqual(this.resource, URI.file(join(this.environmentService.appSettingsHome, 'locale.json')), !isLinux)) { + if (isEqual(this.resource, joinPath(this.environmentService.appSettingsHome, 'locale.json'), !isLinux)) { return 'locale'; } // Check for snippets - if (isEqualOrParent(this.resource, URI.file(join(this.environmentService.appSettingsHome, 'snippets')))) { + if (isEqualOrParent(this.resource, joinPath(this.environmentService.appSettingsHome, 'snippets'))) { return 'snippets'; } @@ -827,7 +813,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil const fileName = basename(this.resource); const path = this.resource.scheme === Schemas.file ? this.resource.fsPath : this.resource.path; const telemetryData = { - mimeType: guessMimeTypes(path).join(', '), + mimeType: guessMimeTypes(this.resource).join(', '), ext, path: hash(path), reason @@ -850,19 +836,22 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } private doTouch(versionId: number): Promise<void> { - const snapshot = this.createSnapshot(); - if (!snapshot) { - throw new Error('invalid snapshot'); + if (!this.isResolved()) { + return Promise.resolve(); } - return this.saveSequentializer.setPending(versionId, this.textFileService.write(this.lastResolvedDiskStat.resource, snapshot, { - mtime: this.lastResolvedDiskStat.mtime, + return this.saveSequentializer.setPending(versionId, this.textFileService.write(this.lastResolvedFileStat.resource, this.createSnapshot(), { + mtime: this.lastResolvedFileStat.mtime, encoding: this.getEncoding(), - etag: this.lastResolvedDiskStat.etag + etag: this.lastResolvedFileStat.etag }).then(stat => { // Updated resolved stat with updated stat since touching it might have changed mtime - this.updateLastResolvedDiskStat(stat); + this.updateLastResolvedFileStat(stat); + + // Emit File Saved Event + this._onDidStateChange.fire(StateChange.SAVED); + }, error => onUnexpectedError(error) /* just log any error but do not notify the user since the file was not dirty */)); } @@ -896,23 +885,23 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // in order to find out if the model changed back to a saved version (e.g. // when undoing long enough to reach to a version that is saved and then to // clear the dirty flag) - if (this.textEditorModel) { + if (this.isResolved()) { this.bufferSavedVersionId = this.textEditorModel.getAlternativeVersionId(); } } - private updateLastResolvedDiskStat(newVersionOnDiskStat: IFileStatWithMetadata): void { + private updateLastResolvedFileStat(newFileStat: IFileStatWithMetadata): void { // First resolve - just take - if (!this.lastResolvedDiskStat) { - this.lastResolvedDiskStat = newVersionOnDiskStat; + if (!this.lastResolvedFileStat) { + this.lastResolvedFileStat = newFileStat; } // Subsequent resolve - make sure that we only assign it if the mtime is equal or has advanced. // This prevents race conditions from loading and saving. If a save comes in late after a revert // was called, the mtime could be out of sync. - else if (this.lastResolvedDiskStat.mtime <= newVersionOnDiskStat.mtime) { - this.lastResolvedDiskStat = newVersionOnDiskStat; + else if (this.lastResolvedFileStat.mtime <= newFileStat.mtime) { + this.lastResolvedFileStat = newFileStat; } } @@ -935,10 +924,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return this.lastSaveAttemptTime; } - getETag(): string | null { - return this.lastResolvedDiskStat ? this.lastResolvedDiskStat.etag || null : null; - } - hasState(state: ModelState): boolean { switch (state) { case ModelState.CONFLICT: @@ -1020,12 +1005,12 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return true; } - isResolved(): boolean { - return !isUndefinedOrNull(this.lastResolvedDiskStat); + isResolved(): this is IResolvedTextFileEditorModel { + return !!this.textEditorModel; } isReadonly(): boolean { - return !!(this.lastResolvedDiskStat && this.lastResolvedDiskStat.isReadonly); + return !!(this.lastResolvedFileStat && this.lastResolvedFileStat.isReadonly); } isDisposed(): boolean { @@ -1037,7 +1022,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } getStat(): IFileStatWithMetadata { - return this.lastResolvedDiskStat; + return this.lastResolvedFileStat; } dispose(): void { @@ -1046,10 +1031,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil this.inOrphanMode = false; this.inErrorMode = false; - this.createTextEditorModelPromise = null; - - this.cancelPendingAutoSave(); - super.dispose(); } } diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index 1baca93526c..af67898ef69 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -153,7 +153,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE // Model does not exist else { - const newModel = model = this.instantiationService.createInstance(TextFileEditorModel, resource, options ? options.encoding : undefined); + const newModel = model = this.instantiationService.createInstance(TextFileEditorModel, resource, options ? options.encoding : undefined, options ? options.mode : undefined); modelPromise = model.load(options); // Install state change listener @@ -204,6 +204,11 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE // Remove from pending loads this.mapResourceToPendingModelLoaders.delete(resource); + // Apply mode if provided + if (options && options.mode) { + resolvedModel.setMode(options.mode); + } + return resolvedModel; } catch (error) { diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index 1c513e1bca5..bb58fa2e153 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -40,6 +40,7 @@ import { trim } from 'vs/base/common/strings'; import { VSBuffer } from 'vs/base/common/buffer'; import { ITextSnapshot } from 'vs/editor/common/model'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; +import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; /** * The workbench file service implementation implements the raw file service spec and adds additional methods on top. @@ -118,7 +119,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer })); } - private beforeShutdown(reason: ShutdownReason): boolean | Promise<boolean> { + protected beforeShutdown(reason: ShutdownReason): boolean | Promise<boolean> { // Dirty files need treatment on shutdown const dirty = this.getDirty(); @@ -238,59 +239,44 @@ export abstract class TextFileService extends Disposable implements ITextFileSer private async doBackupAll(dirtyFileModels: ITextFileEditorModel[], untitledResources: URI[]): Promise<void> { // Handle file resources first - await Promise.all(dirtyFileModels.map(async model => { - const snapshot = model.createSnapshot(); - if (snapshot) { - await this.backupFileService.backupResource(model.getResource(), snapshot, model.getVersionId()); - } - })); + await Promise.all(dirtyFileModels.map(model => model.backup())); // Handle untitled resources - const untitledModelPromises = untitledResources + await Promise.all(untitledResources .filter(untitled => this.untitledEditorService.exists(untitled)) - .map(untitled => this.untitledEditorService.loadOrCreate({ resource: untitled })); - - const untitledModels = await Promise.all(untitledModelPromises); - - await Promise.all(untitledModels.map(async model => { - const snapshot = model.createSnapshot(); - if (snapshot) { - await this.backupFileService.backupResource(model.getResource(), snapshot, model.getVersionId()); - } - })); + .map(async untitled => (await this.untitledEditorService.loadOrCreate({ resource: untitled })).backup())); } - private confirmBeforeShutdown(): boolean | Promise<boolean> { - return this.confirmSave().then(confirm => { + private async confirmBeforeShutdown(): Promise<boolean> { + const confirm = await this.confirmSave(); - // Save - if (confirm === ConfirmResult.SAVE) { - return this.saveAll(true /* includeUntitled */, { skipSaveParticipants: true }).then(result => { - if (result.results.some(r => !r.success)) { - return true; // veto if some saves failed - } + // Save + if (confirm === ConfirmResult.SAVE) { + const result = await this.saveAll(true /* includeUntitled */, { skipSaveParticipants: true }); - return this.noVeto({ cleanUpBackups: true }); - }); + if (result.results.some(r => !r.success)) { + return true; // veto if some saves failed } - // Don't Save - else if (confirm === ConfirmResult.DONT_SAVE) { + return this.noVeto({ cleanUpBackups: true }); + } - // Make sure to revert untitled so that they do not restore - // see https://github.com/Microsoft/vscode/issues/29572 - this.untitledEditorService.revertAll(); + // Don't Save + else if (confirm === ConfirmResult.DONT_SAVE) { - return this.noVeto({ cleanUpBackups: true }); - } + // Make sure to revert untitled so that they do not restore + // see https://github.com/Microsoft/vscode/issues/29572 + this.untitledEditorService.revertAll(); - // Cancel - else if (confirm === ConfirmResult.CANCEL) { - return true; // veto - } + return this.noVeto({ cleanUpBackups: true }); + } - return false; - }); + // Cancel + else if (confirm === ConfirmResult.CANCEL) { + return true; // veto + } + + return false; } private noVeto(options: { cleanUpBackups: boolean }): boolean | Promise<boolean> { @@ -457,7 +443,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer return this.fileService.del(resource, options); } - async move(source: URI, target: URI, overwrite?: boolean): Promise<void> { + async move(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata> { const waitForPromises: Promise<unknown>[] = []; // Event @@ -503,10 +489,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer dirtyTargetModelUris.push(targetModelResource); // Backup dirty source model to the target resource it will become later - const snapshot = sourceModel.createSnapshot(); - if (snapshot) { - await this.backupFileService.backupResource(targetModelResource, snapshot, sourceModel.getVersionId()); - } + await sourceModel.backup(targetModelResource); })); } @@ -515,10 +498,12 @@ export abstract class TextFileService extends Disposable implements ITextFileSer // Rename to target try { - await this.fileService.move(source, target, overwrite); + const stat = await this.fileService.move(source, target, overwrite); // Load models that were dirty before await Promise.all(dirtyTargetModelUris.map(dirtyTargetModel => this.models.loadOrCreate(dirtyTargetModel))); + + return stat; } catch (error) { // In case of an error, discard any dirty target backups that were made @@ -663,18 +648,19 @@ export abstract class TextFileService extends Disposable implements ITextFileSer return result; } - protected async promptForPath(resource: URI, defaultUri: URI): Promise<URI | undefined> { + protected async promptForPath(resource: URI, defaultUri: URI, availableFileSystems?: string[]): Promise<URI | undefined> { // Help user to find a name for the file by opening it first await this.editorService.openEditor({ resource, options: { revealIfOpened: true, preserveFocus: true, } }); - return this.fileDialogService.showSaveDialog(this.getSaveDialogOptions(defaultUri)); + return this.fileDialogService.pickFileToSave(this.getSaveDialogOptions(defaultUri, availableFileSystems)); } - private getSaveDialogOptions(defaultUri: URI): ISaveDialogOptions { + private getSaveDialogOptions(defaultUri: URI, availableFileSystems?: string[]): ISaveDialogOptions { const options: ISaveDialogOptions = { defaultUri, - title: nls.localize('saveAsTitle', "Save As") + title: nls.localize('saveAsTitle', "Save As"), + availableFileSystems, }; // Filters are only enabled on Windows where they work properly @@ -757,14 +743,14 @@ export abstract class TextFileService extends Disposable implements ITextFileSer private getFileModels(arg1?: URI | URI[]): ITextFileEditorModel[] { if (Array.isArray(arg1)) { const models: ITextFileEditorModel[] = []; - (<URI[]>arg1).forEach(resource => { + arg1.forEach(resource => { models.push(...this.getFileModels(resource)); }); return models; } - return this._models.getAll(<URI>arg1); + return this._models.getAll(arg1); } private getDirtyFileModels(resources?: URI | URI[]): ITextFileEditorModel[] { @@ -780,7 +766,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer dialogPath = this.suggestFileName(resource); } - targetResource = await this.promptForPath(resource, dialogPath); + targetResource = await this.promptForPath(resource, dialogPath, options ? options.availableFileSystems : undefined); } if (!targetResource) { @@ -870,19 +856,15 @@ export abstract class TextFileService extends Disposable implements ITextFileSer return false; } - // take over encoding, mode and model value from source model + // take over encoding, mode (only if more specific) and model value from source model targetModel.updatePreferredEncoding(sourceModel.getEncoding()); - if (targetModel.textEditorModel) { - const snapshot = sourceModel.createSnapshot(); - if (snapshot) { - this.modelService.updateModel(targetModel.textEditorModel, createTextBufferFactoryFromSnapshot(snapshot)); - } + if (sourceModel.isResolved() && targetModel.isResolved()) { + this.modelService.updateModel(targetModel.textEditorModel, createTextBufferFactoryFromSnapshot(sourceModel.createSnapshot())); - if (sourceModel.textEditorModel) { - const language = sourceModel.textEditorModel.getLanguageIdentifier(); - if (language.id > 1) { - targetModel.textEditorModel.setMode(language); // only use if more specific than plain/text - } + const sourceMode = sourceModel.textEditorModel.getLanguageIdentifier(); + const targetMode = targetModel.textEditorModel.getLanguageIdentifier(); + if (sourceMode.language !== PLAINTEXT_MODE_ID && targetMode.language === PLAINTEXT_MODE_ID) { + targetModel.textEditorModel.setMode(sourceMode); // only use if more specific than plain/text } } diff --git a/src/vs/workbench/services/textfile/node/textResourcePropertiesService.ts b/src/vs/workbench/services/textfile/common/textResourcePropertiesService.ts similarity index 97% rename from src/vs/workbench/services/textfile/node/textResourcePropertiesService.ts rename to src/vs/workbench/services/textfile/common/textResourcePropertiesService.ts index e9a71315bea..ce097d6fdf5 100644 --- a/src/vs/workbench/services/textfile/node/textResourcePropertiesService.ts +++ b/src/vs/workbench/services/textfile/common/textResourcePropertiesService.ts @@ -17,7 +17,7 @@ import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiatio export class TextResourcePropertiesService implements ITextResourcePropertiesService { - _serviceBrand: ServiceIdentifier<any>; + _serviceBrand: ServiceIdentifier<ITextResourcePropertiesService>; private remoteEnvironment: IRemoteAgentEnvironment | null = null; diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 422ea39499b..a83474a5806 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -6,7 +6,7 @@ import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { IEncodingSupport, ConfirmResult, IRevertOptions } from 'vs/workbench/common/editor'; +import { IEncodingSupport, ConfirmResult, IRevertOptions, IModeSupport } from 'vs/workbench/common/editor'; import { IBaseStatWithMetadata, IFileStatWithMetadata, IReadFileOptions, IWriteFileOptions, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { ITextEditorModel } from 'vs/editor/common/services/resolverService'; @@ -125,7 +125,7 @@ export interface ITextFileService extends IDisposable { /** * Move a file. If the file is dirty, its contents will be preserved and restored. */ - move(source: URI, target: URI, overwrite?: boolean): Promise<void>; + move(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata>; /** * Brings up the confirm dialog to either save, don't save or cancel. @@ -136,12 +136,12 @@ export interface ITextFileService extends IDisposable { confirmSave(resources?: URI[]): Promise<ConfirmResult>; /** - * Convinient fast access to the current auto save mode. + * Convenient fast access to the current auto save mode. */ getAutoSaveMode(): AutoSaveMode; /** - * Convinient fast access to the raw configured auto save settings. + * Convenient fast access to the raw configured auto save settings. */ getAutoSaveConfiguration(): IAutoSaveConfiguration; } @@ -367,6 +367,11 @@ export interface IModelLoadOrCreateOptions { */ reason?: LoadReason; + /** + * The language mode to use for the model text content. + */ + mode?: string; + /** * The encoding to use when resolving the model text content. */ @@ -423,6 +428,7 @@ export interface ISaveOptions { overwriteEncoding?: boolean; skipSaveParticipants?: boolean; writeElevated?: boolean; + availableFileSystems?: string[]; } export interface ILoadOptions { @@ -443,19 +449,15 @@ export interface ILoadOptions { reason?: LoadReason; } -export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport { +export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport, IModeSupport { readonly onDidContentChange: Event<StateChange>; readonly onDidStateChange: Event<StateChange>; - getVersionId(): number; - getResource(): URI; hasState(state: ModelState): boolean; - getETag(): string | null; - updatePreferredEncoding(encoding: string): void; save(options?: ISaveOptions): Promise<void>; @@ -464,16 +466,17 @@ export interface ITextFileEditorModel extends ITextEditorModel, IEncodingSupport revert(soft?: boolean): Promise<void>; - createSnapshot(): ITextSnapshot | null; + backup(target?: URI): Promise<void>; isDirty(): boolean; - isResolved(): boolean; + isResolved(): this is IResolvedTextFileEditorModel; isDisposed(): boolean; } export interface IResolvedTextFileEditorModel extends ITextFileEditorModel { + readonly textEditorModel: ITextModel; createSnapshot(): ITextSnapshot; diff --git a/src/vs/workbench/services/textfile/node/textFileService.ts b/src/vs/workbench/services/textfile/node/textFileService.ts index aa80862877c..242aadab987 100644 --- a/src/vs/workbench/services/textfile/node/textFileService.ts +++ b/src/vs/workbench/services/textfile/node/textFileService.ts @@ -11,7 +11,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { URI } from 'vs/base/common/uri'; import { IFileStatWithMetadata, ICreateFileOptions, FileOperationError, FileOperationResult, IFileStreamContent, IFileService } from 'vs/platform/files/common/files'; import { Schemas } from 'vs/base/common/network'; -import { exists, stat, chmod, rimraf } from 'vs/base/node/pfs'; +import { exists, stat, chmod, rimraf, MAX_FILE_SIZE, MAX_HEAP_SIZE } from 'vs/base/node/pfs'; import { join, dirname } from 'vs/base/common/path'; import { isMacintosh, isLinux } from 'vs/base/common/platform'; import product from 'vs/platform/product/node/product'; @@ -26,7 +26,6 @@ import { VSBufferReadable, VSBuffer, VSBufferReadableStream } from 'vs/base/comm import { Readable } from 'stream'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel'; -import { MAX_FILE_SIZE, MAX_HEAP_SIZE } from 'vs/platform/files/node/fileConstants'; import { ITextSnapshot } from 'vs/editor/common/model'; export class NodeTextFileService extends TextFileService { @@ -391,7 +390,7 @@ export class EncodingOracle extends Disposable implements IResourceEncodings { const defaultEncodingOverrides: IEncodingOverride[] = []; // Global settings - defaultEncodingOverrides.push({ parent: URI.file(this.environmentService.appSettingsHome), encoding: UTF8 }); + defaultEncodingOverrides.push({ parent: this.environmentService.appSettingsHome, encoding: UTF8 }); // Workspace files defaultEncodingOverrides.push({ extension: WORKSPACE_EXTENSION, encoding: UTF8 }); diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts index ac6f0370672..e22cbe034db 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModel.test.ts @@ -14,6 +14,7 @@ import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/commo import { FileOperationResult, FileOperationError, IFileService } from 'vs/platform/files/common/files'; import { IModelService } from 'vs/editor/common/services/modelService'; import { timeout } from 'vs/base/common/async'; +import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry'; class ServiceAccessor { constructor(@ITextFileService public textFileService: TestTextFileService, @IModelService public modelService: IModelService, @IFileService public fileService: TestFileService) { @@ -44,25 +45,53 @@ suite('Files - TextFileEditorModel', () => { accessor.fileService.setContent(content); }); - test('Save', async function () { - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + test('save', async function () { + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); await model.load(); model.textEditorModel!.setValue('bar'); assert.ok(getLastModifiedTime(model) <= Date.now()); + let savedEvent = false; + model.onDidStateChange(e => { + if (e === StateChange.SAVED) { + savedEvent = true; + } + }); + await model.save(); assert.ok(model.getLastSaveAttemptTime() <= Date.now()); assert.ok(!model.isDirty()); + assert.ok(savedEvent); + + model.dispose(); + assert.ok(!accessor.modelService.getModel(model.getResource())); + }); + + test('save - touching also emits saved event', async function () { + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); + + await model.load(); + + let savedEvent = false; + model.onDidStateChange(e => { + if (e === StateChange.SAVED) { + savedEvent = true; + } + }); + + await model.save({ force: true }); + + assert.ok(savedEvent); model.dispose(); assert.ok(!accessor.modelService.getModel(model.getResource())); }); test('setEncoding - encode', function () { - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); model.setEncoding('utf8', EncodingMode.Encode); // no-op assert.equal(getLastModifiedTime(model), -1); @@ -75,7 +104,7 @@ suite('Files - TextFileEditorModel', () => { }); test('setEncoding - decode', async function () { - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); model.setEncoding('utf16', EncodingMode.Decode); @@ -84,8 +113,24 @@ suite('Files - TextFileEditorModel', () => { model.dispose(); }); + test('create with mode', async function () { + const mode = 'text-file-model-test'; + ModesRegistry.registerLanguage({ + id: mode, + }); + + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', mode); + + await model.load(); + + assert.equal(model.textEditorModel!.getModeId(), mode); + + model.dispose(); + assert.ok(!accessor.modelService.getModel(model.getResource())); + }); + test('disposes when underlying model is destroyed', async function () { - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); await model.load(); @@ -94,7 +139,7 @@ suite('Files - TextFileEditorModel', () => { }); test('Load does not trigger save', async function () { - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index.txt'), 'utf8'); + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index.txt'), 'utf8', undefined); assert.ok(model.hasState(ModelState.SAVED)); model.onDidStateChange(e => { @@ -108,7 +153,7 @@ suite('Files - TextFileEditorModel', () => { }); test('Load returns dirty model as long as model is dirty', async function () { - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); await model.load(); model.textEditorModel!.setValue('foo'); @@ -123,7 +168,7 @@ suite('Files - TextFileEditorModel', () => { test('Revert', async function () { let eventCounter = 0; - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); model.onDidStateChange(e => { if (e === StateChange.REVERTED) { @@ -145,7 +190,7 @@ suite('Files - TextFileEditorModel', () => { test('Revert (soft)', async function () { let eventCounter = 0; - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); model.onDidStateChange(e => { if (e === StateChange.REVERTED) { @@ -165,7 +210,7 @@ suite('Files - TextFileEditorModel', () => { }); test('Load and undo turns model dirty', async function () { - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); await model.load(); accessor.fileService.setContent('Hello Change'); @@ -175,7 +220,7 @@ suite('Files - TextFileEditorModel', () => { }); test('File not modified error is handled gracefully', async function () { - let model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + let model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); await model.load(); @@ -190,7 +235,7 @@ suite('Files - TextFileEditorModel', () => { }); test('Load error is handled gracefully if model already exists', async function () { - let model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + let model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); await model.load(); accessor.textFileService.setResolveTextContentErrorOnce(new FileOperationError('error', FileOperationResult.FILE_NOT_FOUND)); @@ -236,7 +281,7 @@ suite('Files - TextFileEditorModel', () => { test('Save Participant', async function () { let eventCounter = 0; - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); model.onDidStateChange(e => { if (e === StateChange.SAVED) { @@ -266,7 +311,7 @@ suite('Files - TextFileEditorModel', () => { test('Save Participant, async participant', async function () { - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); TextFileEditorModel.setSaveParticipant({ participate: (model) => { @@ -284,7 +329,7 @@ suite('Files - TextFileEditorModel', () => { }); test('Save Participant, bad participant', async function () { - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8'); + const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); TextFileEditorModel.setSaveParticipant({ participate: (model) => { diff --git a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts index 15be3b8533f..1b5f490f490 100644 --- a/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileEditorModelManager.test.ts @@ -13,6 +13,7 @@ import { IFileService, FileChangesEvent, FileChangeType } from 'vs/platform/file import { IModelService } from 'vs/editor/common/services/modelService'; import { timeout } from 'vs/base/common/async'; import { toResource } from 'vs/base/test/common/utils'; +import { ModesRegistry, PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; export class TestTextFileEditorModelManager extends TextFileEditorModelManager { @@ -42,9 +43,9 @@ suite('Files - TextFileEditorModelManager', () => { test('add, remove, clear, get, getAll', function () { const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); - const model1: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random1.txt'), 'utf8'); - const model2: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random2.txt'), 'utf8'); - const model3: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random3.txt'), 'utf8'); + const model1: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random1.txt'), 'utf8', undefined); + const model2: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random2.txt'), 'utf8', undefined); + const model3: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random3.txt'), 'utf8', undefined); manager.add(URI.file('/test.html'), model1); manager.add(URI.file('/some/other.html'), model2); @@ -117,9 +118,9 @@ suite('Files - TextFileEditorModelManager', () => { test('removed from cache when model disposed', function () { const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); - const model1: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random1.txt'), 'utf8'); - const model2: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random2.txt'), 'utf8'); - const model3: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random3.txt'), 'utf8'); + const model1: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random1.txt'), 'utf8', undefined); + const model2: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random2.txt'), 'utf8', undefined); + const model3: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/random3.txt'), 'utf8', undefined); manager.add(URI.file('/test.html'), model1); manager.add(URI.file('/some/other.html'), model2); @@ -290,4 +291,24 @@ suite('Files - TextFileEditorModelManager', () => { assert.ok(model.isDisposed()); manager.dispose(); }); + + test('mode', async function () { + const mode = 'text-file-model-manager-test'; + ModesRegistry.registerLanguage({ + id: mode, + }); + + const manager: TestTextFileEditorModelManager = instantiationService.createInstance(TestTextFileEditorModelManager); + + const resource = toResource.call(this, '/path/index_something.txt'); + + let model = await manager.loadOrCreate(resource, { mode }); + assert.equal(model.textEditorModel!.getModeId(), mode); + + model = await manager.loadOrCreate(resource, { mode: 'text' }); + assert.equal(model.textEditorModel!.getModeId(), PLAINTEXT_MODE_ID); + + manager.disposeModel((model as TextFileEditorModel)); + manager.dispose(); + }); }); \ No newline at end of file diff --git a/src/vs/workbench/services/textfile/test/textFileService.io.test.ts b/src/vs/workbench/services/textfile/test/textFileService.io.test.ts index 12d985b1825..3337c56b2a2 100644 --- a/src/vs/workbench/services/textfile/test/textFileService.io.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileService.io.test.ts @@ -17,7 +17,7 @@ import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { Schemas } from 'vs/base/common/network'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { rimraf, RimRafMode, copy, readFile, exists } from 'vs/base/node/pfs'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { FileService } from 'vs/workbench/services/files/common/fileService'; import { NullLogService } from 'vs/platform/log/common/log'; import { getRandomTestPath } from 'vs/base/test/node/testUtils'; @@ -76,7 +76,7 @@ suite('Files - TextFileService i/o', () => { const parentDir = getRandomTestPath(tmpdir(), 'vsctests', 'textfileservice'); let accessor: ServiceAccessor; - let disposables: IDisposable[] = []; + const disposables = new DisposableStore(); let service: ITextFileService; let testDir: string; @@ -88,8 +88,8 @@ suite('Files - TextFileService i/o', () => { const fileService = new FileService(logService); const fileProvider = new DiskFileSystemProvider(logService); - disposables.push(fileService.registerProvider(Schemas.file, fileProvider)); - disposables.push(fileProvider); + disposables.add(fileService.registerProvider(Schemas.file, fileProvider)); + disposables.add(fileProvider); const collection = new ServiceCollection(); collection.set(IFileService, fileService); @@ -108,7 +108,7 @@ suite('Files - TextFileService i/o', () => { (<TextFileEditorModelManager>accessor.textFileService.models).dispose(); accessor.untitledEditorService.revertAll(); - disposables = dispose(disposables); + disposables.clear(); await rimraf(parentDir, RimRafMode.MOVE); }); diff --git a/src/vs/workbench/services/textfile/test/textFileService.test.ts b/src/vs/workbench/services/textfile/test/textFileService.test.ts index 72049db9f11..ce5dd5dd772 100644 --- a/src/vs/workbench/services/textfile/test/textFileService.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileService.test.ts @@ -65,8 +65,8 @@ suite('Files - TextFileService', () => { accessor.untitledEditorService.revertAll(); }); - test('confirm onWillShutdown - no veto', function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + test('confirm onWillShutdown - no veto', async function () { + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model); const event = new BeforeShutdownEventImpl(); @@ -76,14 +76,12 @@ suite('Files - TextFileService', () => { if (typeof veto === 'boolean') { assert.ok(!veto); } else { - veto.then(veto => { - assert.ok(!veto); - }); + assert.ok(!(await veto)); } }); test('confirm onWillShutdown - veto if user cancels', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model); const service = accessor.textFileService; @@ -99,7 +97,7 @@ suite('Files - TextFileService', () => { }); test('confirm onWillShutdown - no veto and backups cleaned up if user does not want to save (hot.exit: off)', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model); const service = accessor.textFileService; @@ -125,7 +123,7 @@ suite('Files - TextFileService', () => { }); test('confirm onWillShutdown - save (hot.exit: off)', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model); const service = accessor.textFileService; @@ -144,7 +142,7 @@ suite('Files - TextFileService', () => { }); test('isDirty/getDirty - files and untitled', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model); const service = accessor.textFileService; @@ -171,7 +169,7 @@ suite('Files - TextFileService', () => { }); test('save - file', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model); const service = accessor.textFileService; @@ -187,11 +185,11 @@ suite('Files - TextFileService', () => { test('save - UNC path', async function () { const untitledUncUri = URI.from({ scheme: 'untitled', authority: 'server', path: '/share/path/file.txt' }); - model = instantiationService.createInstance(TextFileEditorModel, untitledUncUri, 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, untitledUncUri, 'utf8', undefined); (<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model); const mockedFileUri = untitledUncUri.with({ scheme: Schemas.file }); - const mockedEditorInput = instantiationService.createInstance(TextFileEditorModel, mockedFileUri, 'utf8'); + const mockedEditorInput = instantiationService.createInstance(TextFileEditorModel, mockedFileUri, 'utf8', undefined); const loadOrCreateStub = sinon.stub(accessor.textFileService.models, 'loadOrCreate', () => Promise.resolve(mockedEditorInput)); sinon.stub(accessor.untitledEditorService, 'exists', () => true); @@ -211,7 +209,7 @@ suite('Files - TextFileService', () => { }); test('saveAll - file', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model); const service = accessor.textFileService; @@ -228,7 +226,7 @@ suite('Files - TextFileService', () => { }); test('saveAs - file', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model); const service = accessor.textFileService; @@ -244,7 +242,7 @@ suite('Files - TextFileService', () => { }); test('revert - file', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model); const service = accessor.textFileService; @@ -260,7 +258,7 @@ suite('Files - TextFileService', () => { }); test('delete - dirty file', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model); const service = accessor.textFileService; @@ -274,8 +272,8 @@ suite('Files - TextFileService', () => { }); test('move - dirty file', async function () { - let sourceModel: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); - let targetModel: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_target.txt'), 'utf8'); + let sourceModel: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); + let targetModel: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_target.txt'), 'utf8', undefined); (<TextFileEditorModelManager>accessor.textFileService.models).add(sourceModel.getResource(), sourceModel); (<TextFileEditorModelManager>accessor.textFileService.models).add(targetModel.getResource(), targetModel); @@ -395,7 +393,7 @@ suite('Files - TextFileService', () => { }); async function hotExitTest(this: any, setting: string, shutdownReason: ShutdownReason, multipleWindows: boolean, workspace: true, shouldVeto: boolean): Promise<void> { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8'); + model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model); const service = accessor.textFileService; diff --git a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts index 42fc1561a60..a2f3269ecd0 100644 --- a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts +++ b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts @@ -31,7 +31,7 @@ class ResourceModelCollection extends ReferenceCollection<Promise<ITextEditorMod super(); } - createReferencedObject(key: string, skipActivateProvider?: boolean): Promise<ITextEditorModel> { + async createReferencedObject(key: string, skipActivateProvider?: boolean): Promise<ITextEditorModel> { this.modelsToDispose.delete(key); const resource = URI.parse(key); @@ -43,15 +43,19 @@ class ResourceModelCollection extends ReferenceCollection<Promise<ITextEditorMod // Virtual documents if (this.providers[resource.scheme]) { - return this.resolveTextModelContent(key).then(() => this.instantiationService.createInstance(ResourceEditorModel, resource)); + await this.resolveTextModelContent(key); + + return this.instantiationService.createInstance(ResourceEditorModel, resource); } // Either unknown schema, or not yet registered, try to activate if (!skipActivateProvider) { - return this.fileService.activateProvider(resource.scheme).then(() => this.createReferencedObject(key, true)); + await this.fileService.activateProvider(resource.scheme); + + return this.createReferencedObject(key, true); } - return Promise.reject(new Error('resource is not available')); + throw new Error('resource is not available'); } destroyReferencedObject(key: string, modelPromise: Promise<ITextEditorModel>): void { @@ -101,18 +105,17 @@ class ResourceModelCollection extends ReferenceCollection<Promise<ITextEditorMod return this.providers[scheme] !== undefined; } - private resolveTextModelContent(key: string): Promise<ITextModel> { + private async resolveTextModelContent(key: string): Promise<ITextModel> { const resource = URI.parse(key); const providers = this.providers[resource.scheme] || []; const factories = providers.map(p => () => Promise.resolve(p.provideTextContent(resource))); - return first(factories).then(model => { - if (!model) { - return Promise.reject(new Error('resource is not available')); - } + const model = await first(factories); + if (!model) { + throw new Error('resource is not available'); + } - return model; - }); + return model; } } @@ -131,14 +134,16 @@ export class TextModelResolverService implements ITextModelService { } createModelReference(resource: URI): Promise<IReference<IResolvedTextEditorModel>> { - return this._createModelReference(resource); + return this.doCreateModelReference(resource); } - private _createModelReference(resource: URI): Promise<IReference<IResolvedTextEditorModel>> { + private async doCreateModelReference(resource: URI): Promise<IReference<IResolvedTextEditorModel>> { // Untitled Schema: go through cached input if (resource.scheme === network.Schemas.untitled) { - return this.untitledEditorService.loadOrCreate({ resource }).then(model => new ImmortalReference(model as IResolvedTextEditorModel)); + const model = await this.untitledEditorService.loadOrCreate({ resource }); + + return new ImmortalReference(model as IResolvedTextEditorModel); } // InMemory Schema: go through model service cache @@ -146,22 +151,23 @@ export class TextModelResolverService implements ITextModelService { const cachedModel = this.modelService.getModel(resource); if (!cachedModel) { - return Promise.reject(new Error('Cant resolve inmemory resource')); + throw new Error('Cant resolve inmemory resource'); } - return Promise.resolve(new ImmortalReference(this.instantiationService.createInstance(ResourceEditorModel, resource) as IResolvedTextEditorModel)); + return new ImmortalReference(this.instantiationService.createInstance(ResourceEditorModel, resource) as IResolvedTextEditorModel); } const ref = this.resourceModelCollection.acquire(resource.toString()); - return ref.object.then( - model => ({ object: model, dispose: () => ref.dispose() }), - err => { - ref.dispose(); + try { + const model = await ref.object; - return Promise.reject(err); - } - ); + return { object: model as IResolvedTextEditorModel, dispose: () => ref.dispose() }; + } catch (error) { + ref.dispose(); + + throw error; + } } registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): IDisposable { diff --git a/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts b/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts index 10e4b34c2fb..b924c55963d 100644 --- a/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts +++ b/src/vs/workbench/services/textmodelResolver/test/textModelResolverService.test.ts @@ -53,7 +53,7 @@ suite('Workbench - TextModelResolverService', () => { accessor.untitledEditorService.revertAll(); }); - test('resolve resource', function () { + test('resolve resource', async () => { const dispose = accessor.textModelResolverService.registerTextModelContentProvider('test', { provideTextContent: function (resource: URI): Promise<ITextModel> { if (resource.scheme === 'test') { @@ -67,67 +67,60 @@ suite('Workbench - TextModelResolverService', () => { }); let resource = URI.from({ scheme: 'test', authority: null!, path: 'thePath' }); - let input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, 'The Name', 'The Description', resource); + let input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, 'The Name', 'The Description', resource, undefined); - return input.resolve().then(async model => { - assert.ok(model); - assert.equal(snapshotToString((model as ResourceEditorModel).createSnapshot()!), 'Hello Test'); - - let disposed = false; - let disposedPromise = new Promise(resolve => { - Event.once(model.onDispose)(() => { - disposed = true; - resolve(); - }); - }); - input.dispose(); - await disposedPromise; - assert.equal(disposed, true); - - dispose.dispose(); - }); - }); - - test('resolve file', function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_resolver.txt'), 'utf8'); - (<TextFileEditorModelManager>accessor.textFileService.models).add(model.getResource(), model); - - return model.load().then(() => { - return accessor.textModelResolverService.createModelReference(model.getResource()).then(ref => { - const model = ref.object; - const editorModel = model.textEditorModel; - - assert.ok(editorModel); - assert.equal(editorModel.getValue(), 'Hello Html'); - - let disposed = false; - Event.once(model.onDispose)(() => { - disposed = true; - }); - - ref.dispose(); - return timeout(0).then(() => { // due to the reference resolving the model first which is async - assert.equal(disposed, true); - }); + const model = await input.resolve(); + assert.ok(model); + assert.equal(snapshotToString(((model as ResourceEditorModel).createSnapshot()!)), 'Hello Test'); + let disposed = false; + let disposedPromise = new Promise(resolve => { + Event.once(model.onDispose)(() => { + disposed = true; + resolve(); }); }); + input.dispose(); + + await disposedPromise; + assert.equal(disposed, true); + dispose.dispose(); }); - test('resolve untitled', function () { + test('resolve file', async function () { + const textModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_resolver.txt'), 'utf8', undefined); + (<TextFileEditorModelManager>accessor.textFileService.models).add(textModel.getResource(), textModel); + + await textModel.load(); + + const ref = await accessor.textModelResolverService.createModelReference(textModel.getResource()); + + const model = ref.object; + const editorModel = model.textEditorModel; + + assert.ok(editorModel); + assert.equal(editorModel.getValue(), 'Hello Html'); + + let disposed = false; + Event.once(model.onDispose)(() => { + disposed = true; + }); + + ref.dispose(); + await timeout(0); // due to the reference resolving the model first which is async + assert.equal(disposed, true); + }); + + test('resolve untitled', async () => { const service = accessor.untitledEditorService; const input = service.createOrGet(); - return input.resolve().then(() => { - return accessor.textModelResolverService.createModelReference(input.getResource()).then(ref => { - const model = ref.object; - const editorModel = model.textEditorModel; - - assert.ok(editorModel); - ref.dispose(); - - input.dispose(); - }); - }); + await input.resolve(); + const ref = await accessor.textModelResolverService.createModelReference(input.getResource()); + const model = ref.object; + const editorModel = model.textEditorModel; + assert.ok(editorModel); + ref.dispose(); + input.dispose(); }); test('even loading documents should be refcounted', async () => { @@ -135,12 +128,12 @@ suite('Workbench - TextModelResolverService', () => { let waitForIt = new Promise(c => resolveModel = c); const disposable = accessor.textModelResolverService.registerTextModelContentProvider('test', { - provideTextContent: (resource: URI): Promise<ITextModel> => { - return waitForIt.then(_ => { - let modelContent = 'Hello Test'; - let languageSelection = accessor.modeService.create('json'); - return accessor.modelService.createModel(modelContent, languageSelection, resource); - }); + provideTextContent: async (resource: URI): Promise<ITextModel> => { + await waitForIt; + + let modelContent = 'Hello Test'; + let languageSelection = accessor.modeService.create('json'); + return accessor.modelService.createModel(modelContent, languageSelection, resource); } }); diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index 2bb21797ae9..3ff44d30384 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -44,6 +44,7 @@ const defaultThemeExtensionId = 'vscode-theme-defaults'; const oldDefaultThemeExtensionId = 'vscode-theme-colorful-defaults'; const DEFAULT_ICON_THEME_SETTING_VALUE = 'vs-seti'; +const DEFAULT_ICON_THEME_ID = 'vscode.vscode-theme-seti-vs-seti'; const fileIconsEnabledClass = 'file-icons-enabled'; const colorThemeRulesClassName = 'contributedColorTheme'; @@ -190,10 +191,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (!theme) { // current theme is no longer available prevFileIconId = this.currentIconTheme.id; - this.setFileIconTheme(DEFAULT_ICON_THEME_SETTING_VALUE, 'auto'); + this.setFileIconTheme(DEFAULT_ICON_THEME_ID, 'auto'); } else { // restore color - if (this.currentIconTheme.id === DEFAULT_ICON_THEME_SETTING_VALUE && !types.isUndefined(prevFileIconId) && await this.iconThemeStore.findThemeData(prevFileIconId)) { + if (this.currentIconTheme.id === DEFAULT_ICON_THEME_ID && !types.isUndefined(prevFileIconId) && await this.iconThemeStore.findThemeData(prevFileIconId)) { this.setFileIconTheme(prevFileIconId, 'auto'); prevFileIconId = undefined; } @@ -269,7 +270,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (devThemes.length) { return this.setFileIconTheme(devThemes[0].id, ConfigurationTarget.MEMORY); } else { - return this.setFileIconTheme(theme && theme.id || DEFAULT_ICON_THEME_SETTING_VALUE, undefined); + return this.setFileIconTheme(theme ? theme.id : DEFAULT_ICON_THEME_ID, undefined); } }); }), @@ -292,7 +293,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { let iconThemeSetting = this.configurationService.getValue<string | null>(ICON_THEME_SETTING); if (iconThemeSetting !== this.currentIconTheme.settingsId) { this.iconThemeStore.findThemeBySettingsId(iconThemeSetting).then(theme => { - this.setFileIconTheme(theme && theme.id || DEFAULT_ICON_THEME_SETTING_VALUE, undefined); + this.setFileIconTheme(theme ? theme.id : DEFAULT_ICON_THEME_ID, undefined); }); } } @@ -479,7 +480,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.doSetFileIconTheme(newIconTheme); // remember theme data for a quick restore - if (newIconTheme.isLoaded && newIconTheme.location && !getRemoteAuthority(newIconTheme.location)) { + if (newIconTheme.isLoaded && (!newIconTheme.location || !getRemoteAuthority(newIconTheme.location))) { this.storageService.store(PERSISTED_ICON_THEME_STORAGE_KEY, newIconTheme.toStorageData(), StorageScope.GLOBAL); } diff --git a/src/vs/workbench/services/themes/common/colorThemeData.ts b/src/vs/workbench/services/themes/common/colorThemeData.ts index acc4ba8788d..08253f09445 100644 --- a/src/vs/workbench/services/themes/common/colorThemeData.ts +++ b/src/vs/workbench/services/themes/common/colorThemeData.ts @@ -251,7 +251,7 @@ export class ColorThemeData implements IColorTheme { break; case 'themeTokenColors': case 'id': case 'label': case 'settingsId': case 'extensionData': case 'watch': - theme[key] = data[key]; + (theme as any)[key] = data[key]; break; } } diff --git a/src/vs/workbench/services/themes/common/fileIconThemeData.ts b/src/vs/workbench/services/themes/common/fileIconThemeData.ts index 7d84be48b03..306d58f9155 100644 --- a/src/vs/workbench/services/themes/common/fileIconThemeData.ts +++ b/src/vs/workbench/services/themes/common/fileIconThemeData.ts @@ -118,7 +118,7 @@ export class FileIconThemeData implements IFileIconTheme { case 'hidesExplorerArrows': case 'hasFolderIcons': case 'watch': - theme[key] = data[key]; + (theme as any)[key] = data[key]; break; case 'location': theme.location = URI.revive(data.location); diff --git a/src/vs/workbench/services/themes/common/fileIconThemeStore.ts b/src/vs/workbench/services/themes/common/fileIconThemeStore.ts index b887b23dea3..dfc2b180f4e 100644 --- a/src/vs/workbench/services/themes/common/fileIconThemeStore.ts +++ b/src/vs/workbench/services/themes/common/fileIconThemeStore.ts @@ -13,6 +13,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { Event, Emitter } from 'vs/base/common/event'; import { FileIconThemeData } from 'vs/workbench/services/themes/common/fileIconThemeData'; import { URI } from 'vs/base/common/uri'; +import { Disposable } from 'vs/base/common/lifecycle'; const iconThemeExtPoint = ExtensionsRegistry.registerExtensionPoint<IThemeExtensionPoint[]>({ extensionPoint: 'iconThemes', @@ -46,16 +47,16 @@ export interface FileIconThemeChangeEvent { added: FileIconThemeData[]; } -export class FileIconThemeStore { +export class FileIconThemeStore extends Disposable { private knownIconThemes: FileIconThemeData[]; - private readonly onDidChangeEmitter: Emitter<FileIconThemeChangeEvent>; - public get onDidChange(): Event<FileIconThemeChangeEvent> { return this.onDidChangeEmitter.event; } + private readonly onDidChangeEmitter = this._register(new Emitter<FileIconThemeChangeEvent>()); + readonly onDidChange: Event<FileIconThemeChangeEvent> = this.onDidChangeEmitter.event; constructor(@IExtensionService private readonly extensionService: IExtensionService) { + super(); this.knownIconThemes = []; - this.onDidChangeEmitter = new Emitter<FileIconThemeChangeEvent>(); this.initialize(); } @@ -167,5 +168,4 @@ export class FileIconThemeStore { return this.knownIconThemes; }); } - } diff --git a/src/vs/workbench/services/timer/electron-browser/timerService.ts b/src/vs/workbench/services/timer/electron-browser/timerService.ts index b26edf948a8..ffc0854905f 100644 --- a/src/vs/workbench/services/timer/electron-browser/timerService.ts +++ b/src/vs/workbench/services/timer/electron-browser/timerService.ts @@ -12,7 +12,6 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ 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 { 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'; @@ -23,14 +22,12 @@ import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessi /* __GDPR__FRAGMENT__ "IMemoryInfo" : { "workingSetSize" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "peakWorkingSetSize": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "privateBytes": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "sharedBytes": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } } */ export interface IMemoryInfo { readonly workingSetSize: number; - readonly peakWorkingSetSize: number; readonly privateBytes: number; readonly sharedBytes: number; } @@ -211,7 +208,7 @@ export interface IStartupMetrics { readonly ellapsedWorkspaceServiceInit: number; /** - * The time it took to load the main-bundle of the workbench, e.g `workbench.main.js`. + * The time it took to load the main-bundle of the workbench, e.g. `workbench.main.js`. * * * Happens in the renderer-process * * Measured with the `willLoadWorkbenchMain` and `didLoadWorkbenchMain` performance marks. @@ -355,7 +352,13 @@ class TimerService implements ITimerService { release = os.release(); arch = os.arch(); loadavg = os.loadavg(); - meminfo = process.getProcessMemoryInfo(); + + const processMemoryInfo = await process.getProcessMemoryInfo(); + meminfo = { + workingSetSize: processMemoryInfo.residentSet, + privateBytes: processMemoryInfo.private, + sharedBytes: processMemoryInfo.shared + }; isVMLikelyhood = Math.round((virtualMachineHint.value() * 100)); @@ -428,16 +431,19 @@ export function didUseCachedData(): boolean { if (!Boolean((<any>global).require.getConfig().nodeCachedData)) { return false; } - // 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 (isNonEmptyArray(MonacoEnvironment.onNodeCachedData)) { - return false; + // There are loader events that signal if cached data was missing, rejected, + // or used. The former two mean no cached data. + let cachedDataFound = 0; + for (const event of require.getStats()) { + switch (event.type) { + case LoaderEventType.CachedDataRejected: + return false; + case LoaderEventType.CachedDataFound: + cachedDataFound += 1; + break; + } } - return true; + return cachedDataFound > 0; } -declare type OnNodeCachedDataArgs = [{ errorCode: string, path: string, detail?: string }, { path: string, length: number }]; -declare const MonacoEnvironment: { onNodeCachedData: OnNodeCachedDataArgs[] }; - //#endregion diff --git a/src/vs/workbench/services/untitled/common/untitledEditorService.ts b/src/vs/workbench/services/untitled/common/untitledEditorService.ts index 6b343944f7c..35b571ec52b 100644 --- a/src/vs/workbench/services/untitled/common/untitledEditorService.ts +++ b/src/vs/workbench/services/untitled/common/untitledEditorService.ts @@ -21,7 +21,7 @@ export const IUntitledEditorService = createDecorator<IUntitledEditorService>('u export interface IModelLoadOrCreateOptions { resource?: URI; - modeId?: string; + mode?: string; initialValue?: string; encoding?: string; useResourcePath?: boolean; @@ -29,7 +29,7 @@ export interface IModelLoadOrCreateOptions { export interface IUntitledEditorService { - _serviceBrand: any; + _serviceBrand: ServiceIdentifier<IUntitledEditorService>; /** * Events for when untitled editors content changes (e.g. any keystroke). @@ -78,7 +78,7 @@ export interface IUntitledEditorService { * It is valid to pass in a file resource. In that case the path will be used as identifier. * The use case is to be able to create a new file with a specific path with VSCode. */ - createOrGet(resource?: URI, modeId?: string, initialValue?: string, encoding?: string): UntitledEditorInput; + createOrGet(resource?: URI, mode?: string, initialValue?: string, encoding?: string): UntitledEditorInput; /** * Creates a new untitled model with the optional resource URI or returns an existing one @@ -184,10 +184,10 @@ export class UntitledEditorService extends Disposable implements IUntitledEditor } loadOrCreate(options: IModelLoadOrCreateOptions = Object.create(null)): Promise<UntitledEditorModel> { - return this.createOrGet(options.resource, options.modeId, options.initialValue, options.encoding, options.useResourcePath).resolve(); + return this.createOrGet(options.resource, options.mode, options.initialValue, options.encoding, options.useResourcePath).resolve(); } - createOrGet(resource?: URI, modeId?: string, initialValue?: string, encoding?: string, hasAssociatedFilePath: boolean = false): UntitledEditorInput { + createOrGet(resource?: URI, mode?: string, initialValue?: string, encoding?: string, hasAssociatedFilePath: boolean = false): UntitledEditorInput { if (resource) { // Massage resource if it comes with known file based resource @@ -207,44 +207,47 @@ export class UntitledEditorService extends Disposable implements IUntitledEditor } // Create new otherwise - return this.doCreate(resource, hasAssociatedFilePath, modeId, initialValue, encoding); + return this.doCreate(resource, hasAssociatedFilePath, mode, initialValue, encoding); } - private doCreate(resource?: URI, hasAssociatedFilePath?: boolean, modeId?: string, initialValue?: string, encoding?: string): UntitledEditorInput { - if (!resource) { + private doCreate(resource?: URI, hasAssociatedFilePath?: boolean, mode?: string, initialValue?: string, encoding?: string): UntitledEditorInput { + let untitledResource: URI; + if (resource) { + untitledResource = resource; + } else { // Create new taking a resource URI that is not already taken let counter = this.mapResourceToInput.size + 1; do { - resource = URI.from({ scheme: Schemas.untitled, path: `Untitled-${counter}` }); + untitledResource = URI.from({ scheme: Schemas.untitled, path: `Untitled-${counter}` }); counter++; - } while (this.mapResourceToInput.has(resource)); + } while (this.mapResourceToInput.has(untitledResource)); } // Look up default language from settings if any - if (!modeId && !hasAssociatedFilePath) { + if (!mode && !hasAssociatedFilePath) { const configuration = this.configurationService.getValue<IFilesConfiguration>(); if (configuration.files && configuration.files.defaultLanguage) { - modeId = configuration.files.defaultLanguage; + mode = configuration.files.defaultLanguage; } } - const input = this.instantiationService.createInstance(UntitledEditorInput, resource, hasAssociatedFilePath, modeId, initialValue, encoding); + const input = this.instantiationService.createInstance(UntitledEditorInput, untitledResource, hasAssociatedFilePath, mode, initialValue, encoding); const contentListener = input.onDidModelChangeContent(() => { - this._onDidChangeContent.fire(resource!); + this._onDidChangeContent.fire(untitledResource); }); const dirtyListener = input.onDidChangeDirty(() => { - this._onDidChangeDirty.fire(resource!); + this._onDidChangeDirty.fire(untitledResource); }); const encodingListener = input.onDidModelChangeEncoding(() => { - this._onDidChangeEncoding.fire(resource!); + this._onDidChangeEncoding.fire(untitledResource); }); const disposeListener = input.onDispose(() => { - this._onDidDisposeModel.fire(resource!); + this._onDidDisposeModel.fire(untitledResource); }); // Remove from cache on dispose @@ -259,7 +262,7 @@ export class UntitledEditorService extends Disposable implements IUntitledEditor }); // Add to cache - this.mapResourceToInput.set(resource, input); + this.mapResourceToInput.set(untitledResource, input); return input; } diff --git a/src/vs/workbench/services/viewlet/browser/viewlet.ts b/src/vs/workbench/services/viewlet/browser/viewlet.ts index 3883013c4a6..92349b87a4b 100644 --- a/src/vs/workbench/services/viewlet/browser/viewlet.ts +++ b/src/vs/workbench/services/viewlet/browser/viewlet.ts @@ -7,17 +7,18 @@ import { IViewlet } from 'vs/workbench/common/viewlet'; import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { Event } from 'vs/base/common/event'; import { ViewletDescriptor } from 'vs/workbench/browser/viewlet'; -import { IProgressService } from 'vs/platform/progress/common/progress'; +import { IProgressIndicator } from 'vs/platform/progress/common/progress'; export const IViewletService = createDecorator<IViewletService>('viewletService'); export interface IViewletService { + _serviceBrand: ServiceIdentifier<any>; - onDidViewletRegister: Event<ViewletDescriptor>; - onDidViewletDeregister: Event<ViewletDescriptor>; - onDidViewletOpen: Event<IViewlet>; - onDidViewletClose: Event<IViewlet>; + readonly onDidViewletRegister: Event<ViewletDescriptor>; + readonly onDidViewletDeregister: Event<ViewletDescriptor>; + readonly onDidViewletOpen: Event<IViewlet>; + readonly onDidViewletClose: Event<IViewlet>; /** * Opens a viewlet with the given identifier and pass keyboard focus to it if specified. @@ -47,7 +48,7 @@ export interface IViewletService { /** * Returns the progress indicator for the side bar. */ - getProgressIndicator(id: string): IProgressService | null; + getProgressIndicator(id: string): IProgressIndicator | null; /** * Hide the active viewlet. diff --git a/src/vs/workbench/services/window/electron-browser/windowService.ts b/src/vs/workbench/services/window/electron-browser/windowService.ts index d3975f0acdd..52a00f05b26 100644 --- a/src/vs/workbench/services/window/electron-browser/windowService.ts +++ b/src/vs/workbench/services/window/electron-browser/windowService.ts @@ -109,7 +109,7 @@ export class WindowService extends Disposable implements IWindowService { return this.windowsService.closeWindow(this.windowId); } - toggleFullScreen(): Promise<void> { + toggleFullScreen(target?: HTMLElement): Promise<void> { return this.windowsService.toggleFullScreen(this.windowId); } diff --git a/src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts b/src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts index b09b8d1d520..905d2a8b292 100644 --- a/src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts @@ -57,83 +57,93 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { @ILifecycleService readonly lifecycleService: ILifecycleService, @ILabelService readonly labelService: ILabelService ) { + this.registerListeners(); + } - lifecycleService.onBeforeShutdown(async e => { + private registerListeners(): void { + this.lifecycleService.onBeforeShutdown(async e => { const saveOperation = this.saveUntitedBeforeShutdown(e.reason); if (saveOperation) { e.veto(saveOperation); } }); - } - private saveUntitedBeforeShutdown(reason: ShutdownReason): Promise<boolean> | undefined { + private async saveUntitedBeforeShutdown(reason: ShutdownReason): Promise<boolean> { if (reason !== ShutdownReason.LOAD && reason !== ShutdownReason.CLOSE) { - return undefined; // only interested when window is closing or loading + return false; // only interested when window is closing or loading } + const workspaceIdentifier = this.getCurrentWorkspaceIdentifier(); if (!workspaceIdentifier || !isEqualOrParent(workspaceIdentifier.configPath, this.environmentService.untitledWorkspacesHome)) { - return undefined; // only care about untitled workspaces to ask for saving + return false; // only care about untitled workspaces to ask for saving } - return this.windowsService.getWindowCount().then(windowCount => { - if (reason === ShutdownReason.CLOSE && !isMacintosh && windowCount === 1) { - return false; // Windows/Linux: quits when last window is closed, so do not ask then - } - enum ConfirmResult { - SAVE, - DONT_SAVE, - CANCEL - } + const windowCount = await this.windowsService.getWindowCount(); - const save = { label: mnemonicButtonLabel(nls.localize('save', "Save")), result: ConfirmResult.SAVE }; - const dontSave = { label: mnemonicButtonLabel(nls.localize('doNotSave', "Don't Save")), result: ConfirmResult.DONT_SAVE }; - const cancel = { label: nls.localize('cancel', "Cancel"), result: ConfirmResult.CANCEL }; + if (reason === ShutdownReason.CLOSE && !isMacintosh && windowCount === 1) { + return false; // Windows/Linux: quits when last window is closed, so do not ask then + } - const buttons: { label: string; result: ConfirmResult; }[] = []; - if (isWindows) { - buttons.push(save, dontSave, cancel); - } else if (isLinux) { - buttons.push(dontSave, cancel, save); - } else { - buttons.push(save, cancel, dontSave); - } + enum ConfirmResult { + SAVE, + DONT_SAVE, + CANCEL + } - const message = nls.localize('saveWorkspaceMessage', "Do you want to save your workspace configuration as a file?"); - const detail = nls.localize('saveWorkspaceDetail', "Save your workspace if you plan to open it again."); - const cancelId = buttons.indexOf(cancel); + const save = { label: mnemonicButtonLabel(nls.localize('save', "Save")), result: ConfirmResult.SAVE }; + const dontSave = { label: mnemonicButtonLabel(nls.localize('doNotSave', "Don't Save")), result: ConfirmResult.DONT_SAVE }; + const cancel = { label: nls.localize('cancel', "Cancel"), result: ConfirmResult.CANCEL }; - return this.dialogService.show(Severity.Warning, message, buttons.map(button => button.label), { detail, cancelId }).then(res => { - switch (buttons[res].result) { + const buttons: { label: string; result: ConfirmResult; }[] = []; + if (isWindows) { + buttons.push(save, dontSave, cancel); + } else if (isLinux) { + buttons.push(dontSave, cancel, save); + } else { + buttons.push(save, cancel, dontSave); + } - // Cancel: veto unload - case ConfirmResult.CANCEL: - return true; + const message = nls.localize('saveWorkspaceMessage', "Do you want to save your workspace configuration as a file?"); + const detail = nls.localize('saveWorkspaceDetail', "Save your workspace if you plan to open it again."); + const cancelId = buttons.indexOf(cancel); - // Don't Save: delete workspace - case ConfirmResult.DONT_SAVE: - this.workspaceService.deleteUntitledWorkspace(workspaceIdentifier); - return false; + const res = await this.dialogService.show(Severity.Warning, message, buttons.map(button => button.label), { detail, cancelId }); - // Save: save workspace, but do not veto unload - case ConfirmResult.SAVE: { - return this.pickNewWorkspacePath().then(newWorkspacePath => { - if (newWorkspacePath) { - return this.saveWorkspaceAs(workspaceIdentifier, newWorkspacePath).then(_ => { - return this.workspaceService.getWorkspaceIdentifier(newWorkspacePath).then(newWorkspaceIdentifier => { - const label = this.labelService.getWorkspaceLabel(newWorkspaceIdentifier, { verbose: true }); - this.windowsService.addRecentlyOpened([{ label, workspace: newWorkspaceIdentifier }]); - this.workspaceService.deleteUntitledWorkspace(workspaceIdentifier); - return false; - }); - }, () => false); - } - return true; // keep veto if no target was provided - }); - } + switch (buttons[res].result) { + + // Cancel: veto unload + case ConfirmResult.CANCEL: + return true; + + // Don't Save: delete workspace + case ConfirmResult.DONT_SAVE: + this.workspaceService.deleteUntitledWorkspace(workspaceIdentifier); + return false; + + // Save: save workspace, but do not veto unload if path provided + case ConfirmResult.SAVE: { + const newWorkspacePath = await this.pickNewWorkspacePath(); + if (!newWorkspacePath) { + return true; // keep veto if no target was provided } - }); - }); + + try { + await this.saveWorkspaceAs(workspaceIdentifier, newWorkspacePath); + + const newWorkspaceIdentifier = await this.workspaceService.getWorkspaceIdentifier(newWorkspacePath); + + const label = this.labelService.getWorkspaceLabel(newWorkspaceIdentifier, { verbose: true }); + this.windowsService.addRecentlyOpened([{ label, workspace: newWorkspaceIdentifier }]); + + this.workspaceService.deleteUntitledWorkspace(workspaceIdentifier); + } catch (error) { + // ignore + } + + return false; + } + } } pickNewWorkspacePath(): Promise<URI | undefined> { @@ -190,17 +200,31 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { } } - private doUpdateFolders(foldersToAdd: IWorkspaceFolderCreationData[], foldersToDelete: URI[], index?: number, donotNotifyError: boolean = false): Promise<void> { - return this.contextService.updateFolders(foldersToAdd, foldersToDelete, index) - .then(() => null, error => donotNotifyError ? Promise.reject(error) : this.handleWorkspaceConfigurationEditingError(error)); + private async doUpdateFolders(foldersToAdd: IWorkspaceFolderCreationData[], foldersToDelete: URI[], index?: number, donotNotifyError: boolean = false): Promise<void> { + try { + await this.contextService.updateFolders(foldersToAdd, foldersToDelete, index); + } catch (error) { + if (donotNotifyError) { + throw error; + } + + this.handleWorkspaceConfigurationEditingError(error); + } } addFolders(foldersToAdd: IWorkspaceFolderCreationData[], donotNotifyError: boolean = false): Promise<void> { return this.doAddFolders(foldersToAdd, undefined, donotNotifyError); } - private doAddFolders(foldersToAdd: IWorkspaceFolderCreationData[], index?: number, donotNotifyError: boolean = false): Promise<void> { + private async doAddFolders(foldersToAdd: IWorkspaceFolderCreationData[], index?: number, donotNotifyError: boolean = false): Promise<void> { const state = this.contextService.getWorkbenchState(); + if (this.environmentService.configuration.remoteAuthority) { + // Do not allow workspace folders with scheme different than the current remote scheme + const schemas = this.contextService.getWorkspace().folders.map(f => f.uri.scheme); + if (schemas.length && foldersToAdd.some(f => schemas.indexOf(f.uri.scheme) === -1)) { + return Promise.reject(new Error(nls.localize('differentSchemeRoots', "Workspace folders from different providers are not allowed in the same workspace."))); + } + } // If we are in no-workspace or single-folder workspace, adding folders has to // enter a workspace. @@ -210,18 +234,25 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { newWorkspaceFolders = distinct(newWorkspaceFolders, folder => getComparisonKey(folder.uri)); if (state === WorkbenchState.EMPTY && newWorkspaceFolders.length === 0 || state === WorkbenchState.FOLDER && newWorkspaceFolders.length === 1) { - return Promise.resolve(); // return if the operation is a no-op for the current state + return; // return if the operation is a no-op for the current state } return this.createAndEnterWorkspace(newWorkspaceFolders); } // Delegate addition of folders to workspace service otherwise - return this.contextService.addFolders(foldersToAdd, index) - .then(() => null, error => donotNotifyError ? Promise.reject(error) : this.handleWorkspaceConfigurationEditingError(error)); + try { + await this.contextService.addFolders(foldersToAdd, index); + } catch (error) { + if (donotNotifyError) { + throw error; + } + + this.handleWorkspaceConfigurationEditingError(error); + } } - removeFolders(foldersToRemove: URI[], donotNotifyError: boolean = false): Promise<void> { + async removeFolders(foldersToRemove: URI[], donotNotifyError: boolean = false): Promise<void> { // If we are in single-folder state and the opened folder is to be removed, // we create an empty workspace and enter it. @@ -230,8 +261,15 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { } // Delegate removal of folders to workspace service otherwise - return this.contextService.removeFolders(foldersToRemove) - .then(() => null, error => donotNotifyError ? Promise.reject(error) : this.handleWorkspaceConfigurationEditingError(error)); + try { + await this.contextService.removeFolders(foldersToRemove); + } catch (error) { + if (donotNotifyError) { + throw error; + } + + this.handleWorkspaceConfigurationEditingError(error); + } } private includesSingleFolderWorkspace(folders: URI[]): boolean { @@ -245,7 +283,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { async createAndEnterWorkspace(folders: IWorkspaceFolderCreationData[], path?: URI): Promise<void> { if (path && !await this.isValidTargetWorkspacePath(path)) { - return Promise.reject(null); + return; } const remoteAuthority = this.environmentService.configuration.remoteAuthority; const untitledWorkspace = await this.workspaceService.createUntitledWorkspace(folders, remoteAuthority); @@ -259,11 +297,11 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { async saveAndEnterWorkspace(path: URI): Promise<void> { if (!await this.isValidTargetWorkspacePath(path)) { - return Promise.reject(null); + return; } const workspaceIdentifier = this.getCurrentWorkspaceIdentifier(); if (!workspaceIdentifier) { - return Promise.reject(null); + return; } await this.saveWorkspaceAs(workspaceIdentifier, path); @@ -283,10 +321,12 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { detail: nls.localize('workspaceOpenedDetail', "The workspace is already opened in another window. Please close that window first and then try again."), noLink: true }; - return this.windowService.showMessageBox(options).then(() => false); + await this.windowService.showMessageBox(options); + + return false; } - return Promise.resolve(true); // OK + return true; // OK } private async saveWorkspaceAs(workspace: IWorkspaceIdentifier, targetConfigPathURI: URI): Promise<any> { @@ -294,7 +334,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { // Return early if target is same as source if (isEqual(configPathURI, targetConfigPathURI)) { - return Promise.resolve(null); + return; } // Read the contents of the workspace file, update it to new location and save it. @@ -303,18 +343,17 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { await this.textFileService.create(targetConfigPathURI, newRawWorkspaceContents, { overwrite: true }); } - private handleWorkspaceConfigurationEditingError(error: JSONEditingError): Promise<void> { + private handleWorkspaceConfigurationEditingError(error: JSONEditingError): void { switch (error.code) { case JSONEditingErrorCode.ERROR_INVALID_FILE: this.onInvalidWorkspaceConfigurationFileError(); - return Promise.resolve(); + break; case JSONEditingErrorCode.ERROR_FILE_DIRTY: this.onWorkspaceConfigurationFileDirtyError(); - return Promise.resolve(); + break; + default: + this.notificationService.error(error.message); } - this.notificationService.error(error.message); - - return Promise.resolve(); } private onInvalidWorkspaceConfigurationFileError(): void { @@ -338,7 +377,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { async enterWorkspace(path: URI): Promise<void> { if (!!this.environmentService.extensionTestsLocationURI) { - return Promise.reject(new Error('Entering a new workspace is not possible in tests.')); + throw new Error('Entering a new workspace is not possible in tests.'); } const workspace = await this.workspaceService.getWorkspaceIdentifier(path); diff --git a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts index 4f7400dbc91..c1ab20f2573 100644 --- a/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/baseEditor.test.ts @@ -86,7 +86,7 @@ class MyResourceInput extends ResourceEditorInput { } suite('Workbench base editor', () => { - test('BaseEditor API', function () { + test('BaseEditor API', async () => { let e = new MyEditor(NullTelemetryService); let input = new MyOtherInput(); let options = new EditorOptions(); @@ -94,25 +94,24 @@ suite('Workbench base editor', () => { assert(!e.isVisible()); assert(!e.input); assert(!e.options); - return e.setInput(input, options, CancellationToken.None).then(() => { - assert.strictEqual(input, e.input); - assert.strictEqual(options, e.options); - const group = new TestEditorGroup(1); - e.setVisible(true, group); - assert(e.isVisible()); - assert.equal(e.group, group); - input.onDispose(() => { - assert(false); - }); - e.dispose(); - e.clearInput(); - e.setVisible(false, group); - assert(!e.isVisible()); - assert(!e.input); - assert(!e.options); - assert(!e.getControl()); + await e.setInput(input, options, CancellationToken.None); + assert.strictEqual(input, e.input); + assert.strictEqual(options, e.options); + const group = new TestEditorGroup(1); + e.setVisible(true, group); + assert(e.isVisible()); + assert.equal(e.group, group); + input.onDispose(() => { + assert(false); }); + e.dispose(); + e.clearInput(); + e.setVisible(false, group); + assert(!e.isVisible()); + assert(!e.input); + assert(!e.options); + assert(!e.getControl()); }); test('EditorDescriptor', () => { @@ -154,10 +153,10 @@ suite('Workbench base editor', () => { let inst = new TestInstantiationService(); - const editor = EditorRegistry.getEditor(inst.createInstance(MyResourceInput, 'fake', '', URI.file('/fake')))!.instantiate(inst); + const editor = EditorRegistry.getEditor(inst.createInstance(MyResourceInput, 'fake', '', URI.file('/fake'), undefined))!.instantiate(inst); assert.strictEqual(editor.getId(), 'myEditor'); - const otherEditor = EditorRegistry.getEditor(inst.createInstance(ResourceEditorInput, 'fake', '', URI.file('/fake')))!.instantiate(inst); + const otherEditor = EditorRegistry.getEditor(inst.createInstance(ResourceEditorInput, 'fake', '', URI.file('/fake'), undefined))!.instantiate(inst); assert.strictEqual(otherEditor.getId(), 'myOtherEditor'); (<any>EditorRegistry).setEditors(oldEditors); @@ -173,7 +172,7 @@ suite('Workbench base editor', () => { let inst = new TestInstantiationService(); - const editor = EditorRegistry.getEditor(inst.createInstance(MyResourceInput, 'fake', '', URI.file('/fake')))!.instantiate(inst); + const editor = EditorRegistry.getEditor(inst.createInstance(MyResourceInput, 'fake', '', URI.file('/fake'), undefined))!.instantiate(inst); assert.strictEqual('myOtherEditor', editor.getId()); (<any>EditorRegistry).setEditors(oldEditors); diff --git a/src/vs/workbench/test/common/editor/editorDiffModel.test.ts b/src/vs/workbench/test/common/editor/editorDiffModel.test.ts index 3023add68ca..662bd56d224 100644 --- a/src/vs/workbench/test/common/editor/editorDiffModel.test.ts +++ b/src/vs/workbench/test/common/editor/editorDiffModel.test.ts @@ -35,7 +35,7 @@ suite('Workbench editor model', () => { accessor = instantiationService.createInstance(ServiceAccessor); }); - test('TextDiffEditorModel', () => { + test('TextDiffEditorModel', async () => { const dispose = accessor.textModelResolverService.registerTextModelContentProvider('test', { provideTextContent: function (resource: URI): Promise<ITextModel> { if (resource.scheme === 'test') { @@ -48,27 +48,26 @@ suite('Workbench editor model', () => { } }); - let input = instantiationService.createInstance(ResourceEditorInput, 'name', 'description', URI.from({ scheme: 'test', authority: null!, path: 'thePath' })); - let otherInput = instantiationService.createInstance(ResourceEditorInput, 'name2', 'description', URI.from({ scheme: 'test', authority: null!, path: 'thePath' })); + let input = instantiationService.createInstance(ResourceEditorInput, 'name', 'description', URI.from({ scheme: 'test', authority: null!, path: 'thePath' }), undefined); + let otherInput = instantiationService.createInstance(ResourceEditorInput, 'name2', 'description', URI.from({ scheme: 'test', authority: null!, path: 'thePath' }), undefined); let diffInput = new DiffEditorInput('name', 'description', input, otherInput); - return diffInput.resolve().then((model: any) => { - assert(model); - assert(model instanceof TextDiffEditorModel); + let model = await diffInput.resolve() as TextDiffEditorModel; - let diffEditorModel = model.textDiffEditorModel; - assert(diffEditorModel.original); - assert(diffEditorModel.modified); + assert(model); + assert(model instanceof TextDiffEditorModel); - return diffInput.resolve().then((model: any) => { - assert(model.isResolved()); + let diffEditorModel = model.textDiffEditorModel!; + assert(diffEditorModel.original); + assert(diffEditorModel.modified); - assert(diffEditorModel !== model.textDiffEditorModel); - diffInput.dispose(); - assert(!model.textDiffEditorModel); + model = await diffInput.resolve() as TextDiffEditorModel; + assert(model.isResolved()); - dispose.dispose(); - }); - }); + assert(diffEditorModel !== model.textDiffEditorModel); + diffInput.dispose(); + assert(!model.textDiffEditorModel); + + dispose.dispose(); }); }); diff --git a/src/vs/workbench/test/common/editor/editorGroups.test.ts b/src/vs/workbench/test/common/editor/editorGroups.test.ts index fde39c097be..18d862a91eb 100644 --- a/src/vs/workbench/test/common/editor/editorGroups.test.ts +++ b/src/vs/workbench/test/common/editor/editorGroups.test.ts @@ -111,27 +111,17 @@ class TestFileEditorInput extends EditorInput implements IFileEditorInput { } getTypeId() { return 'testFileEditorInputForGroups'; } resolve(): Promise<IEditorModel> { return Promise.resolve(null!); } + setEncoding(encoding: string) { } + getEncoding(): string { return null!; } + setPreferredEncoding(encoding: string) { } + getResource(): URI { return this.resource; } + setForceOpenAsBinary(): void { } + setMode(mode: string) { } + setPreferredMode(mode: string) { } matches(other: TestFileEditorInput): boolean { return other && this.id === other.id && other instanceof TestFileEditorInput; } - - setEncoding(encoding: string) { - } - - getEncoding(): string { - return null!; - } - - setPreferredEncoding(encoding: string) { - } - - getResource(): URI { - return this.resource; - } - - setForceOpenAsBinary(): void { - } } function input(id = String(index++), nonSerializable?: boolean, resource?: URI): EditorInput { diff --git a/src/vs/workbench/test/common/editor/editorModel.test.ts b/src/vs/workbench/test/common/editor/editorModel.test.ts index 513d2783fa1..663f9268501 100644 --- a/src/vs/workbench/test/common/editor/editorModel.test.ts +++ b/src/vs/workbench/test/common/editor/editorModel.test.ts @@ -21,8 +21,8 @@ import { TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTe class MyEditorModel extends EditorModel { } class MyTextEditorModel extends BaseTextEditorModel { - public createTextEditorModel(value: ITextBufferFactory, resource?: URI, modeId?: string) { - return super.createTextEditorModel(value, resource, modeId); + public createTextEditorModel(value: ITextBufferFactory, resource?: URI, preferredMode?: string) { + return super.createTextEditorModel(value, resource, preferredMode); } isReadonly(): boolean { @@ -40,7 +40,7 @@ suite('Workbench editor model', () => { modeService = instantiationService.stub(IModeService, ModeServiceImpl); }); - test('EditorModel', () => { + test('EditorModel', async () => { let counter = 0; let m = new MyEditorModel(); @@ -50,25 +50,23 @@ suite('Workbench editor model', () => { counter++; }); - return m.load().then(model => { - assert(model === m); - assert.strictEqual(m.isResolved(), true); - m.dispose(); - assert.equal(counter, 1); - }); + const model = await m.load(); + assert(model === m); + assert.strictEqual(m.isResolved(), true); + m.dispose(); + assert.equal(counter, 1); }); - test('BaseTextEditorModel', () => { + test('BaseTextEditorModel', async () => { let modelService = stubModelService(instantiationService); let m = new MyTextEditorModel(modelService, modeService); - return m.load().then((model: MyTextEditorModel) => { - assert(model === m); - model.createTextEditorModel(createTextBufferFactory('foo'), null!, 'text/plain'); - assert.strictEqual(m.isResolved(), true); - }).then(() => { - m.dispose(); - }); + const model = await m.load() as MyTextEditorModel; + + assert(model === m); + model.createTextEditorModel(createTextBufferFactory('foo'), null!, 'text/plain'); + assert.strictEqual(m.isResolved(), true); + m.dispose(); }); function stubModelService(instantiationService: TestInstantiationService): IModelService { diff --git a/src/vs/workbench/test/common/editor/resourceEditorInput.test.ts b/src/vs/workbench/test/common/editor/resourceEditorInput.test.ts index 954b83d9384..d1e788e80f4 100644 --- a/src/vs/workbench/test/common/editor/resourceEditorInput.test.ts +++ b/src/vs/workbench/test/common/editor/resourceEditorInput.test.ts @@ -12,17 +12,16 @@ import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestSe import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; +import { ModesRegistry, PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; class ServiceAccessor { constructor( @IModelService public modelService: IModelService, @IModeService public modeService: IModeService - ) { - } + ) { } } suite('Workbench resource editor input', () => { - let instantiationService: IInstantiationService; let accessor: ServiceAccessor; @@ -31,14 +30,33 @@ suite('Workbench resource editor input', () => { accessor = instantiationService.createInstance(ServiceAccessor); }); - test('simple', () => { - let resource = URI.from({ scheme: 'inmemory', authority: null!, path: 'thePath' }); + test('basics', async () => { + const resource = URI.from({ scheme: 'inmemory', authority: null!, path: 'thePath' }); 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 => { - assert.ok(model); - assert.equal(snapshotToString((model as ResourceEditorModel).createSnapshot()!), 'function test() {}'); + const input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, 'The Name', 'The Description', resource, undefined); + + const model = await input.resolve(); + + assert.ok(model); + assert.equal(snapshotToString(((model as ResourceEditorModel).createSnapshot()!)), 'function test() {}'); + }); + + test('custom mode', async () => { + ModesRegistry.registerLanguage({ + id: 'resource-input-test', }); + + const resource = URI.from({ scheme: 'inmemory', authority: null!, path: 'thePath' }); + accessor.modelService.createModel('function test() {}', accessor.modeService.create('text'), resource); + + const input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, 'The Name', 'The Description', resource, 'resource-input-test'); + + const model = await input.resolve(); + assert.ok(model); + assert.equal(model.textEditorModel.getModeId(), 'resource-input-test'); + + input.setMode('text'); + assert.equal(model.textEditorModel.getModeId(), PLAINTEXT_MODE_ID); }); }); \ No newline at end of file diff --git a/src/vs/workbench/test/common/editor/untitledEditor.test.ts b/src/vs/workbench/test/common/editor/untitledEditor.test.ts index 3dc06265b44..001ad7a6a2b 100644 --- a/src/vs/workbench/test/common/editor/untitledEditor.test.ts +++ b/src/vs/workbench/test/common/editor/untitledEditor.test.ts @@ -16,6 +16,7 @@ import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; import { timeout } from 'vs/base/common/async'; import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; +import { ModesRegistry, PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; export class TestUntitledEditorService extends UntitledEditorService { get(resource: URI) { return super.get(resource); } @@ -45,7 +46,7 @@ suite('Workbench untitled editors', () => { accessor.untitledEditorService.dispose(); }); - test('Untitled Editor Service', function (done) { + test('Untitled Editor Service', async (done) => { const service = accessor.untitledEditorService; assert.equal(service.getAll().length, 0); @@ -68,36 +69,35 @@ suite('Workbench untitled editors', () => { assert.equal(service.getAll().length, 1); // dirty - input2.resolve().then(model => { - assert.ok(!service.isDirty(input2.getResource())); + const model = await input2.resolve(); - const listener = service.onDidChangeDirty(resource => { - listener.dispose(); + assert.ok(!service.isDirty(input2.getResource())); - assert.equal(resource.toString(), input2.getResource().toString()); + const listener = service.onDidChangeDirty(resource => { + listener.dispose(); - assert.ok(service.isDirty(input2.getResource())); - assert.equal(service.getDirty()[0].toString(), input2.getResource().toString()); - assert.equal(service.getDirty([input2.getResource()])[0].toString(), input2.getResource().toString()); - assert.equal(service.getDirty([input1.getResource()]).length, 0); + assert.equal(resource.toString(), input2.getResource().toString()); - service.revertAll(); - assert.equal(service.getAll().length, 0); - assert.ok(!input2.isDirty()); - assert.ok(!model.isDirty()); + assert.ok(service.isDirty(input2.getResource())); + assert.equal(service.getDirty()[0].toString(), input2.getResource().toString()); + assert.equal(service.getDirty([input2.getResource()])[0].toString(), input2.getResource().toString()); + assert.equal(service.getDirty([input1.getResource()]).length, 0); - input2.dispose(); + service.revertAll(); + assert.equal(service.getAll().length, 0); + assert.ok(!input2.isDirty()); + assert.ok(!model.isDirty()); - assert.ok(!service.exists(input2.getResource())); + input2.dispose(); - done(); - }); + assert.ok(!service.exists(input2.getResource())); + done(); + }); - model.textEditorModel.setValue('foo bar'); - }, err => done(err)); + model.textEditorModel.setValue('foo bar'); }); - test('Untitled with associated resource', function () { + test('Untitled with associated resource', () => { const service = accessor.untitledEditorService; const file = URI.file(join('C:\\', '/foo/file.txt')); const untitled = service.createOrGet(file); @@ -107,53 +107,49 @@ suite('Workbench untitled editors', () => { untitled.dispose(); }); - test('Untitled no longer dirty when content gets empty', function () { + test('Untitled no longer dirty when content gets empty', async () => { const service = accessor.untitledEditorService; const input = service.createOrGet(); // dirty - return input.resolve().then(model => { - model.textEditorModel.setValue('foo bar'); - assert.ok(model.isDirty()); - - model.textEditorModel.setValue(''); - assert.ok(!model.isDirty()); - - input.dispose(); - }); + const model = await input.resolve(); + model.textEditorModel.setValue('foo bar'); + assert.ok(model.isDirty()); + model.textEditorModel.setValue(''); + assert.ok(!model.isDirty()); + input.dispose(); }); - test('Untitled via loadOrCreate', function () { + test('Untitled via loadOrCreate', async () => { const service = accessor.untitledEditorService; - service.loadOrCreate().then(model1 => { - model1.textEditorModel!.setValue('foo bar'); - assert.ok(model1.isDirty()); - model1.textEditorModel!.setValue(''); - assert.ok(!model1.isDirty()); + const model1 = await service.loadOrCreate(); - return service.loadOrCreate({ initialValue: 'Hello World' }).then(model2 => { - assert.equal(snapshotToString(model2.createSnapshot()!), 'Hello World'); + model1.textEditorModel!.setValue('foo bar'); + assert.ok(model1.isDirty()); - const input = service.createOrGet(); + model1.textEditorModel!.setValue(''); + assert.ok(!model1.isDirty()); - return service.loadOrCreate({ resource: input.getResource() }).then(model3 => { - assert.equal(model3.getResource().toString(), input.getResource().toString()); + const model2 = await service.loadOrCreate({ initialValue: 'Hello World' }); + assert.equal(snapshotToString(model2.createSnapshot()!), 'Hello World'); - const file = URI.file(join('C:\\', '/foo/file44.txt')); - return service.loadOrCreate({ resource: file }).then(model4 => { - assert.ok(service.hasAssociatedFilePath(model4.getResource())); - assert.ok(model4.isDirty()); + const input = service.createOrGet(); - model1.dispose(); - model2.dispose(); - model3.dispose(); - model4.dispose(); - input.dispose(); - }); - }); - }); - }); + const model3 = await service.loadOrCreate({ resource: input.getResource() }); + + assert.equal(model3.getResource().toString(), input.getResource().toString()); + + const file = URI.file(join('C:\\', '/foo/file44.txt')); + const model4 = await service.loadOrCreate({ resource: file }); + assert.ok(service.hasAssociatedFilePath(model4.getResource())); + assert.ok(model4.isDirty()); + + model1.dispose(); + model2.dispose(); + model3.dispose(); + model4.dispose(); + input.dispose(); }); test('Untitled suggest name', function () { @@ -163,24 +159,31 @@ suite('Workbench untitled editors', () => { assert.ok(service.suggestFileName(input.getResource())); }); - test('Untitled with associated path remains dirty when content gets empty', function () { + test('Untitled with associated path remains dirty when content gets empty', async () => { const service = accessor.untitledEditorService; const file = URI.file(join('C:\\', '/foo/file.txt')); const input = service.createOrGet(file); // dirty - return input.resolve().then(model => { - model.textEditorModel.setValue('foo bar'); - assert.ok(model.isDirty()); - - model.textEditorModel.setValue(''); - assert.ok(model.isDirty()); - - input.dispose(); - }); + const model = await input.resolve(); + model.textEditorModel.setValue('foo bar'); + assert.ok(model.isDirty()); + model.textEditorModel.setValue(''); + assert.ok(model.isDirty()); + input.dispose(); }); - test('Untitled created with files.defaultLanguage setting', function () { + test('Untitled with initial content is dirty', async () => { + const service = accessor.untitledEditorService; + const input = service.createOrGet(undefined, undefined, 'Hello World'); + + // dirty + const model = await input.resolve(); + assert.ok(model.isDirty()); + input.dispose(); + }); + + test('Untitled created with files.defaultLanguage setting', () => { const defaultLanguage = 'javascript'; const config = accessor.testConfigurationService; config.setUserConfiguration('files', { 'defaultLanguage': defaultLanguage }); @@ -188,30 +191,52 @@ suite('Workbench untitled editors', () => { const service = accessor.untitledEditorService; const input = service.createOrGet(); - assert.equal(input.getModeId(), defaultLanguage); + assert.equal(input.getMode(), defaultLanguage); config.setUserConfiguration('files', { 'defaultLanguage': undefined }); input.dispose(); }); - test('Untitled created with modeId overrides files.defaultLanguage setting', function () { - const modeId = 'typescript'; + test('Untitled created with mode overrides files.defaultLanguage setting', () => { + const mode = 'typescript'; const defaultLanguage = 'javascript'; const config = accessor.testConfigurationService; config.setUserConfiguration('files', { 'defaultLanguage': defaultLanguage }); const service = accessor.untitledEditorService; - const input = service.createOrGet(null!, modeId); + const input = service.createOrGet(null!, mode); - assert.equal(input.getModeId(), modeId); + assert.equal(input.getMode(), mode); config.setUserConfiguration('files', { 'defaultLanguage': undefined }); input.dispose(); }); - test('encoding change event', function () { + test('Untitled can change mode afterwards', async () => { + const mode = 'untitled-input-test'; + + ModesRegistry.registerLanguage({ + id: mode, + }); + + const service = accessor.untitledEditorService; + const input = service.createOrGet(null!, mode); + + assert.equal(input.getMode(), mode); + + const model = await input.resolve(); + assert.equal(model.getMode(), mode); + + input.setMode('text'); + + assert.equal(input.getMode(), PLAINTEXT_MODE_ID); + + input.dispose(); + }); + + test('encoding change event', async () => { const service = accessor.untitledEditorService; const input = service.createOrGet(); @@ -223,16 +248,13 @@ suite('Workbench untitled editors', () => { }); // dirty - return input.resolve().then(model => { - model.setEncoding('utf16'); - - assert.equal(counter, 1); - - input.dispose(); - }); + const model = await input.resolve(); + model.setEncoding('utf16'); + assert.equal(counter, 1); + input.dispose(); }); - test('onDidChangeContent event', () => { + test('onDidChangeContent event', async () => { const service = accessor.untitledEditorService; const input = service.createOrGet(); @@ -245,39 +267,32 @@ suite('Workbench untitled editors', () => { assert.equal(r.toString(), input.getResource().toString()); }); - return input.resolve().then(model => { - model.textEditorModel.setValue('foo'); - assert.equal(counter, 0, 'Dirty model should not trigger event immediately'); + const model = await input.resolve(); + model.textEditorModel.setValue('foo'); + assert.equal(counter, 0, 'Dirty model should not trigger event immediately'); - return timeout(3).then(() => { - assert.equal(counter, 1, 'Dirty model should trigger event'); + await timeout(3); + assert.equal(counter, 1, 'Dirty model should trigger event'); + model.textEditorModel.setValue('bar'); - model.textEditorModel.setValue('bar'); - return timeout(3).then(() => { - assert.equal(counter, 2, 'Content change when dirty should trigger event'); + await timeout(3); + assert.equal(counter, 2, 'Content change when dirty should trigger event'); + model.textEditorModel.setValue(''); - model.textEditorModel.setValue(''); - return timeout(3).then(() => { - assert.equal(counter, 3, 'Manual revert should trigger event'); + await timeout(3); + assert.equal(counter, 3, 'Manual revert should trigger event'); + model.textEditorModel.setValue('foo'); - model.textEditorModel.setValue('foo'); - return timeout(3).then(() => { - assert.equal(counter, 4, 'Dirty model should trigger event'); + await timeout(3); + assert.equal(counter, 4, 'Dirty model should trigger event'); + model.revert(); - model.revert(); - return timeout(3).then(() => { - assert.equal(counter, 5, 'Revert should trigger event'); - - input.dispose(); - }); - }); - }); - }); - }); - }); + await timeout(3); + assert.equal(counter, 5, 'Revert should trigger event'); + input.dispose(); }); - test('onDidDisposeModel event', () => { + test('onDidDisposeModel event', async () => { const service = accessor.untitledEditorService; const input = service.createOrGet(); @@ -288,10 +303,9 @@ suite('Workbench untitled editors', () => { assert.equal(r.toString(), input.getResource().toString()); }); - return input.resolve().then(model => { - assert.equal(counter, 0); - input.dispose(); - assert.equal(counter, 1); - }); + await input.resolve(); + assert.equal(counter, 0); + input.dispose(); + assert.equal(counter, 1); }); }); \ No newline at end of file diff --git a/src/vs/workbench/test/common/notifications.test.ts b/src/vs/workbench/test/common/notifications.test.ts index 678c6aab20b..30312d509e3 100644 --- a/src/vs/workbench/test/common/notifications.test.ts +++ b/src/vs/workbench/test/common/notifications.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { NotificationsModel, NotificationViewItem, INotificationChangeEvent, NotificationChangeType, NotificationViewItemLabelKind } from 'vs/workbench/common/notifications'; +import { NotificationsModel, NotificationViewItem, INotificationChangeEvent, NotificationChangeType, NotificationViewItemLabelKind, IStatusMessageChangeEvent, StatusMessageChangeType } from 'vs/workbench/common/notifications'; import { Action } from 'vs/base/common/actions'; import { INotification, Severity } from 'vs/platform/notification/common/notification'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; @@ -132,9 +132,14 @@ suite('Notifications', () => { test('Model', () => { const model = new NotificationsModel(); - let lastEvent!: INotificationChangeEvent; + let lastNotificationEvent!: INotificationChangeEvent; model.onDidNotificationChange(e => { - lastEvent = e; + lastNotificationEvent = e; + }); + + let lastStatusMessageEvent!: IStatusMessageChangeEvent; + model.onDidStatusMessageChange(e => { + lastStatusMessageEvent = e; }); let item1: INotification = { severity: Severity.Error, message: 'Error Message', actions: { primary: [new Action('id', 'label')] } }; @@ -142,23 +147,23 @@ suite('Notifications', () => { let item2Duplicate: INotification = { severity: Severity.Warning, message: 'Warning Message', source: 'Some Source' }; let item3: INotification = { severity: Severity.Info, message: 'Info Message' }; - let item1Handle = model.notify(item1); - assert.equal(lastEvent.item.severity, item1.severity); - assert.equal(lastEvent.item.message.value, item1.message); - assert.equal(lastEvent.index, 0); - assert.equal(lastEvent.kind, NotificationChangeType.ADD); + let item1Handle = model.addNotification(item1); + assert.equal(lastNotificationEvent.item.severity, item1.severity); + assert.equal(lastNotificationEvent.item.message.value, item1.message); + assert.equal(lastNotificationEvent.index, 0); + assert.equal(lastNotificationEvent.kind, NotificationChangeType.ADD); - let item2Handle = model.notify(item2); - assert.equal(lastEvent.item.severity, item2.severity); - assert.equal(lastEvent.item.message.value, item2.message); - assert.equal(lastEvent.index, 0); - assert.equal(lastEvent.kind, NotificationChangeType.ADD); + let item2Handle = model.addNotification(item2); + assert.equal(lastNotificationEvent.item.severity, item2.severity); + assert.equal(lastNotificationEvent.item.message.value, item2.message); + assert.equal(lastNotificationEvent.index, 0); + assert.equal(lastNotificationEvent.kind, NotificationChangeType.ADD); - model.notify(item3); - assert.equal(lastEvent.item.severity, item3.severity); - assert.equal(lastEvent.item.message.value, item3.message); - assert.equal(lastEvent.index, 0); - assert.equal(lastEvent.kind, NotificationChangeType.ADD); + model.addNotification(item3); + assert.equal(lastNotificationEvent.item.severity, item3.severity); + assert.equal(lastNotificationEvent.item.message.value, item3.message); + assert.equal(lastNotificationEvent.index, 0); + assert.equal(lastNotificationEvent.kind, NotificationChangeType.ADD); assert.equal(model.notifications.length, 3); @@ -170,29 +175,48 @@ suite('Notifications', () => { item1Handle.close(); assert.equal(called, 1); assert.equal(model.notifications.length, 2); - assert.equal(lastEvent.item.severity, item1.severity); - assert.equal(lastEvent.item.message.value, item1.message); - assert.equal(lastEvent.index, 2); - assert.equal(lastEvent.kind, NotificationChangeType.REMOVE); + assert.equal(lastNotificationEvent.item.severity, item1.severity); + assert.equal(lastNotificationEvent.item.message.value, item1.message); + assert.equal(lastNotificationEvent.index, 2); + assert.equal(lastNotificationEvent.kind, NotificationChangeType.REMOVE); - model.notify(item2Duplicate); + model.addNotification(item2Duplicate); assert.equal(model.notifications.length, 2); - assert.equal(lastEvent.item.severity, item2Duplicate.severity); - assert.equal(lastEvent.item.message.value, item2Duplicate.message); - assert.equal(lastEvent.index, 0); - assert.equal(lastEvent.kind, NotificationChangeType.ADD); + assert.equal(lastNotificationEvent.item.severity, item2Duplicate.severity); + assert.equal(lastNotificationEvent.item.message.value, item2Duplicate.message); + assert.equal(lastNotificationEvent.index, 0); + assert.equal(lastNotificationEvent.kind, NotificationChangeType.ADD); item2Handle.close(); assert.equal(model.notifications.length, 1); - assert.equal(lastEvent.item.severity, item2Duplicate.severity); - assert.equal(lastEvent.item.message.value, item2Duplicate.message); - assert.equal(lastEvent.index, 0); - assert.equal(lastEvent.kind, NotificationChangeType.REMOVE); + assert.equal(lastNotificationEvent.item.severity, item2Duplicate.severity); + assert.equal(lastNotificationEvent.item.message.value, item2Duplicate.message); + assert.equal(lastNotificationEvent.index, 0); + assert.equal(lastNotificationEvent.kind, NotificationChangeType.REMOVE); model.notifications[0].expand(); - assert.equal(lastEvent.item.severity, item3.severity); - assert.equal(lastEvent.item.message.value, item3.message); - assert.equal(lastEvent.index, 0); - assert.equal(lastEvent.kind, NotificationChangeType.CHANGE); + assert.equal(lastNotificationEvent.item.severity, item3.severity); + assert.equal(lastNotificationEvent.item.message.value, item3.message); + assert.equal(lastNotificationEvent.index, 0); + assert.equal(lastNotificationEvent.kind, NotificationChangeType.CHANGE); + + const disposable = model.showStatusMessage('Hello World'); + assert.equal(model.statusMessage!.message, 'Hello World'); + assert.equal(lastStatusMessageEvent.item.message, model.statusMessage!.message); + assert.equal(lastStatusMessageEvent.kind, StatusMessageChangeType.ADD); + disposable.dispose(); + assert.ok(!model.statusMessage); + assert.equal(lastStatusMessageEvent.kind, StatusMessageChangeType.REMOVE); + + let disposable2 = model.showStatusMessage('Hello World 2'); + const disposable3 = model.showStatusMessage('Hello World 3'); + + assert.equal(model.statusMessage!.message, 'Hello World 3'); + + disposable2.dispose(); + assert.equal(model.statusMessage!.message, 'Hello World 3'); + + disposable3.dispose(); + assert.ok(!model.statusMessage); }); }); \ No newline at end of file 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 2f39779eb3a..8eb327c11c7 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts @@ -16,10 +16,8 @@ import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/c import { IModelService } from 'vs/editor/common/services/modelService'; import { ExtHostLanguageFeatures } from 'vs/workbench/api/common/extHostLanguageFeatures'; import { MainThreadLanguageFeatures } from 'vs/workbench/api/browser/mainThreadLanguageFeatures'; -import { IHeapService, NullHeapService } from 'vs/workbench/services/heap/common/heap'; import { ExtHostApiCommands } from 'vs/workbench/api/common/extHostApiCommands'; import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; -import { ExtHostHeapService } from 'vs/workbench/api/common/extHostHeapService'; import { MainThreadCommands } from 'vs/workbench/api/browser/mainThreadCommands'; import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; @@ -67,7 +65,6 @@ suite('ExtHostLanguageFeatureCommands', function () { { let instantiationService = new TestInstantiationService(); rpcProtocol = new TestRPCProtocol(); - instantiationService.stub(IHeapService, NullHeapService); instantiationService.stub(ICommandService, { _serviceBrand: undefined, executeCommand(id: string, args: any): any { @@ -109,9 +106,7 @@ suite('ExtHostLanguageFeatureCommands', function () { const extHostDocuments = new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors); rpcProtocol.set(ExtHostContext.ExtHostDocuments, extHostDocuments); - const heapService = new ExtHostHeapService(); - - commands = new ExtHostCommands(rpcProtocol, heapService, new NullLogService()); + commands = new ExtHostCommands(rpcProtocol, new NullLogService()); rpcProtocol.set(ExtHostContext.ExtHostCommands, commands); rpcProtocol.set(MainContext.MainThreadCommands, inst.createInstance(MainThreadCommands, rpcProtocol)); ExtHostApiCommands.register(commands); @@ -119,7 +114,7 @@ suite('ExtHostLanguageFeatureCommands', function () { const diagnostics = new ExtHostDiagnostics(rpcProtocol); rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, diagnostics); - extHost = new ExtHostLanguageFeatures(rpcProtocol, null, extHostDocuments, commands, heapService, diagnostics, new NullLogService()); + extHost = new ExtHostLanguageFeatures(rpcProtocol, null, extHostDocuments, commands, diagnostics, new NullLogService()); rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, extHost); mainThread = rpcProtocol.set(MainContext.MainThreadLanguageFeatures, inst.createInstance(MainThreadLanguageFeatures, rpcProtocol)); @@ -801,9 +796,9 @@ suite('ExtHostLanguageFeatureCommands', function () { })); await rpcProtocol.sync(); - let value = await commands.executeCommand<vscode.SelectionRange[][]>('vscode.executeSelectionRangeProvider', model.uri, [new types.Position(0, 10)]); + let value = await commands.executeCommand<vscode.SelectionRange[]>('vscode.executeSelectionRangeProvider', model.uri, [new types.Position(0, 10)]); assert.equal(value.length, 1); - assert.ok(value[0].length >= 2); + assert.ok(value[0].parent); }); }); diff --git a/src/vs/workbench/test/electron-browser/api/extHostCommands.test.ts b/src/vs/workbench/test/electron-browser/api/extHostCommands.test.ts index 07bb3ac3cea..d0cf59f320e 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostCommands.test.ts @@ -26,7 +26,7 @@ suite('ExtHostCommands', function () { } }; - const commands = new ExtHostCommands(SingleProxyRPCProtocol(shape), undefined!, new NullLogService()); + const commands = new ExtHostCommands(SingleProxyRPCProtocol(shape), new NullLogService()); commands.registerCommand(true, 'foo', (): any => { }).dispose(); assert.equal(lastUnregister!, 'foo'); assert.equal(CommandsRegistry.getCommand('foo'), undefined); @@ -46,7 +46,7 @@ suite('ExtHostCommands', function () { } }; - const commands = new ExtHostCommands(SingleProxyRPCProtocol(shape), undefined!, new NullLogService()); + const commands = new ExtHostCommands(SingleProxyRPCProtocol(shape), new NullLogService()); const reg = commands.registerCommand(true, 'foo', (): any => { }); reg.dispose(); reg.dispose(); diff --git a/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts b/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts index 38c627a64c5..4eca9307e09 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostDiagnostics.test.ts @@ -91,18 +91,18 @@ suite('ExtHostDiagnostics', () => { new Diagnostic(new Range(0, 0, 1, 1), 'message-2') ]); - let array = collection.get(URI.parse('foo:bar')); + let array = collection.get(URI.parse('foo:bar')) as Diagnostic[]; assert.throws(() => array.length = 0); assert.throws(() => array.pop()); assert.throws(() => array[0] = new Diagnostic(new Range(0, 0, 0, 0), 'evil')); - collection.forEach((uri, array) => { + collection.forEach((uri, array: Diagnostic[]) => { assert.throws(() => array.length = 0); assert.throws(() => array.pop()); assert.throws(() => array[0] = new Diagnostic(new Range(0, 0, 0, 0), 'evil')); }); - array = collection.get(URI.parse('foo:bar')); + array = collection.get(URI.parse('foo:bar')) as Diagnostic[]; assert.equal(array.length, 2); collection.dispose(); diff --git a/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts b/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts index 0096e76b934..99724c53a21 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts @@ -85,7 +85,7 @@ suite('ExtHostDocumentSaveParticipant', () => { sub.dispose(); assert.ok(event); - assert.throws(() => { event.document = null!; }); + assert.throws(() => { (event.document as any) = null!; }); }); }); 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 a31180c8d3b..18ecedc673b 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts @@ -18,7 +18,6 @@ import { ExtHostLanguageFeatures } from 'vs/workbench/api/common/extHostLanguage import { MainThreadLanguageFeatures } from 'vs/workbench/api/browser/mainThreadLanguageFeatures'; import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { MainThreadCommands } from 'vs/workbench/api/browser/mainThreadCommands'; -import { IHeapService, NullHeapService } from 'vs/workbench/services/heap/common/heap'; import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { getDocumentSymbols } from 'vs/editor/contrib/quickOpen/quickOpen'; @@ -37,7 +36,6 @@ import { getDocumentFormattingEditsUntilResult, getDocumentRangeFormattingEditsU import { getLinks } from 'vs/editor/contrib/links/getLinks'; import { MainContext, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostDiagnostics } from 'vs/workbench/api/common/extHostDiagnostics'; -import { ExtHostHeapService } from 'vs/workbench/api/common/extHostHeapService'; import * as vscode from 'vscode'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { NullLogService } from 'vs/platform/log/common/log'; @@ -81,7 +79,6 @@ suite('ExtHostLanguageFeatures', function () { { let instantiationService = new TestInstantiationService(); instantiationService.stub(IMarkerService, MarkerService); - instantiationService.stub(IHeapService, NullHeapService); inst = instantiationService; } @@ -102,16 +99,14 @@ suite('ExtHostLanguageFeatures', function () { const extHostDocuments = new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors); rpcProtocol.set(ExtHostContext.ExtHostDocuments, extHostDocuments); - const heapService = new ExtHostHeapService(); - - const commands = new ExtHostCommands(rpcProtocol, heapService, new NullLogService()); + const commands = new ExtHostCommands(rpcProtocol, new NullLogService()); rpcProtocol.set(ExtHostContext.ExtHostCommands, commands); rpcProtocol.set(MainContext.MainThreadCommands, inst.createInstance(MainThreadCommands, rpcProtocol)); const diagnostics = new ExtHostDiagnostics(rpcProtocol); rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, diagnostics); - extHost = new ExtHostLanguageFeatures(rpcProtocol, null, extHostDocuments, commands, heapService, diagnostics, new NullLogService()); + extHost = new ExtHostLanguageFeatures(rpcProtocol, null, extHostDocuments, commands, diagnostics, new NullLogService()); rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, extHost); mainThread = rpcProtocol.set(MainContext.MainThreadLanguageFeatures, inst.createInstance(MainThreadLanguageFeatures, rpcProtocol)); @@ -194,7 +189,7 @@ suite('ExtHostLanguageFeatures', function () { await rpcProtocol.sync(); const value = await getCodeLensData(model, CancellationToken.None); - assert.equal(value.length, 1); + assert.equal(value.lenses.length, 1); }); test('CodeLens, do not resolve a resolved lens', async () => { @@ -212,8 +207,8 @@ suite('ExtHostLanguageFeatures', function () { await rpcProtocol.sync(); const value = await getCodeLensData(model, CancellationToken.None); - assert.equal(value.length, 1); - const data = value[0]; + assert.equal(value.lenses.length, 1); + const [data] = value.lenses; const symbol = await Promise.resolve(data.provider.resolveCodeLens!(model, data.symbol, CancellationToken.None)); assert.equal(symbol!.command!.id, 'id'); assert.equal(symbol!.command!.title, 'Title'); @@ -229,8 +224,8 @@ suite('ExtHostLanguageFeatures', function () { await rpcProtocol.sync(); const value = await getCodeLensData(model, CancellationToken.None); - assert.equal(value.length, 1); - let data = value[0]; + assert.equal(value.lenses.length, 1); + let [data] = value.lenses; const symbol = await Promise.resolve(data.provider.resolveCodeLens!(model, data.symbol, CancellationToken.None)); assert.equal(symbol!.command!.id, 'missing'); assert.equal(symbol!.command!.title, '!!MISSING: command!!'); @@ -1041,7 +1036,9 @@ suite('ExtHostLanguageFeatures', function () { disposables.push(extHost.registerDocumentLinkProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentLinkProvider { provideDocumentLinks() { - return [new types.DocumentLink(new types.Range(0, 0, 1, 1), URI.parse('foo:bar#3'))]; + const link = new types.DocumentLink(new types.Range(0, 0, 1, 1), URI.parse('foo:bar#3')); + link.tooltip = 'tooltip'; + return [link]; } })); @@ -1051,6 +1048,7 @@ suite('ExtHostLanguageFeatures', function () { let [first] = links; assert.equal(first.url, 'foo:bar#3'); assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 2, endColumn: 2 }); + assert.equal(first.tooltip, 'tooltip'); }); test('Links, evil provider', async () => { diff --git a/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts b/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts index c19beb1371f..991bfbede8a 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts @@ -6,9 +6,11 @@ import * as assert from 'assert'; import { MainThreadMessageService } from 'vs/workbench/api/browser/mainThreadMessageService'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { INotificationService, INotification, NoOpNotification, INotificationHandle, Severity, IPromptChoice, IPromptOptions } from 'vs/platform/notification/common/notification'; +import { INotificationService, INotification, NoOpNotification, INotificationHandle, Severity, IPromptChoice, IPromptOptions, IStatusMessageOptions } from 'vs/platform/notification/common/notification'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; const emptyDialogService = new class implements IDialogService { _serviceBrand: 'dialogService'; @@ -31,7 +33,7 @@ const emptyCommandService: ICommandService = { }; const emptyNotificationService = new class implements INotificationService { - _serviceBrand: 'notificiationService'; + _serviceBrand: ServiceIdentifier<INotificationService>; notify(...args: any[]): never { throw new Error('not implemented'); } @@ -47,11 +49,13 @@ const emptyNotificationService = new class implements INotificationService { prompt(severity: Severity, message: string, choices: IPromptChoice[], options?: IPromptOptions): INotificationHandle { throw new Error('not implemented'); } + status(message: string | Error, options?: IStatusMessageOptions): IDisposable { + return Disposable.None; + } }; class EmptyNotificationService implements INotificationService { - - _serviceBrand: any; + _serviceBrand: ServiceIdentifier<INotificationService>; constructor(private withNotify: (notification: INotification) => void) { } @@ -73,6 +77,9 @@ class EmptyNotificationService implements INotificationService { prompt(severity: Severity, message: string, choices: IPromptChoice[], options?: IPromptOptions): INotificationHandle { throw new Error('not implemented'); } + status(message: string, options?: IStatusMessageOptions): IDisposable { + return Disposable.None; + } } suite('ExtHostMessageService', function () { diff --git a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts index ed496ed0eb6..0d15201d893 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts @@ -7,7 +7,7 @@ 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 { DisposableStore } from 'vs/base/common/lifecycle'; import { joinPath } from 'vs/base/common/resources'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as pfs from 'vs/base/node/pfs'; @@ -16,12 +16,12 @@ import { ExtHostSearch } from 'vs/workbench/api/node/extHostSearch'; import { Range } from 'vs/workbench/api/common/extHostTypes'; import { IFileMatch, IFileQuery, IPatternInfo, IRawFileMatch2, ISearchCompleteStats, ISearchQuery, ITextQuery, QueryType, resultIsMatch } from 'vs/workbench/services/search/common/search'; import { TestRPCProtocol } from 'vs/workbench/test/electron-browser/api/testRPCProtocol'; -import { TestLogService } from 'vs/workbench/test/workbenchTestServices'; import * as vscode from 'vscode'; +import { NullLogService } from 'vs/platform/log/common/log'; let rpcProtocol: TestRPCProtocol; let extHostSearch: ExtHostSearch; -let disposables: vscode.Disposable[] = []; +const disposables = new DisposableStore(); let mockMainThreadSearch: MockMainThreadSearch; class MockMainThreadSearch implements MainThreadSearchShape { @@ -63,12 +63,12 @@ export function extensionResultIsMatch(data: vscode.TextSearchResult): data is v suite('ExtHostSearch', () => { async function registerTestTextSearchProvider(provider: vscode.TextSearchProvider, scheme = 'file'): Promise<void> { - disposables.push(extHostSearch.registerTextSearchProvider(scheme, provider)); + disposables.add(extHostSearch.registerTextSearchProvider(scheme, provider)); await rpcProtocol.sync(); } async function registerTestFileSearchProvider(provider: vscode.FileSearchProvider, scheme = 'file'): Promise<void> { - disposables.push(extHostSearch.registerFileSearchProvider(scheme, provider)); + disposables.add(extHostSearch.registerFileSearchProvider(scheme, provider)); await rpcProtocol.sync(); } @@ -130,7 +130,7 @@ suite('ExtHostSearch', () => { rpcProtocol = new TestRPCProtocol(); mockMainThreadSearch = new MockMainThreadSearch(); - const logService = new TestLogService(); + const logService = new NullLogService(); rpcProtocol.set(MainContext.MainThreadSearch, mockMainThreadSearch); @@ -139,7 +139,7 @@ suite('ExtHostSearch', () => { }); teardown(() => { - dispose(disposables); + disposables.clear(); return rpcProtocol.sync(); }); 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 7619369f02c..5f05a9bc453 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts @@ -11,7 +11,6 @@ import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { MainThreadTreeViewsShape, MainContext } from 'vs/workbench/api/common/extHost.protocol'; import { TreeDataProvider, TreeItem } from 'vscode'; import { TestRPCProtocol } from './testRPCProtocol'; -import { ExtHostHeapService } from 'vs/workbench/api/common/extHostHeapService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { MainThreadCommands } from 'vs/workbench/api/browser/mainThreadCommands'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -72,7 +71,7 @@ suite('ExtHostTreeView', function () { rpcProtocol.set(MainContext.MainThreadCommands, inst.createInstance(MainThreadCommands, rpcProtocol)); target = new RecordingShape(); - testObject = new ExtHostTreeViews(target, new ExtHostCommands(rpcProtocol, new ExtHostHeapService(), new NullLogService()), new NullLogService()); + testObject = new ExtHostTreeViews(target, new ExtHostCommands(rpcProtocol, new NullLogService()), new NullLogService()); onDidChangeTreeNode = new Emitter<{ key: string }>(); onDidChangeTreeNodeWithId = new Emitter<{ key: string }>(); testObject.createTreeView('testNodeTreeProvider', { treeDataProvider: aNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription); 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 56517bdd73c..f33a4d5f6e6 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts @@ -31,6 +31,7 @@ suite('ExtHostTypes', function () { scheme: 'file', path: '/path/test.file', fsPath: '/path/test.file'.replace(/\//g, isWindows ? '\\' : '/'), + _sep: isWindows ? 1 : undefined, }); assert.ok(uri.toString()); @@ -39,6 +40,7 @@ suite('ExtHostTypes', function () { scheme: 'file', path: '/path/test.file', fsPath: '/path/test.file'.replace(/\//g, isWindows ? '\\' : '/'), + _sep: isWindows ? 1 : undefined, external: 'file:///path/test.file' }); }); diff --git a/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts b/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts index a0d8a8c41ad..2650c602cdf 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostWebview.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { MainThreadWebviews } from 'vs/workbench/api/electron-browser/mainThreadWebview'; +import { MainThreadWebviews } from 'vs/workbench/api/browser/mainThreadWebview'; import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; import * as vscode from 'vscode'; 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 129d48cab74..35489e8cbeb 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts @@ -62,7 +62,7 @@ suite('MainThreadEditors', () => { } move(source: URI, target: URI) { movedResources.set(source, target); - return Promise.resolve(undefined); + return Promise.resolve(Object.create(null)); } models = <any>{ onModelSaved: Event.None, diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts index 54cced7af70..502d3fae8e9 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadSaveParticipant.test.ts @@ -37,7 +37,7 @@ suite('MainThreadSaveParticipant', function () { }); test('insert final new line', async function () { - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/final_new_line.txt'), 'utf8') as IResolvedTextFileEditorModel; + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel; await model.load(); const configService = new TestConfigurationService(); @@ -70,7 +70,7 @@ suite('MainThreadSaveParticipant', function () { }); test('trim final new lines', async function () { - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8') as IResolvedTextFileEditorModel; + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel; await model.load(); const configService = new TestConfigurationService(); @@ -105,7 +105,7 @@ suite('MainThreadSaveParticipant', function () { }); test('trim final new lines bug#39750', async function () { - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8') as IResolvedTextFileEditorModel; + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel; await model.load(); const configService = new TestConfigurationService(); @@ -132,7 +132,7 @@ suite('MainThreadSaveParticipant', function () { }); test('trim final new lines bug#46075', async function () { - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8') as IResolvedTextFileEditorModel; + const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel; await model.load(); const configService = new TestConfigurationService(); diff --git a/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts b/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts index 4d5e9e0a436..4b246719249 100644 --- a/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts +++ b/src/vs/workbench/test/electron-browser/quickopen.perf.integrationTest.ts @@ -26,9 +26,10 @@ import { Extensions, IQuickOpenRegistry } from 'vs/workbench/browser/quickopen'; import 'vs/workbench/contrib/search/browser/search.contribution'; // load contributions import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { SearchService } from 'vs/workbench/services/search/node/searchService'; +import { LocalSearchService } from 'vs/workbench/services/search/node/searchService'; import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { TestContextService, TestEditorGroupsService, TestEditorService, TestEnvironmentService, TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices'; +import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings'; namespace Timer { export interface ITimerEvent { @@ -78,7 +79,7 @@ suite.skip('QuickOpen performance (integration)', () => { [IEditorGroupsService, new TestEditorGroupsService()], [IEnvironmentService, TestEnvironmentService], [IUntitledEditorService, createSyncDescriptor(UntitledEditorService)], - [ISearchService, createSyncDescriptor(SearchService)] + [ISearchService, createSyncDescriptor(LocalSearchService)] )); const registry = Registry.as<IQuickOpenRegistry>(Extensions.Quickopen); @@ -172,6 +173,10 @@ class TestTelemetryService implements ITelemetryService { return Promise.resolve(undefined); } + public publicLog2<E extends ClassifiedEvent<T> = never, T extends GDPRClassification<T> = never>(eventName: string, data?: StrictPropertyCheck<T, E>) { + return this.publicLog(eventName, data as any); + } + public getTelemetryInfo(): Promise<ITelemetryInfo> { return Promise.resolve({ instanceId: 'someValue.instanceId', 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 10e2781f7ef..e08748c6364 100644 --- a/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts +++ b/src/vs/workbench/test/electron-browser/textsearch.perf.integrationTest.ts @@ -15,7 +15,7 @@ import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/serv import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import * as minimist from 'minimist'; import * as path from 'vs/base/common/path'; -import { SearchService } from 'vs/workbench/services/search/node/searchService'; +import { LocalSearchService } 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'; @@ -33,6 +33,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { NullLogService, ILogService } from 'vs/platform/log/common/log'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; +import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings'; declare var __dirname: string; @@ -68,7 +69,7 @@ suite.skip('TextSearch performance (integration)', () => { [IEditorGroupsService, new TestEditorGroupsService()], [IEnvironmentService, TestEnvironmentService], [IUntitledEditorService, createSyncDescriptor(UntitledEditorService)], - [ISearchService, createSyncDescriptor(SearchService)], + [ISearchService, createSyncDescriptor(LocalSearchService)], [ILogService, new NullLogService()] )); @@ -165,6 +166,10 @@ class TestTelemetryService implements ITelemetryService { return Promise.resolve(); } + public publicLog2<E extends ClassifiedEvent<T> = never, T extends GDPRClassification<T> = never>(eventName: string, data?: StrictPropertyCheck<T, E>) { + return this.publicLog(eventName, data as any); + } + public getTelemetryInfo(): Promise<ITelemetryInfo> { return Promise.resolve({ instanceId: 'someValue.instanceId', diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 1e00736484c..45bf09e967a 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -15,7 +15,7 @@ import { ConfirmResult, IEditorInputWithOptions, CloseDirection, IEditorIdentifi import { IEditorOpeningEvent, EditorServiceImpl, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor'; import { Event, Emitter } from 'vs/base/common/event'; import Severity from 'vs/base/common/severity'; -import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchLayoutService, Parts, Position as PartPosition } from 'vs/workbench/services/layout/browser/layoutService'; import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; @@ -38,7 +38,7 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/ import { IWindowsService, IWindowService, INativeOpenDialogOptions, IEnterWorkspaceResult, IMessageBoxResult, MenuBarVisibility, IURIToOpen, IOpenSettings, IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; @@ -64,14 +64,14 @@ import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IDecorationRenderOptions } from 'vs/editor/common/editorCommon'; import { EditorGroup } from 'vs/workbench/common/editor/editorGroup'; import { Dimension } from 'vs/base/browser/dom'; -import { ILogService, LogLevel } from 'vs/platform/log/common/log'; +import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { ILabelService } from 'vs/platform/label/common/label'; import { timeout } from 'vs/base/common/async'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ViewletDescriptor, Viewlet } from 'vs/workbench/browser/viewlet'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage'; -import { isLinux, isMacintosh } from 'vs/base/common/platform'; +import { isLinux, isMacintosh, IProcessEnvironment } from 'vs/base/common/platform'; import { LabelService } from 'vs/workbench/services/label/common/labelService'; import { IDimension } from 'vs/platform/layout/browser/layoutService'; import { Part } from 'vs/workbench/browser/part'; @@ -82,10 +82,11 @@ import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedPr import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/node/environmentService'; import { VSBuffer, VSBufferReadable } from 'vs/base/common/buffer'; -import { BrowserTextFileService } from 'vs/workbench/services/textfile/browser/textFileService'; +import { NodeTextFileService } from 'vs/workbench/services/textfile/node/textFileService'; +import { Schemas } from 'vs/base/common/network'; export function createFileInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { - return instantiationService.createInstance(FileEditorInput, resource, undefined); + return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined); } export const TestEnvironmentService = new WorkbenchEnvironmentService(parseArgs(process.argv) as IWindowConfiguration, process.execPath); @@ -176,7 +177,7 @@ export class TestContextService implements IWorkspaceContextService { } } -export class TestTextFileService extends BrowserTextFileService { +export class TestTextFileService extends NodeTextFileService { public cleanupBackupsBeforeShutdownCalled: boolean; private promptPath: URI; @@ -310,7 +311,7 @@ export function workbenchInstantiationService(): IInstantiationService { instantiationService.stub(ITextFileService, <ITextFileService>instantiationService.createInstance(TestTextFileService)); instantiationService.stub(ITextModelService, <ITextModelService>instantiationService.createInstance(TextModelResolverService)); instantiationService.stub(IThemeService, new TestThemeService()); - instantiationService.stub(ILogService, new TestLogService()); + instantiationService.stub(ILogService, new NullLogService()); instantiationService.stub(IEditorGroupsService, new TestEditorGroupsService([new TestEditorGroup(0)])); instantiationService.stub(ILabelService, <ILabelService>instantiationService.createInstance(LabelService)); const editorService = new TestEditorService(); @@ -321,19 +322,6 @@ export function workbenchInstantiationService(): IInstantiationService { return instantiationService; } -export class TestLogService implements ILogService { - _serviceBrand: any; onDidChangeLogLevel: Event<LogLevel>; - getLevel(): LogLevel { return LogLevel.Info; } - setLevel(_level: LogLevel): void { } - trace(_message: string, ..._args: any[]): void { } - debug(_message: string, ..._args: any[]): void { } - info(_message: string, ..._args: any[]): void { } - warn(_message: string, ..._args: any[]): void { } - error(_message: string | Error, ..._args: any[]): void { } - critical(_message: string | Error, ..._args: any[]): void { } - dispose(): void { } -} - export class TestDecorationsService implements IDecorationsService { _serviceBrand: any; onDidChangeDecorations: Event<IResourceDecorationChangeEvent> = Event.None; @@ -438,6 +426,9 @@ export class TestFileDialogService implements IFileDialogService { public pickWorkspaceAndOpen(_options: IPickAndOpenOptions): Promise<any> { return Promise.resolve(0); } + public pickFileToSave(_options: ISaveDialogOptions): Promise<URI | undefined> { + return Promise.resolve(undefined); + } public showSaveDialog(_options: ISaveDialogOptions): Promise<URI | undefined> { return Promise.resolve(undefined); } @@ -455,6 +446,9 @@ export class TestLayoutService implements IWorkbenchLayoutService { container: HTMLElement = window.document.body; onZenModeChange: Event<boolean> = Event.None; + onCenteredLayoutChange: Event<boolean> = Event.None; + onFullscreenChange: Event<boolean> = Event.None; + onPanelPositionChange: Event<string> = Event.None; onLayout = Event.None; private _onTitleBarVisibilityChange = new Emitter<void>(); @@ -616,6 +610,10 @@ export class TestPanelService implements IPanelService { return null!; } + public getPanel(id: string): any { + return activeViewlet; + } + public getPanels(): any[] { return []; } @@ -637,6 +635,10 @@ export class TestPanelService implements IPanelService { throw new Error('Method not implemented.'); } + public getProgressIndicator(id: string) { + return null!; + } + public hideActivePanel(): void { } public getLastActivePanelId(): string { @@ -1093,7 +1095,7 @@ export class TestBackupFileService implements IBackupFileService { throw new Error('not implemented'); } - public backupResource(_resource: URI, _content: ITextSnapshot): Promise<void> { + public backupResource<T extends object>(_resource: URI, _content: ITextSnapshot, versionId?: number, meta?: T): Promise<void> { return Promise.resolve(); } @@ -1108,7 +1110,7 @@ export class TestBackupFileService implements IBackupFileService { return textBuffer.getValueInRange(range, EndOfLinePreference.TextDefined); } - public resolveBackupContent(_backup: URI): Promise<ITextBufferFactory> { + public resolveBackupContent<T extends object>(_backup: URI): Promise<IResolvedBackup<T>> { throw new Error('not implemented'); } @@ -1445,6 +1447,10 @@ export class TestWindowsService implements IWindowsService { return Promise.resolve(); } + openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise<void> { + return Promise.resolve(); + } + getWindows(): Promise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]> { throw new Error('not implemented'); } @@ -1597,4 +1603,33 @@ export class NullFileSystemProvider implements IFileSystemProvider { close?(fd: number): Promise<void> { return Promise.resolve(undefined!); } read?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> { return Promise.resolve(undefined!); } write?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> { return Promise.resolve(undefined!); } +} + +export class RemoteFileSystemProvider implements IFileSystemProvider { + + constructor(private readonly diskFileSystemProvider: IFileSystemProvider, private readonly remoteAuthority: string) { } + + readonly capabilities: FileSystemProviderCapabilities = this.diskFileSystemProvider.capabilities; + readonly onDidChangeCapabilities: Event<void> = this.diskFileSystemProvider.onDidChangeCapabilities; + + readonly onDidChangeFile: Event<IFileChange[]> = Event.map(this.diskFileSystemProvider.onDidChangeFile, changes => changes.map(c => { c.resource = c.resource.with({ scheme: Schemas.vscodeRemote, authority: this.remoteAuthority }); return c; })); + watch(resource: URI, opts: IWatchOptions): IDisposable { return this.diskFileSystemProvider.watch(this.toFileResource(resource), opts); } + + stat(resource: URI): Promise<IStat> { return this.diskFileSystemProvider.stat(this.toFileResource(resource)); } + mkdir(resource: URI): Promise<void> { return this.diskFileSystemProvider.mkdir(this.toFileResource(resource)); } + readdir(resource: URI): Promise<[string, FileType][]> { return this.diskFileSystemProvider.readdir(this.toFileResource(resource)); } + delete(resource: URI, opts: FileDeleteOptions): Promise<void> { return this.diskFileSystemProvider.delete(this.toFileResource(resource), opts); } + + rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> { return this.diskFileSystemProvider.rename(this.toFileResource(from), this.toFileResource(to), opts); } + copy(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> { return this.diskFileSystemProvider.copy!(this.toFileResource(from), this.toFileResource(to), opts); } + + readFile(resource: URI): Promise<Uint8Array> { return this.diskFileSystemProvider.readFile!(this.toFileResource(resource)); } + writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> { return this.diskFileSystemProvider.writeFile!(this.toFileResource(resource), content, opts); } + + open(resource: URI, opts: FileOpenOptions): Promise<number> { return this.diskFileSystemProvider.open!(this.toFileResource(resource), opts); } + close(fd: number): Promise<void> { return this.diskFileSystemProvider.close!(fd); } + read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> { return this.diskFileSystemProvider.read!(fd, pos, data, offset, length); } + write(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> { return this.diskFileSystemProvider.write!(fd, pos, data, offset, length); } + + private toFileResource(resource: URI): URI { return resource.with({ scheme: Schemas.file, authority: '' }); } } \ No newline at end of file diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index de7f0954eba..8535066a63b 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -7,7 +7,7 @@ import 'vs/editor/editor.all'; -import 'vs/workbench/api/electron-browser/extensionHost.contribution'; +import 'vs/workbench/api/browser/extensionHost.contribution'; import 'vs/workbench/electron-browser/main.contribution'; import 'vs/workbench/browser/workbench.contribution'; @@ -20,6 +20,8 @@ import 'vs/workbench/electron-browser/main'; //#region --- workbench actions import 'vs/workbench/browser/actions/layoutActions'; +import 'vs/workbench/browser/actions/windowActions'; +import 'vs/workbench/browser/actions/developerActions'; import 'vs/workbench/browser/actions/listCommands'; import 'vs/workbench/browser/actions/navigationActions'; import 'vs/workbench/browser/parts/quickopen/quickOpenActions'; @@ -89,10 +91,8 @@ import { IURLService } from 'vs/platform/url/common/url'; import { RelayURLService } from 'vs/platform/url/electron-browser/urlService'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; import { TunnelService } from 'vs/workbench/services/remote/node/tunnelService'; -import { ConfigurationResolverService } from 'vs/workbench/services/configurationResolver/electron-browser/configurationResolverService'; -import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; - -import 'vs/platform/remote/node/tunnelService'; +import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; +import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService'; import 'vs/workbench/services/bulkEdit/browser/bulkEditService'; import 'vs/workbench/services/integrity/node/integrityService'; @@ -102,9 +102,8 @@ import 'vs/workbench/services/workspace/electron-browser/workspaceEditingService import 'vs/workbench/services/extensions/common/inactiveExtensionUrlHandler'; import 'vs/workbench/services/decorations/browser/decorationsService'; import 'vs/workbench/services/search/node/searchService'; -import 'vs/workbench/services/progress/browser/progressService2'; +import 'vs/workbench/services/progress/browser/progressService'; import 'vs/workbench/services/editor/browser/codeEditorService'; -import 'vs/workbench/services/broadcast/electron-browser/broadcastService'; import 'vs/workbench/services/extensions/electron-browser/extensionHostDebugService'; import 'vs/workbench/services/preferences/browser/preferencesService'; import 'vs/workbench/services/output/node/outputChannelModelService'; @@ -118,9 +117,11 @@ import 'vs/workbench/services/editor/browser/editorService'; import 'vs/workbench/services/history/browser/history'; import 'vs/workbench/services/activity/browser/activityService'; import 'vs/workbench/browser/parts/views/views'; -import 'vs/workbench/services/keybinding/electron-browser/keybindingService'; +import 'vs/workbench/services/keybinding/electron-browser/nativeKeymapService'; +import 'vs/workbench/services/keybinding/electron-browser/keybinding.contribution'; +import 'vs/workbench/services/keybinding/browser/keybindingService'; import 'vs/workbench/services/untitled/common/untitledEditorService'; -import 'vs/workbench/services/textfile/node/textResourcePropertiesService'; +import 'vs/workbench/services/textfile/common/textResourcePropertiesService'; import 'vs/workbench/services/mode/common/workbenchModeService'; import 'vs/workbench/services/commands/common/commandService'; import 'vs/workbench/services/themes/browser/workbenchThemeService'; @@ -132,9 +133,9 @@ import 'vs/workbench/services/label/common/labelService'; import 'vs/workbench/services/extensions/electron-browser/extensionManagementServerService'; import 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import 'vs/workbench/services/notification/common/notificationService'; -import 'vs/workbench/services/heap/node/heap'; import 'vs/workbench/services/window/electron-browser/windowService'; import 'vs/workbench/services/telemetry/electron-browser/telemetryService'; +import 'vs/workbench/services/configurationResolver/electron-browser/configurationResolverService'; registerSingleton(IMenuService, MenuService, true); registerSingleton(IListService, ListService, true); @@ -162,10 +163,11 @@ registerSingleton(IWorkspacesService, WorkspacesService); registerSingleton(IMenubarService, MenubarService); registerSingleton(IURLService, RelayURLService); registerSingleton(ITunnelService, TunnelService, true); -registerSingleton(IConfigurationResolverService, ConfigurationResolverService, true); +registerSingleton(ICredentialsService, KeytarCredentialsService, true); //#endregion + //#region --- workbench parts import 'vs/workbench/browser/parts/quickinput/quickInput'; @@ -179,6 +181,7 @@ import 'vs/workbench/browser/parts/statusbar/statusbarPart'; //#endregion + //#region --- workbench contributions // Workspace File Watching @@ -191,11 +194,15 @@ import 'vs/workbench/contrib/telemetry/browser/telemetry.contribution'; import 'vs/workbench/contrib/localizations/browser/localizations.contribution'; // Preferences -import 'vs/workbench/contrib/preferences/electron-browser/preferences.contribution'; +import 'vs/workbench/contrib/preferences/browser/preferences.contribution'; import 'vs/workbench/contrib/preferences/browser/keybindingsEditorContribution'; +import { IPreferencesSearchService } from 'vs/workbench/contrib/preferences/common/preferences'; +import { PreferencesSearchService } from 'vs/workbench/contrib/preferences/electron-browser/preferencesSearch'; +registerSingleton(IPreferencesSearchService, PreferencesSearchService, true); // Logs import 'vs/workbench/contrib/logs/common/logs.contribution'; +import 'vs/workbench/contrib/logs/electron-browser/logs.contribution'; // Quick Open Handlers import 'vs/workbench/contrib/quickopen/browser/quickopen.contribution'; @@ -209,7 +216,7 @@ import 'vs/workbench/contrib/files/browser/files.contribution'; import 'vs/workbench/contrib/backup/common/backup.contribution'; // Stats -import 'vs/workbench/contrib/stats/node/stats.contribution'; +import 'vs/workbench/contrib/stats/electron-browser/stats.contribution'; // Rapid Render Splash import 'vs/workbench/contrib/splash/electron-browser/partsSplash.contribution'; @@ -224,11 +231,12 @@ import 'vs/workbench/contrib/scm/browser/scm.contribution'; import 'vs/workbench/contrib/scm/browser/scmViewlet'; // Debug -import 'vs/workbench/contrib/debug/electron-browser/debug.contribution'; +import 'vs/workbench/contrib/debug/browser/debug.contribution'; import 'vs/workbench/contrib/debug/browser/debugQuickOpen'; import 'vs/workbench/contrib/debug/browser/debugEditorContribution'; import 'vs/workbench/contrib/debug/browser/repl'; import 'vs/workbench/contrib/debug/browser/debugViewlet'; +import 'vs/workbench/contrib/debug/node/debugHelperService'; // Markers import 'vs/workbench/contrib/markers/browser/markers.contribution'; @@ -262,7 +270,14 @@ import 'vs/workbench/contrib/terminal/browser/terminalPanel'; import 'vs/workbench/contrib/relauncher/electron-browser/relauncher.contribution'; // Tasks -import 'vs/workbench/contrib/tasks/electron-browser/task.contribution'; +import 'vs/workbench/contrib/tasks/browser/task.contribution'; +import { TaskService } from 'vs/workbench/contrib/tasks/electron-browser/taskService'; +import { ITaskService } from 'vs/workbench/contrib/tasks/common/taskService'; +registerSingleton(ITaskService, TaskService, true); + +// Remote +import 'vs/workbench/contrib/remote/common/remote.contribution'; +import 'vs/workbench/contrib/remote/electron-browser/remote.contribution'; // Emmet import 'vs/workbench/contrib/emmet/browser/emmet.contribution'; @@ -285,7 +300,7 @@ import 'vs/workbench/contrib/snippets/browser/tabCompletion'; import 'vs/workbench/contrib/format/browser/format.contribution'; // Send a Smile -import 'vs/workbench/contrib/feedback/electron-browser/feedback.contribution'; +import 'vs/workbench/contrib/feedback/browser/feedback.contribution'; // Update import 'vs/workbench/contrib/update/electron-browser/update.contribution'; @@ -322,9 +337,6 @@ import 'vs/workbench/contrib/outline/browser/outline.contribution'; // Experiments import 'vs/workbench/contrib/experiments/electron-browser/experiments.contribution'; -// Code Insets -import 'vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution'; - // Issues import 'vs/workbench/contrib/issue/electron-browser/issue.contribution'; diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts new file mode 100644 index 00000000000..af41878783d --- /dev/null +++ b/src/vs/workbench/workbench.web.api.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/workbench/workbench.web.main'; +import { main } from 'vs/workbench/browser/web.main'; +import { UriComponents } from 'vs/base/common/uri'; +import { Event } from 'vs/base/common/event'; + +export interface IWorkbenchConstructionOptions { + + /** + * Experimental: the remote authority is the IP:PORT from where the workbench is served + * from. It is for example being used for the websocket connections as address. + */ + remoteAuthority: string; + + /** + * Experimental: An endpoint to serve iframe content ("webview") from. This is required + * to provide full security isolation from the workbench host. + */ + webviewEndpoint?: string; + + /** + * Experimental: An optional folder that is set as workspace context for the workbench. + */ + folderUri?: UriComponents; + + /** + * Experimental: An optional workspace that is set as workspace context for the workbench. + */ + workspaceUri?: UriComponents; + + /** + * Experimental: The userData namespace is used to handle user specific application + * data like settings, keybindings, UI state and snippets. + */ + userDataProvider?: { + readonly onDidChangeFile: Event<string[]>; + + readFile(path: string): Promise<Uint8Array>; + writeFile(path: string, content: Uint8Array): Promise<void>; + deleteFile(path: string): Promise<void>; + + listFiles(path: string): Promise<string[]>; + }; +} + +/** + * Experimental: Creates the workbench with the provided options in the provided container. + * + * @param domElement the container to create the workbench in + * @param options for setting up the workbench + */ +function create(domElement: HTMLElement, options: IWorkbenchConstructionOptions): Promise<void> { + return main(domElement, options); +} + +export { + create +}; \ No newline at end of file diff --git a/src/vs/workbench/workbench.nodeless.main.css b/src/vs/workbench/workbench.web.main.css similarity index 100% rename from src/vs/workbench/workbench.nodeless.main.css rename to src/vs/workbench/workbench.web.main.css diff --git a/src/vs/workbench/workbench.nodeless.main.nls.js b/src/vs/workbench/workbench.web.main.nls.js similarity index 100% rename from src/vs/workbench/workbench.nodeless.main.nls.js rename to src/vs/workbench/workbench.web.main.nls.js diff --git a/src/vs/workbench/workbench.nodeless.main.ts b/src/vs/workbench/workbench.web.main.ts similarity index 75% rename from src/vs/workbench/workbench.nodeless.main.ts rename to src/vs/workbench/workbench.web.main.ts index 97cf1b80dda..c28adc0ad98 100644 --- a/src/vs/workbench/workbench.nodeless.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -7,12 +7,11 @@ import 'vs/editor/editor.all'; -// import 'vs/workbench/api/electron-browser/extensionHost.contribution'; +import 'vs/workbench/api/browser/extensionHost.contribution'; -// import 'vs/workbench/electron-browser/main.contribution'; import 'vs/workbench/browser/workbench.contribution'; -import 'vs/workbench/browser/nodeless.main'; +import 'vs/workbench/browser/web.main'; //#endregion @@ -20,6 +19,8 @@ import 'vs/workbench/browser/nodeless.main'; //#region --- workbench actions import 'vs/workbench/browser/actions/layoutActions'; +import 'vs/workbench/browser/actions/windowActions'; +import 'vs/workbench/browser/actions/developerActions'; import 'vs/workbench/browser/actions/listCommands'; import 'vs/workbench/browser/actions/navigationActions'; import 'vs/workbench/browser/parts/quickopen/quickOpenActions'; @@ -62,19 +63,18 @@ import { ITextResourceConfigurationService } from 'vs/editor/common/services/res import { TextResourceConfigurationService } from 'vs/editor/common/services/resourceConfigurationImpl'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { BrowserAccessibilityService } from 'vs/platform/accessibility/common/accessibilityService'; -import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { ContextMenuService } from 'vs/platform/contextview/browser/contextMenuService'; +// import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService'; // import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; // import { IRequestService } from 'vs/platform/request/node/request'; // import { RequestService } from 'vs/platform/request/electron-browser/requestService'; -// import { LifecycleService } from 'vs/platform/lifecycle/electron-browser/lifecycleService'; -// import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { BrowserLifecycleService } from 'vs/platform/lifecycle/browser/lifecycleService'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { DialogService } from 'vs/platform/dialogs/browser/dialogService'; // import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; // import { LocalizationsService } from 'vs/platform/localizations/electron-browser/localizationsService'; // import { ISharedProcessService, SharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; -// import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-browser/remoteAuthorityResolverService'; -// import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; // import { IProductService } from 'vs/platform/product/common/product'; // import { ProductService } from 'vs/platform/product/node/productService'; // import { IWindowsService } from 'vs/platform/windows/common/windows'; @@ -89,26 +89,21 @@ import { ContextViewService } from 'vs/platform/contextview/browser/contextViewS // import { MenubarService } from 'vs/platform/menubar/electron-browser/menubarService'; // import { IURLService } from 'vs/platform/url/common/url'; // import { RelayURLService } from 'vs/platform/url/electron-browser/urlService'; -import { IHeapService, NullHeapService } from 'vs/workbench/services/heap/common/heap'; -import { IBroadcastService, NullBroadcastService } from 'vs/workbench/services/broadcast/common/broadcast'; -import { ConfigurationResolverService } from 'vs/workbench/services/configurationResolver/browser/configurationResolverService'; -import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; - -import 'vs/workbench/browser/nodeless.simpleservices'; -import 'vs/platform/dialogs/browser/dialogService'; - - +// import { ITunnelService } from 'vs/platform/remote/common/tunnel'; +// import { TunnelService } from 'vs/workbench/services/remote/node/tunnelService'; +// import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; +// import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService'; import 'vs/workbench/services/bulkEdit/browser/bulkEditService'; // import 'vs/workbench/services/integrity/node/integrityService'; import 'vs/workbench/services/keybinding/common/keybindingEditing'; -// import 'vs/workbench/services/textMate/electron-browser/textMateService'; +import 'vs/workbench/services/textMate/browser/textMateService'; // import 'vs/workbench/services/workspace/electron-browser/workspaceEditingService'; // import 'vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler'; import 'vs/workbench/services/decorations/browser/decorationsService'; -// import 'vs/workbench/services/search/node/searchService'; -import 'vs/workbench/services/progress/browser/progressService2'; +import 'vs/workbench/services/search/common/searchService'; +import 'vs/workbench/services/progress/browser/progressService'; import 'vs/workbench/services/editor/browser/codeEditorService'; -// import 'vs/workbench/services/broadcast/electron-browser/broadcastService'; +// import 'vs/workbench/services/extensions/electron-browser/extensionHostDebugService'; import 'vs/workbench/services/preferences/browser/preferencesService'; import 'vs/workbench/services/output/common/outputChannelModelService'; import 'vs/workbench/services/configuration/common/jsonEditingService'; @@ -121,25 +116,30 @@ import 'vs/workbench/services/editor/browser/editorService'; import 'vs/workbench/services/history/browser/history'; import 'vs/workbench/services/activity/browser/activityService'; import 'vs/workbench/browser/parts/views/views'; -// import 'vs/workbench/services/keybinding/electron-browser/keybindingService'; +import 'vs/workbench/services/keybinding/browser/keymapService'; +import 'vs/workbench/services/keybinding/browser/keybindingService'; import 'vs/workbench/services/untitled/common/untitledEditorService'; -// import 'vs/workbench/services/textfile/node/textResourcePropertiesService'; +import 'vs/workbench/services/textfile/common/textResourcePropertiesService'; import 'vs/workbench/services/mode/common/workbenchModeService'; import 'vs/workbench/services/commands/common/commandService'; import 'vs/workbench/services/themes/browser/workbenchThemeService'; // import 'vs/workbench/services/extensionManagement/node/extensionEnablementService'; -// import 'vs/workbench/services/extensions/electron-browser/extensionService'; +import 'vs/workbench/services/extensions/browser/extensionService'; // import 'vs/workbench/services/contextmenu/electron-browser/contextmenuService'; -// import 'vs/workbench/services/extensionManagement/node/multiExtensionManagement'; +// import 'vs/workbench/services/extensions/node/multiExtensionManagement'; import 'vs/workbench/services/label/common/labelService'; // import 'vs/workbench/services/extensions/electron-browser/extensionManagementServerService'; // import 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import 'vs/workbench/services/notification/common/notificationService'; -// import 'vs/workbench/services/heap/node/heap'; // import 'vs/workbench/services/window/electron-browser/windowService'; // import 'vs/workbench/services/telemetry/electron-browser/telemetryService'; +import 'vs/workbench/services/configurationResolver/browser/configurationResolverService'; +import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { ContextMenuService } from 'vs/platform/contextview/browser/contextMenuService'; +import 'vs/workbench/browser/web.simpleservices'; +registerSingleton(IDialogService, DialogService, true); registerSingleton(IMenuService, MenuService, true); registerSingleton(IListService, ListService, true); registerSingleton(IOpenerService, OpenerService, true); @@ -155,23 +155,22 @@ registerSingleton(IAccessibilityService, BrowserAccessibilityService, true); registerSingleton(IContextViewService, ContextViewService, true); // registerSingleton(IExtensionGalleryService, ExtensionGalleryService, true); // registerSingleton(IRequestService, RequestService, true); -// registerSingleton(ILifecycleService, LifecycleService); +registerSingleton(ILifecycleService, BrowserLifecycleService); // registerSingleton(ILocalizationsService, LocalizationsService); // registerSingleton(ISharedProcessService, SharedProcessService, true); -// registerSingleton(IProductService, ProductService, true); // registerSingleton(IWindowsService, WindowsService); // registerSingleton(IUpdateService, UpdateService); // registerSingleton(IIssueService, IssueService); // registerSingleton(IWorkspacesService, WorkspacesService); // registerSingleton(IMenubarService, MenubarService); // registerSingleton(IURLService, RelayURLService); -registerSingleton(IHeapService, NullHeapService); -registerSingleton(IBroadcastService, NullBroadcastService); +// registerSingleton(ITunnelService, TunnelService, true); +// registerSingleton(ICredentialsService, KeytarCredentialsService, true); registerSingleton(IContextMenuService, ContextMenuService); -registerSingleton(IConfigurationResolverService, ConfigurationResolverService, true); //#endregion + //#region --- workbench parts import 'vs/workbench/browser/parts/quickinput/quickInput'; @@ -185,6 +184,7 @@ import 'vs/workbench/browser/parts/statusbar/statusbarPart'; //#endregion + //#region --- workbench contributions // Workspace File Watching @@ -197,8 +197,12 @@ import 'vs/workbench/contrib/telemetry/browser/telemetry.contribution'; // import 'vs/workbench/contrib/localizations/browser/localizations.contribution'; // Preferences -// import 'vs/workbench/contrib/preferences/electron-browser/preferences.contribution'; +import 'vs/workbench/contrib/preferences/browser/preferences.contribution'; import 'vs/workbench/contrib/preferences/browser/keybindingsEditorContribution'; +import 'vs/workbench/contrib/preferences/browser/keyboardLayoutPicker'; +import { IPreferencesSearchService } from 'vs/workbench/contrib/preferences/common/preferences'; +import { PreferencesSearchService } from 'vs/workbench/contrib/preferences/browser/preferencesSearch'; +registerSingleton(IPreferencesSearchService, PreferencesSearchService, true); // Logs import 'vs/workbench/contrib/logs/common/logs.contribution'; @@ -230,12 +234,12 @@ import 'vs/workbench/contrib/scm/browser/scm.contribution'; import 'vs/workbench/contrib/scm/browser/scmViewlet'; // Debug -// import 'vs/workbench/contrib/debug/electron-browser/debug.contribution'; -// import 'vs/workbench/contrib/debug/browser/debugQuickOpen'; -// import 'vs/workbench/contrib/debug/browser/debugEditorContribution'; -// import 'vs/workbench/contrib/debug/browser/repl'; -// import 'vs/workbench/contrib/debug/browser/debugViewlet'; -// import 'vs/workbench/services/extensions/electron-browser/extensionHostDebugService'; +import 'vs/workbench/contrib/debug/browser/debug.contribution'; +import 'vs/workbench/contrib/debug/browser/debugQuickOpen'; +import 'vs/workbench/contrib/debug/browser/debugEditorContribution'; +import 'vs/workbench/contrib/debug/browser/repl'; +import 'vs/workbench/contrib/debug/browser/debugViewlet'; +import 'vs/workbench/contrib/debug/browser/debugHelperService'; // Markers import 'vs/workbench/contrib/markers/browser/markers.contribution'; @@ -247,7 +251,13 @@ import 'vs/workbench/contrib/markers/browser/markers.contribution'; import 'vs/workbench/contrib/url/common/url.contribution'; // Webview -// import 'vs/workbench/contrib/webview/electron-browser/webview.contribution'; +import 'vs/workbench/contrib/webview/browser/webview.contribution'; + +import { IWebviewService } from 'vs/workbench/contrib/webview/common/webview'; +import { WebviewService } from 'vs/workbench/contrib/webview/browser/webviewService'; +import { IWebviewEditorService, WebviewEditorService } from 'vs/workbench/contrib/webview/browser/webviewEditorService'; +registerSingleton(IWebviewService, WebviewService, true); +registerSingleton(IWebviewEditorService, WebviewEditorService, true); // Extensions Management // import 'vs/workbench/contrib/extensions/electron-browser/extensions.contribution'; @@ -259,16 +269,30 @@ import 'vs/workbench/contrib/output/browser/output.contribution'; import 'vs/workbench/contrib/output/browser/outputPanel'; // Terminal -// import 'vs/workbench/contrib/terminal/browser/terminal.contribution'; +import 'vs/workbench/contrib/terminal/browser/terminal.contribution'; // import 'vs/workbench/contrib/terminal/electron-browser/terminal.contribution'; -// import 'vs/workbench/contrib/terminal/browser/terminalQuickOpen'; -// import 'vs/workbench/contrib/terminal/browser/terminalPanel'; +import 'vs/workbench/contrib/terminal/browser/terminalQuickOpen'; +import 'vs/workbench/contrib/terminal/browser/terminalPanel'; + +import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalNativeService } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TerminalNativeService } from 'vs/workbench/contrib/terminal/browser/terminalNativeService'; +import { TerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminalInstanceService'; +registerSingleton(ITerminalNativeService, TerminalNativeService, true); +registerSingleton(ITerminalInstanceService, TerminalInstanceService, true); // Relauncher // import 'vs/workbench/contrib/relauncher/electron-browser/relauncher.contribution'; // Tasks -// import 'vs/workbench/contrib/tasks/electron-browser/task.contribution'; +import 'vs/workbench/contrib/tasks/browser/task.contribution'; +import { TaskService } from 'vs/workbench/contrib/tasks/browser/taskService'; +import { ITaskService } from 'vs/workbench/contrib/tasks/common/taskService'; +registerSingleton(ITaskService, TaskService, true); + +// Remote +import 'vs/workbench/contrib/remote/common/remote.contribution'; +// import 'vs/workbench/contrib/remote/electron-browser/remote.contribution'; // Emmet import 'vs/workbench/contrib/emmet/browser/emmet.contribution'; @@ -291,7 +315,7 @@ import 'vs/workbench/contrib/snippets/browser/tabCompletion'; import 'vs/workbench/contrib/format/browser/format.contribution'; // Send a Smile -// import 'vs/workbench/contrib/feedback/electron-browser/feedback.contribution'; +// import 'vs/workbench/contrib/feedback/browser/feedback.contribution'; // Update // import 'vs/workbench/contrib/update/electron-browser/update.contribution'; @@ -319,15 +343,15 @@ import 'vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contributio import 'vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay'; // import 'vs/workbench/contrib/welcome/page/browser/welcomePage.contribution'; +// Call Hierarchy +import 'vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution'; + // Outline import 'vs/workbench/contrib/outline/browser/outline.contribution'; // Experiments // import 'vs/workbench/contrib/experiments/electron-browser/experiments.contribution'; -// Code Insets -// import 'vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution'; - // Issues // import 'vs/workbench/contrib/issue/electron-browser/issue.contribution'; diff --git a/test/electron/index.js b/test/electron/index.js index ebd97e3b98b..b0d82f6d1bd 100644 --- a/test/electron/index.js +++ b/test/electron/index.js @@ -100,6 +100,13 @@ function parseReporterOption(value) { app.on('ready', () => { + ipcMain.on('error', (_, err) => { + if (!argv.debug) { + console.error(err); + app.exit(1); + } + }); + const win = new BrowserWindow({ height: 600, width: 800, diff --git a/test/electron/renderer.js b/test/electron/renderer.js index addb1f0b93c..bd8cba69214 100644 --- a/test/electron/renderer.js +++ b/test/electron/renderer.js @@ -273,5 +273,12 @@ function runTests(opts) { ipcRenderer.on('run', (e, opts) => { initLoader(opts); - runTests(opts).catch(err => console.error(typeof err === 'string' ? err : JSON.stringify(err))); + runTests(opts).catch(err => { + if (!(typeof err !== 'string')) { + err = JSON.stringify(err); + } + + console.error(err); + ipcRenderer.send('error', err); + }); }); diff --git a/test/smoke/.gitignore b/test/smoke/.gitignore index 6601bb4c588..d7ed700e659 100644 --- a/test/smoke/.gitignore +++ b/test/smoke/.gitignore @@ -5,4 +5,5 @@ node_modules/ out/ keybindings.*.json test_data/ -src/vscode/driver.d.ts \ No newline at end of file +src/vscode/driver.d.ts +vscode-server*/ \ No newline at end of file diff --git a/test/smoke/README.md b/test/smoke/README.md index b8b98a358de..b5be966e518 100644 --- a/test/smoke/README.md +++ b/test/smoke/README.md @@ -15,19 +15,22 @@ yarn smoketest # Build yarn smoketest --build PATH_TO_NEW_BUILD_PARENT_FOLDER --stable-build PATH_TO_LAST_STABLE_BUILD_PARENT_FOLDER + +# Remote +yarn smoketest --build PATH_TO_NEW_BUILD_PARENT_FOLDER --remote ``` ### Run for a release -You must always run the smoketest version which matches the release you are testing. So, if you want to run the smoketest for a release build (eg `release/1.22`), you need that version of the smoke tests too: +You must always run the smoketest version which matches the release you are testing. So, if you want to run the smoketest for a release build (e.g. `release/1.22`), you need that version of the smoke tests too: ```bash git checkout release/1.22 yarn ``` -In addition to the new build to be released you will need the previous stable build so that the smoketest can test the data migration. -The recommended way to make these builds available for the smoketest is by downloading their archive version (\*.zip) and extracting +In addition to the new build to be released you will need the previous stable build so that the smoketest can test the data migration. +The recommended way to make these builds available for the smoketest is by downloading their archive version (\*.zip) and extracting them into two folders. Pass the folder paths to the smoketest as follows: ```bash diff --git a/test/smoke/package.json b/test/smoke/package.json index 00b04f8453d..80f93f04d89 100644 --- a/test/smoke/package.json +++ b/test/smoke/package.json @@ -17,12 +17,12 @@ "@types/mkdirp": "0.5.1", "@types/mocha": "2.2.41", "@types/ncp": "2.0.1", - "@types/node": "8.0.33", + "@types/node": "^10.14.8", "@types/rimraf": "2.0.2", "@types/webdriverio": "4.6.1", "concurrently": "^3.5.1", "cpx": "^1.5.0", - "electron": "3.1.8", + "electron": "4.2.5", "htmlparser2": "^3.9.2", "mkdirp": "^0.5.1", "mocha": "^5.2.0", diff --git a/test/smoke/src/application.ts b/test/smoke/src/application.ts index 58e5b301a2c..472d081ec4b 100644 --- a/test/smoke/src/application.ts +++ b/test/smoke/src/application.ts @@ -47,6 +47,10 @@ export class Application { return this.options.logger; } + get remote(): boolean { + return !!this.options.remote; + } + private _workspacePathOrFolder: string; get workspacePathOrFolder(): string { return this._workspacePathOrFolder; @@ -135,8 +139,12 @@ export class Application { await this.code.waitForWindowIds(ids => ids.length > 0); await this.code.waitForElement('.monaco-workbench'); + if (this.remote) { + await this.code.waitForElement('.monaco-workbench .statusbar-item[title="Editing on TestResolver"]'); + } + // wait a bit, since focus might be stolen off widgets - // as soon as they open (eg quick open) + // as soon as they open (e.g. quick open) await new Promise(c => setTimeout(c, 1000)); } } diff --git a/test/smoke/src/areas/editor/peek.ts b/test/smoke/src/areas/editor/peek.ts index be1dcfe5b7e..dc25336625b 100644 --- a/test/smoke/src/areas/editor/peek.ts +++ b/test/smoke/src/areas/editor/peek.ts @@ -10,7 +10,7 @@ export class References { private static readonly REFERENCES_WIDGET = '.monaco-editor .zone-widget .zone-widget-container.peekview-widget.reference-zone-widget.results-loaded'; private static readonly REFERENCES_TITLE_FILE_NAME = `${References.REFERENCES_WIDGET} .head .peekview-title .filename`; private static readonly REFERENCES_TITLE_COUNT = `${References.REFERENCES_WIDGET} .head .peekview-title .meta`; - private static readonly REFERENCES = `${References.REFERENCES_WIDGET} .body .ref-tree.inline .monaco-list-row .reference`; + private static readonly REFERENCES = `${References.REFERENCES_WIDGET} .body .ref-tree.inline .monaco-list-row .highlight`; constructor(private code: Code) { } diff --git a/test/smoke/src/areas/extensions/extensions.test.ts b/test/smoke/src/areas/extensions/extensions.test.ts index 4215a7fe247..1ff6dce2df3 100644 --- a/test/smoke/src/areas/extensions/extensions.test.ts +++ b/test/smoke/src/areas/extensions/extensions.test.ts @@ -20,6 +20,10 @@ export function setup() { await app.workbench.extensions.installExtension('michelkaporin.vscode-smoketest-check', 'vscode-smoketest-check'); await app.workbench.extensions.waitForExtensionsViewlet(); + + if (app.remote) { + await app.reload(); + } await app.workbench.quickopen.runCommand('Smoke Test Check'); await app.workbench.statusbar.waitForStatusbarText('smoke test', 'VS Code Smoke Test Check'); }); diff --git a/test/smoke/src/areas/git/git.test.ts b/test/smoke/src/areas/git/git.test.ts index d04677eafbc..11546f7d7fe 100644 --- a/test/smoke/src/areas/git/git.test.ts +++ b/test/smoke/src/areas/git/git.test.ts @@ -7,7 +7,7 @@ import * as cp from 'child_process'; import { Application } from '../../application'; const DIFF_EDITOR_LINE_INSERT = '.monaco-diff-editor .editor.modified .line-insert'; -const SYNC_STATUSBAR = 'div[id="workbench.parts.statusbar"] .statusbar-entry a[title$="Synchronize Changes"]'; +const SYNC_STATUSBAR = 'div[id="workbench.parts.statusbar"] .statusbar-item[title$="Synchronize Changes"]'; export function setup() { describe('Git', () => { diff --git a/test/smoke/src/areas/multiroot/multiroot.test.ts b/test/smoke/src/areas/multiroot/multiroot.test.ts index f3ca92b8868..d6c656bc433 100644 --- a/test/smoke/src/areas/multiroot/multiroot.test.ts +++ b/test/smoke/src/areas/multiroot/multiroot.test.ts @@ -47,7 +47,8 @@ export function setup() { const app = this.app as Application; await app.workbench.quickopen.openQuickOpen('*.*'); - await app.workbench.quickopen.waitForQuickOpenElements(names => names.length === 6); + // TODO roblourens: Go to files finds welcome page: issue 74875 + await app.workbench.quickopen.waitForQuickOpenElements(names => names.length === 6 || names.length === 7); await app.workbench.quickopen.closeQuickOpen(); }); diff --git a/test/smoke/src/areas/problems/problems.ts b/test/smoke/src/areas/problems/problems.ts index c71b7d33791..e0499151dcb 100644 --- a/test/smoke/src/areas/problems/problems.ts +++ b/test/smoke/src/areas/problems/problems.ts @@ -39,7 +39,7 @@ export class Problems { } public static getSelectorInProblemsView(problemType: ProblemSeverity): string { - let selector = problemType === ProblemSeverity.WARNING ? 'warning' : 'error'; + let selector = problemType === ProblemSeverity.WARNING ? 'severity-warning' : 'severity-error'; return `div[id="workbench.panel.markers"] .monaco-tl-contents .marker-icon.${selector}`; } diff --git a/test/smoke/src/areas/statusbar/statusbar.ts b/test/smoke/src/areas/statusbar/statusbar.ts index b36678ea7d2..66e1186fd4c 100644 --- a/test/smoke/src/areas/statusbar/statusbar.ts +++ b/test/smoke/src/areas/statusbar/statusbar.ts @@ -38,7 +38,7 @@ export class StatusBar { } async waitForStatusbarText(title: string, text: string): Promise<void> { - await this.code.waitForTextContent(`${this.mainSelector} span[title="${title}"]`, text); + await this.code.waitForTextContent(`${this.mainSelector} .statusbar-item[title="${title}"]`, text); } private getSelector(element: StatusBarElement): string { @@ -48,17 +48,17 @@ export class StatusBar { case StatusBarElement.SYNC_STATUS: return `${this.mainSelector} ${this.leftSelector} .octicon.octicon-sync`; case StatusBarElement.PROBLEMS_STATUS: - return `${this.mainSelector} ${this.leftSelector} .task-statusbar-item[title="Problems"]`; + return `${this.mainSelector} ${this.leftSelector} .octicon.octicon-error`; case StatusBarElement.SELECTION_STATUS: - return `${this.mainSelector} ${this.rightSelector} .editor-status-selection`; + return `${this.mainSelector} ${this.rightSelector}[title="Go to Line"]`; case StatusBarElement.INDENTATION_STATUS: - return `${this.mainSelector} ${this.rightSelector} .editor-status-indentation`; + return `${this.mainSelector} ${this.rightSelector}[title="Select Indentation"]`; case StatusBarElement.ENCODING_STATUS: - return `${this.mainSelector} ${this.rightSelector} .editor-status-encoding`; + return `${this.mainSelector} ${this.rightSelector}[title="Select Encoding"]`; case StatusBarElement.EOL_STATUS: - return `${this.mainSelector} ${this.rightSelector} .editor-status-eol`; + return `${this.mainSelector} ${this.rightSelector}[title="Select End of Line Sequence"]`; case StatusBarElement.LANGUAGE_STATUS: - return `${this.mainSelector} ${this.rightSelector} .editor-status-mode`; + return `${this.mainSelector} ${this.rightSelector}[title="Select Language Mode"]`; case StatusBarElement.FEEDBACK_ICON: return `${this.mainSelector} ${this.rightSelector} .monaco-dropdown.send-feedback`; default: diff --git a/test/smoke/src/vscode/code.ts b/test/smoke/src/vscode/code.ts index e657d0825f5..0b9c739cb69 100644 --- a/test/smoke/src/vscode/code.ts +++ b/test/smoke/src/vscode/code.ts @@ -6,9 +6,12 @@ import * as path from 'path'; import * as cp from 'child_process'; import * as os from 'os'; +import * as fs from 'fs'; +import * as mkdirp from 'mkdirp'; import { tmpName } from 'tmp'; import { IDriver, connect as connectDriver, IDisposable, IElement, Thenable } from './driver'; import { Logger } from '../logger'; +import { ncp } from 'ncp'; const repoPath = path.join(__dirname, '../../../..'); @@ -121,6 +124,8 @@ export async function spawn(options: SpawnOptions): Promise<Code> { '--driver', handle ]; + const env = process.env; + if (options.remote) { // Replace workspace path with URI args.shift(); @@ -128,6 +133,18 @@ export async function spawn(options: SpawnOptions): Promise<Code> { `--${options.workspacePath.endsWith('.code-workspace') ? 'file' : 'folder'}-uri`, `vscode-remote://test+test${options.workspacePath}`, ); + if (codePath) { + // running against a build: copy the test resolver extension + const testResolverExtPath = path.join(options.extensionsPath, 'vscode-test-resolver'); + if (!fs.existsSync(testResolverExtPath)) { + const orig = path.join(repoPath, 'extensions', 'vscode-test-resolver'); + await new Promise((c, e) => ncp(orig, testResolverExtPath, err => err ? e(err) : c())); + } + } + args.push('--enable-proposed-api=vscode.vscode-test-resolver'); + const remoteDataDir = `${options.userDataDir}-server`; + mkdirp.sync(remoteDataDir); + env['TESTRESOLVER_DATA_FOLDER'] = remoteDataDir; } if (!codePath) { @@ -146,7 +163,7 @@ export async function spawn(options: SpawnOptions): Promise<Code> { args.push(...options.extraArgs); } - const spawnOptions: cp.SpawnOptions = {}; + const spawnOptions: cp.SpawnOptions = { env }; const child = cp.spawn(electronPath, args, spawnOptions); diff --git a/test/smoke/yarn.lock b/test/smoke/yarn.lock index 5e1fd36aa4f..b6424c799b9 100644 --- a/test/smoke/yarn.lock +++ b/test/smoke/yarn.lock @@ -44,15 +44,15 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.51.tgz#b31d716fb8d58eeb95c068a039b9b6292817d5fb" integrity sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ== -"@types/node@8.0.33": - version "8.0.33" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.33.tgz#1126e94374014e54478092830704f6ea89df04cd" - integrity sha512-vmCdO8Bm1ExT+FWfC9sd9r4jwqM7o97gGy2WBshkkXbf/2nLAJQUrZfIhw27yVOtLUev6kSZc4cav/46KbDd8A== +"@types/node@^10.12.18": + version "10.12.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" + integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ== -"@types/node@^8.0.24": - version "8.10.23" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.23.tgz#e5ccfdafff42af5397c29669b6d7d65f7d629a00" - integrity sha512-aEp5ZTLr4mYhR9S85cJ+sEYkcsgFY10N1Si5m49iTAVzanZXOwp/pgw6ibFLKXxpflqm71aSWZCRtnTXXO56gA== +"@types/node@^10.14.8": + version "10.14.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9" + integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw== "@types/rimraf@2.0.2": version "2.0.2" @@ -596,12 +596,12 @@ electron-download@^4.1.0: semver "^5.4.1" sumchecker "^2.0.2" -electron@3.1.8: - version "3.1.8" - resolved "https://registry.yarnpkg.com/electron/-/electron-3.1.8.tgz#01b0b147dfcca47967ff07dbf72bf5e96125a2ac" - integrity sha512-1MiFoMzxGaR0wDfwFE5Ydnuk6ry/4lKgF0c+NFyEItxM/WyEHNZPNjJAeKJ+M/0sevmZ+6W4syNZnQL5M3GgsQ== +electron@4.2.5: + version "4.2.5" + resolved "https://registry.yarnpkg.com/electron/-/electron-4.2.5.tgz#1d1432c38e2b2190318f7ca30897cdfdcf942e5a" + integrity sha512-P132MXzTtyn2ZaekhKi5JeHzmTAMuR/uQt4hrg3vfJV7fpncx9SL6UFwHAK1DU13iiyZJqqIziNUu+o8nODHsA== dependencies: - "@types/node" "^8.0.24" + "@types/node" "^10.12.18" electron-download "^4.1.0" extract-zip "^1.0.3" diff --git a/tslint.json b/tslint.json index 311eec214aa..bf277c33bd1 100644 --- a/tslint.json +++ b/tslint.json @@ -364,6 +364,7 @@ "**/vs/platform/*/{common,browser}/**", "**/vs/editor/{common,browser}/**", "**/vs/editor/contrib/**", // editor/contrib is equivalent to /browser/ by convention + "**/vs/workbench/workbench.web.api", "**/vs/workbench/{common,browser}/**", "**/vs/workbench/services/*/{common,browser}/**", "assert" @@ -440,8 +441,11 @@ "**/vs/base/**/{common,browser}/**", "**/vs/platform/**/{common,browser}/**", "**/vs/editor/{common,browser}/**", + "**/vs/workbench/workbench.web.api", "**/vs/workbench/{common,browser}/**", - "**/vs/workbench/services/**/{common,browser}/**" + "**/vs/workbench/services/**/{common,browser}/**", + "vscode-textmate", + "onigasm-umd" ] }, { @@ -504,7 +508,9 @@ { "target": "**/vs/workbench/contrib/terminal/browser/**", "restrictions": [ - "vscode-xterm", + // xterm and its addons are strictly browser-only components + "xterm", + "xterm-addon-*", "**/vs/**" ] }, @@ -539,7 +545,19 @@ "**/vs/base/parts/**/{common,browser,node,electron-main}/**", "**/vs/platform/**/{common,browser,node,electron-main}/**", "**/vs/code/**/{common,browser,node,electron-main}/**", - "**/vs/code/code.main", + "*" // node modules + ] + }, + { + "target": "**/vs/server/**", + "restrictions": [ + "vs/nls", + "**/vs/base/**/{common,node}/**", + "**/vs/base/parts/**/{common,node}/**", + "**/vs/platform/**/{common,node}/**", + "**/vs/workbench/**/{common,node}/**", + "**/vs/server/**", + "**/vs/code/**/{common,node}/**", "*" // node modules ] }, diff --git a/yarn.lock b/yarn.lock index 1e8975e4d53..acd186d7f7e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -303,10 +303,10 @@ acorn@^5.0.0, acorn@^5.6.2: resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8" integrity sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ== -acorn@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.2.1.tgz#317ac7821826c22c702d66189ab8359675f135d7" - integrity sha512-jG0u7c4Ly+3QkkW18V+NRDN+4bWHdln30NL1ZL2AvFZZmQe/BfopYCtghCKKVBUSetZ4QKcyA0pY6/4Gw8Pv8w== +acorn@^5.5.0: + version "5.7.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" + integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== acorn@^6.0.2: version "6.0.7" @@ -327,24 +327,16 @@ agent-base@~4.2.0: 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" - integrity sha1-MU3QpLM2j609/NxU7eYXG4htrzw= +ajv-keywords@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" + integrity sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I= ajv-keywords@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a" integrity sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo= -ajv@^4.7.0: - version "4.11.8" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" - integrity sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY= - 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" @@ -355,7 +347,7 @@ ajv@^5.1.0: fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" -ajv@^5.3.0: +ajv@^5.2.3, ajv@^5.3.0: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU= @@ -423,11 +415,6 @@ ansi-cyan@^0.1.1: dependencies: ansi-wrap "0.1.0" -ansi-escapes@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" - integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= - ansi-escapes@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" @@ -757,7 +744,7 @@ async-settle@^1.0.0: dependencies: async-done "^1.2.2" -async@1.x, async@^1.4.0: +async@1.x, async@^1.4.0, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= @@ -828,7 +815,7 @@ azure-storage@^2.10.2: xml2js "0.2.8" xmlbuilder "^9.0.7" -babel-code-frame@^6.16.0, babel-code-frame@^6.22.0: +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= @@ -920,11 +907,18 @@ binaryextensions@~1.0.0: resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-1.0.1.tgz#1e637488b35b58bda5f4774bf96a5212a8c90755" integrity sha1-HmN0iLNbWL2l9HdL+WpSEqjJB1U= -bindings@^1.2.1, bindings@^1.3.0: +bindings@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.0.tgz#b346f6ecf6a95f5a815c5839fc7cdb22502f1ed7" integrity sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw== +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + bl@^1.0.0: version "1.2.2" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c" @@ -1302,7 +1296,7 @@ chalk@2.3.1: escape-string-regexp "^1.0.5" supports-color "^5.2.0" -chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: +chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= @@ -1340,6 +1334,11 @@ chalk@^2.3.0: escape-string-regexp "^1.0.5" supports-color "^4.0.0" +chardet@^0.4.0: + version "0.4.2" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" + integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I= + chardet@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.5.0.tgz#fe3ac73c00c3d865ffcc02a0682e2c20b6a06029" @@ -1466,21 +1465,6 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -clean-css@3.4.6: - version "3.4.6" - resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-3.4.6.tgz#fcb4f17057ddb7f8721616f70b07b294d95ffc45" - integrity sha1-/LTxcFfdt/hyFhb3CweylNlf/EU= - dependencies: - commander "2.8.x" - source-map "0.4.x" - -cli-cursor@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" - integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc= - dependencies: - restore-cursor "^1.0.1" - cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -1635,6 +1619,11 @@ colormin@^1.0.5: css-color-names "0.0.4" has "^1.0.1" +colors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" + integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= + colors@^1.1.2: version "1.2.1" resolved "https://registry.yarnpkg.com/colors/-/colors-1.2.1.tgz#f4a3d302976aaf042356ba1ade3b1a2c62d9d794" @@ -1686,13 +1675,6 @@ commander@2.3.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.3.0.tgz#fd430e889832ec353b9acd1de217c11cb3eef873" integrity sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM= -commander@2.8.x: - version "2.8.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4" - integrity sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ= - dependencies: - graceful-readlink ">= 1.0.0" - commander@^2.12.1, commander@~2.13.0: version "2.13.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" @@ -1723,7 +1705,7 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@1.6.0, concat-stream@^1.5.2: +concat-stream@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" integrity sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc= @@ -1850,6 +1832,11 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= +corser@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/corser/-/corser-2.0.1.tgz#8eda252ecaab5840dcd975ceb90d9370c819ff87" + integrity sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c= + coveralls@^2.11.11: version "2.13.3" resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-2.13.3.tgz#9ad7c2ae527417f361e8b626483f48ee92dd2bc7" @@ -1892,7 +1879,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -cross-spawn@^5.0.1: +cross-spawn@^5.0.1, cross-spawn@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= @@ -2081,7 +2068,7 @@ 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: +debug@2.6.9, 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== @@ -2095,6 +2082,13 @@ debug@3.1.0, debug@^3.1.0: dependencies: ms "2.0.0" +debug@^3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + debug@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" @@ -2310,14 +2304,6 @@ dir-glob@^2.0.0: arrify "^1.0.1" path-type "^3.0.0" -doctrine@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63" - integrity sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM= - dependencies: - esutils "^2.0.2" - isarray "^1.0.0" - doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -2431,6 +2417,16 @@ ecc-jsbn@~0.1.1: dependencies: jsbn "~0.1.0" +ecstatic@^3.0.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/ecstatic/-/ecstatic-3.3.1.tgz#b15b5b036c2233defc78d7bacbd8765226c95577" + integrity sha512-/rrctvxZ78HMI/tPIsqdvFKHHscxR3IJuKrZI2ZoUgkt2SiufyLFBmcco+aqQBIu6P1qBsUNG3drAAGLx80vTQ== + dependencies: + he "^1.1.1" + mime "^1.6.0" + minimist "^1.1.0" + url-join "^2.0.5" + editions@^1.1.1, editions@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/editions/-/editions-1.3.4.tgz#3662cb592347c3168eb8e498a0ff73271d67f50b" @@ -2573,18 +2569,6 @@ es6-iterator@^2.0.1, es6-iterator@~2.0.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" - integrity sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA= - 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-promise@^4.0.3: version "4.2.4" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.4.tgz#dc4221c2b16518760bd8c39a52d8f356fc00ed29" @@ -2597,18 +2581,7 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" -es6-set@~0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" - integrity sha1-0rPsXU2ADO2BjbU40ol02wpzzLE= - 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: +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" integrity sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc= @@ -2665,13 +2638,11 @@ escodegen@1.8.x: 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" - integrity sha1-4Bl16BJ4GhY6ba392AOY3GTIicM= +eslint-scope@^3.7.1: + version "3.7.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.3.tgz#bb507200d3d17f60247636160b4826284b108535" + integrity sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA== dependencies: - es6-map "^0.1.3" - es6-weak-map "^2.0.1" esrecurse "^4.1.0" estraverse "^4.1.1" @@ -2693,46 +2664,49 @@ eslint-visitor-keys@^1.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" integrity sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ== -eslint@^3.4.0: - version "3.19.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.19.0.tgz#c8fc6201c7f40dd08941b87c085767386a679acc" - integrity sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw= +eslint@^4.18.2: + version "4.19.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300" + integrity sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ== 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" + ajv "^5.3.0" + babel-code-frame "^6.22.0" + chalk "^2.1.0" + concat-stream "^1.6.0" + cross-spawn "^5.1.0" + debug "^3.1.0" + doctrine "^2.1.0" + eslint-scope "^3.7.1" + eslint-visitor-keys "^1.0.0" + espree "^3.5.4" 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" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.0.1" + ignore "^3.3.3" imurmurhash "^0.1.4" - inquirer "^0.12.0" - is-my-json-valid "^2.10.0" + inquirer "^3.0.6" is-resolvable "^1.0.0" - js-yaml "^3.5.1" - json-stable-stringify "^1.0.0" + js-yaml "^3.9.1" + json-stable-stringify-without-jsonify "^1.0.1" levn "^0.3.0" - lodash "^4.0.0" - mkdirp "^0.5.0" + lodash "^4.17.4" + minimatch "^3.0.2" + mkdirp "^0.5.1" 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" + path-is-inside "^1.0.2" + pluralize "^7.0.0" + progress "^2.0.0" + regexpp "^1.0.1" + require-uncached "^1.0.3" + semver "^5.3.0" + strip-ansi "^4.0.0" strip-json-comments "~2.0.1" - table "^3.7.8" + table "4.0.2" text-table "~0.2.0" - user-home "^2.0.0" eslint@^5.0.1: version "5.13.0" @@ -2776,12 +2750,12 @@ eslint@^5.0.1: table "^5.0.2" text-table "^0.2.0" -espree@^3.4.0: - version "3.5.2" - resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.2.tgz#756ada8b979e9dcfcdb30aad8d1a9304a905e1ca" - integrity sha512-sadKeYwaR/aJ3stC2CdvgXu1T16TdYN+qwCpcWbMnGJ8s0zNWemzrvb2GbD4OhmJ/fwpJjudThAlLobGbWZbCQ== +espree@^3.5.4: + version "3.5.4" + resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7" + integrity sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A== dependencies: - acorn "^5.2.1" + acorn "^5.5.0" acorn-jsx "^3.0.0" espree@^5.0.0: @@ -2840,7 +2814,7 @@ estraverse@^1.9.1: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" integrity sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q= -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= @@ -2855,14 +2829,6 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -event-emitter@~0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" - integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk= - dependencies: - d "1" - es5-ext "~0.10.14" - event-stream@3.3.4, event-stream@^3.3.4: version "3.3.4" resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" @@ -2889,6 +2855,11 @@ event-stream@~3.3.4: stream-combiner "^0.2.2" through "^2.3.8" +eventemitter3@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" + integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== + events@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" @@ -2915,11 +2886,6 @@ execa@^0.7.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" - integrity sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g= - expand-brackets@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" @@ -3027,6 +2993,15 @@ extend@^3.0.2, extend@~3.0.2: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== +external-editor@^2.0.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" + integrity sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A== + dependencies: + chardet "^0.4.0" + iconv-lite "^0.4.17" + tmp "^0.0.33" + external-editor@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.0.tgz#dc35c48c6f98a30ca27a20e9687d7f3c77704bb6" @@ -3146,13 +3121,12 @@ fd-slicer@~1.0.1: dependencies: pend "~1.2.0" -figures@^1.3.5: - version "1.7.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" - integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= +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: - escape-string-regexp "^1.0.5" - object-assign "^4.1.0" + pend "~1.2.0" figures@^2.0.0: version "2.0.0" @@ -3169,6 +3143,11 @@ file-entry-cache@^2.0.0: flat-cache "^1.2.1" object-assign "^4.0.1" +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + filename-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775" @@ -3306,6 +3285,13 @@ flush-write-stream@^1.0.0, flush-write-stream@^1.0.2: inherits "^2.0.1" readable-stream "^2.0.4" +follow-redirects@^1.0.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.7.0.tgz#489ebc198dc0e7f64167bd23b03c4c19b5784c76" + integrity sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ== + dependencies: + debug "^3.2.6" + for-in@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.5.tgz#007374e2b6d5c67420a1479bdb75a04872b738c4" @@ -3512,11 +3498,6 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -gc-signals@^0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/gc-signals/-/gc-signals-0.0.2.tgz#1cfa8a00adecaeeb93ea0dda72dad9e9f333e62f" - integrity sha512-Ghj4Co6x5bd3dvbAFuiDc6gN+BVK8ic8CBn70dXjzrtbC5hq4a+s4S6acEvftMP7LcQuHKN5v+30PGXhkCLoCQ== - generate-function@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" @@ -3674,7 +3655,7 @@ glob@^6.0.4: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2: +glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== @@ -3723,16 +3704,16 @@ global-prefix@^1.0.1: is-windows "^1.0.1" which "^1.2.14" +globals@^11.0.1: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + globals@^11.7.0: version "11.10.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.10.0.tgz#1e09776dffda5e01816b3bb4077c8b59c24eaa50" integrity sha512-0GZF1RiPKU97IHUO5TORo9w1PwrH/NBPl+fS7oMLdaTRiYmYbwK4NWoZWrAdd0/abG9R2BU+OiwyQpTpE6pdfQ== -globals@^9.14.0: - version "9.18.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" - integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== - globby@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" @@ -3769,11 +3750,6 @@ graceful-fs@4.1.11, graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg= -"graceful-readlink@>= 1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" - integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= - growl@1.9.2: version "1.9.2" resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" @@ -4213,6 +4189,11 @@ hawk@~6.0.2: hoek "4.x.x" sntp "2.x.x" +he@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -4279,6 +4260,29 @@ http-proxy-agent@2.1.0, http-proxy-agent@^2.1.0: agent-base "4" debug "3.1.0" +http-proxy@^1.8.1: + version "1.17.0" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.17.0.tgz#7ad38494658f84605e2f6db4436df410f4e5be9a" + integrity sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g== + dependencies: + eventemitter3 "^3.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +http-server@^0.11.1: + version "0.11.1" + resolved "https://registry.yarnpkg.com/http-server/-/http-server-0.11.1.tgz#2302a56a6ffef7f9abea0147d838a5e9b6b6a79b" + integrity sha512-6JeGDGoujJLmhjiRGlt8yK8Z9Kl0vnl/dQoQZlc4oeqaUoAKQg94NILLfrY3oWzSyFaQCVNTcKE5PZ3cH8VP9w== + dependencies: + colors "1.0.3" + corser "~2.0.0" + ecstatic "^3.0.0" + http-proxy "^1.8.1" + opener "~1.4.0" + optimist "0.6.x" + portfinder "^1.0.13" + union "~0.4.3" + http-signature@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" @@ -4332,7 +4336,7 @@ iconv-lite@0.4.23, iconv-lite@^0.4.22, iconv-lite@^0.4.4: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@^0.4.24: +iconv-lite@^0.4.17, 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== @@ -4356,12 +4360,7 @@ ignore-walk@^3.0.1: dependencies: minimatch "^3.0.4" -ignore@^3.2.0: - version "3.3.7" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021" - integrity sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA== - -ignore@^3.3.5: +ignore@^3.3.3, ignore@^3.3.5: version "3.3.10" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== @@ -4432,28 +4431,29 @@ ini@^1.3.4, ini@~1.3.0: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" integrity sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4= -innosetup-compiler@^5.5.60: - version "5.5.62" - resolved "https://registry.yarnpkg.com/innosetup-compiler/-/innosetup-compiler-5.5.62.tgz#fc12cd8d17cf75a2e3833b2754a5c2bc4f26cc4e" - integrity sha1-/BLNjRfPdaLjgzsnVKXCvE8mzE4= +innosetup@5.6.1: + version "5.6.1" + resolved "https://registry.yarnpkg.com/innosetup/-/innosetup-5.6.1.tgz#6e7031ba35b23e716e4f29686bc994052e0c278c" + integrity sha512-Eit24N3JR8O0Wpuq/dMWCl2r550eiNP2124SbdbwOob43x89WPGL/SGpZG5EPHu20kV2N+4TwvHwFIM8pFUJ0g== -inquirer@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" - integrity sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34= +inquirer@^3.0.6: + version "3.3.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" + integrity sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ== dependencies: - ansi-escapes "^1.1.0" - ansi-regex "^2.0.0" - chalk "^1.0.0" - cli-cursor "^1.0.1" + ansi-escapes "^3.0.0" + chalk "^2.0.0" + cli-cursor "^2.1.0" cli-width "^2.0.0" - figures "^1.3.5" + external-editor "^2.0.4" + figures "^2.0.0" 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" + mute-stream "0.0.7" + run-async "^2.2.0" + rx-lite "^4.0.8" + rx-lite-aggregates "^4.0.8" + string-width "^2.1.0" + strip-ansi "^4.0.0" through "^2.3.6" inquirer@^6.0.0: @@ -4499,11 +4499,6 @@ int64-buffer@^0.1.9: resolved "https://registry.yarnpkg.com/int64-buffer/-/int64-buffer-0.1.9.tgz#9e039da043b24f78b196b283e04653ef5e990f61" integrity sha1-ngOdoEOyT3ixlrKD4EZT716ZD2E= -interpret@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.4.tgz#820cdd588b868ffb191a809506d6c9c8f212b1b0" - integrity sha1-ggzdWIuGj/sZGoCVBtbJyPISsbA= - interpret@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" @@ -4701,7 +4696,7 @@ is-glob@^4.0.0: dependencies: is-extglob "^2.1.1" -is-my-json-valid@^2.10.0, is-my-json-valid@^2.12.4: +is-my-json-valid@^2.12.4: version "2.16.1" resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz#5a846777e2c2620d1e69104e5d3a03b1f6088f11" integrity sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ== @@ -4863,6 +4858,11 @@ is-windows@^1.0.1, is-windows@^1.0.2: resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + is@^3.1.0, is@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/is/-/is-3.2.1.tgz#d0ac2ad55eb7b0bec926a5266f6c662aaa83dca5" @@ -4990,7 +4990,7 @@ js-yaml@3.6.1: argparse "^1.0.7" esprima "^2.6.0" -js-yaml@3.x, js-yaml@^3.5.1, js-yaml@^3.7.0: +js-yaml@3.x: version "3.10.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc" integrity sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA== @@ -5006,6 +5006,14 @@ js-yaml@^3.12.0: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^3.13.0, js-yaml@^3.9.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + js-yaml@~3.7.0: version "3.7.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80" @@ -5071,7 +5079,7 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= -json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: +json-stable-stringify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= @@ -5365,7 +5373,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.0.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.3.0: +lodash@^4.13.1, lodash@^4.15.0, lodash@^4.3.0: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" integrity sha1-eCA6TRwyiuHYbcpkYONptX9AVa4= @@ -5688,7 +5696,7 @@ mime@1.4.1, mime@^1.3.4: resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== -mime@^1.4.1: +mime@^1.4.1, mime@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== @@ -5891,22 +5899,12 @@ mute-stdout@^1.0.0: resolved "https://registry.yarnpkg.com/mute-stdout/-/mute-stdout-1.0.1.tgz#acb0300eb4de23a7ddeec014e3e96044b3472331" integrity sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg== -mute-stream@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" - integrity sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA= - mute-stream@0.0.7, 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= -nan@2.12.1, nan@^2.12.1: - version "2.12.1" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.12.1.tgz#7b1aa193e9aa86057e3c7bbd0ac448e770925552" - integrity sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw== - -nan@2.8.0, nan@^2.8.0: +nan@2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a" integrity sha1-7XFfP+neArV6XmJS2QqWZ14fCFo= @@ -5916,6 +5914,16 @@ nan@^2.10.0: resolved "https://registry.yarnpkg.com/nan/-/nan-2.11.0.tgz#574e360e4d954ab16966ec102c0c049fd961a099" integrity sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw== +nan@^2.12.1: + version "2.12.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.12.1.tgz#7b1aa193e9aa86057e3c7bbd0ac448e770925552" + integrity sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw== + +nan@^2.13.2, nan@^2.14.0: + version "2.14.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + nan@^2.9.2, nan@~2.10.0: version "2.10.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" @@ -5943,10 +5951,10 @@ native-is-elevated@^0.2.1: resolved "https://registry.yarnpkg.com/native-is-elevated/-/native-is-elevated-0.2.1.tgz#70a2123a8575b9f624a3ef465d98cb74ae017385" integrity sha1-cKISOoV1ufYko+9GXZjLdK4Bc4U= -native-keymap@1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/native-keymap/-/native-keymap-1.2.5.tgz#1035a9417b9a9340cf8097763a43c76d588165a5" - integrity sha1-EDWpQXuak0DPgJd2OkPHbViBZaU= +native-keymap@1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/native-keymap/-/native-keymap-1.2.6.tgz#93d1b4c4ae0e9136bc14538cafe02c0bbe95bebf" + integrity sha512-8hEr6wNkb7OmGPFLFk1cAsnOt2Y3F4mtBffr8uOyX0kKOjr2JVetSt9TKjk0xyJw/B/HcEgMhXmjFKzGN+9JjA== native-watchdog@1.0.0: version "1.0.0" @@ -6039,12 +6047,12 @@ node-pre-gyp@^0.10.0: semver "^5.3.0" tar "^4" -node-pty@0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.8.1.tgz#94b457bec013e7a09b8d9141f63b0787fa25c23f" - integrity sha512-j+/g0Q5dR+vkELclpJpz32HcS3O/3EdPSGPvDXJZVJQLCvgG0toEbfmymxAEyQyZEpaoKHAcoL+PvKM+4N9nlw== +node-pty@0.9.0-beta17: + version "0.9.0-beta17" + resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.9.0-beta17.tgz#9b490df86a8124dea595e9fbedeaaf4b2eedbbcb" + integrity sha512-E94XwIs3JxLKAboquHY9Kytbbj/T/tJtRpQoAUdfPE7UXRta/NV+xdmRNhZkeU9jCji+plm656GbYFievgNPkQ== dependencies: - nan "2.12.1" + nan "^2.13.2" node.extend@~1.1.2: version "1.1.6" @@ -6305,11 +6313,6 @@ once@1.x, once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0: dependencies: wrappy "1" -onetime@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" - integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= - onetime@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" @@ -6317,6 +6320,11 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" +onigasm-umd@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/onigasm-umd/-/onigasm-umd-2.2.2.tgz#b989d762df61f899a3052ac794a50bd93fe20257" + integrity sha512-v2eMOJu7iE444L2iJN+U6s6s5S0y7oj/N0DAkrd6wokRtTVoq/v/yaDI1lIqFrTeJbNtqNzYvguDF5yNzW3Rvw== + oniguruma@^7.0.0: version "7.0.2" resolved "https://registry.yarnpkg.com/oniguruma/-/oniguruma-7.0.2.tgz#a5c922cf7066da1dbcc60f6385a90437a83f8d0b" @@ -6324,6 +6332,18 @@ oniguruma@^7.0.0: dependencies: nan "^2.10.0" +opener@~1.4.0: + version "1.4.3" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8" + integrity sha1-XG2ixdflgx6P+jlklQ+NZnSskLg= + +opn@^5.4.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" + integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== + dependencies: + is-wsl "^1.1.0" + optimist@0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.3.5.tgz#03654b52417030312d109f39b159825b60309304" @@ -6331,7 +6351,7 @@ optimist@0.3.5: dependencies: wordwrap "~0.0.2" -optimist@^0.6.1: +optimist@0.6.x, optimist@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= @@ -6751,10 +6771,19 @@ plugin-error@1.0.1, plugin-error@^1.0.1: arr-union "^3.1.0" extend-shallow "^3.0.2" -pluralize@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" - integrity sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU= +pluralize@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" + integrity sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow== + +portfinder@^1.0.13: + version "1.0.20" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.20.tgz#bea68632e54b2e13ab7b0c4775e9b41bf270e44a" + integrity sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw== + dependencies: + async "^1.5.2" + debug "^2.2.0" + mkdirp "0.5.x" posix-character-classes@^0.1.0: version "0.1.1" @@ -7198,6 +7227,11 @@ qs@6.5.1, qs@~6.5.1: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" integrity sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A== +qs@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-2.3.3.tgz#e9e85adbe75da0bbe4c8e0476a086290f863b404" + integrity sha1-6eha2+ddoLvkyOBHaghikPhjtAQ= + qs@~6.3.0: version "6.3.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c" @@ -7425,15 +7459,6 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" -readline2@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35" - integrity sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU= - 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" @@ -7481,6 +7506,11 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexpp@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab" + integrity sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw== + regexpp@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" @@ -7659,7 +7689,7 @@ require-main-filename@^1.0.1: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= -require-uncached@^1.0.2: +require-uncached@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" integrity sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM= @@ -7667,6 +7697,11 @@ require-uncached@^1.0.2: caller-path "^0.1.0" resolve-from "^1.0.0" +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" @@ -7728,14 +7763,6 @@ resolve@^1.4.0: dependencies: path-parse "^1.0.6" -restore-cursor@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" - integrity sha1-NGYfRohjJ/7SmRR5FSJS35LapUE= - dependencies: - exit-hook "^1.0.0" - onetime "^1.0.0" - restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -7790,13 +7817,6 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -run-async@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" - integrity sha1-yK1KXhEGYeQCp9IbUw4AnyX444k= - dependencies: - once "^1.3.0" - run-async@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" @@ -7811,10 +7831,17 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rx-lite@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" - integrity sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI= +rx-lite-aggregates@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" + integrity sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74= + dependencies: + rx-lite "*" + +rx-lite@*, rx-lite@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" + integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ= rxjs@^6.1.0: version "6.2.2" @@ -8011,15 +8038,6 @@ shebang-regex@^1.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= -shelljs@^0.7.5: - version "0.7.8" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.8.tgz#decbcf874b0d1e5fb72e14b164a9683048e9acb3" - integrity sha1-3svPh0sNHl+3LhSxZKloMEjprLM= - 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" @@ -8066,10 +8084,12 @@ slash@^1.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= -slice-ansi@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" - integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= +slice-ansi@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" + integrity sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg== + dependencies: + is-fullwidth-code-point "^2.0.0" slice-ansi@^2.0.0: version "2.1.0" @@ -8173,18 +8193,18 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= -source-map@0.4.x, source-map@^0.4.4: +source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" integrity sha1-66T12pwNyZneaAMti092FzZSA2s= dependencies: amdefine ">=0.0.4" -source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - 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" @@ -8202,14 +8222,14 @@ sparkles@^1.0.0: resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.0.tgz#1acbbfb592436d10bbe8f785b7cc6f82815012c3" integrity sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM= -spdlog@0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/spdlog/-/spdlog-0.8.1.tgz#dfb3f3422ab3efe32be79e4769b95440ed72699f" - integrity sha512-W0s8IOXpn86md+8PJ4mJeB/22thykzH5YaNc3Rgnql4x4/zFIhvNiEx6/a1arnqvmJF0HtRO0Ehlswg0WcwTLQ== +spdlog@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/spdlog/-/spdlog-0.9.0.tgz#c85dd9d0b9cd385f6f3f5b92dc9d2e1691092b5c" + integrity sha512-AeLWPCYjGi4w5DfpXFKb9pCdgMe4gFBMroGfgwXiNfzwmcNYGoFQkIuD1MChZBR1Iwrx0nGhsTSHFslt/qfTAQ== dependencies: - bindings "^1.3.0" + bindings "^1.5.0" mkdirp "^0.5.1" - nan "^2.8.0" + nan "^2.14.0" spdx-correct@~1.0.0: version "1.0.2" @@ -8457,11 +8477,6 @@ strip-bom@^2.0.0: dependencies: 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" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= - strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" @@ -8550,17 +8565,17 @@ svgo@^0.7.0: 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" - integrity sha1-K7xULw/amGGnVdOUf+/Ys/UThV8= +table@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" + integrity sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA== 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" + ajv "^5.2.3" + ajv-keywords "^2.1.0" + chalk "^2.1.0" + lodash "^4.17.4" + slice-ansi "1.0.0" + string-width "^2.1.1" table@^5.0.2: version "5.2.2" @@ -8851,25 +8866,26 @@ tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== -tslint@^5.11.0: - version "5.11.0" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.11.0.tgz#98f30c02eae3cde7006201e4c33cb08b48581eed" - integrity sha1-mPMMAurjzecAYgHkwzywi0hYHu0= +tslint@^5.16.0: + version "5.16.0" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.16.0.tgz#ae61f9c5a98d295b9a4f4553b1b1e831c1984d67" + integrity sha512-UxG2yNxJ5pgGwmMzPMYh/CCnCnh0HfPgtlVRDs1ykZklufFBL1ZoTlWFRz2NQjcoEiDoRp+JyT0lhBbbH/obyA== dependencies: - babel-code-frame "^6.22.0" + "@babel/code-frame" "^7.0.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" + js-yaml "^3.13.0" minimatch "^3.0.4" + mkdirp "^0.5.1" resolve "^1.3.2" semver "^5.3.0" tslib "^1.8.0" - tsutils "^2.27.2" + tsutils "^2.29.0" -tsutils@^2.27.2: +tsutils@^2.29.0: version "2.29.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== @@ -8946,18 +8962,10 @@ typescript-formatter@7.1.0: commandpost "^1.0.0" editorconfig "^0.15.0" -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.4.5: - version "3.4.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.5.tgz#2d2618d10bb566572b8d7aad5180d84257d70a99" - integrity sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw== +typescript@3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.2.tgz#a09e1dc69bc9551cadf17dba10ee42cf55e5d56c" + integrity sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA== typescript@^2.6.2: version "2.6.2" @@ -9067,6 +9075,13 @@ union-value@^1.0.0: is-extendable "^0.1.1" set-value "^0.4.3" +union@~0.4.3: + version "0.4.6" + resolved "https://registry.yarnpkg.com/union/-/union-0.4.6.tgz#198fbdaeba254e788b0efcb630bc11f24a2959e0" + integrity sha1-GY+9rrolTniLDvy2MLwR8kopWeA= + dependencies: + qs "~2.3.3" + uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" @@ -9146,6 +9161,11 @@ url-join@^1.1.0: resolved "https://registry.yarnpkg.com/url-join/-/url-join-1.1.0.tgz#741c6c2f4596c4830d6718460920d0c92202dc78" integrity sha1-dBxsL0WWxIMNZxhGCSDQySIC3Hg= +url-join@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/url-join/-/url-join-2.0.5.tgz#5af22f18c052a000a48d7b82c5e9c2e2feeda728" + integrity sha1-WvIvGMBSoACkjXuCxenC4v7tpyg= + url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" @@ -9159,13 +9179,6 @@ use@^3.1.0: resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== -user-home@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" - integrity sha1-nHC/2Babwdy/SGBODwS4tJzenp8= - dependencies: - os-homedir "^1.0.0" - 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" @@ -9427,10 +9440,10 @@ vscode-chokidar@1.6.5: optionalDependencies: vscode-fsevents "0.3.10" -vscode-debugprotocol@1.34.0: - version "1.34.0" - resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.34.0.tgz#aef63274166ccbc6d1d68e68c7d7f6d013802f08" - integrity sha512-tcMThtgk9TUtE8zzAIwPvHZfgnEYnVa7cI3YaQk/o54Q9cme+TLd/ao60a6ycj5rCrI/B5r/mAfeK5EKSItm7g== +vscode-debugprotocol@1.35.0: + version "1.35.0" + resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.35.0.tgz#565140cd42945e30c6c85cafb38c631457d4a46c" + integrity sha512-+OMm11R1bGYbpIJ5eQIkwoDGFF4GvBz3Ztl6/VM+/RNNb2Gjk2c0Ku+oMmfhlTmTlPCpgHBsH4JqVCbUYhu5bA== vscode-fsevents@0.3.10: version "0.3.10" @@ -9439,32 +9452,6 @@ vscode-fsevents@0.3.10: 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.5: version "3.2.5" resolved "https://registry.yarnpkg.com/vscode-nls-dev/-/vscode-nls-dev-3.2.5.tgz#bea2b6e0cae709c48144180585e1a511edc9fb8d" @@ -9483,10 +9470,10 @@ vscode-nls-dev@3.2.5: xml2js "^0.4.19" yargs "^10.1.1" -vscode-nsfw@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/vscode-nsfw/-/vscode-nsfw-1.1.1.tgz#7c3febe153677c5850b197a0b64a197cd11e95c7" - integrity sha512-Wg3vzN1U3T6P1uE13LdVVRIhdy7XWnWkwmAXhkLsIkH2QY0E/pvNDRLrwAMMW6GC1Fvvbxm3hzdIrCmr7Hq3FA== +vscode-nsfw@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vscode-nsfw/-/vscode-nsfw-1.1.2.tgz#9cb9073b5854386801afe41f7152f721b4ea9e80" + integrity sha512-J0So+JNK/5kQboTO1hKNk4ie/wwUegrJilYSY5sVxU9JJlo3aQdP0zi2NtU8CEK3kkN6qRp0MbXCzbT0LKGorg== dependencies: fs-extra "^7.0.0" lodash.isinteger "^4.0.4" @@ -9515,18 +9502,13 @@ vscode-sqlite3@4.0.7: dependencies: nan "~2.10.0" -vscode-textmate@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-4.0.1.tgz#6c36f28e9059ce12bc34907f7a33ea43166b26a8" - integrity sha512-gHTXTj04TUgbjB8y7pkVwxOiuCuD6aU5gnFzIByQuqdgFpe/bJaaEIS4geGjbjWbd1XJh6zG1EthLfpNaXEqUw== +vscode-textmate@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-4.1.1.tgz#857e836fbc13a376ec624242437e1747d79610a9" + integrity sha512-xBjq9LH6fMhWDhIVkbKlB1JeCu6lT3FI/QKN24Xi4RKPBUm16IhHTqs6Q6SUGewkNsFZGkb1tJdZsuMnlmVpgw== dependencies: oniguruma "^7.0.0" -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-windows-ca-certs@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/vscode-windows-ca-certs/-/vscode-windows-ca-certs-0.1.0.tgz#d58eeb40b536130918cfde2b01e6dc7e5c1bd757" @@ -9541,11 +9523,6 @@ vscode-windows-registry@1.0.1: dependencies: nan "^2.12.1" -vscode-xterm@3.13.0-beta3: - version "3.13.0-beta3" - resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.13.0-beta3.tgz#ab642ed77df07c2adfca7b15ae39c18328db3fc6" - integrity sha512-XvgD/P6CCV0+79UYM7CEL6Ziv2RiDopI3Wa1hIm3Dm8InWxkl3QwykINlempMNub+r0gwVwLKSQYr+Q/jg1IAw== - 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" @@ -9815,6 +9792,21 @@ xtend@~2.1.1: dependencies: object-keys "~0.4.0" +xterm-addon-search@0.1.0-beta6: + version "0.1.0-beta6" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.1.0-beta6.tgz#e2a2b441f8f7b0245c63731d0b2af32c7d4e6747" + integrity sha512-XKxdfO48HkCJW2m1wXW0PK/BOk00WEaN+W2LgDQqCBwwUjyBzWc9HaV8gzLXhSCDAYesWvtQa3RfqHfSp9qsbQ== + +xterm-addon-web-links@0.1.0-beta10: + version "0.1.0-beta10" + resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.1.0-beta10.tgz#610fa9773a2a5ccd41c1c83ba0e2dd2c9eb66a23" + integrity sha512-xfpjy0V6bB4BR44qIgZQPoCMVakxb65gMscPkHpO//QxvUxKzabV3dxOsIbeZRFkUGsWTFlvz2OoaBLoNtv5gg== + +xterm@3.15.0-beta50: + version "3.15.0-beta50" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.15.0-beta50.tgz#6413057fe36ff5808a41eba337f83076144d5c10" + integrity sha512-LAJ8kP3U8oXnVR3uaL4NxNFKcFDt3Zoec53hgYppwW8P5LdmL/dc0eDpgqiEsPLx4GgD37d8J92EcoMzKs2vVw== + y18n@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" @@ -9928,7 +9920,7 @@ yauzl@2.4.1: dependencies: fd-slicer "~1.0.1" -yauzl@^2.2.1, yauzl@^2.3.1, yauzl@^2.9.1: +yauzl@^2.2.1, yauzl@^2.3.1: version "2.9.1" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.9.1.tgz#a81981ea70a57946133883f029c5821a89359a7f" integrity sha1-qBmB6nCleUYTOIPwKcWCGok1mn8= @@ -9936,6 +9928,14 @@ yauzl@^2.2.1, yauzl@^2.3.1, yauzl@^2.9.1: buffer-crc32 "~0.2.3" fd-slicer "~1.0.1" +yauzl@^2.9.2: + 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.1, yazl@^2.2.2, yazl@^2.4.3: version "2.4.3" resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.4.3.tgz#ec26e5cc87d5601b9df8432dbdd3cd2e5173a071"