diff --git a/.github/calendar.yml b/.github/calendar.yml index a08deeaee6c..6d3e0f593d7 100644 --- a/.github/calendar.yml +++ b/.github/calendar.yml @@ -21,5 +21,9 @@ '2018-05-15 12:00, US/Pacific': 'development', '2018-05-28 18:00, US/Pacific': 'endgame', # 'release' not needed anymore, return to 'development' after releasing. - '2018-06-06 12:00, US/Pacific': 'development', + '2018-06-06 12:00, US/Pacific': 'development', # 1.24.0 released + '2018-06-25 18:00, US/Pacific': 'endgame', + '2018-07-05 12:00, US/Pacific': 'development', # 1.25.0 released + '2018-07-30 18:00, US/Pacific': 'endgame', + # '2018-08-08 12:00, US/Pacific': 'development', # 1.26.0 released } diff --git a/.gitignore b/.gitignore index 0b257508fe9..08adb4af663 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ node_modules/ out/ out-build/ out-editor/ +out-editor-src/ +out-editor-build/ out-editor-esm/ out-editor-min/ out-monaco-editor-core/ diff --git a/OSSREADME.json b/OSSREADME.json index 8f167afc904..ddfe2a1fa05 100644 --- a/OSSREADME.json +++ b/OSSREADME.json @@ -2,7 +2,7 @@ [ { "name": "chromium", - "version": "58.0.3029.110", + "version": "61.0.3163.100", "repositoryURL": "http://www.chromium.org/Home", "licenseDetail": [ "BSD License", @@ -38,20 +38,20 @@ }, { "name": "libchromiumcontent", - "version": "58.0.3029.110", + "version": "61.0.3163.100", "license": "MIT", "repositoryURL": "https://github.com/electron/libchromiumcontent", "isProd": true }, { "name": "nodejs", - "version": "7.9.0", + "version": "8.9.3", "repositoryURL": "https://github.com/nodejs/node", "isProd": true }, { "name": "electron", - "version": "1.7.3", + "version": "2.0.5", "license": "MIT", "repositoryURL": "https://github.com/electron/electron", "isProd": true diff --git a/README.md b/README.md index 4c4f434141d..b2ba4ca1dcb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # Visual Studio Code - Open Source [![Build Status](https://vscode.visualstudio.com/_apis/public/build/definitions/a4cdce18-a05c-4bb8-9476-5d07e63bfd76/1/badge?branch=master)](https://aka.ms/vscode-builds) -[![Coverage Status](https://img.shields.io/coveralls/Microsoft/vscode/master.svg)](https://coveralls.io/github/Microsoft/vscode?branch=master) [![Gitter](https://img.shields.io/badge/chat-on%20gitter-blue.svg)](https://gitter.im/Microsoft/vscode) [VS Code](https://code.visualstudio.com) is a new type of tool that combines the simplicity of diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index b2fe9e4c585..80e9fd6e808 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -21,48 +21,51 @@ This project incorporates components from the projects listed below. The origina 14. davidrios/pug-tmbundle (https://github.com/davidrios/pug-tmbundle) 15. definitelytyped (https://github.com/DefinitelyTyped/DefinitelyTyped) 16. demyte/language-cshtml (https://github.com/demyte/language-cshtml) -17. dotnet/csharp-tmLanguage version 0.1.0 (https://github.com/dotnet/csharp-tmLanguage) -18. expand-abbreviation version 0.5.8 (https://github.com/emmetio/expand-abbreviation) -19. fadeevab/make.tmbundle (https://github.com/fadeevab/make.tmbundle) -20. freebroccolo/atom-language-swift (https://github.com/freebroccolo/atom-language-swift) -21. HTML 5.1 W3C Working Draft version 08 October 2015 (http://www.w3.org/TR/2015/WD-html51-20151008/) -22. Ikuyadeu/vscode-R (https://github.com/Ikuyadeu/vscode-R) -23. Ionic documentation version 1.2.4 (https://github.com/ionic-team/ionic-site) -24. ionide/ionide-fsgrammar (https://github.com/ionide/ionide-fsgrammar) -25. js-beautify version 1.6.8 (https://github.com/beautify-web/js-beautify) -26. Jxck/assert version 1.0.0 (https://github.com/Jxck/assert) -27. language-docker (https://github.com/moby/moby) -28. language-go version 0.39.0 (https://github.com/atom/language-go) -29. language-less (https://github.com/atom/language-less) -30. language-php (https://github.com/atom/language-php) -31. language-rust version 0.4.9 (https://github.com/zargony/atom-language-rust) -32. MagicStack/MagicPython (https://github.com/MagicStack/MagicPython) -33. mdn-data version 1.1.12 (https://github.com/mdn/data) -34. Microsoft/TypeScript-TmLanguage version 0.0.1 (https://github.com/Microsoft/TypeScript-TmLanguage) -35. Microsoft/vscode-JSON.tmLanguage (https://github.com/Microsoft/vscode-JSON.tmLanguage) -36. Microsoft/vscode-mssql (https://github.com/Microsoft/vscode-mssql) -37. mmims/language-batchfile (https://github.com/mmims/language-batchfile) -38. octicons-code version 3.1.0 (https://octicons.github.com) -39. octicons-font version 3.1.0 (https://octicons.github.com) -40. PowerShell/EditorSyntax (https://github.com/powershell/editorsyntax) -41. seti-ui version 0.1.0 (https://github.com/jesseweed/seti-ui) -42. shaders-tmLanguage version 0.1.0 (https://github.com/tgjones/shaders-tmLanguage) -43. textmate/asp.vb.net.tmbundle (https://github.com/textmate/asp.vb.net.tmbundle) -44. textmate/c.tmbundle (https://github.com/textmate/c.tmbundle) -45. textmate/diff.tmbundle (https://github.com/textmate/diff.tmbundle) -46. textmate/git.tmbundle (https://github.com/textmate/git.tmbundle) -47. textmate/groovy.tmbundle (https://github.com/textmate/groovy.tmbundle) -48. textmate/html.tmbundle (https://github.com/textmate/html.tmbundle) -49. textmate/ini.tmbundle (https://github.com/textmate/ini.tmbundle) -50. textmate/javascript.tmbundle (https://github.com/textmate/javascript.tmbundle) -51. textmate/lua.tmbundle (https://github.com/textmate/lua.tmbundle) -52. textmate/markdown.tmbundle (https://github.com/textmate/markdown.tmbundle) -53. textmate/perl.tmbundle (https://github.com/textmate/perl.tmbundle) -54. textmate/ruby.tmbundle (https://github.com/textmate/ruby.tmbundle) -55. textmate/yaml.tmbundle (https://github.com/textmate/yaml.tmbundle) -56. TypeScript-TmLanguage version 0.1.8 (https://github.com/Microsoft/TypeScript-TmLanguage) -57. vscode-logfile-highlighter version 1.2.0 (https://github.com/emilast/vscode-logfile-highlighter) -58. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) +17. Document Object Model () +18. dotnet/csharp-tmLanguage version 0.1.0 (https://github.com/dotnet/csharp-tmLanguage) +19. expand-abbreviation version 0.5.8 (https://github.com/emmetio/expand-abbreviation) +20. fadeevab/make.tmbundle (https://github.com/fadeevab/make.tmbundle) +21. freebroccolo/atom-language-swift (https://github.com/freebroccolo/atom-language-swift) +22. HTML 5.1 W3C Working Draft version 08 October 2015 (http://www.w3.org/TR/2015/WD-html51-20151008/) +23. Ikuyadeu/vscode-R (https://github.com/Ikuyadeu/vscode-R) +24. Ionic documentation version 1.2.4 (https://github.com/ionic-team/ionic-site) +25. ionide/ionide-fsgrammar (https://github.com/ionide/ionide-fsgrammar) +26. js-beautify version 1.6.8 (https://github.com/beautify-web/js-beautify) +27. Jxck/assert version 1.0.0 (https://github.com/Jxck/assert) +28. language-docker (https://github.com/moby/moby) +29. language-go version 0.39.0 (https://github.com/atom/language-go) +30. language-less (https://github.com/atom/language-less) +31. language-php (https://github.com/atom/language-php) +32. language-rust version 0.4.9 (https://github.com/zargony/atom-language-rust) +33. MagicStack/MagicPython (https://github.com/MagicStack/MagicPython) +34. mdn-data version 1.1.12 (https://github.com/mdn/data) +35. Microsoft/TypeScript-TmLanguage version 0.0.1 (https://github.com/Microsoft/TypeScript-TmLanguage) +36. Microsoft/vscode-JSON.tmLanguage (https://github.com/Microsoft/vscode-JSON.tmLanguage) +37. Microsoft/vscode-mssql (https://github.com/Microsoft/vscode-mssql) +38. mmims/language-batchfile (https://github.com/mmims/language-batchfile) +39. octicons-code version 3.1.0 (https://octicons.github.com) +40. octicons-font version 3.1.0 (https://octicons.github.com) +41. PowerShell/EditorSyntax (https://github.com/powershell/editorsyntax) +42. seti-ui version 0.1.0 (https://github.com/jesseweed/seti-ui) +43. shaders-tmLanguage version 0.1.0 (https://github.com/tgjones/shaders-tmLanguage) +44. textmate/asp.vb.net.tmbundle (https://github.com/textmate/asp.vb.net.tmbundle) +45. textmate/c.tmbundle (https://github.com/textmate/c.tmbundle) +46. textmate/diff.tmbundle (https://github.com/textmate/diff.tmbundle) +47. textmate/git.tmbundle (https://github.com/textmate/git.tmbundle) +48. textmate/groovy.tmbundle (https://github.com/textmate/groovy.tmbundle) +49. textmate/html.tmbundle (https://github.com/textmate/html.tmbundle) +50. textmate/ini.tmbundle (https://github.com/textmate/ini.tmbundle) +51. textmate/javascript.tmbundle (https://github.com/textmate/javascript.tmbundle) +52. textmate/lua.tmbundle (https://github.com/textmate/lua.tmbundle) +53. textmate/markdown.tmbundle (https://github.com/textmate/markdown.tmbundle) +54. textmate/perl.tmbundle (https://github.com/textmate/perl.tmbundle) +55. textmate/ruby.tmbundle (https://github.com/textmate/ruby.tmbundle) +56. textmate/yaml.tmbundle (https://github.com/textmate/yaml.tmbundle) +57. TypeScript-TmLanguage version 0.1.8 (https://github.com/Microsoft/TypeScript-TmLanguage) +58. Unicode () +59. vscode-logfile-highlighter version 1.2.0 (https://github.com/emilast/vscode-logfile-highlighter) +60. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) +61. Web Background Synchronization (https://github.com/WICG/BackgroundSync) %% atom/language-c NOTICES AND INFORMATION BEGIN HERE @@ -600,6 +603,27 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ========================================= END OF demyte/language-cshtml NOTICES AND INFORMATION +%% Document Object Model NOTICES AND INFORMATION BEGIN HERE +========================================= +W3C License +This work is being provided by the copyright holders under the following license. +By obtaining and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions. +Permission to copy, modify, and distribute this work, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following +on ALL copies of the work or portions thereof, including modifications: +* The full text of this NOTICE in a location viewable to users of the redistributed or derivative work. +* Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, the W3C Software and Document Short Notice should be included. +* Notice of any changes or modifications, through a copyright statement on the new code or document such as "This software or document includes material copied from or derived +from Document Object Model. Copyright © 2015 W3C® (MIT, ERCIM, Keio, Beihang)." +Disclaimers +THIS WORK IS PROVIDED "AS IS + AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR +FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. +COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT. +The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the work without specific, written prior permission. +Title to copyright in this work will at all times remain with copyright holders. +========================================= +END OF Document Object Model NOTICES AND INFORMATION + %% dotnet/csharp-tmLanguage NOTICES AND INFORMATION BEGIN HERE ========================================= MIT License @@ -831,7 +855,7 @@ END OF ionide/ionide-fsgrammar NOTICES AND INFORMATION ========================================= The MIT License (MIT) -Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors. +Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors. 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: @@ -2225,6 +2249,66 @@ THE SOFTWARE. ========================================= END OF TypeScript-TmLanguage NOTICES AND INFORMATION +%% Unicode NOTICES AND INFORMATION BEGIN HERE +========================================= +Unicode Data Files include all data files under the directories +http://www.unicode.org/Public/, http://www.unicode.org/reports/, +http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and +http://www.unicode.org/utility/trac/browser/. + +Unicode Data Files do not include PDF online code charts under the +directory http://www.unicode.org/Public/. + +Software includes any source code published in the Unicode Standard +or under the directories +http://www.unicode.org/Public/, http://www.unicode.org/reports/, +http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and +http://www.unicode.org/utility/trac/browser/. + +NOTICE TO USER: Carefully read the following legal agreement. +BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S +DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), +YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. +IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE +THE DATA FILES OR SOFTWARE. + +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1991-2017 Unicode, Inc. All rights reserved. +Distributed under the Terms of Use in http://www.unicode.org/copyright.html. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Unicode data files and any associated documentation +(the "Data Files") or Unicode software and any associated documentation +(the "Software") to deal in the Data Files or Software +without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, and/or sell copies of +the Data Files or Software, and to permit persons to whom the Data Files +or Software are furnished to do so, provided that either +(a) this copyright and permission notice appear with all copies +of the Data Files or Software, or +(b) this copyright and permission notice appear in associated +Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT OF THIRD PARTY RIGHTS. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS +NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL +DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THE DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, +use or other dealings in these Data Files or Software without prior +written authorization of the copyright holder. +========================================= +END OF Unicode NOTICES AND INFORMATION + %% vscode-logfile-highlighter NOTICES AND INFORMATION BEGIN HERE ========================================= The MIT License (MIT) @@ -2274,4 +2358,210 @@ 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. ========================================= -END OF vscode-swift NOTICES AND INFORMATION \ No newline at end of file +END OF vscode-swift NOTICES AND INFORMATION + +%% Web Background Synchronization NOTICES AND INFORMATION BEGIN HERE +========================================= +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +========================================= +END OF Web Background Synchronization NOTICES AND INFORMATION \ No newline at end of file diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index f8a3a759f5a..e927280e32f 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -1,12 +1,12 @@ [ { "name": "ms-vscode.node-debug", - "version": "1.26.4", + "version": "1.27.1", "repo": "https://github.com/Microsoft/vscode-node-debug" }, { "name": "ms-vscode.node-debug2", - "version": "1.26.2", + "version": "1.26.8", "repo": "https://github.com/Microsoft/vscode-node-debug2" } ] diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index abfeb7e4a4b..562ee1dd754 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -12,6 +12,9 @@ const File = require('vinyl'); const i18n = require('./lib/i18n'); const standalone = require('./lib/standalone'); const cp = require('child_process'); +const compilation = require('./lib/compilation'); +const monacoapi = require('./monaco/api'); +const fs = require('fs'); var root = path.dirname(__dirname); var sha1 = util.getVersion(root); @@ -58,29 +61,56 @@ var BUNDLED_FILE_HEADER = [ '' ].join('\n'); -function editorLoaderConfig() { - var result = common.loaderConfig(); - - // never ship octicons in editor - result.paths['vs/base/browser/ui/octiconLabel/octiconLabel'] = 'out-build/vs/base/browser/ui/octiconLabel/octiconLabel.mock'; - - // force css inlining to use base64 -- see https://github.com/Microsoft/monaco-editor/issues/148 - result['vs/css'] = { - inlineResources: 'base64', - inlineResourcesLimit: 3000 // see https://github.com/Microsoft/monaco-editor/issues/336 - }; - - return result; -} - const languages = i18n.defaultLanguages.concat([]); // i18n.defaultLanguages.concat(process.env.VSCODE_QUALITY !== 'stable' ? i18n.extraLanguages : []); +gulp.task('clean-editor-src', util.rimraf('out-editor-src')); +gulp.task('extract-editor-src', ['clean-editor-src'], function () { + console.log(`If the build fails, consider tweaking shakeLevel below to a lower value.`); + const apiusages = monacoapi.execute().usageContent; + const extrausages = fs.readFileSync(path.join(root, 'build', 'monaco', 'monaco.usage.recipe')).toString(); + standalone.extractEditor({ + sourcesRoot: path.join(root, 'src'), + entryPoints: [ + 'vs/editor/editor.main', + 'vs/editor/editor.worker', + 'vs/base/worker/workerMain', + ], + inlineEntryPoints: [ + apiusages, + extrausages + ], + libs: [ + `lib.d.ts`, + `lib.es2015.collection.d.ts` + ], + redirects: { + 'vs/base/browser/ui/octiconLabel/octiconLabel': 'vs/base/browser/ui/octiconLabel/octiconLabel.mock', + }, + compilerOptions: { + module: 2, // ModuleKind.AMD + }, + shakeLevel: 2, // 0-Files, 1-InnerFile, 2-ClassMembers + importIgnorePattern: /^vs\/css!/, + destRoot: path.join(root, 'out-editor-src') + }); +}); + +// Full compile, including nls and inline sources in sourcemaps, for build +gulp.task('clean-editor-build', util.rimraf('out-editor-build')); +gulp.task('compile-editor-build', ['clean-editor-build', 'extract-editor-src'], compilation.compileTask('out-editor-src', 'out-editor-build', true)); + gulp.task('clean-optimized-editor', util.rimraf('out-editor')); -gulp.task('optimize-editor', ['clean-optimized-editor', 'compile-client-build'], common.optimizeTask({ +gulp.task('optimize-editor', ['clean-optimized-editor', 'compile-editor-build'], common.optimizeTask({ + src: 'out-editor-build', entryPoints: editorEntryPoints, otherSources: editorOtherSources, resources: editorResources, - loaderConfig: editorLoaderConfig(), + loaderConfig: { + paths: { + 'vs': 'out-editor-build/vs', + 'vscode': 'empty:' + } + }, bundleLoader: false, header: BUNDLED_FILE_HEADER, bundleInfo: true, @@ -229,7 +259,7 @@ gulp.task('editor-distro', ['clean-editor-distro', 'compile-editor-esm', 'minify }); gulp.task('analyze-editor-distro', function () { - // @ts-ignore + // @ts-ignore var bundleInfo = require('../out-editor/bundleInfo.json'); var graph = bundleInfo.graph; var bundles = bundleInfo.bundles; diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 71c9530041b..090db00ffeb 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -95,6 +95,7 @@ const BUNDLED_FILE_HEADER = [ gulp.task('clean-optimized-vscode', util.rimraf('out-vscode')); gulp.task('optimize-vscode', ['clean-optimized-vscode', 'compile-build', 'compile-extensions-build'], common.optimizeTask({ + src: 'out-build', entryPoints: vscodeEntryPoints, otherSources: [], resources: vscodeResources, diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 998ebb4f379..ad73e2dcf73 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -18,18 +18,21 @@ var _ = require("underscore"); var monacodts = require("../monaco/api"); var fs = require("fs"); var reporter = reporter_1.createReporter(); -var rootDir = path.join(__dirname, '../../src'); -var options = require('../../src/tsconfig.json').compilerOptions; -options.verbose = false; -options.sourceMap = true; -if (process.env['VSCODE_NO_SOURCEMAP']) { // To be used by developers in a hurry - options.sourceMap = false; +function getTypeScriptCompilerOptions(src) { + var rootDir = path.join(__dirname, "../../" + src); + var options = require("../../" + src + "/tsconfig.json").compilerOptions; + options.verbose = false; + options.sourceMap = true; + if (process.env['VSCODE_NO_SOURCEMAP']) { // To be used by developers in a hurry + options.sourceMap = false; + } + options.rootDir = rootDir; + options.sourceRoot = util.toFileUri(rootDir); + options.newLine = /\r\n/.test(fs.readFileSync(__filename, 'utf8')) ? 'CRLF' : 'LF'; + return options; } -options.rootDir = rootDir; -options.sourceRoot = util.toFileUri(rootDir); -options.newLine = /\r\n/.test(fs.readFileSync(__filename, 'utf8')) ? 'CRLF' : 'LF'; -function createCompile(build, emitError) { - var opts = _.clone(options); +function createCompile(src, build, emitError) { + var opts = _.clone(getTypeScriptCompilerOptions(src)); opts.inlineSources = !!build; opts.noFilesystemLookup = true; var ts = tsb.create(opts, null, null, function (err) { return reporter(err.toString()); }); @@ -51,31 +54,31 @@ function createCompile(build, emitError) { .pipe(sourcemaps.write('.', { addComment: false, includeContent: !!build, - sourceRoot: options.sourceRoot + sourceRoot: opts.sourceRoot })) .pipe(tsFilter.restore) .pipe(reporter.end(emitError)); return es.duplex(input, output); }; } -function compileTask(out, build) { +function compileTask(src, out, build) { return function () { - var compile = createCompile(build, true); - var src = es.merge(gulp.src('src/**', { base: 'src' }), gulp.src('node_modules/typescript/lib/lib.d.ts')); + var compile = createCompile(src, build, true); + var srcPipe = es.merge(gulp.src(src + "/**", { base: "" + src }), gulp.src('node_modules/typescript/lib/lib.d.ts')); // Do not write .d.ts files to disk, as they are not needed there. var dtsFilter = util.filter(function (data) { return !/\.d\.ts$/.test(data.path); }); - return src + return srcPipe .pipe(compile()) .pipe(dtsFilter) .pipe(gulp.dest(out)) .pipe(dtsFilter.restore) - .pipe(monacodtsTask(out, false)); + .pipe(src !== 'src' ? es.through() : monacodtsTask(out, false)); }; } exports.compileTask = compileTask; function watchTask(out, build) { return function () { - var compile = createCompile(build); + var compile = createCompile('src', build); var src = es.merge(gulp.src('src/**', { base: 'src' }), gulp.src('node_modules/typescript/lib/lib.d.ts')); var watchSrc = watch('src/**', { base: 'src' }); // Do not write .d.ts files to disk, as they are not needed there. @@ -122,6 +125,7 @@ function monacodtsTask(out, isWatch) { fs.writeFileSync(result.filePath, result.content); } else { + fs.writeFileSync(result.filePath, result.content); resultStream.emit('error', 'monaco.d.ts is no longer up to date. Please run gulp watch and commit the new file.'); } } diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index cedcb4155b6..33d8c111690 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -21,19 +21,22 @@ import * as fs from 'fs'; const reporter = createReporter(); -const rootDir = path.join(__dirname, '../../src'); -const options = require('../../src/tsconfig.json').compilerOptions; -options.verbose = false; -options.sourceMap = true; -if (process.env['VSCODE_NO_SOURCEMAP']) { // To be used by developers in a hurry - options.sourceMap = false; +function getTypeScriptCompilerOptions(src: string) { + const rootDir = path.join(__dirname, `../../${src}`); + const options = require(`../../${src}/tsconfig.json`).compilerOptions; + options.verbose = false; + options.sourceMap = true; + if (process.env['VSCODE_NO_SOURCEMAP']) { // To be used by developers in a hurry + options.sourceMap = false; + } + options.rootDir = rootDir; + options.sourceRoot = util.toFileUri(rootDir); + options.newLine = /\r\n/.test(fs.readFileSync(__filename, 'utf8')) ? 'CRLF' : 'LF'; + return options; } -options.rootDir = rootDir; -options.sourceRoot = util.toFileUri(rootDir); -options.newLine = /\r\n/.test(fs.readFileSync(__filename, 'utf8')) ? 'CRLF' : 'LF'; -function createCompile(build: boolean, emitError?: boolean): (token?: util.ICancellationToken) => NodeJS.ReadWriteStream { - const opts = _.clone(options); +function createCompile(src: string, build: boolean, emitError?: boolean): (token?: util.ICancellationToken) => NodeJS.ReadWriteStream { + const opts = _.clone(getTypeScriptCompilerOptions(src)); opts.inlineSources = !!build; opts.noFilesystemLookup = true; @@ -59,7 +62,7 @@ function createCompile(build: boolean, emitError?: boolean): (token?: util.ICanc .pipe(sourcemaps.write('.', { addComment: false, includeContent: !!build, - sourceRoot: options.sourceRoot + sourceRoot: opts.sourceRoot })) .pipe(tsFilter.restore) .pipe(reporter.end(emitError)); @@ -68,32 +71,32 @@ function createCompile(build: boolean, emitError?: boolean): (token?: util.ICanc }; } -export function compileTask(out: string, build: boolean): () => NodeJS.ReadWriteStream { +export function compileTask(src: string, out: string, build: boolean): () => NodeJS.ReadWriteStream { return function () { - const compile = createCompile(build, true); + const compile = createCompile(src, build, true); - const src = es.merge( - gulp.src('src/**', { base: 'src' }), + const srcPipe = es.merge( + gulp.src(`${src}/**`, { base: `${src}` }), gulp.src('node_modules/typescript/lib/lib.d.ts'), ); // Do not write .d.ts files to disk, as they are not needed there. const dtsFilter = util.filter(data => !/\.d\.ts$/.test(data.path)); - return src + return srcPipe .pipe(compile()) .pipe(dtsFilter) .pipe(gulp.dest(out)) .pipe(dtsFilter.restore) - .pipe(monacodtsTask(out, false)); + .pipe(src !== 'src' ? es.through() : monacodtsTask(out, false)); }; } export function watchTask(out: string, build: boolean): () => NodeJS.ReadWriteStream { return function () { - const compile = createCompile(build); + const compile = createCompile('src', build); const src = es.merge( gulp.src('src/**', { base: 'src' }), @@ -150,6 +153,7 @@ function monacodtsTask(out: string, isWatch: boolean): NodeJS.ReadWriteStream { if (isWatch) { fs.writeFileSync(result.filePath, result.content); } else { + fs.writeFileSync(result.filePath, result.content); resultStream.emit('error', 'monaco.d.ts is no longer up to date. Please run gulp watch and commit the new file.'); } } diff --git a/build/lib/optimize.js b/build/lib/optimize.js index 3599086a4b4..4fafbd800a9 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -37,19 +37,19 @@ function loaderConfig(emptyPaths) { } exports.loaderConfig = loaderConfig; var IS_OUR_COPYRIGHT_REGEXP = /Copyright \(C\) Microsoft Corporation/i; -function loader(bundledFileHeader, bundleLoader) { +function loader(src, bundledFileHeader, bundleLoader) { var sources = [ - 'out-build/vs/loader.js' + src + "/vs/loader.js" ]; if (bundleLoader) { sources = sources.concat([ - 'out-build/vs/css.js', - 'out-build/vs/nls.js' + src + "/vs/css.js", + src + "/vs/nls.js" ]); } var isFirst = true; return (gulp - .src(sources, { base: 'out-build' }) + .src(sources, { base: "" + src }) .pipe(es.through(function (data) { if (isFirst) { isFirst = false; @@ -71,7 +71,7 @@ function loader(bundledFileHeader, bundleLoader) { return f; }))); } -function toConcatStream(bundledFileHeader, sources, dest) { +function toConcatStream(src, bundledFileHeader, sources, dest) { var useSourcemaps = /\.js$/.test(dest) && !/\.nls\.js$/.test(dest); // If a bundle ends up including in any of the sources our copyright, then // insert a fake source at the beginning of each bundle with our copyright @@ -91,7 +91,7 @@ function toConcatStream(bundledFileHeader, sources, dest) { } var treatedSources = sources.map(function (source) { var root = source.path ? REPO_ROOT_PATH.replace(/\\/g, '/') : ''; - var base = source.path ? root + '/out-build' : ''; + var base = source.path ? root + ("/" + src) : ''; return new VinylFile({ path: source.path ? root + '/' + source.path.replace(/\\/g, '/') : 'fake', base: base, @@ -102,12 +102,13 @@ function toConcatStream(bundledFileHeader, sources, dest) { .pipe(useSourcemaps ? util.loadSourcemaps() : es.through()) .pipe(concat(dest)); } -function toBundleStream(bundledFileHeader, bundles) { +function toBundleStream(src, bundledFileHeader, bundles) { return es.merge(bundles.map(function (bundle) { - return toConcatStream(bundledFileHeader, bundle.sources, bundle.dest); + return toConcatStream(src, bundledFileHeader, bundle.sources, bundle.dest); })); } function optimizeTask(opts) { + var src = opts.src; var entryPoints = opts.entryPoints; var otherSources = opts.otherSources; var resources = opts.resources; @@ -123,7 +124,7 @@ function optimizeTask(opts) { if (err) { return bundlesStream.emit('error', JSON.stringify(err)); } - toBundleStream(bundledFileHeader, result.files).pipe(bundlesStream); + toBundleStream(src, bundledFileHeader, result.files).pipe(bundlesStream); // Remove css inlined resources var filteredResources = resources.slice(); result.cssInlinedResources.forEach(function (resource) { @@ -132,7 +133,7 @@ function optimizeTask(opts) { } filteredResources.push('!' + resource); }); - gulp.src(filteredResources, { base: 'out-build' }).pipe(resourcesStream); + gulp.src(filteredResources, { base: "" + src }).pipe(resourcesStream); var bundleInfoArray = []; if (opts.bundleInfo) { bundleInfoArray.push(new VinylFile({ @@ -145,9 +146,9 @@ function optimizeTask(opts) { }); var otherSourcesStream = es.through(); var otherSourcesStreamArr = []; - gulp.src(otherSources, { base: 'out-build' }) + gulp.src(otherSources, { base: "" + src }) .pipe(es.through(function (data) { - otherSourcesStreamArr.push(toConcatStream(bundledFileHeader, [data], data.relative)); + otherSourcesStreamArr.push(toConcatStream(src, bundledFileHeader, [data], data.relative)); }, function () { if (!otherSourcesStreamArr.length) { setTimeout(function () { otherSourcesStream.emit('end'); }, 0); @@ -156,7 +157,7 @@ function optimizeTask(opts) { es.merge(otherSourcesStreamArr).pipe(otherSourcesStream); } })); - var result = es.merge(loader(bundledFileHeader, bundleLoader), bundlesStream, otherSourcesStream, resourcesStream, bundleInfoStream); + var result = es.merge(loader(src, bundledFileHeader, bundleLoader), bundlesStream, otherSourcesStream, resourcesStream, bundleInfoStream); return result .pipe(sourcemaps.write('./', { sourceRoot: null, diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index 86e8db56a7d..78328e3ce2c 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -50,21 +50,21 @@ declare class FileSourceMap extends VinylFile { public sourceMap: sm.RawSourceMap; } -function loader(bundledFileHeader: string, bundleLoader: boolean): NodeJS.ReadWriteStream { +function loader(src: string, bundledFileHeader: string, bundleLoader: boolean): NodeJS.ReadWriteStream { let sources = [ - 'out-build/vs/loader.js' + `${src}/vs/loader.js` ]; if (bundleLoader) { sources = sources.concat([ - 'out-build/vs/css.js', - 'out-build/vs/nls.js' + `${src}/vs/css.js`, + `${src}/vs/nls.js` ]); } let isFirst = true; return ( gulp - .src(sources, { base: 'out-build' }) + .src(sources, { base: `${src}` }) .pipe(es.through(function (data) { if (isFirst) { isFirst = false; @@ -87,7 +87,7 @@ function loader(bundledFileHeader: string, bundleLoader: boolean): NodeJS.ReadWr ); } -function toConcatStream(bundledFileHeader: string, sources: bundle.IFile[], dest: string): NodeJS.ReadWriteStream { +function toConcatStream(src: string, bundledFileHeader: string, sources: bundle.IFile[], dest: string): NodeJS.ReadWriteStream { const useSourcemaps = /\.js$/.test(dest) && !/\.nls\.js$/.test(dest); // If a bundle ends up including in any of the sources our copyright, then @@ -110,7 +110,7 @@ function toConcatStream(bundledFileHeader: string, sources: bundle.IFile[], dest const treatedSources = sources.map(function (source) { const root = source.path ? REPO_ROOT_PATH.replace(/\\/g, '/') : ''; - const base = source.path ? root + '/out-build' : ''; + const base = source.path ? root + `/${src}` : ''; return new VinylFile({ path: source.path ? root + '/' + source.path.replace(/\\/g, '/') : 'fake', @@ -124,13 +124,17 @@ function toConcatStream(bundledFileHeader: string, sources: bundle.IFile[], dest .pipe(concat(dest)); } -function toBundleStream(bundledFileHeader: string, bundles: bundle.IConcatFile[]): NodeJS.ReadWriteStream { +function toBundleStream(src:string, bundledFileHeader: string, bundles: bundle.IConcatFile[]): NodeJS.ReadWriteStream { return es.merge(bundles.map(function (bundle) { - return toConcatStream(bundledFileHeader, bundle.sources, bundle.dest); + return toConcatStream(src, bundledFileHeader, bundle.sources, bundle.dest); })); } export interface IOptimizeTaskOpts { + /** + * The folder to read files from. + */ + src: string; /** * (for AMD files, will get bundled and get Copyright treatment) */ @@ -167,6 +171,7 @@ export interface IOptimizeTaskOpts { } export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStream { + const src = opts.src; const entryPoints = opts.entryPoints; const otherSources = opts.otherSources; const resources = opts.resources; @@ -183,7 +188,7 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr bundle.bundle(entryPoints, loaderConfig, function (err, result) { if (err) { return bundlesStream.emit('error', JSON.stringify(err)); } - toBundleStream(bundledFileHeader, result.files).pipe(bundlesStream); + toBundleStream(src, bundledFileHeader, result.files).pipe(bundlesStream); // Remove css inlined resources const filteredResources = resources.slice(); @@ -193,7 +198,7 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr } filteredResources.push('!' + resource); }); - gulp.src(filteredResources, { base: 'out-build' }).pipe(resourcesStream); + gulp.src(filteredResources, { base: `${src}` }).pipe(resourcesStream); const bundleInfoArray: VinylFile[] = []; if (opts.bundleInfo) { @@ -209,9 +214,9 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr const otherSourcesStream = es.through(); const otherSourcesStreamArr: NodeJS.ReadWriteStream[] = []; - gulp.src(otherSources, { base: 'out-build' }) + gulp.src(otherSources, { base: `${src}` }) .pipe(es.through(function (data) { - otherSourcesStreamArr.push(toConcatStream(bundledFileHeader, [data], data.relative)); + otherSourcesStreamArr.push(toConcatStream(src, bundledFileHeader, [data], data.relative)); }, function () { if (!otherSourcesStreamArr.length) { setTimeout(function () { otherSourcesStream.emit('end'); }, 0); @@ -221,7 +226,7 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr })); const result = es.merge( - loader(bundledFileHeader, bundleLoader), + loader(src, bundledFileHeader, bundleLoader), bundlesStream, otherSourcesStream, resourcesStream, diff --git a/build/lib/standalone.js b/build/lib/standalone.js index 12511b01d36..885d3e789f5 100644 --- a/build/lib/standalone.js +++ b/build/lib/standalone.js @@ -7,9 +7,93 @@ Object.defineProperty(exports, "__esModule", { value: true }); var ts = require("typescript"); var fs = require("fs"); var path = require("path"); +var tss = require("./treeshaking"); var REPO_ROOT = path.join(__dirname, '../../'); var SRC_DIR = path.join(REPO_ROOT, 'src'); var OUT_EDITOR = path.join(REPO_ROOT, 'out-editor'); +var dirCache = {}; +function writeFile(filePath, contents) { + function ensureDirs(dirPath) { + if (dirCache[dirPath]) { + return; + } + dirCache[dirPath] = true; + ensureDirs(path.dirname(dirPath)); + if (fs.existsSync(dirPath)) { + return; + } + fs.mkdirSync(dirPath); + } + ensureDirs(path.dirname(filePath)); + fs.writeFileSync(filePath, contents); +} +function extractEditor(options) { + var result = tss.shake(options); + for (var fileName in result) { + if (result.hasOwnProperty(fileName)) { + writeFile(path.join(options.destRoot, fileName), result[fileName]); + } + } + var copied = {}; + var copyFile = function (fileName) { + if (copied[fileName]) { + return; + } + copied[fileName] = true; + var srcPath = path.join(options.sourcesRoot, fileName); + var dstPath = path.join(options.destRoot, fileName); + writeFile(dstPath, fs.readFileSync(srcPath)); + }; + var writeOutputFile = function (fileName, contents) { + writeFile(path.join(options.destRoot, fileName), contents); + }; + for (var fileName in result) { + if (result.hasOwnProperty(fileName)) { + var fileContents = result[fileName]; + var info = ts.preProcessFile(fileContents); + for (var i = info.importedFiles.length - 1; i >= 0; i--) { + var importedFileName = info.importedFiles[i].fileName; + var importedFilePath = void 0; + if (/^vs\/css!/.test(importedFileName)) { + importedFilePath = importedFileName.substr('vs/css!'.length) + '.css'; + } + else { + importedFilePath = importedFileName; + } + if (/(^\.\/)|(^\.\.\/)/.test(importedFilePath)) { + importedFilePath = path.join(path.dirname(fileName), importedFilePath); + } + if (/\.css$/.test(importedFilePath)) { + transportCSS(importedFilePath, copyFile, writeOutputFile); + } + else { + if (fs.existsSync(path.join(options.sourcesRoot, importedFilePath + '.js'))) { + copyFile(importedFilePath + '.js'); + } + } + } + } + } + var tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.json')).toString()); + tsConfig.compilerOptions.noUnusedLocals = false; + writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); + [ + 'vs/css.build.js', + 'vs/css.d.ts', + 'vs/css.js', + 'vs/loader.js', + 'vs/monaco.d.ts', + 'vs/nls.build.js', + 'vs/nls.d.ts', + 'vs/nls.js', + 'vs/nls.mock.ts', + 'typings/lib.ie11_safe_es6.d.ts', + 'typings/thenable.d.ts', + 'typings/es6-promise.d.ts', + 'typings/require.d.ts', + ].forEach(copyFile); +} +exports.extractEditor = extractEditor; function createESMSourcesAndResources(options) { var OUT_FOLDER = path.join(REPO_ROOT, options.outFolder); var OUT_RESOURCES_FOLDER = path.join(REPO_ROOT, options.outResourcesFolder); @@ -94,7 +178,7 @@ function createESMSourcesAndResources(options) { options.entryPoints.forEach(function (entryPoint) { return enqueue(entryPoint); }); while (queue.length > 0) { var module_1 = queue.shift(); - if (transportCSS(options, module_1, enqueue, write)) { + if (transportCSS(module_1, enqueue, write)) { continue; } if (transportResource(options, module_1, enqueue, write)) { @@ -171,7 +255,7 @@ function createESMSourcesAndResources(options) { fs.writeFileSync(path.join(OUT_FOLDER, 'vs/monaco.d.ts'), monacodts); } exports.createESMSourcesAndResources = createESMSourcesAndResources; -function transportCSS(options, module, enqueue, write) { +function transportCSS(module, enqueue, write) { if (!/\.css/.test(module)) { return false; } @@ -179,10 +263,10 @@ function transportCSS(options, module, enqueue, write) { var fileContents = fs.readFileSync(filename).toString(); var inlineResources = 'base64'; // see https://github.com/Microsoft/monaco-editor/issues/148 var inlineResourcesLimit = 300000; //3000; // see https://github.com/Microsoft/monaco-editor/issues/336 - var newContents = _rewriteOrInlineUrls(filename, fileContents, inlineResources === 'base64', inlineResourcesLimit); + var newContents = _rewriteOrInlineUrls(fileContents, inlineResources === 'base64', inlineResourcesLimit); write(module, newContents); return true; - function _rewriteOrInlineUrls(originalFileFSPath, contents, forceBase64, inlineByteLimit) { + function _rewriteOrInlineUrls(contents, forceBase64, inlineByteLimit) { return _replaceURL(contents, function (url) { var imagePath = path.join(path.dirname(module), url); var fileContents = fs.readFileSync(path.join(SRC_DIR, imagePath)); diff --git a/build/lib/standalone.ts b/build/lib/standalone.ts index a402cf68405..7931737fadb 100644 --- a/build/lib/standalone.ts +++ b/build/lib/standalone.ts @@ -6,11 +6,101 @@ import * as ts from 'typescript'; import * as fs from 'fs'; import * as path from 'path'; +import * as tss from './treeshaking'; const REPO_ROOT = path.join(__dirname, '../../'); const SRC_DIR = path.join(REPO_ROOT, 'src'); const OUT_EDITOR = path.join(REPO_ROOT, 'out-editor'); +let dirCache: { [dir: string]: boolean; } = {}; + +function writeFile(filePath: string, contents: Buffer | string): void { + function ensureDirs(dirPath: string): void { + if (dirCache[dirPath]) { + return; + } + dirCache[dirPath] = true; + + ensureDirs(path.dirname(dirPath)); + if (fs.existsSync(dirPath)) { + return; + } + fs.mkdirSync(dirPath); + } + ensureDirs(path.dirname(filePath)); + fs.writeFileSync(filePath, contents); +} + +export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: string }): void { + let result = tss.shake(options); + for (let fileName in result) { + if (result.hasOwnProperty(fileName)) { + writeFile(path.join(options.destRoot, fileName), result[fileName]); + } + } + let copied: { [fileName:string]: boolean; } = {}; + const copyFile = (fileName: string) => { + if (copied[fileName]) { + return; + } + copied[fileName] = true; + const srcPath = path.join(options.sourcesRoot, fileName); + const dstPath = path.join(options.destRoot, fileName); + writeFile(dstPath, fs.readFileSync(srcPath)); + }; + const writeOutputFile = (fileName: string, contents: string) => { + writeFile(path.join(options.destRoot, fileName), contents); + }; + for (let fileName in result) { + if (result.hasOwnProperty(fileName)) { + const fileContents = result[fileName]; + const info = ts.preProcessFile(fileContents); + + for (let i = info.importedFiles.length - 1; i >= 0; i--) { + const importedFileName = info.importedFiles[i].fileName; + + let importedFilePath: string; + if (/^vs\/css!/.test(importedFileName)) { + importedFilePath = importedFileName.substr('vs/css!'.length) + '.css'; + } else { + importedFilePath = importedFileName; + } + if (/(^\.\/)|(^\.\.\/)/.test(importedFilePath)) { + importedFilePath = path.join(path.dirname(fileName), importedFilePath); + } + + if (/\.css$/.test(importedFilePath)) { + transportCSS(importedFilePath, copyFile, writeOutputFile); + } else { + if (fs.existsSync(path.join(options.sourcesRoot, importedFilePath + '.js'))) { + copyFile(importedFilePath + '.js'); + } + } + } + } + } + + const tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.json')).toString()); + tsConfig.compilerOptions.noUnusedLocals = false; + writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); + + [ + 'vs/css.build.js', + 'vs/css.d.ts', + 'vs/css.js', + 'vs/loader.js', + 'vs/monaco.d.ts', + 'vs/nls.build.js', + 'vs/nls.d.ts', + 'vs/nls.js', + 'vs/nls.mock.ts', + 'typings/lib.ie11_safe_es6.d.ts', + 'typings/thenable.d.ts', + 'typings/es6-promise.d.ts', + 'typings/require.d.ts', + ].forEach(copyFile); +} + export interface IOptions { entryPoints: string[]; outFolder: string; @@ -111,7 +201,7 @@ export function createESMSourcesAndResources(options: IOptions): void { while (queue.length > 0) { const module = queue.shift(); - if (transportCSS(options, module, enqueue, write)) { + if (transportCSS(module, enqueue, write)) { continue; } if (transportResource(options, module, enqueue, write)) { @@ -198,7 +288,7 @@ export function createESMSourcesAndResources(options: IOptions): void { } -function transportCSS(options: IOptions, module: string, enqueue: (module: string) => void, write: (path: string, contents: string | Buffer) => void): boolean { +function transportCSS(module: string, enqueue: (module: string) => void, write: (path: string, contents: string | Buffer) => void): boolean { if (!/\.css/.test(module)) { return false; @@ -209,11 +299,11 @@ function transportCSS(options: IOptions, module: string, enqueue: (module: strin const inlineResources = 'base64'; // see https://github.com/Microsoft/monaco-editor/issues/148 const inlineResourcesLimit = 300000;//3000; // see https://github.com/Microsoft/monaco-editor/issues/336 - const newContents = _rewriteOrInlineUrls(filename, fileContents, inlineResources === 'base64', inlineResourcesLimit); + const newContents = _rewriteOrInlineUrls(fileContents, inlineResources === 'base64', inlineResourcesLimit); write(module, newContents); return true; - function _rewriteOrInlineUrls(originalFileFSPath: string, contents: string, forceBase64: boolean, inlineByteLimit: number): string { + function _rewriteOrInlineUrls(contents: string, forceBase64: boolean, inlineByteLimit: number): string { return _replaceURL(contents, (url) => { let imagePath = path.join(path.dirname(module), url); let fileContents = fs.readFileSync(path.join(SRC_DIR, imagePath)); diff --git a/build/lib/treeshaking.js b/build/lib/treeshaking.js new file mode 100644 index 00000000000..c77fa128912 --- /dev/null +++ b/build/lib/treeshaking.js @@ -0,0 +1,682 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +var fs = require("fs"); +var path = require("path"); +var ts = require("typescript"); +var TYPESCRIPT_LIB_FOLDER = path.dirname(require.resolve('typescript/lib/lib.d.ts')); +var ShakeLevel; +(function (ShakeLevel) { + ShakeLevel[ShakeLevel["Files"] = 0] = "Files"; + ShakeLevel[ShakeLevel["InnerFile"] = 1] = "InnerFile"; + ShakeLevel[ShakeLevel["ClassMembers"] = 2] = "ClassMembers"; +})(ShakeLevel = exports.ShakeLevel || (exports.ShakeLevel = {})); +function shake(options) { + var languageService = createTypeScriptLanguageService(options); + markNodes(languageService, options); + return generateResult(languageService, options.shakeLevel); +} +exports.shake = shake; +//#region Discovery, LanguageService & Setup +function createTypeScriptLanguageService(options) { + // Discover referenced files + var FILES = discoverAndReadFiles(options); + // Add fake usage files + options.inlineEntryPoints.forEach(function (inlineEntryPoint, index) { + FILES["inlineEntryPoint:" + index + ".ts"] = inlineEntryPoint; + }); + // Resolve libs + var RESOLVED_LIBS = {}; + options.libs.forEach(function (filename) { + var filepath = path.join(TYPESCRIPT_LIB_FOLDER, filename); + RESOLVED_LIBS["defaultLib:" + filename] = fs.readFileSync(filepath).toString(); + }); + var host = new TypeScriptLanguageServiceHost(RESOLVED_LIBS, FILES, options.compilerOptions); + return ts.createLanguageService(host); +} +/** + * Read imports and follow them until all files have been handled + */ +function discoverAndReadFiles(options) { + var FILES = {}; + var in_queue = Object.create(null); + var queue = []; + var enqueue = function (moduleId) { + if (in_queue[moduleId]) { + return; + } + in_queue[moduleId] = true; + queue.push(moduleId); + }; + options.entryPoints.forEach(function (entryPoint) { return enqueue(entryPoint); }); + while (queue.length > 0) { + var moduleId = queue.shift(); + var dts_filename = path.join(options.sourcesRoot, moduleId + '.d.ts'); + if (fs.existsSync(dts_filename)) { + var dts_filecontents = fs.readFileSync(dts_filename).toString(); + FILES[moduleId + '.d.ts'] = dts_filecontents; + continue; + } + var ts_filename = void 0; + if (options.redirects[moduleId]) { + ts_filename = path.join(options.sourcesRoot, options.redirects[moduleId] + '.ts'); + } + else { + ts_filename = path.join(options.sourcesRoot, moduleId + '.ts'); + } + var ts_filecontents = fs.readFileSync(ts_filename).toString(); + var info = ts.preProcessFile(ts_filecontents); + for (var i = info.importedFiles.length - 1; i >= 0; i--) { + var importedFileName = info.importedFiles[i].fileName; + if (options.importIgnorePattern.test(importedFileName)) { + // Ignore vs/css! imports + continue; + } + var importedModuleId = importedFileName; + if (/(^\.\/)|(^\.\.\/)/.test(importedModuleId)) { + importedModuleId = path.join(path.dirname(moduleId), importedModuleId); + } + enqueue(importedModuleId); + } + FILES[moduleId + '.ts'] = ts_filecontents; + } + return FILES; +} +/** + * A TypeScript language service host + */ +var TypeScriptLanguageServiceHost = /** @class */ (function () { + function TypeScriptLanguageServiceHost(libs, files, compilerOptions) { + this._libs = libs; + this._files = files; + this._compilerOptions = compilerOptions; + } + // --- language service host --------------- + TypeScriptLanguageServiceHost.prototype.getCompilationSettings = function () { + return this._compilerOptions; + }; + TypeScriptLanguageServiceHost.prototype.getScriptFileNames = function () { + return ([] + .concat(Object.keys(this._libs)) + .concat(Object.keys(this._files))); + }; + TypeScriptLanguageServiceHost.prototype.getScriptVersion = function (fileName) { + return '1'; + }; + TypeScriptLanguageServiceHost.prototype.getProjectVersion = function () { + return '1'; + }; + TypeScriptLanguageServiceHost.prototype.getScriptSnapshot = function (fileName) { + if (this._files.hasOwnProperty(fileName)) { + return ts.ScriptSnapshot.fromString(this._files[fileName]); + } + else if (this._libs.hasOwnProperty(fileName)) { + return ts.ScriptSnapshot.fromString(this._libs[fileName]); + } + else { + return ts.ScriptSnapshot.fromString(''); + } + }; + TypeScriptLanguageServiceHost.prototype.getScriptKind = function (fileName) { + return ts.ScriptKind.TS; + }; + TypeScriptLanguageServiceHost.prototype.getCurrentDirectory = function () { + return ''; + }; + TypeScriptLanguageServiceHost.prototype.getDefaultLibFileName = function (options) { + return 'defaultLib:lib.d.ts'; + }; + TypeScriptLanguageServiceHost.prototype.isDefaultLibFileName = function (fileName) { + return fileName === this.getDefaultLibFileName(this._compilerOptions); + }; + return TypeScriptLanguageServiceHost; +}()); +//#endregion +//#region Tree Shaking +var NodeColor; +(function (NodeColor) { + NodeColor[NodeColor["White"] = 0] = "White"; + NodeColor[NodeColor["Gray"] = 1] = "Gray"; + NodeColor[NodeColor["Black"] = 2] = "Black"; +})(NodeColor || (NodeColor = {})); +function getColor(node) { + return node.$$$color || 0 /* White */; +} +function setColor(node, color) { + node.$$$color = color; +} +function nodeOrParentIsBlack(node) { + while (node) { + var color = getColor(node); + if (color === 2 /* Black */) { + return true; + } + node = node.parent; + } + return false; +} +function nodeOrChildIsBlack(node) { + if (getColor(node) === 2 /* Black */) { + return true; + } + for (var _i = 0, _a = node.getChildren(); _i < _a.length; _i++) { + var child = _a[_i]; + if (nodeOrChildIsBlack(child)) { + return true; + } + } + return false; +} +function markNodes(languageService, options) { + var program = languageService.getProgram(); + if (options.shakeLevel === 0 /* Files */) { + // Mark all source files Black + program.getSourceFiles().forEach(function (sourceFile) { + setColor(sourceFile, 2 /* Black */); + }); + return; + } + var black_queue = []; + var gray_queue = []; + var sourceFilesLoaded = {}; + function enqueueTopLevelModuleStatements(sourceFile) { + sourceFile.forEachChild(function (node) { + if (ts.isImportDeclaration(node)) { + if (!node.importClause && ts.isStringLiteral(node.moduleSpecifier)) { + setColor(node, 2 /* Black */); + enqueueImport(node, node.moduleSpecifier.text); + } + return; + } + if (ts.isExportDeclaration(node)) { + if (ts.isStringLiteral(node.moduleSpecifier)) { + setColor(node, 2 /* Black */); + enqueueImport(node, node.moduleSpecifier.text); + } + return; + } + if (ts.isExpressionStatement(node) + || ts.isIfStatement(node) + || ts.isIterationStatement(node, true) + || ts.isExportAssignment(node)) { + enqueue_black(node); + } + if (ts.isImportEqualsDeclaration(node)) { + if (/export/.test(node.getFullText(sourceFile))) { + // e.g. "export import Severity = BaseSeverity;" + enqueue_black(node); + } + } + }); + } + function enqueue_gray(node) { + if (nodeOrParentIsBlack(node) || getColor(node) === 1 /* Gray */) { + return; + } + setColor(node, 1 /* Gray */); + gray_queue.push(node); + } + function enqueue_black(node) { + var previousColor = getColor(node); + if (previousColor === 2 /* Black */) { + return; + } + if (previousColor === 1 /* Gray */) { + // remove from gray queue + gray_queue.splice(gray_queue.indexOf(node), 1); + setColor(node, 0 /* White */); + // add to black queue + enqueue_black(node); + // // move from one queue to the other + // black_queue.push(node); + // setColor(node, NodeColor.Black); + return; + } + if (nodeOrParentIsBlack(node)) { + return; + } + var fileName = node.getSourceFile().fileName; + if (/^defaultLib:/.test(fileName) || /\.d\.ts$/.test(fileName)) { + setColor(node, 2 /* Black */); + return; + } + var sourceFile = node.getSourceFile(); + if (!sourceFilesLoaded[sourceFile.fileName]) { + sourceFilesLoaded[sourceFile.fileName] = true; + enqueueTopLevelModuleStatements(sourceFile); + } + if (ts.isSourceFile(node)) { + return; + } + setColor(node, 2 /* Black */); + black_queue.push(node); + if (options.shakeLevel === 2 /* ClassMembers */ && (ts.isMethodDeclaration(node) || ts.isMethodSignature(node) || ts.isPropertySignature(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node))) { + var references = languageService.getReferencesAtPosition(node.getSourceFile().fileName, node.name.pos + node.name.getLeadingTriviaWidth()); + if (references) { + for (var i = 0, len = references.length; i < len; i++) { + var reference = references[i]; + var referenceSourceFile = program.getSourceFile(reference.fileName); + var referenceNode = getTokenAtPosition(referenceSourceFile, reference.textSpan.start, false, false); + if (ts.isMethodDeclaration(referenceNode.parent) + || ts.isPropertyDeclaration(referenceNode.parent) + || ts.isGetAccessor(referenceNode.parent) + || ts.isSetAccessor(referenceNode.parent)) { + enqueue_gray(referenceNode.parent); + } + } + } + } + } + function enqueueFile(filename) { + var sourceFile = program.getSourceFile(filename); + if (!sourceFile) { + console.warn("Cannot find source file " + filename); + return; + } + enqueue_black(sourceFile); + } + function enqueueImport(node, importText) { + if (options.importIgnorePattern.test(importText)) { + // this import should be ignored + return; + } + var nodeSourceFile = node.getSourceFile(); + var fullPath; + if (/(^\.\/)|(^\.\.\/)/.test(importText)) { + fullPath = path.join(path.dirname(nodeSourceFile.fileName), importText) + '.ts'; + } + else { + fullPath = importText + '.ts'; + } + enqueueFile(fullPath); + } + options.entryPoints.forEach(function (moduleId) { return enqueueFile(moduleId + '.ts'); }); + // Add fake usage files + options.inlineEntryPoints.forEach(function (_, index) { return enqueueFile("inlineEntryPoint:" + index + ".ts"); }); + var step = 0; + var checker = program.getTypeChecker(); + var _loop_1 = function () { + ++step; + var node = void 0; + if (step % 100 === 0) { + console.log(step + "/" + (step + black_queue.length + gray_queue.length) + " (" + black_queue.length + ", " + gray_queue.length + ")"); + } + if (black_queue.length === 0) { + for (var i = 0; i < gray_queue.length; i++) { + var node_1 = gray_queue[i]; + var nodeParent = node_1.parent; + if ((ts.isClassDeclaration(nodeParent) || ts.isInterfaceDeclaration(nodeParent)) && nodeOrChildIsBlack(nodeParent)) { + gray_queue.splice(i, 1); + black_queue.push(node_1); + setColor(node_1, 2 /* Black */); + i--; + } + } + } + if (black_queue.length > 0) { + node = black_queue.shift(); + } + else { + return "break"; + } + var nodeSourceFile = node.getSourceFile(); + var loop = function (node) { + var _a = getRealNodeSymbol(checker, node), symbol = _a[0], symbolImportNode = _a[1]; + if (symbolImportNode) { + setColor(symbolImportNode, 2 /* Black */); + } + if (symbol && !nodeIsInItsOwnDeclaration(nodeSourceFile, node, symbol)) { + for (var i = 0, len = symbol.declarations.length; i < len; i++) { + var declaration = symbol.declarations[i]; + if (ts.isSourceFile(declaration)) { + // Do not enqueue full source files + // (they can be the declaration of a module import) + continue; + } + if (options.shakeLevel === 2 /* ClassMembers */ && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration))) { + enqueue_black(declaration.name); + for (var j = 0; j < declaration.members.length; j++) { + var member = declaration.members[j]; + var memberName = member.name ? member.name.getText() : null; + if (ts.isConstructorDeclaration(member) + || ts.isConstructSignatureDeclaration(member) + || ts.isIndexSignatureDeclaration(member) + || ts.isCallSignatureDeclaration(member) + || memberName === 'toJSON' + || memberName === 'toString' + || memberName === 'dispose' // TODO: keeping all `dispose` methods + ) { + enqueue_black(member); + } + } + // queue the heritage clauses + if (declaration.heritageClauses) { + for (var _i = 0, _b = declaration.heritageClauses; _i < _b.length; _i++) { + var heritageClause = _b[_i]; + enqueue_black(heritageClause); + } + } + } + else { + enqueue_black(declaration); + } + } + } + node.forEachChild(loop); + }; + node.forEachChild(loop); + }; + while (black_queue.length > 0 || gray_queue.length > 0) { + var state_1 = _loop_1(); + if (state_1 === "break") + break; + } +} +function nodeIsInItsOwnDeclaration(nodeSourceFile, node, symbol) { + for (var i = 0, len = symbol.declarations.length; i < len; i++) { + var declaration = symbol.declarations[i]; + var declarationSourceFile = declaration.getSourceFile(); + if (nodeSourceFile === declarationSourceFile) { + if (declaration.pos <= node.pos && node.end <= declaration.end) { + return true; + } + } + } + return false; +} +function generateResult(languageService, shakeLevel) { + var program = languageService.getProgram(); + var result = {}; + var writeFile = function (filePath, contents) { + result[filePath] = contents; + }; + program.getSourceFiles().forEach(function (sourceFile) { + var fileName = sourceFile.fileName; + if (/^defaultLib:/.test(fileName)) { + return; + } + var destination = fileName; + if (/\.d\.ts$/.test(fileName)) { + if (nodeOrChildIsBlack(sourceFile)) { + writeFile(destination, sourceFile.text); + } + return; + } + var text = sourceFile.text; + var result = ''; + function keep(node) { + result += text.substring(node.pos, node.end); + } + function write(data) { + result += data; + } + function writeMarkedNodes(node) { + if (getColor(node) === 2 /* Black */) { + return keep(node); + } + // Always keep certain top-level statements + if (ts.isSourceFile(node.parent)) { + if (ts.isExpressionStatement(node) && ts.isStringLiteral(node.expression) && node.expression.text === 'use strict') { + return keep(node); + } + if (ts.isVariableStatement(node) && nodeOrChildIsBlack(node)) { + return keep(node); + } + } + // Keep the entire import in import * as X cases + if (ts.isImportDeclaration(node)) { + if (node.importClause && node.importClause.namedBindings) { + if (ts.isNamespaceImport(node.importClause.namedBindings)) { + if (getColor(node.importClause.namedBindings) === 2 /* Black */) { + return keep(node); + } + } + else { + var survivingImports = []; + for (var i = 0; i < node.importClause.namedBindings.elements.length; i++) { + var importNode = node.importClause.namedBindings.elements[i]; + if (getColor(importNode) === 2 /* Black */) { + survivingImports.push(importNode.getFullText(sourceFile)); + } + } + var leadingTriviaWidth = node.getLeadingTriviaWidth(); + var leadingTrivia = sourceFile.text.substr(node.pos, leadingTriviaWidth); + if (survivingImports.length > 0) { + if (node.importClause && getColor(node.importClause) === 2 /* Black */) { + return write(leadingTrivia + "import " + node.importClause.name.text + ", {" + survivingImports.join(',') + " } from" + node.moduleSpecifier.getFullText(sourceFile) + ";"); + } + return write(leadingTrivia + "import {" + survivingImports.join(',') + " } from" + node.moduleSpecifier.getFullText(sourceFile) + ";"); + } + else { + if (node.importClause && getColor(node.importClause) === 2 /* Black */) { + return write(leadingTrivia + "import " + node.importClause.name.text + " from" + node.moduleSpecifier.getFullText(sourceFile) + ";"); + } + } + } + } + else { + if (node.importClause && getColor(node.importClause) === 2 /* Black */) { + return keep(node); + } + } + } + if (shakeLevel === 2 /* ClassMembers */ && (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) && nodeOrChildIsBlack(node)) { + var toWrite = node.getFullText(); + for (var i = node.members.length - 1; i >= 0; i--) { + var member = node.members[i]; + if (getColor(member) === 2 /* Black */) { + // keep method + continue; + } + if (/^_(.*)Brand$/.test(member.name.getText())) { + // TODO: keep all members ending with `Brand`... + continue; + } + var pos = member.pos - node.pos; + var end = member.end - node.pos; + toWrite = toWrite.substring(0, pos) + toWrite.substring(end); + } + return write(toWrite); + } + if (ts.isFunctionDeclaration(node)) { + // Do not go inside functions if they haven't been marked + return; + } + node.forEachChild(writeMarkedNodes); + } + if (getColor(sourceFile) !== 2 /* Black */) { + if (!nodeOrChildIsBlack(sourceFile)) { + // none of the elements are reachable => don't write this file at all! + return; + } + sourceFile.forEachChild(writeMarkedNodes); + result += sourceFile.endOfFileToken.getFullText(sourceFile); + } + else { + result = text; + } + writeFile(destination, result); + }); + return result; +} +//#endregion +//#region Utils +/** + * Returns the node's symbol and the `import` node (if the symbol resolved from a different module) + */ +function getRealNodeSymbol(checker, node) { + /** + * Returns the containing object literal property declaration given a possible name node, e.g. "a" in x = { "a": 1 } + */ + /* @internal */ + function getContainingObjectLiteralElement(node) { + switch (node.kind) { + case ts.SyntaxKind.StringLiteral: + case ts.SyntaxKind.NumericLiteral: + if (node.parent.kind === ts.SyntaxKind.ComputedPropertyName) { + return ts.isObjectLiteralElement(node.parent.parent) ? node.parent.parent : undefined; + } + // falls through + case ts.SyntaxKind.Identifier: + return ts.isObjectLiteralElement(node.parent) && + (node.parent.parent.kind === ts.SyntaxKind.ObjectLiteralExpression || node.parent.parent.kind === ts.SyntaxKind.JsxAttributes) && + node.parent.name === node ? node.parent : undefined; + } + return undefined; + } + function getPropertySymbolsFromType(type, propName) { + function getTextOfPropertyName(name) { + function isStringOrNumericLiteral(node) { + var kind = node.kind; + return kind === ts.SyntaxKind.StringLiteral + || kind === ts.SyntaxKind.NumericLiteral; + } + switch (name.kind) { + case ts.SyntaxKind.Identifier: + return name.text; + case ts.SyntaxKind.StringLiteral: + case ts.SyntaxKind.NumericLiteral: + return name.text; + case ts.SyntaxKind.ComputedPropertyName: + return isStringOrNumericLiteral(name.expression) ? name.expression.text : undefined; + } + } + var name = getTextOfPropertyName(propName); + if (name && type) { + var result = []; + var symbol_1 = type.getProperty(name); + if (type.flags & ts.TypeFlags.Union) { + for (var _i = 0, _a = type.types; _i < _a.length; _i++) { + var t = _a[_i]; + var symbol_2 = t.getProperty(name); + if (symbol_2) { + result.push(symbol_2); + } + } + return result; + } + if (symbol_1) { + result.push(symbol_1); + return result; + } + } + return undefined; + } + function getPropertySymbolsFromContextualType(typeChecker, node) { + var objectLiteral = node.parent; + var contextualType = typeChecker.getContextualType(objectLiteral); + return getPropertySymbolsFromType(contextualType, node.name); + } + // Go to the original declaration for cases: + // + // (1) when the aliased symbol was declared in the location(parent). + // (2) when the aliased symbol is originating from an import. + // + function shouldSkipAlias(node, declaration) { + if (node.kind !== ts.SyntaxKind.Identifier) { + return false; + } + if (node.parent === declaration) { + return true; + } + switch (declaration.kind) { + case ts.SyntaxKind.ImportClause: + case ts.SyntaxKind.ImportEqualsDeclaration: + return true; + case ts.SyntaxKind.ImportSpecifier: + return declaration.parent.kind === ts.SyntaxKind.NamedImports; + default: + return false; + } + } + if (!ts.isShorthandPropertyAssignment(node)) { + if (node.getChildCount() !== 0) { + return [null, null]; + } + } + var symbol = checker.getSymbolAtLocation(node); + var importNode = null; + if (symbol && symbol.flags & ts.SymbolFlags.Alias && shouldSkipAlias(node, symbol.declarations[0])) { + var aliased = checker.getAliasedSymbol(symbol); + if (aliased.declarations) { + // We should mark the import as visited + importNode = symbol.declarations[0]; + symbol = aliased; + } + } + if (symbol) { + // Because name in short-hand property assignment has two different meanings: property name and property value, + // using go-to-definition at such position should go to the variable declaration of the property value rather than + // go to the declaration of the property name (in this case stay at the same position). However, if go-to-definition + // is performed at the location of property access, we would like to go to definition of the property in the short-hand + // assignment. This case and others are handled by the following code. + if (node.parent.kind === ts.SyntaxKind.ShorthandPropertyAssignment) { + symbol = checker.getShorthandAssignmentValueSymbol(symbol.valueDeclaration); + } + // If the node is the name of a BindingElement within an ObjectBindingPattern instead of just returning the + // declaration the symbol (which is itself), we should try to get to the original type of the ObjectBindingPattern + // and return the property declaration for the referenced property. + // For example: + // import('./foo').then(({ b/*goto*/ar }) => undefined); => should get use to the declaration in file "./foo" + // + // function bar(onfulfilled: (value: T) => void) { //....} + // interface Test { + // pr/*destination*/op1: number + // } + // bar(({pr/*goto*/op1})=>{}); + if (ts.isPropertyName(node) && ts.isBindingElement(node.parent) && ts.isObjectBindingPattern(node.parent.parent) && + (node === (node.parent.propertyName || node.parent.name))) { + var type = checker.getTypeAtLocation(node.parent.parent); + if (type) { + var propSymbols = getPropertySymbolsFromType(type, node); + if (propSymbols) { + symbol = propSymbols[0]; + } + } + } + // If the current location we want to find its definition is in an object literal, try to get the contextual type for the + // object literal, lookup the property symbol in the contextual type, and use this for goto-definition. + // For example + // interface Props{ + // /*first*/prop1: number + // prop2: boolean + // } + // function Foo(arg: Props) {} + // Foo( { pr/*1*/op1: 10, prop2: false }) + var element = getContainingObjectLiteralElement(node); + if (element && checker.getContextualType(element.parent)) { + var propertySymbols = getPropertySymbolsFromContextualType(checker, element); + if (propertySymbols) { + symbol = propertySymbols[0]; + } + } + } + if (symbol && symbol.declarations) { + return [symbol, importNode]; + } + return [null, null]; +} +/** Get the token whose text contains the position */ +function getTokenAtPosition(sourceFile, position, allowPositionInLeadingTrivia, includeEndPosition) { + var current = sourceFile; + outer: while (true) { + // find the child that contains 'position' + for (var _i = 0, _a = current.getChildren(); _i < _a.length; _i++) { + var child = _a[_i]; + var start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile, /*includeJsDoc*/ true); + if (start > position) { + // If this child begins after position, then all subsequent children will as well. + break; + } + var end = child.getEnd(); + if (position < end || (position === end && (child.kind === ts.SyntaxKind.EndOfFileToken || includeEndPosition))) { + current = child; + continue outer; + } + } + return current; + } +} diff --git a/build/lib/treeshaking.ts b/build/lib/treeshaking.ts new file mode 100644 index 00000000000..0527fe3ebce --- /dev/null +++ b/build/lib/treeshaking.ts @@ -0,0 +1,817 @@ +/*--------------------------------------------------------------------------------------------- + * 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 path from 'path'; +import * as ts from 'typescript'; + +const TYPESCRIPT_LIB_FOLDER = path.dirname(require.resolve('typescript/lib/lib.d.ts')); + +export const enum ShakeLevel { + Files = 0, + InnerFile = 1, + ClassMembers = 2 +} + +export interface ITreeShakingOptions { + /** + * The full path to the root where sources are. + */ + sourcesRoot: string; + /** + * Module ids. + * e.g. `vs/editor/editor.main` or `index` + */ + entryPoints: string[]; + /** + * Inline usages. + */ + inlineEntryPoints: string[]; + /** + * TypeScript libs. + * e.g. `lib.d.ts`, `lib.es2015.collection.d.ts` + */ + libs: string[]; + /** + * TypeScript compiler options. + */ + compilerOptions: ts.CompilerOptions; + /** + * The shake level to perform. + */ + shakeLevel: ShakeLevel; + /** + * regex pattern to ignore certain imports e.g. `vs/css!` imports + */ + importIgnorePattern: RegExp; + + redirects: { [module: string]: string; }; +} + +export interface ITreeShakingResult { + [file: string]: string; +} + +export function shake(options: ITreeShakingOptions): ITreeShakingResult { + const languageService = createTypeScriptLanguageService(options); + + markNodes(languageService, options); + + return generateResult(languageService, options.shakeLevel); +} + +//#region Discovery, LanguageService & Setup +function createTypeScriptLanguageService(options: ITreeShakingOptions): ts.LanguageService { + // Discover referenced files + const FILES = discoverAndReadFiles(options); + + // Add fake usage files + options.inlineEntryPoints.forEach((inlineEntryPoint, index) => { + FILES[`inlineEntryPoint:${index}.ts`] = inlineEntryPoint; + }); + + // Resolve libs + const RESOLVED_LIBS: ILibMap = {}; + options.libs.forEach((filename) => { + const filepath = path.join(TYPESCRIPT_LIB_FOLDER, filename); + RESOLVED_LIBS[`defaultLib:${filename}`] = fs.readFileSync(filepath).toString(); + }); + + const host = new TypeScriptLanguageServiceHost(RESOLVED_LIBS, FILES, options.compilerOptions); + return ts.createLanguageService(host); +} + +/** + * Read imports and follow them until all files have been handled + */ +function discoverAndReadFiles(options: ITreeShakingOptions): IFileMap { + const FILES: IFileMap = {}; + + const in_queue: { [module: string]: boolean; } = Object.create(null); + const queue: string[] = []; + + const enqueue = (moduleId: string) => { + if (in_queue[moduleId]) { + return; + } + in_queue[moduleId] = true; + queue.push(moduleId); + }; + + options.entryPoints.forEach((entryPoint) => enqueue(entryPoint)); + + while (queue.length > 0) { + const moduleId = queue.shift(); + const dts_filename = path.join(options.sourcesRoot, moduleId + '.d.ts'); + if (fs.existsSync(dts_filename)) { + const dts_filecontents = fs.readFileSync(dts_filename).toString(); + FILES[moduleId + '.d.ts'] = dts_filecontents; + continue; + } + + let ts_filename: string; + if (options.redirects[moduleId]) { + ts_filename = path.join(options.sourcesRoot, options.redirects[moduleId] + '.ts'); + } else { + ts_filename = path.join(options.sourcesRoot, moduleId + '.ts'); + } + const ts_filecontents = fs.readFileSync(ts_filename).toString(); + const info = ts.preProcessFile(ts_filecontents); + for (let i = info.importedFiles.length - 1; i >= 0; i--) { + const importedFileName = info.importedFiles[i].fileName; + + if (options.importIgnorePattern.test(importedFileName)) { + // Ignore vs/css! imports + continue; + } + + let importedModuleId = importedFileName; + if (/(^\.\/)|(^\.\.\/)/.test(importedModuleId)) { + importedModuleId = path.join(path.dirname(moduleId), importedModuleId); + } + enqueue(importedModuleId); + } + + FILES[moduleId + '.ts'] = ts_filecontents; + } + + return FILES; +} + +interface ILibMap { [libName: string]: string; } +interface IFileMap { [fileName: string]: string; } + +/** + * A TypeScript language service host + */ +class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost { + + private readonly _libs: ILibMap; + private readonly _files: IFileMap; + private readonly _compilerOptions: ts.CompilerOptions; + + constructor(libs: ILibMap, files: IFileMap, compilerOptions: ts.CompilerOptions) { + this._libs = libs; + this._files = files; + this._compilerOptions = compilerOptions; + } + + // --- language service host --------------- + + getCompilationSettings(): ts.CompilerOptions { + return this._compilerOptions; + } + getScriptFileNames(): string[] { + return ( + [] + .concat(Object.keys(this._libs)) + .concat(Object.keys(this._files)) + ); + } + getScriptVersion(fileName: string): string { + return '1'; + } + getProjectVersion(): string { + return '1'; + } + getScriptSnapshot(fileName: string): ts.IScriptSnapshot { + if (this._files.hasOwnProperty(fileName)) { + return ts.ScriptSnapshot.fromString(this._files[fileName]); + } else if (this._libs.hasOwnProperty(fileName)) { + return ts.ScriptSnapshot.fromString(this._libs[fileName]); + } else { + return ts.ScriptSnapshot.fromString(''); + } + } + getScriptKind(fileName: string): ts.ScriptKind { + return ts.ScriptKind.TS; + } + getCurrentDirectory(): string { + return ''; + } + getDefaultLibFileName(options: ts.CompilerOptions): string { + return 'defaultLib:lib.d.ts'; + } + isDefaultLibFileName(fileName: string): boolean { + return fileName === this.getDefaultLibFileName(this._compilerOptions); + } +} +//#endregion + +//#region Tree Shaking + +const enum NodeColor { + White = 0, + Gray = 1, + Black = 2 +} + +function getColor(node: ts.Node): NodeColor { + return (node).$$$color || NodeColor.White; +} +function setColor(node: ts.Node, color: NodeColor): void { + (node).$$$color = color; +} +function nodeOrParentIsBlack(node: ts.Node): boolean { + while (node) { + const color = getColor(node); + if (color === NodeColor.Black) { + return true; + } + node = node.parent; + } + return false; +} +function nodeOrChildIsBlack(node: ts.Node): boolean { + if (getColor(node) === NodeColor.Black) { + return true; + } + for (const child of node.getChildren()) { + if (nodeOrChildIsBlack(child)) { + return true; + } + } + return false; +} + +function markNodes(languageService: ts.LanguageService, options: ITreeShakingOptions) { + const program = languageService.getProgram(); + + if (options.shakeLevel === ShakeLevel.Files) { + // Mark all source files Black + program.getSourceFiles().forEach((sourceFile) => { + setColor(sourceFile, NodeColor.Black); + }); + return; + } + + const black_queue: ts.Node[] = []; + const gray_queue: ts.Node[] = []; + const sourceFilesLoaded: { [fileName: string]: boolean } = {}; + + function enqueueTopLevelModuleStatements(sourceFile: ts.SourceFile): void { + + sourceFile.forEachChild((node: ts.Node) => { + + if (ts.isImportDeclaration(node)) { + if (!node.importClause && ts.isStringLiteral(node.moduleSpecifier)) { + setColor(node, NodeColor.Black); + enqueueImport(node, node.moduleSpecifier.text); + } + return; + } + + if (ts.isExportDeclaration(node)) { + if (ts.isStringLiteral(node.moduleSpecifier)) { + setColor(node, NodeColor.Black); + enqueueImport(node, node.moduleSpecifier.text); + } + return; + } + + if ( + ts.isExpressionStatement(node) + || ts.isIfStatement(node) + || ts.isIterationStatement(node, true) + || ts.isExportAssignment(node) + ) { + enqueue_black(node); + } + + if (ts.isImportEqualsDeclaration(node)) { + if (/export/.test(node.getFullText(sourceFile))) { + // e.g. "export import Severity = BaseSeverity;" + enqueue_black(node); + } + } + + }); + } + + function enqueue_gray(node: ts.Node): void { + if (nodeOrParentIsBlack(node) || getColor(node) === NodeColor.Gray) { + return; + } + setColor(node, NodeColor.Gray); + gray_queue.push(node); + } + + function enqueue_black(node: ts.Node): void { + const previousColor = getColor(node); + + if (previousColor === NodeColor.Black) { + return; + } + + if (previousColor === NodeColor.Gray) { + // remove from gray queue + gray_queue.splice(gray_queue.indexOf(node), 1); + setColor(node, NodeColor.White); + + // add to black queue + enqueue_black(node); + + // // move from one queue to the other + // black_queue.push(node); + // setColor(node, NodeColor.Black); + return; + } + + if (nodeOrParentIsBlack(node)) { + return; + } + + const fileName = node.getSourceFile().fileName; + if (/^defaultLib:/.test(fileName) || /\.d\.ts$/.test(fileName)) { + setColor(node, NodeColor.Black); + return; + } + + const sourceFile = node.getSourceFile(); + if (!sourceFilesLoaded[sourceFile.fileName]) { + sourceFilesLoaded[sourceFile.fileName] = true; + enqueueTopLevelModuleStatements(sourceFile); + } + + if (ts.isSourceFile(node)) { + return; + } + + setColor(node, NodeColor.Black); + black_queue.push(node); + + if (options.shakeLevel === ShakeLevel.ClassMembers && (ts.isMethodDeclaration(node) || ts.isMethodSignature(node) || ts.isPropertySignature(node) || ts.isGetAccessor(node) || ts.isSetAccessor(node))) { + const references = languageService.getReferencesAtPosition(node.getSourceFile().fileName, node.name.pos + node.name.getLeadingTriviaWidth()); + if (references) { + for (let i = 0, len = references.length; i < len; i++) { + const reference = references[i]; + const referenceSourceFile = program.getSourceFile(reference.fileName); + const referenceNode = getTokenAtPosition(referenceSourceFile, reference.textSpan.start, false, false); + if ( + ts.isMethodDeclaration(referenceNode.parent) + || ts.isPropertyDeclaration(referenceNode.parent) + || ts.isGetAccessor(referenceNode.parent) + || ts.isSetAccessor(referenceNode.parent) + ) { + enqueue_gray(referenceNode.parent); + } + } + } + } + } + + function enqueueFile(filename: string): void { + const sourceFile = program.getSourceFile(filename); + if (!sourceFile) { + console.warn(`Cannot find source file ${filename}`); + return; + } + enqueue_black(sourceFile); + } + + function enqueueImport(node: ts.Node, importText: string): void { + if (options.importIgnorePattern.test(importText)) { + // this import should be ignored + return; + } + + const nodeSourceFile = node.getSourceFile(); + let fullPath: string; + if (/(^\.\/)|(^\.\.\/)/.test(importText)) { + fullPath = path.join(path.dirname(nodeSourceFile.fileName), importText) + '.ts'; + } else { + fullPath = importText + '.ts'; + } + enqueueFile(fullPath); + } + + options.entryPoints.forEach(moduleId => enqueueFile(moduleId + '.ts')); + // Add fake usage files + options.inlineEntryPoints.forEach((_, index) => enqueueFile(`inlineEntryPoint:${index}.ts`)); + + let step = 0; + + const checker = program.getTypeChecker(); + while (black_queue.length > 0 || gray_queue.length > 0) { + ++step; + let node: ts.Node; + + if (step % 100 === 0) { + console.log(`${step}/${step+black_queue.length+gray_queue.length} (${black_queue.length}, ${gray_queue.length})`); + } + + if (black_queue.length === 0) { + for (let i = 0; i < gray_queue.length; i++) { + const node = gray_queue[i]; + const nodeParent = node.parent; + if ((ts.isClassDeclaration(nodeParent) || ts.isInterfaceDeclaration(nodeParent)) && nodeOrChildIsBlack(nodeParent)) { + gray_queue.splice(i, 1); + black_queue.push(node); + setColor(node, NodeColor.Black); + i--; + } + } + } + + if (black_queue.length > 0) { + node = black_queue.shift(); + } else { + // only gray nodes remaining... + break; + } + const nodeSourceFile = node.getSourceFile(); + + const loop = (node: ts.Node) => { + const [symbol, symbolImportNode] = getRealNodeSymbol(checker, node); + if (symbolImportNode) { + setColor(symbolImportNode, NodeColor.Black); + } + + if (symbol && !nodeIsInItsOwnDeclaration(nodeSourceFile, node, symbol)) { + for (let i = 0, len = symbol.declarations.length; i < len; i++) { + const declaration = symbol.declarations[i]; + if (ts.isSourceFile(declaration)) { + // Do not enqueue full source files + // (they can be the declaration of a module import) + continue; + } + + if (options.shakeLevel === ShakeLevel.ClassMembers && (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration))) { + enqueue_black(declaration.name); + + for (let j = 0; j < declaration.members.length; j++) { + const member = declaration.members[j]; + const memberName = member.name ? member.name.getText() : null; + if ( + ts.isConstructorDeclaration(member) + || ts.isConstructSignatureDeclaration(member) + || ts.isIndexSignatureDeclaration(member) + || ts.isCallSignatureDeclaration(member) + || memberName === 'toJSON' + || memberName === 'toString' + || memberName === 'dispose'// TODO: keeping all `dispose` methods + ) { + enqueue_black(member); + } + } + + // queue the heritage clauses + if (declaration.heritageClauses) { + for (let heritageClause of declaration.heritageClauses) { + enqueue_black(heritageClause); + } + } + } else { + enqueue_black(declaration); + } + } + } + node.forEachChild(loop); + }; + node.forEachChild(loop); + } +} + +function nodeIsInItsOwnDeclaration(nodeSourceFile: ts.SourceFile, node: ts.Node, symbol: ts.Symbol): boolean { + for (let i = 0, len = symbol.declarations.length; i < len; i++) { + const declaration = symbol.declarations[i]; + const declarationSourceFile = declaration.getSourceFile(); + + if (nodeSourceFile === declarationSourceFile) { + if (declaration.pos <= node.pos && node.end <= declaration.end) { + return true; + } + } + } + + return false; +} + +function generateResult(languageService: ts.LanguageService, shakeLevel: ShakeLevel): ITreeShakingResult { + const program = languageService.getProgram(); + + let result: ITreeShakingResult = {}; + const writeFile = (filePath: string, contents: string): void => { + result[filePath] = contents; + }; + + program.getSourceFiles().forEach((sourceFile) => { + const fileName = sourceFile.fileName; + if (/^defaultLib:/.test(fileName)) { + return; + } + const destination = fileName; + if (/\.d\.ts$/.test(fileName)) { + if (nodeOrChildIsBlack(sourceFile)) { + writeFile(destination, sourceFile.text); + } + return; + } + + let text = sourceFile.text; + let result = ''; + + function keep(node: ts.Node): void { + result += text.substring(node.pos, node.end); + } + function write(data: string): void { + result += data; + } + + function writeMarkedNodes(node: ts.Node): void { + if (getColor(node) === NodeColor.Black) { + return keep(node); + } + + // Always keep certain top-level statements + if (ts.isSourceFile(node.parent)) { + if (ts.isExpressionStatement(node) && ts.isStringLiteral(node.expression) && node.expression.text === 'use strict') { + return keep(node); + } + + if (ts.isVariableStatement(node) && nodeOrChildIsBlack(node)) { + return keep(node); + } + } + + // Keep the entire import in import * as X cases + if (ts.isImportDeclaration(node)) { + if (node.importClause && node.importClause.namedBindings) { + if (ts.isNamespaceImport(node.importClause.namedBindings)) { + if (getColor(node.importClause.namedBindings) === NodeColor.Black) { + return keep(node); + } + } else { + let survivingImports: string[] = []; + for (let i = 0; i < node.importClause.namedBindings.elements.length; i++) { + const importNode = node.importClause.namedBindings.elements[i]; + if (getColor(importNode) === NodeColor.Black) { + survivingImports.push(importNode.getFullText(sourceFile)); + } + } + const leadingTriviaWidth = node.getLeadingTriviaWidth(); + const leadingTrivia = sourceFile.text.substr(node.pos, leadingTriviaWidth); + if (survivingImports.length > 0) { + if (node.importClause && getColor(node.importClause) === NodeColor.Black) { + return write(`${leadingTrivia}import ${node.importClause.name.text}, {${survivingImports.join(',')} } from${node.moduleSpecifier.getFullText(sourceFile)};`); + } + return write(`${leadingTrivia}import {${survivingImports.join(',')} } from${node.moduleSpecifier.getFullText(sourceFile)};`); + } else { + if (node.importClause && getColor(node.importClause) === NodeColor.Black) { + return write(`${leadingTrivia}import ${node.importClause.name.text} from${node.moduleSpecifier.getFullText(sourceFile)};`); + } + } + } + } else { + if (node.importClause && getColor(node.importClause) === NodeColor.Black) { + return keep(node); + } + } + } + + if (shakeLevel === ShakeLevel.ClassMembers && (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) && nodeOrChildIsBlack(node)) { + let toWrite = node.getFullText(); + for (let i = node.members.length - 1; i >= 0; i--) { + const member = node.members[i]; + if (getColor(member) === NodeColor.Black) { + // keep method + continue; + } + if (/^_(.*)Brand$/.test(member.name.getText())) { + // TODO: keep all members ending with `Brand`... + continue; + } + + let pos = member.pos - node.pos; + let end = member.end - node.pos; + toWrite = toWrite.substring(0, pos) + toWrite.substring(end); + } + return write(toWrite); + } + + if (ts.isFunctionDeclaration(node)) { + // Do not go inside functions if they haven't been marked + return; + } + + node.forEachChild(writeMarkedNodes); + } + + if (getColor(sourceFile) !== NodeColor.Black) { + if (!nodeOrChildIsBlack(sourceFile)) { + // none of the elements are reachable => don't write this file at all! + return; + } + sourceFile.forEachChild(writeMarkedNodes); + result += sourceFile.endOfFileToken.getFullText(sourceFile); + } else { + result = text; + } + + writeFile(destination, result); + }); + + return result; +} + +//#endregion + +//#region Utils + +/** + * Returns the node's symbol and the `import` node (if the symbol resolved from a different module) + */ +function getRealNodeSymbol(checker: ts.TypeChecker, node: ts.Node): [ts.Symbol, ts.Declaration] { + /** + * Returns the containing object literal property declaration given a possible name node, e.g. "a" in x = { "a": 1 } + */ + /* @internal */ + function getContainingObjectLiteralElement(node: ts.Node): ts.ObjectLiteralElement | undefined { + switch (node.kind) { + case ts.SyntaxKind.StringLiteral: + case ts.SyntaxKind.NumericLiteral: + if (node.parent.kind === ts.SyntaxKind.ComputedPropertyName) { + return ts.isObjectLiteralElement(node.parent.parent) ? node.parent.parent : undefined; + } + // falls through + case ts.SyntaxKind.Identifier: + return ts.isObjectLiteralElement(node.parent) && + (node.parent.parent.kind === ts.SyntaxKind.ObjectLiteralExpression || node.parent.parent.kind === ts.SyntaxKind.JsxAttributes) && + node.parent.name === node ? node.parent : undefined; + } + return undefined; + } + + function getPropertySymbolsFromType(type: ts.Type, propName: ts.PropertyName) { + function getTextOfPropertyName(name: ts.PropertyName): string { + + function isStringOrNumericLiteral(node: ts.Node): node is ts.StringLiteral | ts.NumericLiteral { + const kind = node.kind; + return kind === ts.SyntaxKind.StringLiteral + || kind === ts.SyntaxKind.NumericLiteral; + } + + switch (name.kind) { + case ts.SyntaxKind.Identifier: + return name.text; + case ts.SyntaxKind.StringLiteral: + case ts.SyntaxKind.NumericLiteral: + return name.text; + case ts.SyntaxKind.ComputedPropertyName: + return isStringOrNumericLiteral(name.expression) ? name.expression.text : undefined!; + } + } + + const name = getTextOfPropertyName(propName); + if (name && type) { + const result: ts.Symbol[] = []; + const symbol = type.getProperty(name); + if (type.flags & ts.TypeFlags.Union) { + for (const t of (type).types) { + const symbol = t.getProperty(name); + if (symbol) { + result.push(symbol); + } + } + return result; + } + + if (symbol) { + result.push(symbol); + return result; + } + } + return undefined; + } + + function getPropertySymbolsFromContextualType(typeChecker: ts.TypeChecker, node: ts.ObjectLiteralElement): ts.Symbol[] { + const objectLiteral = node.parent; + const contextualType = typeChecker.getContextualType(objectLiteral)!; + return getPropertySymbolsFromType(contextualType, node.name!)!; + } + + // Go to the original declaration for cases: + // + // (1) when the aliased symbol was declared in the location(parent). + // (2) when the aliased symbol is originating from an import. + // + function shouldSkipAlias(node: ts.Node, declaration: ts.Node): boolean { + if (node.kind !== ts.SyntaxKind.Identifier) { + return false; + } + if (node.parent === declaration) { + return true; + } + switch (declaration.kind) { + case ts.SyntaxKind.ImportClause: + case ts.SyntaxKind.ImportEqualsDeclaration: + return true; + case ts.SyntaxKind.ImportSpecifier: + return declaration.parent.kind === ts.SyntaxKind.NamedImports; + default: + return false; + } + } + + if (!ts.isShorthandPropertyAssignment(node)) { + if (node.getChildCount() !== 0) { + return [null, null]; + } + } + + let symbol = checker.getSymbolAtLocation(node); + let importNode: ts.Declaration = null; + if (symbol && symbol.flags & ts.SymbolFlags.Alias && shouldSkipAlias(node, symbol.declarations[0])) { + const aliased = checker.getAliasedSymbol(symbol); + if (aliased.declarations) { + // We should mark the import as visited + importNode = symbol.declarations[0]; + symbol = aliased; + } + } + + if (symbol) { + // Because name in short-hand property assignment has two different meanings: property name and property value, + // using go-to-definition at such position should go to the variable declaration of the property value rather than + // go to the declaration of the property name (in this case stay at the same position). However, if go-to-definition + // is performed at the location of property access, we would like to go to definition of the property in the short-hand + // assignment. This case and others are handled by the following code. + if (node.parent.kind === ts.SyntaxKind.ShorthandPropertyAssignment) { + symbol = checker.getShorthandAssignmentValueSymbol(symbol.valueDeclaration); + } + + // If the node is the name of a BindingElement within an ObjectBindingPattern instead of just returning the + // declaration the symbol (which is itself), we should try to get to the original type of the ObjectBindingPattern + // and return the property declaration for the referenced property. + // For example: + // import('./foo').then(({ b/*goto*/ar }) => undefined); => should get use to the declaration in file "./foo" + // + // function bar(onfulfilled: (value: T) => void) { //....} + // interface Test { + // pr/*destination*/op1: number + // } + // bar(({pr/*goto*/op1})=>{}); + if (ts.isPropertyName(node) && ts.isBindingElement(node.parent) && ts.isObjectBindingPattern(node.parent.parent) && + (node === (node.parent.propertyName || node.parent.name))) { + const type = checker.getTypeAtLocation(node.parent.parent); + if (type) { + const propSymbols = getPropertySymbolsFromType(type, node); + if (propSymbols) { + symbol = propSymbols[0]; + } + } + } + + // If the current location we want to find its definition is in an object literal, try to get the contextual type for the + // object literal, lookup the property symbol in the contextual type, and use this for goto-definition. + // For example + // interface Props{ + // /*first*/prop1: number + // prop2: boolean + // } + // function Foo(arg: Props) {} + // Foo( { pr/*1*/op1: 10, prop2: false }) + const element = getContainingObjectLiteralElement(node); + if (element && checker.getContextualType(element.parent as ts.Expression)) { + const propertySymbols = getPropertySymbolsFromContextualType(checker, element); + if (propertySymbols) { + symbol = propertySymbols[0]; + } + } + } + + if (symbol && symbol.declarations) { + return [symbol, importNode]; + } + + return [null, null]; +} + +/** Get the token whose text contains the position */ +function getTokenAtPosition(sourceFile: ts.SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includeEndPosition: boolean): ts.Node { + let current: ts.Node = sourceFile; + outer: while (true) { + // find the child that contains 'position' + for (const child of current.getChildren()) { + const start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile, /*includeJsDoc*/ true); + if (start > position) { + // If this child begins after position, then all subsequent children will as well. + break; + } + + const end = child.getEnd(); + if (position < end || (position === end && (child.kind === ts.SyntaxKind.EndOfFileToken || includeEndPosition))) { + current = child; + continue outer; + } + } + + return current; + } +} + +//#endregion diff --git a/build/monaco/api.js b/build/monaco/api.js index 74e9273f35e..ae4a0d26616 100644 --- a/build/monaco/api.js +++ b/build/monaco/api.js @@ -134,7 +134,25 @@ function getTopLevelDeclaration(sourceFile, typeName) { function getNodeText(sourceFile, node) { return sourceFile.getFullText().substring(node.pos, node.end); } -function getMassagedTopLevelDeclarationText(sourceFile, declaration) { +function hasModifier(modifiers, kind) { + if (modifiers) { + for (var i = 0; i < modifiers.length; i++) { + var mod = modifiers[i]; + if (mod.kind === kind) { + return true; + } + } + } + return false; +} +function isStatic(member) { + return hasModifier(member.modifiers, ts.SyntaxKind.StaticKeyword); +} +function isDefaultExport(declaration) { + return (hasModifier(declaration.modifiers, ts.SyntaxKind.DefaultKeyword) + && hasModifier(declaration.modifiers, ts.SyntaxKind.ExportKeyword)); +} +function getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage) { var result = getNodeText(sourceFile, declaration); // if (result.indexOf('MonacoWorker') >= 0) { // console.log('here!'); @@ -142,6 +160,18 @@ function getMassagedTopLevelDeclarationText(sourceFile, declaration) { // } if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) { var interfaceDeclaration = declaration; + var staticTypeName_1 = (isDefaultExport(interfaceDeclaration) + ? importName + ".default" + : importName + "." + declaration.name.text); + var instanceTypeName_1 = staticTypeName_1; + var typeParametersCnt = (interfaceDeclaration.typeParameters ? interfaceDeclaration.typeParameters.length : 0); + if (typeParametersCnt > 0) { + var arr = []; + for (var i = 0; i < typeParametersCnt; i++) { + arr.push('any'); + } + instanceTypeName_1 = instanceTypeName_1 + "<" + arr.join(',') + ">"; + } var members = interfaceDeclaration.members; members.forEach(function (member) { try { @@ -151,6 +181,15 @@ function getMassagedTopLevelDeclarationText(sourceFile, declaration) { result = result.replace(memberText, ''); // console.log('AFTER: ', result); } + else { + var memberName = member.name.text; + if (isStatic(member)) { + usage.push("a = " + staticTypeName_1 + "." + memberName + ";"); + } + else { + usage.push("a = (<" + instanceTypeName_1 + ">b)." + memberName + ";"); + } + } } catch (err) { // life.. @@ -211,6 +250,16 @@ function generateDeclarationFile(out, inputFiles, recipe) { var endl = /\r\n/.test(recipe) ? '\r\n' : '\n'; var lines = recipe.split(endl); var result = []; + var usageCounter = 0; + var usageImports = []; + var usage = []; + usage.push("var a;"); + usage.push("var b;"); + var generateUsageImport = function (moduleId) { + var importName = 'm' + (++usageCounter); + usageImports.push("import * as " + importName + " from '" + moduleId.replace(/\.d\.ts$/, '') + "';"); + return importName; + }; lines.forEach(function (line) { var m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/); if (m1) { @@ -220,6 +269,7 @@ function generateDeclarationFile(out, inputFiles, recipe) { if (!sourceFile_1) { return; } + var importName_1 = generateUsageImport(moduleId); var replacer_1 = createReplacer(m1[2]); var typeNames = m1[3].split(/,/); typeNames.forEach(function (typeName) { @@ -232,7 +282,7 @@ function generateDeclarationFile(out, inputFiles, recipe) { logErr('Cannot find type ' + typeName); return; } - result.push(replacer_1(getMassagedTopLevelDeclarationText(sourceFile_1, declaration))); + result.push(replacer_1(getMassagedTopLevelDeclarationText(sourceFile_1, declaration, importName_1, usage))); }); return; } @@ -244,6 +294,7 @@ function generateDeclarationFile(out, inputFiles, recipe) { if (!sourceFile_2) { return; } + var importName_2 = generateUsageImport(moduleId); var replacer_2 = createReplacer(m2[2]); var typeNames = m2[3].split(/,/); var typesToExcludeMap_1 = {}; @@ -271,7 +322,7 @@ function generateDeclarationFile(out, inputFiles, recipe) { } } } - result.push(replacer_2(getMassagedTopLevelDeclarationText(sourceFile_2, declaration))); + result.push(replacer_2(getMassagedTopLevelDeclarationText(sourceFile_2, declaration, importName_2, usage))); }); return; } @@ -282,9 +333,12 @@ function generateDeclarationFile(out, inputFiles, recipe) { resultTxt = resultTxt.replace(/\bEvent, kind: ts.SyntaxKind): boolean { + if (modifiers) { + for (let i = 0; i < modifiers.length; i++) { + let mod = modifiers[i]; + if (mod.kind === kind) { + return true; + } + } + } + return false; +} -function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declaration: TSTopLevelDeclare): string { +function isStatic(member: ts.ClassElement | ts.TypeElement): boolean { + return hasModifier(member.modifiers, ts.SyntaxKind.StaticKeyword); +} + +function isDefaultExport(declaration: ts.InterfaceDeclaration | ts.ClassDeclaration): boolean { + return ( + hasModifier(declaration.modifiers, ts.SyntaxKind.DefaultKeyword) + && hasModifier(declaration.modifiers, ts.SyntaxKind.ExportKeyword) + ); +} + +function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declaration: TSTopLevelDeclare, importName: string, usage: string[]): string { let result = getNodeText(sourceFile, declaration); // if (result.indexOf('MonacoWorker') >= 0) { // console.log('here!'); @@ -163,7 +185,23 @@ function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declarati if (declaration.kind === ts.SyntaxKind.InterfaceDeclaration || declaration.kind === ts.SyntaxKind.ClassDeclaration) { let interfaceDeclaration = declaration; - let members: ts.NodeArray = interfaceDeclaration.members; + const staticTypeName = ( + isDefaultExport(interfaceDeclaration) + ? `${importName}.default` + : `${importName}.${declaration.name.text}` + ); + + let instanceTypeName = staticTypeName; + const typeParametersCnt = (interfaceDeclaration.typeParameters ? interfaceDeclaration.typeParameters.length : 0); + if (typeParametersCnt > 0) { + let arr: string[] = []; + for (let i = 0; i < typeParametersCnt; i++) { + arr.push('any'); + } + instanceTypeName = `${instanceTypeName}<${arr.join(',')}>`; + } + + const members: ts.NodeArray = interfaceDeclaration.members; members.forEach((member) => { try { let memberText = getNodeText(sourceFile, member); @@ -171,6 +209,13 @@ function getMassagedTopLevelDeclarationText(sourceFile: ts.SourceFile, declarati // console.log('BEFORE: ', result); result = result.replace(memberText, ''); // console.log('AFTER: ', result); + } else { + const memberName = (member.name).text; + if (isStatic(member)) { + usage.push(`a = ${staticTypeName}.${memberName};`); + } else { + usage.push(`a = (<${instanceTypeName}>b).${memberName};`); + } } } catch (err) { // life.. @@ -237,11 +282,24 @@ function createReplacer(data: string): (str: string) => string { }; } -function generateDeclarationFile(out: string, inputFiles: { [file: string]: string; }, recipe: string): string { +function generateDeclarationFile(out: string, inputFiles: { [file: string]: string; }, recipe: string): [string, string] { const endl = /\r\n/.test(recipe) ? '\r\n' : '\n'; let lines = recipe.split(endl); - let result = []; + let result: string[] = []; + + let usageCounter = 0; + let usageImports: string[] = []; + let usage: string[] = []; + + usage.push(`var a;`); + usage.push(`var b;`); + + const generateUsageImport = (moduleId: string) => { + let importName = 'm' + (++usageCounter); + usageImports.push(`import * as ${importName} from '${moduleId.replace(/\.d\.ts$/, '')}';`); + return importName; + }; lines.forEach(line => { @@ -254,6 +312,8 @@ function generateDeclarationFile(out: string, inputFiles: { [file: string]: stri return; } + const importName = generateUsageImport(moduleId); + let replacer = createReplacer(m1[2]); let typeNames = m1[3].split(/,/); @@ -267,7 +327,7 @@ function generateDeclarationFile(out: string, inputFiles: { [file: string]: stri logErr('Cannot find type ' + typeName); return; } - result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration))); + result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage))); }); return; } @@ -281,6 +341,8 @@ function generateDeclarationFile(out: string, inputFiles: { [file: string]: stri return; } + const importName = generateUsageImport(moduleId); + let replacer = createReplacer(m2[2]); let typeNames = m2[3].split(/,/); @@ -309,7 +371,7 @@ function generateDeclarationFile(out: string, inputFiles: { [file: string]: stri } } } - result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration))); + result.push(replacer(getMassagedTopLevelDeclarationText(sourceFile, declaration, importName, usage))); }); return; } @@ -324,10 +386,13 @@ function generateDeclarationFile(out: string, inputFiles: { [file: string]: stri resultTxt = format(resultTxt); - return resultTxt; + return [ + resultTxt, + `${usageImports.join('\n')}\n\n${usage.join('\n')}` + ]; } -export function getFilesToWatch(out: string): string[] { +function getIncludesInRecipe(): string[] { let recipe = fs.readFileSync(RECIPE_PATH).toString(); let lines = recipe.split(/\r\n|\n|\r/); let result = []; @@ -337,14 +402,14 @@ export function getFilesToWatch(out: string): string[] { let m1 = line.match(/^\s*#include\(([^;)]*)(;[^)]*)?\)\:(.*)$/); if (m1) { let moduleId = m1[1]; - result.push(moduleIdToPath(out, moduleId)); + result.push(moduleId); return; } let m2 = line.match(/^\s*#includeAll\(([^;)]*)(;[^)]*)?\)\:(.*)$/); if (m2) { let moduleId = m2[1]; - result.push(moduleIdToPath(out, moduleId)); + result.push(moduleId); return; } }); @@ -352,8 +417,13 @@ export function getFilesToWatch(out: string): string[] { return result; } +export function getFilesToWatch(out: string): string[] { + return getIncludesInRecipe().map((moduleId) => moduleIdToPath(out, moduleId)); +} + export interface IMonacoDeclarationResult { content: string; + usageContent: string; filePath: string; isTheSame: boolean; } @@ -363,7 +433,7 @@ export function run(out: string, inputFiles: { [file: string]: string; }): IMona SOURCE_FILE_MAP = {}; let recipe = fs.readFileSync(RECIPE_PATH).toString(); - let result = generateDeclarationFile(out, inputFiles, recipe); + let [result, usageContent] = generateDeclarationFile(out, inputFiles, recipe); let currentContent = fs.readFileSync(DECLARATION_PATH).toString(); log('Finished monaco.d.ts generation'); @@ -374,6 +444,7 @@ export function run(out: string, inputFiles: { [file: string]: string; }): IMona return { content: result, + usageContent: usageContent, filePath: DECLARATION_PATH, isTheSame }; @@ -382,3 +453,96 @@ export function run(out: string, inputFiles: { [file: string]: string; }): IMona export function complainErrors() { logErr('Not running monaco.d.ts generation due to compile errors'); } + + + +interface ILibMap { [libName: string]: string; } +interface IFileMap { [fileName: string]: string; } + +class TypeScriptLanguageServiceHost implements ts.LanguageServiceHost { + + private readonly _libs: ILibMap; + private readonly _files: IFileMap; + private readonly _compilerOptions: ts.CompilerOptions; + + constructor(libs: ILibMap, files: IFileMap, compilerOptions: ts.CompilerOptions) { + this._libs = libs; + this._files = files; + this._compilerOptions = compilerOptions; + } + + // --- language service host --------------- + + getCompilationSettings(): ts.CompilerOptions { + return this._compilerOptions; + } + getScriptFileNames(): string[] { + return ( + [] + .concat(Object.keys(this._libs)) + .concat(Object.keys(this._files)) + ); + } + getScriptVersion(fileName: string): string { + return '1'; + } + getProjectVersion(): string { + return '1'; + } + getScriptSnapshot(fileName: string): ts.IScriptSnapshot { + if (this._files.hasOwnProperty(fileName)) { + return ts.ScriptSnapshot.fromString(this._files[fileName]); + } else if (this._libs.hasOwnProperty(fileName)) { + return ts.ScriptSnapshot.fromString(this._libs[fileName]); + } else { + return ts.ScriptSnapshot.fromString(''); + } + } + getScriptKind(fileName: string): ts.ScriptKind { + return ts.ScriptKind.TS; + } + getCurrentDirectory(): string { + return ''; + } + getDefaultLibFileName(options: ts.CompilerOptions): string { + return 'defaultLib:es5'; + } + isDefaultLibFileName(fileName: string): boolean { + return fileName === this.getDefaultLibFileName(this._compilerOptions); + } +} + +export function execute(): IMonacoDeclarationResult { + + const OUTPUT_FILES: { [file: string]: string; } = {}; + const SRC_FILES: IFileMap = {}; + const SRC_FILE_TO_EXPECTED_NAME: { [filename: string]: string; } = {}; + getIncludesInRecipe().forEach((moduleId) => { + if (/\.d\.ts$/.test(moduleId)) { + let fileName = path.join(SRC, moduleId); + OUTPUT_FILES[moduleIdToPath('src', moduleId)] = fs.readFileSync(fileName).toString(); + return; + } + + let fileName = path.join(SRC, moduleId) + '.ts'; + SRC_FILES[fileName] = fs.readFileSync(fileName).toString(); + SRC_FILE_TO_EXPECTED_NAME[fileName] = moduleIdToPath('src', moduleId); + }); + + const languageService = ts.createLanguageService(new TypeScriptLanguageServiceHost({}, SRC_FILES, {})); + + var t1 = Date.now(); + Object.keys(SRC_FILES).forEach((fileName) => { + var t = Date.now(); + const emitOutput = languageService.getEmitOutput(fileName, true); + OUTPUT_FILES[SRC_FILE_TO_EXPECTED_NAME[fileName]] = emitOutput.outputFiles[0].text; + // console.log(`Generating .d.ts for ${fileName} took ${Date.now() - t} ms`); + }); + console.log(`Generating .d.ts took ${Date.now() - t1} ms`); + + // console.log(result.filePath); + // fs.writeFileSync(result.filePath, result.content.replace(/\r\n/gm, '\n')); + // fs.writeFileSync(path.join(SRC, 'user.ts'), result.usageContent.replace(/\r\n/gm, '\n')); + + return run('src', OUTPUT_FILES); +} diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index 218d4cdf8f5..e39100bc3bc 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -44,7 +44,7 @@ declare namespace monaco { } -#include(vs/base/common/winjs.base.d.ts): TValueCallback, ProgressCallback, Promise +#include(vs/base/common/winjs.base.d.ts): TValueCallback, Promise #include(vs/base/common/cancellation): CancellationTokenSource, CancellationToken #include(vs/base/common/uri): URI, UriComponents #include(vs/editor/common/standalone/standaloneBase): KeyCode, KeyMod diff --git a/build/monaco/monaco.usage.recipe b/build/monaco/monaco.usage.recipe new file mode 100644 index 00000000000..05377a19ba0 --- /dev/null +++ b/build/monaco/monaco.usage.recipe @@ -0,0 +1,82 @@ + +// This file is adding references to various symbols which should not be removed via tree shaking + +import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IHighlight } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; +import { SimpleWorkerClient, create as create1 } from 'vs/base/common/worker/simpleWorker'; +import { create as create2 } from 'vs/editor/common/services/editorSimpleWorker'; +import { QuickOpenWidget } from 'vs/base/parts/quickopen/browser/quickOpenWidget'; +import { SyncDescriptor0, SyncDescriptor1, SyncDescriptor2, SyncDescriptor3, SyncDescriptor4, SyncDescriptor5, SyncDescriptor6, SyncDescriptor7, SyncDescriptor8 } from 'vs/platform/instantiation/common/descriptors'; +import { PolyfillPromise } from 'vs/base/common/winjs.polyfill.promise'; +import { DiffNavigator } from 'vs/editor/browser/widget/diffNavigator'; +import * as editorAPI from 'vs/editor/editor.api'; + +(function () { + var a: any; + var b: any; + a = (b).layout; // IContextViewProvider + a = (b).getWorkspaceFolder; // IWorkspaceFolderProvider + a = (b).getWorkspace; // IWorkspaceFolderProvider + a = (b).style; // IThemable + a = (b).style; // IThemable + a = (b).userHome; // IUserHomeProvider + a = (b).previous; // IDiffNavigator + a = (>b).type; + a = (b).start; + a = (b).end; + a = (>b).getProxyObject; // IWorkerClient + a = create1; + a = create2; + + // promise polyfill + a = PolyfillPromise.all; + a = PolyfillPromise.race; + a = PolyfillPromise.resolve; + a = PolyfillPromise.reject; + a = (b).then; + a = (b).catch; + + // injection madness + a = (>b).ctor; + a = (>b).bind; + a = (>b).ctor; + a = (>b).bind; + a = (>b).ctor; + a = (>b).bind; + a = (>b).ctor; + a = (>b).bind; + a = (>b).ctor; + a = (>b).bind; + a = (>b).ctor; + a = (>b).bind; + a = (>b).ctor; + a = (>b).bind; + a = (>b).ctor; + a = (>b).bind; + a = (>b).ctor; + a = (>b).bind; + a = (>b).ctor; + a = (>b).bind; + + // exported API + a = editorAPI.CancellationTokenSource; + a = editorAPI.Emitter; + a = editorAPI.KeyCode; + a = editorAPI.KeyMod; + a = editorAPI.Position; + a = editorAPI.Range; + a = editorAPI.Selection; + a = editorAPI.SelectionDirection; + a = editorAPI.Severity; + a = editorAPI.MarkerSeverity; + a = editorAPI.MarkerTag; + a = editorAPI.Promise; + a = editorAPI.Uri; + a = editorAPI.Token; + a = editorAPI.editor; + a = editorAPI.languages; +})(); diff --git a/build/tfs/linux/product-build-linux.yml b/build/tfs/linux/product-build-linux.yml index f6e067ebfe3..9505d71ff34 100644 --- a/build/tfs/linux/product-build-linux.yml +++ b/build/tfs/linux/product-build-linux.yml @@ -103,10 +103,10 @@ steps: # Write config files needed by API, use eval to force environment variable expansion pushd build/tfs/linux # Submit to apt repo - if [ "$DEB_ARCH" = "amd64" ]; then - echo "{ \"server\": \"azure-apt-cat.cloudapp.net\", \"protocol\": \"https\", \"port\": \"443\", \"repositoryId\": \"58a4adf642421134a1a48d1a\", \"username\": \"vscode\", \"password\": \"$(LINUX_REPO_PASSWORD)\" }" > apt-config.json - ./repoapi_client.sh -config apt-config.json -addfile $DEB_PATH - fi + # if [ "$DEB_ARCH" = "amd64" ]; then + # echo "{ \"server\": \"azure-apt-cat.cloudapp.net\", \"protocol\": \"https\", \"port\": \"443\", \"repositoryId\": \"58a4adf642421134a1a48d1a\", \"username\": \"vscode\", \"password\": \"$(LINUX_REPO_PASSWORD)\" }" > apt-config.json + # ./repoapi_client.sh -config apt-config.json -addfile $DEB_PATH + # fi # Submit to yum repo (disabled as it's manual until signing is automated) # eval echo '{ \"server\": \"azure-apt-cat.cloudapp.net\", \"protocol\": \"https\", \"port\": \"443\", \"repositoryId\": \"58a4ae3542421134a1a48d1b\", \"username\": \"vscode\", \"password\": \"$(LINUX_REPO_PASSWORD)\" }' > yum-config.json diff --git a/build/win32/code.iss b/build/win32/code.iss index cad2e27d65e..4be0ea854b5 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -975,7 +975,7 @@ begin RegKey := 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\' + copy('{#IncompatibleTargetAppId}', 2, 38) + '_is1'; if RegKeyExists({#IncompatibleArchRootKey}, RegKey) then begin - if MsgBox('{#NameShort} is already installed on this system for all users. Are you sure you want to install it for this user?', mbConfirmation, MB_YESNO) = IDNO then begin + if MsgBox('{#NameShort} is already installed on this system for all users. We recommend first uninstalling that version before installing this one. Are you sure you want to continue the installation?', mbConfirmation, MB_YESNO) = IDNO then begin Result := false; end; end; @@ -1139,4 +1139,4 @@ end; #ifdef Debug #expr SaveToFile(AddBackslash(SourcePath) + "code-processed.iss") -#endif \ No newline at end of file +#endif diff --git a/extensions/bat/test/colorize-results/test_bat.json b/extensions/bat/test/colorize-results/test_bat.json index 3c2abc5d2dc..97155fafc8b 100644 --- a/extensions/bat/test/colorize-results/test_bat.json +++ b/extensions/bat/test/colorize-results/test_bat.json @@ -135,9 +135,9 @@ "c": "::", "t": "source.batchfile comment.line.colon.batchfile punctuation.definition.comment.batchfile", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -146,9 +146,9 @@ "c": " Node modules", "t": "source.batchfile comment.line.colon.batchfile", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -245,9 +245,9 @@ "c": "::", "t": "source.batchfile comment.line.colon.batchfile punctuation.definition.comment.batchfile", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -256,9 +256,9 @@ "c": " Get electron", "t": "source.batchfile comment.line.colon.batchfile", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -278,9 +278,9 @@ "c": "::", "t": "source.batchfile comment.line.colon.batchfile punctuation.definition.comment.batchfile", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -289,9 +289,9 @@ "c": " Build", "t": "source.batchfile comment.line.colon.batchfile", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -366,9 +366,9 @@ "c": "::", "t": "source.batchfile comment.line.colon.batchfile punctuation.definition.comment.batchfile", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -377,9 +377,9 @@ "c": " Configuration", "t": "source.batchfile comment.line.colon.batchfile", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/clojure/test/colorize-results/test_clj.json b/extensions/clojure/test/colorize-results/test_clj.json index 8a704fb9683..b7dd17d91e8 100644 --- a/extensions/clojure/test/colorize-results/test_clj.json +++ b/extensions/clojure/test/colorize-results/test_clj.json @@ -3,9 +3,9 @@ "c": ";", "t": "source.clojure comment.line.semicolon.clojure punctuation.definition.comment.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -14,9 +14,9 @@ "c": "; from http://clojure-doc.org/articles/tutorials/introduction.html", "t": "source.clojure comment.line.semicolon.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -311,9 +311,9 @@ "c": ";", "t": "source.clojure comment.line.semicolon.clojure punctuation.definition.comment.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -322,9 +322,9 @@ "c": " A vector", "t": "source.clojure comment.line.semicolon.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -905,9 +905,9 @@ "c": ";", "t": "source.clojure comment.line.semicolon.clojure punctuation.definition.comment.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -916,9 +916,9 @@ "c": " this is more typical usage.", "t": "source.clojure comment.line.semicolon.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1433,9 +1433,9 @@ "c": ";", "t": "source.clojure comment.line.semicolon.clojure punctuation.definition.comment.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1444,9 +1444,9 @@ "c": "; ⇒ (+ 1 2 3)", "t": "source.clojure comment.line.semicolon.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1840,9 +1840,9 @@ "c": ";", "t": "source.clojure comment.line.semicolon.clojure punctuation.definition.comment.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1851,9 +1851,9 @@ "c": "; Vectors", "t": "source.clojure comment.line.semicolon.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2236,9 +2236,9 @@ "c": ";", "t": "source.clojure comment.line.semicolon.clojure punctuation.definition.comment.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2247,9 +2247,9 @@ "c": " ⇒ [:a :b :c :d]", "t": "source.clojure comment.line.semicolon.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2346,9 +2346,9 @@ "c": ";", "t": "source.clojure comment.line.semicolon.clojure punctuation.definition.comment.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2357,9 +2357,9 @@ "c": " ⇒ (:d :a :b :c)", "t": "source.clojure comment.line.semicolon.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2390,9 +2390,9 @@ "c": ";", "t": "source.clojure comment.line.semicolon.clojure punctuation.definition.comment.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2401,9 +2401,9 @@ "c": " ⇒ is still [:a :b :c]", "t": "source.clojure comment.line.semicolon.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2434,9 +2434,9 @@ "c": ";", "t": "source.clojure comment.line.semicolon.clojure punctuation.definition.comment.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2445,9 +2445,9 @@ "c": " ⇒ is still (:a :b :c)", "t": "source.clojure comment.line.semicolon.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2456,9 +2456,9 @@ "c": ";", "t": "source.clojure comment.line.semicolon.clojure punctuation.definition.comment.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2467,9 +2467,9 @@ "c": "; Maps", "t": "source.clojure comment.line.semicolon.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2753,9 +2753,9 @@ "c": ";", "t": "source.clojure comment.line.semicolon.clojure punctuation.definition.comment.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2764,9 +2764,9 @@ "c": " ⇒ {:a 1 :c 3 :b 2}", "t": "source.clojure comment.line.semicolon.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2863,9 +2863,9 @@ "c": ";", "t": "source.clojure comment.line.semicolon.clojure punctuation.definition.comment.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2874,9 +2874,9 @@ "c": " ⇒ {:a 1}", "t": "source.clojure comment.line.semicolon.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3050,9 +3050,9 @@ "c": ";", "t": "source.clojure comment.line.semicolon.clojure punctuation.definition.comment.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3061,9 +3061,9 @@ "c": "; ⇒ #'user/my-atom", "t": "source.clojure comment.line.semicolon.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3094,9 +3094,9 @@ "c": ";", "t": "source.clojure comment.line.semicolon.clojure punctuation.definition.comment.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3105,9 +3105,9 @@ "c": "; ⇒ {:foo 1}", "t": "source.clojure comment.line.semicolon.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3259,9 +3259,9 @@ "c": ";", "t": "source.clojure comment.line.semicolon.clojure punctuation.definition.comment.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3270,9 +3270,9 @@ "c": "; ⇒ {:foo 2}", "t": "source.clojure comment.line.semicolon.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3303,9 +3303,9 @@ "c": ";", "t": "source.clojure comment.line.semicolon.clojure punctuation.definition.comment.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3314,9 +3314,9 @@ "c": "; ⇒ {:foo 2}", "t": "source.clojure comment.line.semicolon.clojure", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/coffeescript/test/colorize-results/test-regex_coffee.json b/extensions/coffeescript/test/colorize-results/test-regex_coffee.json index ad11ba9d687..9daab0d5533 100644 --- a/extensions/coffeescript/test/colorize-results/test-regex_coffee.json +++ b/extensions/coffeescript/test/colorize-results/test-regex_coffee.json @@ -575,9 +575,9 @@ "c": "#", "t": "source.coffee comment.line.number-sign.coffee punctuation.definition.comment.coffee", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -586,9 +586,9 @@ "c": " numbers", "t": "source.coffee comment.line.number-sign.coffee", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -663,9 +663,9 @@ "c": "#", "t": "source.coffee comment.line.number-sign.coffee punctuation.definition.comment.coffee", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -674,9 +674,9 @@ "c": " letters", "t": "source.coffee comment.line.number-sign.coffee", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -696,9 +696,9 @@ "c": "#", "t": "source.coffee comment.line.number-sign.coffee punctuation.definition.comment.coffee", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -707,9 +707,9 @@ "c": " the end", "t": "source.coffee comment.line.number-sign.coffee", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/coffeescript/test/colorize-results/test_coffee.json b/extensions/coffeescript/test/colorize-results/test_coffee.json index e7eae7d047f..d3de07d3f82 100644 --- a/extensions/coffeescript/test/colorize-results/test_coffee.json +++ b/extensions/coffeescript/test/colorize-results/test_coffee.json @@ -1433,9 +1433,9 @@ "c": "#", "t": "source.coffee string.regexp.multiline.coffee comment.line.number-sign.coffee punctuation.definition.comment.coffee", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1444,9 +1444,9 @@ "c": " numbers", "t": "source.coffee string.regexp.multiline.coffee comment.line.number-sign.coffee", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1521,9 +1521,9 @@ "c": "#", "t": "source.coffee string.regexp.multiline.coffee comment.line.number-sign.coffee punctuation.definition.comment.coffee", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1532,9 +1532,9 @@ "c": " letters", "t": "source.coffee string.regexp.multiline.coffee comment.line.number-sign.coffee", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1576,9 +1576,9 @@ "c": "#", "t": "source.coffee string.regexp.multiline.coffee comment.line.number-sign.coffee punctuation.definition.comment.coffee", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1587,9 +1587,9 @@ "c": " the end", "t": "source.coffee string.regexp.multiline.coffee comment.line.number-sign.coffee", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/cpp/test/colorize-results/test_c.json b/extensions/cpp/test/colorize-results/test_c.json index d3bac881c43..0725010d8c2 100644 --- a/extensions/cpp/test/colorize-results/test_c.json +++ b/extensions/cpp/test/colorize-results/test_c.json @@ -3,9 +3,9 @@ "c": "/*", "t": "source.c comment.block.c punctuation.definition.comment.begin.c", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -14,9 +14,9 @@ "c": " C Program to find roots of a quadratic equation when coefficients are entered by user. ", "t": "source.c comment.block.c", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -25,9 +25,9 @@ "c": "*/", "t": "source.c comment.block.c punctuation.definition.comment.end.c", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -36,9 +36,9 @@ "c": "/*", "t": "source.c comment.block.c punctuation.definition.comment.begin.c", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -47,9 +47,9 @@ "c": " Library function sqrt() computes the square root. ", "t": "source.c comment.block.c", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -58,9 +58,9 @@ "c": "*/", "t": "source.c comment.block.c punctuation.definition.comment.end.c", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -212,9 +212,9 @@ "c": "/*", "t": "source.c comment.block.c punctuation.definition.comment.begin.c", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -223,9 +223,9 @@ "c": " This is needed to use sqrt() function.", "t": "source.c comment.block.c", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -234,9 +234,9 @@ "c": "*/", "t": "source.c comment.block.c punctuation.definition.comment.end.c", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/cpp/test/colorize-results/test_cc.json b/extensions/cpp/test/colorize-results/test_cc.json index 845a693b3ab..f3f72320fb5 100644 --- a/extensions/cpp/test/colorize-results/test_cc.json +++ b/extensions/cpp/test/colorize-results/test_cc.json @@ -1114,9 +1114,9 @@ "c": "//", "t": "source.cpp meta.block.c comment.line.double-slash.cpp punctuation.definition.comment.cpp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1125,9 +1125,9 @@ "c": " everything from this point on is interpeted as a string literal...", "t": "source.cpp meta.block.c comment.line.double-slash.cpp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1312,9 +1312,9 @@ "c": "//", "t": "source.cpp meta.block.c comment.line.double-slash.cpp punctuation.definition.comment.cpp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1323,9 +1323,9 @@ "c": " sadness.", "t": "source.cpp meta.block.c comment.line.double-slash.cpp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1708,9 +1708,9 @@ "c": "//", "t": "source.cpp meta.block.c comment.line.double-slash.cpp punctuation.definition.comment.cpp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1719,9 +1719,9 @@ "c": " the rest of", "t": "source.cpp meta.block.c comment.line.double-slash.cpp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1961,9 +1961,9 @@ "c": "//", "t": "source.cpp meta.block.c comment.line.double-slash.cpp punctuation.definition.comment.cpp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1972,9 +1972,9 @@ "c": " the rest of", "t": "source.cpp meta.block.c comment.line.double-slash.cpp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/cpp/test/colorize-results/test_cpp.json b/extensions/cpp/test/colorize-results/test_cpp.json index 8527e98a4f2..b3c9a841cc4 100644 --- a/extensions/cpp/test/colorize-results/test_cpp.json +++ b/extensions/cpp/test/colorize-results/test_cpp.json @@ -3,9 +3,9 @@ "c": "//", "t": "source.cpp comment.line.double-slash.cpp punctuation.definition.comment.cpp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -14,9 +14,9 @@ "c": " classes example", "t": "source.cpp comment.line.double-slash.cpp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/csharp/test/colorize-results/test_cs.json b/extensions/csharp/test/colorize-results/test_cs.json index 6b56bff9935..1fc73fb341c 100644 --- a/extensions/csharp/test/colorize-results/test_cs.json +++ b/extensions/csharp/test/colorize-results/test_cs.json @@ -1114,9 +1114,9 @@ "c": "//", "t": "source.cs comment.line.double-slash.cs punctuation.definition.comment.cs", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1125,9 +1125,9 @@ "c": " Display the number of command line arguments:", "t": "source.cs comment.line.double-slash.cs", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/css-language-features/.vscode/launch.json b/extensions/css-language-features/.vscode/launch.json index 9aad19d5b4e..d6393141c5d 100644 --- a/extensions/css-language-features/.vscode/launch.json +++ b/extensions/css-language-features/.vscode/launch.json @@ -54,6 +54,26 @@ ], "smartStep": true, "restart": true + }, + { + "name": "Server Unit Tests", + "type": "node", + "request": "launch", + "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", + "stopOnEntry": false, + "args": [ + "--timeout", + "999999", + "--colors" + ], + "cwd": "${workspaceRoot}", + "runtimeExecutable": null, + "runtimeArgs": [], + "env": {}, + "sourceMaps": true, + "outFiles": [ + "${workspaceRoot}/server/out/**" + ] } ] } \ No newline at end of file diff --git a/extensions/css-language-features/client/src/cssMain.ts b/extensions/css-language-features/client/src/cssMain.ts index a7a031bda3c..dce55982cb9 100644 --- a/extensions/css-language-features/client/src/cssMain.ts +++ b/extensions/css-language-features/client/src/cssMain.ts @@ -8,9 +8,8 @@ import * as path from 'path'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { languages, window, commands, ExtensionContext, Range, Position, TextDocument, CompletionItem, CompletionItemKind, TextEdit, SnippetString, FoldingRangeKind, FoldingRange, FoldingContext, CancellationToken } from 'vscode'; +import { languages, window, commands, ExtensionContext, Range, Position, CompletionItem, CompletionItemKind, TextEdit, SnippetString } from 'vscode'; import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, Disposable } from 'vscode-languageclient'; -import { FoldingRangeRequest, FoldingRangeRequestParam, FoldingRangeClientCapabilities, FoldingRangeKind as LSFoldingRangeKind } from 'vscode-languageserver-protocol-foldingprovider'; // this method is called when vs code is activated export function activate(context: ExtensionContext) { @@ -42,21 +41,6 @@ export function activate(context: ExtensionContext) { // Create the language client and start the client. let client = new LanguageClient('css', localize('cssserver.name', 'CSS Language Server'), serverOptions, clientOptions); client.registerProposedFeatures(); - client.registerFeature({ - fillClientCapabilities(capabilities: FoldingRangeClientCapabilities): void { - let textDocumentCap = capabilities.textDocument; - if (!textDocumentCap) { - textDocumentCap = capabilities.textDocument = {}; - } - textDocumentCap.foldingRange = { - dynamicRegistration: false, - rangeLimit: 5000, - lineFoldingOnly: true - }; - }, - initialize(capabilities, documentSelector): void { - } - }); let disposable = client.start(); // Push the disposable to the context's subscriptions so that the @@ -85,7 +69,6 @@ export function activate(context: ExtensionContext) { client.onReady().then(() => { context.subscriptions.push(initCompletionProvider()); - context.subscriptions.push(initFoldingProvider()); }); function initCompletionProvider(): Disposable { @@ -116,38 +99,6 @@ export function activate(context: ExtensionContext) { }); } - function initFoldingProvider(): Disposable { - function getKind(kind: string | undefined): FoldingRangeKind | undefined { - if (kind) { - switch (kind) { - case LSFoldingRangeKind.Comment: - return FoldingRangeKind.Comment; - case LSFoldingRangeKind.Imports: - return FoldingRangeKind.Imports; - case LSFoldingRangeKind.Region: - return FoldingRangeKind.Region; - } - } - return void 0; - } - return languages.registerFoldingRangeProvider(documentSelector, { - provideFoldingRanges(document: TextDocument, context: FoldingContext, token: CancellationToken) { - const param: FoldingRangeRequestParam = { - textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document) - }; - return client.sendRequest(FoldingRangeRequest.type, param, token).then(ranges => { - if (Array.isArray(ranges)) { - return ranges.map(r => new FoldingRange(r.startLine, r.endLine, getKind(r.kind))); - } - return null; - }, error => { - client.logFailedRequest(FoldingRangeRequest.type, error); - return null; - }); - } - }); - } - commands.registerCommand('_css.applyCodeAction', applyCodeAction); function applyCodeAction(uri: string, documentVersion: number, edits: TextEdit[]) { diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index dc78673bf07..41b83b02164 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -19,6 +19,7 @@ "scripts": { "compile": "gulp compile-extension:css-language-features-client compile-extension:css-language-features-server", "watch": "gulp watch-extension:css-language-features-client watch-extension:css-language-features-server", + "test": "mocha", "postinstall": "cd server && yarn install", "install-client-next": "yarn add vscode-languageclient@next" }, @@ -109,7 +110,7 @@ "error" ], "default": "ignore", - "description": "%css.lint.boxModel.desc%" + "markdownDescription": "%css.lint.boxModel.desc%" }, "css.lint.universalSelector": { "type": "string", @@ -120,7 +121,7 @@ "error" ], "default": "ignore", - "description": "%css.lint.universalSelector.desc%" + "markdownDescription": "%css.lint.universalSelector.desc%" }, "css.lint.zeroUnits": { "type": "string", @@ -208,7 +209,7 @@ "error" ], "default": "warning", - "description": "%css.lint.propertyIgnoredDueToDisplay.desc%" + "markdownDescription": "%css.lint.propertyIgnoredDueToDisplay.desc%" }, "css.lint.important": { "type": "string", @@ -230,7 +231,7 @@ "error" ], "default": "ignore", - "description": "%css.lint.float.desc%" + "markdownDescription": "%css.lint.float.desc%" }, "css.lint.idSelector": { "type": "string", @@ -349,7 +350,7 @@ "error" ], "default": "ignore", - "description": "%scss.lint.boxModel.desc%" + "markdownDescription": "%scss.lint.boxModel.desc%" }, "scss.lint.universalSelector": { "type": "string", @@ -360,7 +361,7 @@ "error" ], "default": "ignore", - "description": "%scss.lint.universalSelector.desc%" + "markdownDescription": "%scss.lint.universalSelector.desc%" }, "scss.lint.zeroUnits": { "type": "string", @@ -382,7 +383,7 @@ "error" ], "default": "warning", - "description": "%scss.lint.fontFaceProperties.desc%" + "markdownDescription": "%scss.lint.fontFaceProperties.desc%" }, "scss.lint.hexColorLength": { "type": "string", @@ -448,7 +449,7 @@ "error" ], "default": "warning", - "description": "%scss.lint.propertyIgnoredDueToDisplay.desc%" + "markdownDescription": "%scss.lint.propertyIgnoredDueToDisplay.desc%" }, "scss.lint.important": { "type": "string", @@ -459,7 +460,7 @@ "error" ], "default": "ignore", - "description": "%scss.lint.important.desc%" + "markdownDescription": "%scss.lint.important.desc%" }, "scss.lint.float": { "type": "string", @@ -470,7 +471,7 @@ "error" ], "default": "ignore", - "description": "%scss.lint.float.desc%" + "markdownDescription": "%scss.lint.float.desc%" }, "scss.lint.idSelector": { "type": "string", @@ -568,7 +569,7 @@ "error" ], "default": "ignore", - "description": "%less.lint.boxModel.desc%" + "markdownDescription": "%less.lint.boxModel.desc%" }, "less.lint.universalSelector": { "type": "string", @@ -579,7 +580,7 @@ "error" ], "default": "ignore", - "description": "%less.lint.universalSelector.desc%" + "markdownDescription": "%less.lint.universalSelector.desc%" }, "less.lint.zeroUnits": { "type": "string", @@ -601,7 +602,7 @@ "error" ], "default": "warning", - "description": "%less.lint.fontFaceProperties.desc%" + "markdownDescription": "%less.lint.fontFaceProperties.desc%" }, "less.lint.hexColorLength": { "type": "string", @@ -667,7 +668,7 @@ "error" ], "default": "warning", - "description": "%less.lint.propertyIgnoredDueToDisplay.desc%" + "markdownDescription": "%less.lint.propertyIgnoredDueToDisplay.desc%" }, "less.lint.important": { "type": "string", @@ -689,7 +690,7 @@ "error" ], "default": "ignore", - "description": "%less.lint.float.desc%" + "markdownDescription": "%less.lint.float.desc%" }, "less.lint.idSelector": { "type": "string", @@ -707,12 +708,11 @@ ] }, "dependencies": { - "vscode-languageclient": "^4.1.4", - "vscode-languageserver-protocol-foldingprovider": "^2.0.1", + "vscode-languageclient": "^4.4.0", "vscode-nls": "^3.2.4" }, "devDependencies": { "@types/node": "7.0.43", "mocha": "^5.2.0" } -} \ No newline at end of file +} diff --git a/extensions/css-language-features/package.nls.json b/extensions/css-language-features/package.nls.json index b4078e7f5cd..f62c6fb669c 100644 --- a/extensions/css-language-features/package.nls.json +++ b/extensions/css-language-features/package.nls.json @@ -2,73 +2,73 @@ "displayName": "CSS Language Features", "description": "Provides rich language support for CSS, LESS and SCSS files.", "css.title": "CSS", - "css.lint.argumentsInColorFunction.desc": "Invalid number of parameters", - "css.lint.boxModel.desc": "Do not use width or height when using padding or border", - "css.lint.compatibleVendorPrefixes.desc": "When using a vendor-specific prefix make sure to also include all other vendor-specific properties", - "css.lint.duplicateProperties.desc": "Do not use duplicate style definitions", - "css.lint.emptyRules.desc": "Do not use empty rulesets", - "css.lint.float.desc": "Avoid using 'float'. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes.", - "css.lint.fontFaceProperties.desc": "@font-face rule must define 'src' and 'font-family' properties", - "css.lint.hexColorLength.desc": "Hex colors must consist of three or six hex numbers", + "css.lint.argumentsInColorFunction.desc": "Invalid number of parameters.", + "css.lint.boxModel.desc": "Do not use `width` or `height` when using `padding` or `border`.", + "css.lint.compatibleVendorPrefixes.desc": "When using a vendor-specific prefix make sure to also include all other vendor-specific properties.", + "css.lint.duplicateProperties.desc": "Do not use duplicate style definitions.", + "css.lint.emptyRules.desc": "Do not use empty rulesets.", + "css.lint.float.desc": "Avoid using `float`. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes.", + "css.lint.fontFaceProperties.desc": "`@font-face` rule must define `src` and `font-family` properties.", + "css.lint.hexColorLength.desc": "Hex colors must consist of three or six hex numbers.", "css.lint.idSelector.desc": "Selectors should not contain IDs because these rules are too tightly coupled with the HTML.", - "css.lint.ieHack.desc": "IE hacks are only necessary when supporting IE7 and older", - "css.lint.important.desc": "Avoid using !important. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored.", - "css.lint.importStatement.desc": "Import statements do not load in parallel", - "css.lint.propertyIgnoredDueToDisplay.desc": "Property is ignored due to the display. E.g. with 'display: inline', the width, height, margin-top, margin-bottom, and float properties have no effect", - "css.lint.universalSelector.desc": "The universal selector (*) is known to be slow", + "css.lint.ieHack.desc": "IE hacks are only necessary when supporting IE7 and older.", + "css.lint.important.desc": "Avoid using `!important`. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored.", + "css.lint.importStatement.desc": "Import statements do not load in parallel.", + "css.lint.propertyIgnoredDueToDisplay.desc": "Property is ignored due to the display. E.g. with `display: inline`, the `width`, `height`, `margin-top`, `margin-bottom`, and `float` properties have no effect.", + "css.lint.universalSelector.desc": "The universal selector (`*`) is known to be slow.", "css.lint.unknownAtRules.desc": "Unknown at-rule.", "css.lint.unknownProperties.desc": "Unknown property.", "css.lint.unknownVendorSpecificProperties.desc": "Unknown vendor specific property.", - "css.lint.vendorPrefix.desc": "When using a vendor-specific prefix also include the standard property", - "css.lint.zeroUnits.desc": "No unit for zero needed", + "css.lint.vendorPrefix.desc": "When using a vendor-specific prefix, also include the standard property.", + "css.lint.zeroUnits.desc": "No unit for zero needed.", "css.trace.server.desc": "Traces the communication between VS Code and the CSS language server.", "css.validate.title": "Controls CSS validation and problem severities.", - "css.validate.desc": "Enables or disables all validations", + "css.validate.desc": "Enables or disables all validations.", "less.title": "LESS", - "less.lint.argumentsInColorFunction.desc": "Invalid number of parameters", - "less.lint.boxModel.desc": "Do not use width or height when using padding or border", - "less.lint.compatibleVendorPrefixes.desc": "When using a vendor-specific prefix make sure to also include all other vendor-specific properties", - "less.lint.duplicateProperties.desc": "Do not use duplicate style definitions", - "less.lint.emptyRules.desc": "Do not use empty rulesets", - "less.lint.float.desc": "Avoid using 'float'. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes.", - "less.lint.fontFaceProperties.desc": "@font-face rule must define 'src' and 'font-family' properties", - "less.lint.hexColorLength.desc": "Hex colors must consist of three or six hex numbers", + "less.lint.argumentsInColorFunction.desc": "Invalid number of parameters.", + "less.lint.boxModel.desc": "Do not use `width` or `height` when using `padding` or `border`.", + "less.lint.compatibleVendorPrefixes.desc": "When using a vendor-specific prefix make sure to also include all other vendor-specific properties.", + "less.lint.duplicateProperties.desc": "Do not use duplicate style definitions.", + "less.lint.emptyRules.desc": "Do not use empty rulesets.", + "less.lint.float.desc": "Avoid using `float`. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes.", + "less.lint.fontFaceProperties.desc": "`@font-face` rule must define `src` and `font-family` properties.", + "less.lint.hexColorLength.desc": "Hex colors must consist of three or six hex numbers.", "less.lint.idSelector.desc": "Selectors should not contain IDs because these rules are too tightly coupled with the HTML.", - "less.lint.ieHack.desc": "IE hacks are only necessary when supporting IE7 and older", + "less.lint.ieHack.desc": "IE hacks are only necessary when supporting IE7 and older.", "less.lint.important.desc": "Avoid using !important. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored.", - "less.lint.importStatement.desc": "Import statements do not load in parallel", - "less.lint.propertyIgnoredDueToDisplay.desc": "Property is ignored due to the display. E.g. with 'display: inline', the width, height, margin-top, margin-bottom, and float properties have no effect", - "less.lint.universalSelector.desc": "The universal selector (*) is known to be slow", + "less.lint.importStatement.desc": "Import statements do not load in parallel.", + "less.lint.propertyIgnoredDueToDisplay.desc": "Property is ignored due to the display. E.g. with `display: inline`, the `width`, `height`, `margin-top`, `margin-bottom`, and `float` properties have no effect.", + "less.lint.universalSelector.desc": "The universal selector (`*`) is known to be slow.", "less.lint.unknownProperties.desc": "Unknown property.", "less.lint.unknownVendorSpecificProperties.desc": "Unknown vendor specific property.", - "less.lint.vendorPrefix.desc": "When using a vendor-specific prefix also include the standard property", - "less.lint.zeroUnits.desc": "No unit for zero needed", + "less.lint.vendorPrefix.desc": "When using a vendor-specific prefix, also include the standard property.", + "less.lint.zeroUnits.desc": "No unit for zero needed.", "less.validate.title": "Controls LESS validation and problem severities.", - "less.validate.desc": "Enables or disables all validations", + "less.validate.desc": "Enables or disables all validations.", "scss.title": "SCSS (Sass)", - "scss.lint.argumentsInColorFunction.desc": "Invalid number of parameters", - "scss.lint.boxModel.desc": "Do not use width or height when using padding or border", - "scss.lint.compatibleVendorPrefixes.desc": "When using a vendor-specific prefix make sure to also include all other vendor-specific properties", - "scss.lint.duplicateProperties.desc": "Do not use duplicate style definitions", - "scss.lint.emptyRules.desc": "Do not use empty rulesets", - "scss.lint.float.desc": "Avoid using 'float'. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes.", - "scss.lint.fontFaceProperties.desc": "@font-face rule must define 'src' and 'font-family' properties", - "scss.lint.hexColorLength.desc": "Hex colors must consist of three or six hex numbers", + "scss.lint.argumentsInColorFunction.desc": "Invalid number of parameters.", + "scss.lint.boxModel.desc": "Do not use `width` or `height` when using `padding` or `border`.", + "scss.lint.compatibleVendorPrefixes.desc": "When using a vendor-specific prefix make sure to also include all other vendor-specific properties.", + "scss.lint.duplicateProperties.desc": "Do not use duplicate style definitions.", + "scss.lint.emptyRules.desc": "Do not use empty rulesets.", + "scss.lint.float.desc": "Avoid using `float`. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes.", + "scss.lint.fontFaceProperties.desc": "`@font-face` rule must define `src` and `font-family` properties.", + "scss.lint.hexColorLength.desc": "Hex colors must consist of three or six hex numbers.", "scss.lint.idSelector.desc": "Selectors should not contain IDs because these rules are too tightly coupled with the HTML.", - "scss.lint.ieHack.desc": "IE hacks are only necessary when supporting IE7 and older", + "scss.lint.ieHack.desc": "IE hacks are only necessary when supporting IE7 and older.", "scss.lint.important.desc": "Avoid using !important. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored.", - "scss.lint.importStatement.desc": "Import statements do not load in parallel", - "scss.lint.propertyIgnoredDueToDisplay.desc": "Property is ignored due to the display. E.g. with 'display: inline', the width, height, margin-top, margin-bottom, and float properties have no effect", - "scss.lint.universalSelector.desc": "The universal selector (*) is known to be slow", + "scss.lint.importStatement.desc": "Import statements do not load in parallel.", + "scss.lint.propertyIgnoredDueToDisplay.desc": "Property is ignored due to the display. E.g. with `display: inline`, the `width`, `height`, `margin-top`, `margin-bottom`, and `float` properties have no effect.", + "scss.lint.universalSelector.desc": "The universal selector (`*`) is known to be slow.", "scss.lint.unknownProperties.desc": "Unknown property.", "scss.lint.unknownVendorSpecificProperties.desc": "Unknown vendor specific property.", - "scss.lint.vendorPrefix.desc": "When using a vendor-specific prefix also include the standard property", - "scss.lint.zeroUnits.desc": "No unit for zero needed", + "scss.lint.vendorPrefix.desc": "When using a vendor-specific prefix, also include the standard property.", + "scss.lint.zeroUnits.desc": "No unit for zero needed.", "scss.validate.title": "Controls SCSS validation and problem severities.", - "scss.validate.desc": "Enables or disables all validations", - "less.colorDecorators.enable.desc": "Enables or disables color decorators", - "scss.colorDecorators.enable.desc": "Enables or disables color decorators", - "css.colorDecorators.enable.desc": "Enables or disables color decorators", + "scss.validate.desc": "Enables or disables all validations.", + "less.colorDecorators.enable.desc": "Enables or disables color decorators.", + "scss.colorDecorators.enable.desc": "Enables or disables color decorators.", + "css.colorDecorators.enable.desc": "Enables or disables color decorators.", "css.colorDecorators.enable.deprecationMessage": "The setting `css.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`.", "scss.colorDecorators.enable.deprecationMessage": "The setting `scss.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`.", "less.colorDecorators.enable.deprecationMessage": "The setting `less.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`." diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index 59fb5eefee2..7b98111d447 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -8,9 +8,8 @@ "node": "*" }, "dependencies": { - "vscode-css-languageservice": "^3.0.9-next.20", - "vscode-languageserver": "^4.1.3", - "vscode-languageserver-protocol-foldingprovider": "^2.0.1" + "vscode-css-languageservice": "^3.0.10-next.2", + "vscode-languageserver": "^4.4.0" }, "devDependencies": { "@types/mocha": "2.2.33", diff --git a/extensions/css-language-features/server/src/cssServerMain.ts b/extensions/css-language-features/server/src/cssServerMain.ts index 8778faf2b79..8a822ce8b64 100644 --- a/extensions/css-language-features/server/src/cssServerMain.ts +++ b/extensions/css-language-features/server/src/cssServerMain.ts @@ -15,7 +15,6 @@ import { getLanguageModelCache } from './languageModelCache'; import { formatError, runSafe } from './utils/runner'; import URI from 'vscode-uri'; import { getPathCompletionParticipant } from './pathCompletion'; -import { FoldingRangeServerCapabilities, FoldingRangeRequest } from 'vscode-languageserver-protocol-foldingprovider'; export interface Settings { css: LanguageSettings; @@ -78,7 +77,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { scopedSettingsSupport = !!getClientCapability('workspace.configuration', false); foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); - const capabilities: ServerCapabilities & FoldingRangeServerCapabilities = { + const capabilities: ServerCapabilities = { // Tell the client that the server works in FULL text document sync mode textDocumentSync: documents.syncKind, completionProvider: snippetSupport ? { resolveProvider: false, triggerCharacters: ['/'] } : undefined, @@ -306,7 +305,7 @@ connection.onRenameRequest((renameParameters, token) => { }, null, `Error while computing renames for ${renameParameters.textDocument.uri}`, token); }); -connection.onRequest(FoldingRangeRequest.type, (params, token) => { +connection.onFoldingRanges((params, token) => { return runSafe(() => { const document = documents.get(params.textDocument.uri); if (document) { diff --git a/extensions/css-language-features/server/src/pathCompletion.ts b/extensions/css-language-features/server/src/pathCompletion.ts index b072c4136f6..4ea06882be2 100644 --- a/extensions/css-language-features/server/src/pathCompletion.ts +++ b/extensions/css-language-features/server/src/pathCompletion.ts @@ -12,7 +12,7 @@ import { TextDocument, CompletionList, CompletionItemKind, CompletionItem, TextE import { WorkspaceFolder } from 'vscode-languageserver'; import { ICompletionParticipant } from 'vscode-css-languageservice'; -import { startsWith } from './utils/strings'; +import { startsWith, endsWith } from './utils/strings'; export function getPathCompletionParticipant( document: TextDocument, @@ -21,32 +21,73 @@ export function getPathCompletionParticipant( ): ICompletionParticipant { return { onCssURILiteralValue: ({ position, range, uriValue }) => { - const isValueQuoted = startsWith(uriValue, `'`) || startsWith(uriValue, `"`); const fullValue = stripQuotes(uriValue); - const valueBeforeCursor = isValueQuoted - ? fullValue.slice(0, position.character - (range.start.character + 1)) - : fullValue.slice(0, position.character - range.start.character); - - if (fullValue === '.' || fullValue === '..') { - result.isIncomplete = true; + if (!shouldDoPathCompletion(uriValue, workspaceFolders)) { + if (fullValue === '.' || fullValue === '..') { + result.isIncomplete = true; + } return; } - if (!workspaceFolders || workspaceFolders.length === 0) { + let suggestions = providePathSuggestions(uriValue, position, range, document, workspaceFolders); + result.items = [...suggestions, ...result.items]; + }, + onCssImportPath: ({ position, range, pathValue }) => { + const fullValue = stripQuotes(pathValue); + if (!shouldDoPathCompletion(pathValue, workspaceFolders)) { + if (fullValue === '.' || fullValue === '..') { + result.isIncomplete = true; + } return; } - const workspaceRoot = resolveWorkspaceRoot(document, workspaceFolders); - const paths = providePaths(valueBeforeCursor, URI.parse(document.uri).fsPath, workspaceRoot); - const fullValueRange = isValueQuoted ? shiftRange(range, 1, -1) : range; - const replaceRange = pathToReplaceRange(valueBeforeCursor, fullValue, fullValueRange); - const suggestions = paths.map(p => pathToSuggestion(p, replaceRange)); + let suggestions = providePathSuggestions(pathValue, position, range, document, workspaceFolders); + + if (document.languageId === 'scss') { + suggestions.forEach(s => { + if (startsWith(s.label, '_') && endsWith(s.label, '.scss')) { + if (s.textEdit) { + s.textEdit.newText = s.label.slice(1, -5); + } else { + s.label = s.label.slice(1, -5); + } + } + }); + } result.items = [...suggestions, ...result.items]; } - }; } +function providePathSuggestions(pathValue: string, position: Position, range: Range, document: TextDocument, workspaceFolders: WorkspaceFolder[]) { + const fullValue = stripQuotes(pathValue); + const isValueQuoted = startsWith(pathValue, `'`) || startsWith(pathValue, `"`); + const valueBeforeCursor = isValueQuoted + ? fullValue.slice(0, position.character - (range.start.character + 1)) + : fullValue.slice(0, position.character - range.start.character); + const workspaceRoot = resolveWorkspaceRoot(document, workspaceFolders); + + const paths = providePaths(valueBeforeCursor, URI.parse(document.uri).fsPath, workspaceRoot); + const fullValueRange = isValueQuoted ? shiftRange(range, 1, -1) : range; + const replaceRange = pathToReplaceRange(valueBeforeCursor, fullValue, fullValueRange); + + const suggestions = paths.map(p => pathToSuggestion(p, replaceRange)); + return suggestions; +} + +function shouldDoPathCompletion(pathValue: string, workspaceFolders: WorkspaceFolder[]): boolean { + const fullValue = stripQuotes(pathValue); + if (fullValue === '.' || fullValue === '..') { + return false; + } + + if (!workspaceFolders || workspaceFolders.length === 0) { + return false; + } + + return true; +} + function stripQuotes(fullValue: string) { if (startsWith(fullValue, `'`) || startsWith(fullValue, `"`)) { return fullValue.slice(1, -1); diff --git a/extensions/css-language-features/server/src/test/completion.test.ts b/extensions/css-language-features/server/src/test/completion.test.ts index 62094de6b5b..2a68b5797cf 100644 --- a/extensions/css-language-features/server/src/test/completion.test.ts +++ b/extensions/css-language-features/server/src/test/completion.test.ts @@ -33,11 +33,11 @@ suite('Completions', () => { } }; - function assertCompletions(value: string, expected: { count?: number, items?: ItemDescription[] }, testUri: string, workspaceFolders?: WorkspaceFolder[]): void { + function assertCompletions(value: string, expected: { count?: number, items?: 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, 'css', 0, value); + const document = TextDocument.create(testUri, lang, 0, value); const position = document.positionAt(offset); if (!workspaceFolders) { @@ -61,7 +61,7 @@ suite('Completions', () => { } } - test('CSS Path completion', function () { + test('CSS url() Path completion', function () { let testUri = Uri.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/about/about.css')).toString(); let folders = [{ name: 'x', uri: Uri.file(path.resolve(__dirname, '../../test')).toString() }]; @@ -121,7 +121,7 @@ suite('Completions', () => { }, testUri, folders); }); - test('CSS Path Completion - Unquoted url', function () { + test('CSS url() Path Completion - Unquoted url', function () { let testUri = Uri.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/about/about.css')).toString(); let folders = [{ name: 'x', uri: Uri.file(path.resolve(__dirname, '../../test')).toString() }]; @@ -149,4 +149,50 @@ suite('Completions', () => { ] }, testUri, folders); }); + + test('CSS @import Path completion', function () { + let testUri = Uri.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/about/about.css')).toString(); + let folders = [{ name: 'x', uri: Uri.file(path.resolve(__dirname, '../../test')).toString() }]; + + assertCompletions(`@import './|'`, { + items: [ + { label: 'about.css', resultText: `@import './about.css'` }, + { label: 'about.html', resultText: `@import './about.html'` }, + ] + }, testUri, folders); + + assertCompletions(`@import '../|'`, { + items: [ + { label: 'about/', resultText: `@import '../about/'` }, + { label: 'scss/', resultText: `@import '../scss/'` }, + { label: 'index.html', resultText: `@import '../index.html'` }, + { label: 'src/', resultText: `@import '../src/'` } + ] + }, testUri, folders); + }); + + /** + * For SCSS, `@import 'foo';` can be used for importing partial file `_foo.scss` + */ + test('SCSS @import Path completion', function () { + let testCSSUri = Uri.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/about/about.css')).toString(); + let folders = [{ name: 'x', uri: Uri.file(path.resolve(__dirname, '../../test')).toString() }]; + + /** + * We are in a CSS file, so no special treatment for SCSS partial files + */ + assertCompletions(`@import '../scss/|'`, { + items: [ + { label: 'main.scss', resultText: `@import '../scss/main.scss'` }, + { label: '_foo.scss', resultText: `@import '../scss/_foo.scss'` } + ] + }, testCSSUri, folders); + + let testSCSSUri = Uri.file(path.resolve(__dirname, '../../test/pathCompletionFixtures/scss/main.scss')).toString(); + assertCompletions(`@import './|'`, { + items: [ + { label: '_foo.scss', resultText: `@import './foo'` } + ] + }, testSCSSUri, folders, 'scss'); + }); }); \ No newline at end of file diff --git a/extensions/css-language-features/server/src/utils/strings.ts b/extensions/css-language-features/server/src/utils/strings.ts index f7ad0845cc8..114fb4f0808 100644 --- a/extensions/css-language-features/server/src/utils/strings.ts +++ b/extensions/css-language-features/server/src/utils/strings.ts @@ -17,3 +17,17 @@ export function startsWith(haystack: string, needle: string): boolean { return true; } + +/** + * Determines if haystack ends with needle. + */ +export function endsWith(haystack: string, needle: string): boolean { + let diff = haystack.length - needle.length; + if (diff > 0) { + return haystack.lastIndexOf(needle) === diff; + } else if (diff === 0) { + return haystack === needle; + } else { + return false; + } +} diff --git a/extensions/css-language-features/server/test/pathCompletionFixtures/scss/_foo.scss b/extensions/css-language-features/server/test/pathCompletionFixtures/scss/_foo.scss new file mode 100644 index 00000000000..adae63e647c --- /dev/null +++ b/extensions/css-language-features/server/test/pathCompletionFixtures/scss/_foo.scss @@ -0,0 +1,4 @@ +/*--------------------------------------------------------------------------------------------- + * 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/css-language-features/server/test/pathCompletionFixtures/scss/main.scss b/extensions/css-language-features/server/test/pathCompletionFixtures/scss/main.scss new file mode 100644 index 00000000000..adae63e647c --- /dev/null +++ b/extensions/css-language-features/server/test/pathCompletionFixtures/scss/main.scss @@ -0,0 +1,4 @@ +/*--------------------------------------------------------------------------------------------- + * 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/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index dd17147de5b..6878038d8f8 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -194,49 +194,42 @@ supports-color@5.4.0: dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^3.0.9-next.20: - version "3.0.9-next.20" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.9-next.20.tgz#8229aee66aa877929af5d2fd81a21731b415c92e" +vscode-css-languageservice@^3.0.10-next.2: + version "3.0.10-next.2" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.10-next.2.tgz#b703af89be433507836178efd7f88bb0669fc4e8" dependencies: - vscode-languageserver-types "^3.7.2" - vscode-nls "^3.2.2" + vscode-languageserver-types "^3.10.0" + vscode-nls "^3.2.4" vscode-jsonrpc@^3.6.2: version "3.6.2" resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.6.2.tgz#3b5eef691159a15556ecc500e9a8a0dd143470c8" -vscode-languageserver-protocol-foldingprovider@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol-foldingprovider/-/vscode-languageserver-protocol-foldingprovider-2.0.1.tgz#051d0d9e58d1b79dc4681acd48f21797f5515bfd" - dependencies: - vscode-languageserver-protocol "^3.7.2" - vscode-languageserver-types "^3.7.2" - -vscode-languageserver-protocol@^3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.7.2.tgz#df58621c032139010888b6a9ddc969423f9ba9d6" +vscode-languageserver-protocol@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.10.0.tgz#f8dcdf987687f64a26e7c32d498fc781a0e886dc" dependencies: vscode-jsonrpc "^3.6.2" - vscode-languageserver-types "^3.7.2" + vscode-languageserver-types "^3.10.0" -vscode-languageserver-types@^3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.7.2.tgz#aad8846f8e3e27962648554de5a8417e358f34eb" +vscode-languageserver-types@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.10.0.tgz#944e5308f3b36a3f372c766f1a344e903ec9c389" -vscode-languageserver@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-4.1.3.tgz#937d37c955b6b9c2409388413cd6f54d1eb9fe7d" +vscode-languageserver@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-4.4.0.tgz#b6e8b37a739ccb629d92f3635f0099d191c856fa" dependencies: - vscode-languageserver-protocol "^3.7.2" - vscode-uri "^1.0.1" + vscode-languageserver-protocol "^3.10.0" + vscode-uri "^1.0.3" -vscode-nls@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.2.tgz#3817eca5b985c2393de325197cf4e15eb2aa5350" +vscode-nls@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398" -vscode-uri@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.1.tgz#11a86befeac3c4aa3ec08623651a3c81a6d0bbc8" +vscode-uri@^1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.5.tgz#3b899a8ef71c37f3054d79bdbdda31c7bf36f20d" wrappy@1: version "1.0.2" diff --git a/extensions/css-language-features/test/mocha.opts b/extensions/css-language-features/test/mocha.opts new file mode 100644 index 00000000000..20fcfb6eef6 --- /dev/null +++ b/extensions/css-language-features/test/mocha.opts @@ -0,0 +1,3 @@ +--ui tdd +--useColors true +server/out/test/**.test.js \ No newline at end of file diff --git a/extensions/css-language-features/yarn.lock b/extensions/css-language-features/yarn.lock index ac76cfede80..6338d75b098 100644 --- a/extensions/css-language-features/yarn.lock +++ b/extensions/css-language-features/yarn.lock @@ -137,29 +137,22 @@ vscode-jsonrpc@^3.6.2: version "3.6.2" resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.6.2.tgz#3b5eef691159a15556ecc500e9a8a0dd143470c8" -vscode-languageclient@^4.1.4: - version "4.1.4" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-4.1.4.tgz#fff1a6bca4714835dca7fce35bc4ce81442fdf2c" +vscode-languageclient@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-4.4.0.tgz#b05868f6477b6f0c9910b24daae4f3e8c4b65902" dependencies: - vscode-languageserver-protocol "^3.7.2" + vscode-languageserver-protocol "^3.10.0" -vscode-languageserver-protocol-foldingprovider@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol-foldingprovider/-/vscode-languageserver-protocol-foldingprovider-2.0.1.tgz#051d0d9e58d1b79dc4681acd48f21797f5515bfd" - dependencies: - vscode-languageserver-protocol "^3.7.2" - vscode-languageserver-types "^3.7.2" - -vscode-languageserver-protocol@^3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.7.2.tgz#df58621c032139010888b6a9ddc969423f9ba9d6" +vscode-languageserver-protocol@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.10.0.tgz#f8dcdf987687f64a26e7c32d498fc781a0e886dc" dependencies: vscode-jsonrpc "^3.6.2" - vscode-languageserver-types "^3.7.2" + vscode-languageserver-types "^3.10.0" -vscode-languageserver-types@^3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.7.2.tgz#aad8846f8e3e27962648554de5a8417e358f34eb" +vscode-languageserver-types@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.10.0.tgz#944e5308f3b36a3f372c766f1a344e903ec9c389" vscode-nls@^3.2.4: version "3.2.4" diff --git a/extensions/css/syntaxes/css.tmLanguage.json b/extensions/css/syntaxes/css.tmLanguage.json index f426e662f58..07977e3835a 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/f8a70d3f2540a7bf12a7cbc13c1d7bb76cc8cdd0", + "version": "https://github.com/octref/language-css/commit/aadd130de82cf2351b459041109b49f586142f11", "name": "CSS", "scopeName": "source.css", "patterns": [ @@ -508,7 +508,7 @@ ] }, { - "begin": "(?i)((@)viewport)(?=[\\s'\"{;]|/\\*|$)", + "begin": "(?i)((@)(-ms-|-o-)?viewport)(?=[\\s'\"{;]|/\\*|$)", "beginCaptures": { "1": { "name": "keyword.control.at-rule.viewport.css" diff --git a/extensions/css/test/colorize-results/test_css.json b/extensions/css/test/colorize-results/test_css.json index 6daa830028e..f4abc368d09 100644 --- a/extensions/css/test/colorize-results/test_css.json +++ b/extensions/css/test/colorize-results/test_css.json @@ -3,9 +3,9 @@ "c": "/*", "t": "source.css comment.block.css punctuation.definition.comment.begin.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -14,9 +14,9 @@ "c": " css Zen Garden default style v1.02 ", "t": "source.css comment.block.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -25,9 +25,9 @@ "c": "*/", "t": "source.css comment.block.css punctuation.definition.comment.end.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -36,9 +36,9 @@ "c": "/*", "t": "source.css comment.block.css punctuation.definition.comment.begin.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -47,9 +47,9 @@ "c": " css released under Creative Commons License - http://creativecommons.org/licenses/by-nc-sa/1.0/ ", "t": "source.css comment.block.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -58,9 +58,9 @@ "c": "*/", "t": "source.css comment.block.css punctuation.definition.comment.end.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -69,9 +69,9 @@ "c": "/*", "t": "source.css comment.block.css punctuation.definition.comment.begin.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -80,9 +80,9 @@ "c": " This file based on 'Tranquille' by Dave Shea ", "t": "source.css comment.block.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -91,9 +91,9 @@ "c": "*/", "t": "source.css comment.block.css punctuation.definition.comment.end.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -102,9 +102,9 @@ "c": "/*", "t": "source.css comment.block.css punctuation.definition.comment.begin.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -113,9 +113,9 @@ "c": " You may use this file as a foundation for any new work, but you may find it easier to start from scratch. ", "t": "source.css comment.block.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -124,9 +124,9 @@ "c": "*/", "t": "source.css comment.block.css punctuation.definition.comment.end.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -135,9 +135,9 @@ "c": "/*", "t": "source.css comment.block.css punctuation.definition.comment.begin.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -146,9 +146,9 @@ "c": " Not all elements are defined in this file, so you'll most likely want to refer to the xhtml as well. ", "t": "source.css comment.block.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -157,9 +157,9 @@ "c": "*/", "t": "source.css comment.block.css punctuation.definition.comment.end.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -168,9 +168,9 @@ "c": "/*", "t": "source.css comment.block.css punctuation.definition.comment.begin.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -179,9 +179,9 @@ "c": " Your images should be linked as if the CSS file sits in the same folder as the images. ie. no paths. ", "t": "source.css comment.block.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -190,9 +190,9 @@ "c": "*/", "t": "source.css comment.block.css punctuation.definition.comment.end.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -201,9 +201,9 @@ "c": "/*", "t": "source.css comment.block.css punctuation.definition.comment.begin.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -212,9 +212,9 @@ "c": " basic elements ", "t": "source.css comment.block.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -223,9 +223,9 @@ "c": "*/", "t": "source.css comment.block.css punctuation.definition.comment.end.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3842,9 +3842,9 @@ "c": "/*", "t": "source.css comment.block.css punctuation.definition.comment.begin.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3853,9 +3853,9 @@ "c": " specific divs ", "t": "source.css comment.block.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3864,9 +3864,9 @@ "c": "*/", "t": "source.css comment.block.css punctuation.definition.comment.end.css", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/debug-auto-launch/src/autoAttach.ts b/extensions/debug-auto-launch/src/autoAttach.ts deleted file mode 100644 index a6bb925ef4f..00000000000 --- a/extensions/debug-auto-launch/src/autoAttach.ts +++ /dev/null @@ -1,24 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -'use strict'; - -import * as vscode from 'vscode'; -import * as nls from 'vscode-nls'; -import { basename } from 'path'; -import { pollProcesses, attachToProcess } from './nodeProcessTree'; - -const localize = nls.loadMessageBundle(); - -export function startAutoAttach(rootPid: number): vscode.Disposable { - - return pollProcesses(rootPid, true, (pid, cmdPath, args) => { - const cmdName = basename(cmdPath, '.exe'); - if (cmdName === 'node') { - const name = localize('process.with.pid.label', "Process {0}", pid); - attachToProcess(undefined, name, pid, args); - } - }); -} diff --git a/extensions/debug-auto-launch/src/nodeProcessTree.ts b/extensions/debug-auto-launch/src/nodeProcessTree.ts index 16bc55476e1..1a62971bc30 100644 --- a/extensions/debug-auto-launch/src/nodeProcessTree.ts +++ b/extensions/debug-auto-launch/src/nodeProcessTree.ts @@ -95,10 +95,10 @@ export function attachToProcess(folder: vscode.WorkspaceFolder | undefined, name function findChildProcesses(rootPid: number, inTerminal: boolean, cb: (pid: number, cmd: string, args: string) => void): Promise { - function walker(node: ProcessTreeNode, terminal: boolean, renderer: number) { + function walker(node: ProcessTreeNode, terminal: boolean, terminalPids: number[]) { - if (node.args.indexOf('--type=terminal') >= 0 && (renderer === 0 || node.ppid === renderer)) { - terminal = true; + if (terminalPids.indexOf(node.pid) >= 0) { + terminal = true; // found the terminal shell } let { protocol } = analyseArguments(node.args); @@ -107,32 +107,17 @@ function findChildProcesses(rootPid: number, inTerminal: boolean, cb: (pid: numb } for (const child of node.children || []) { - walker(child, terminal, renderer); + walker(child, terminal, terminalPids); } } - function finder(node: ProcessTreeNode, pid: number): ProcessTreeNode | undefined { - if (node.pid === pid) { - return node; - } - for (const child of node.children || []) { - const p = finder(child, pid); - if (p) { - return p; - } - } - return undefined; - } - return getProcessTree(rootPid).then(tree => { if (tree) { - - // find the pid of the renderer process - const extensionHost = finder(tree, process.pid); - let rendererPid = extensionHost ? extensionHost.ppid : 0; - - for (const child of tree.children || []) { - walker(child, !inTerminal, rendererPid); + const terminals = vscode.window.terminals; + if (terminals.length > 0) { + Promise.all(terminals.map(terminal => terminal.processId)).then(terminalPids => { + walker(tree, !inTerminal, terminalPids); + }); } } }); diff --git a/extensions/docker/test/colorize-results/Dockerfile.json b/extensions/docker/test/colorize-results/Dockerfile.json index a18ec445c04..fcb2e004c16 100644 --- a/extensions/docker/test/colorize-results/Dockerfile.json +++ b/extensions/docker/test/colorize-results/Dockerfile.json @@ -179,9 +179,9 @@ "c": "#", "t": "source.dockerfile comment.line.number-sign.dockerfile punctuation.definition.comment.dockerfile", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -190,9 +190,9 @@ "c": "RUN apt-get install -y nodejs=0.6.12~dfsg1-1ubuntu1", "t": "source.dockerfile comment.line.number-sign.dockerfile", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index 406b25c97d2..d9be62f74d1 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -39,17 +39,17 @@ "inMarkupAndStylesheetFilesOnly" ], "default": "always", - "description": "%emmetShowExpandedAbbreviation%" + "markdownDescription": "%emmetShowExpandedAbbreviation%" }, "emmet.showAbbreviationSuggestions": { "type": "boolean", "default": true, - "description": "%emmetShowAbbreviationSuggestions%" + "markdownDescription": "%emmetShowAbbreviationSuggestions%" }, "emmet.includeLanguages": { "type": "object", "default": {}, - "description": "%emmetIncludeLanguages%" + "markdownDescription": "%emmetIncludeLanguages%" }, "emmet.variables": { "type": "object", @@ -183,22 +183,22 @@ "css.webkitProperties": { "type": "string", "default": null, - "description": "%emmetPreferencesCssWebkitProperties%" + "markdownDescription": "%emmetPreferencesCssWebkitProperties%" }, "css.mozProperties": { "type": "string", "default": null, - "description": "%emmetPreferencesCssMozProperties%" + "markdownDescription": "%emmetPreferencesCssMozProperties%" }, "css.oProperties": { "type": "string", "default": null, - "description": "%emmetPreferencesCssOProperties%" + "markdownDescription": "%emmetPreferencesCssOProperties%" }, "css.msProperties": { "type": "string", "default": null, - "description": "%emmetPreferencesCssMsProperties%" + "markdownDescription": "%emmetPreferencesCssMsProperties%" }, "css.fuzzySearchMinScore": { "type": "number", @@ -210,12 +210,12 @@ "emmet.showSuggestionsAsSnippets": { "type": "boolean", "default": false, - "description": "%emmetShowSuggestionsAsSnippets%" + "markdownDescription": "%emmetShowSuggestionsAsSnippets%" }, "emmet.optimizeStylesheetParsing": { "type": "boolean", "default": true, - "description": "%emmetOptimizeStylesheetParsing%" + "markdownDescription": "%emmetOptimizeStylesheetParsing%" } } }, @@ -447,8 +447,6 @@ "@emmetio/html-matcher": "^0.3.3", "@emmetio/math-expression": "^0.1.1", "image-size": "^0.5.2", - "vscode-emmet-helper": "^1.2.10", - "vscode-languageserver-types": "^3.5.0", - "vscode-nls": "3.2.4" + "vscode-emmet-helper": "^1.2.11" } } diff --git a/extensions/emmet/package.nls.json b/extensions/emmet/package.nls.json index d43ba09bc46..3f3bf912fd8 100644 --- a/extensions/emmet/package.nls.json +++ b/extensions/emmet/package.nls.json @@ -25,10 +25,10 @@ "command.decrementNumberByTen": "Decrement by 10", "emmetSyntaxProfiles": "Define profile for specified syntax or use your own profile with specific rules.", "emmetExclude": "An array of languages where Emmet abbreviations should not be expanded.", - "emmetExtensionsPath": "Path to a folder containing Emmet profiles and snippets.'", - "emmetShowExpandedAbbreviation": "Shows expanded Emmet abbreviations as suggestions.\nThe option \"inMarkupAndStylesheetFilesOnly\" applies to html, haml, jade, slim, xml, xsl, css, scss, sass, less and stylus.\nThe option \"always\" applies to all parts of the file regardless of markup/css.", - "emmetShowAbbreviationSuggestions": "Shows possible Emmet abbreviations as suggestions. Not applicable in stylesheets or when emmet.showExpandedAbbreviation is set to \"never\".", - "emmetIncludeLanguages": "Enable Emmet abbreviations in languages that are not supported by default. Add a mapping here between the language and emmet supported language.\n E.g.: {\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}", + "emmetExtensionsPath": "Path to a folder containing Emmet profiles and snippets.", + "emmetShowExpandedAbbreviation": "Shows expanded Emmet abbreviations as suggestions.\nThe option `\"inMarkupAndStylesheetFilesOnly\"` applies to html, haml, jade, slim, xml, xsl, css, scss, sass, less and stylus.\nThe option `\"always\"` applies to all parts of the file regardless of markup/css.", + "emmetShowAbbreviationSuggestions": "Shows possible Emmet abbreviations as suggestions. Not applicable in stylesheets or when emmet.showExpandedAbbreviation is set to `\"never\"`.", + "emmetIncludeLanguages": "Enable Emmet abbreviations in languages that are not supported by default. Add a mapping here between the language and emmet supported language.\n E.g.: `{\"vue-html\": \"html\", \"javascript\": \"javascriptreact\"}`", "emmetVariables": "Variables to be used in Emmet snippets", "emmetTriggerExpansionOnTab": "When enabled, Emmet abbreviations are expanded when pressing TAB.", "emmetPreferences": "Preferences used to modify behavior of some actions and resolvers of Emmet.", @@ -40,7 +40,7 @@ "emmetPreferencesCssBetween": "Symbol to be placed at the between CSS property and value when expanding CSS abbreviations", "emmetPreferencesSassBetween": "Symbol to be placed at the between CSS property and value when expanding CSS abbreviations in Sass files", "emmetPreferencesStylusBetween": "Symbol to be placed at the between CSS property and value when expanding CSS abbreviations in Stylus files", - "emmetShowSuggestionsAsSnippets": "If true, then Emmet suggestions will show up as snippets allowing you to order them as per editor.snippetSuggestions setting.", + "emmetShowSuggestionsAsSnippets": "If `true`, then Emmet suggestions will show up as snippets allowing you to order them as per `#editor.snippetSuggestions#` setting.", "emmetPreferencesBemElementSeparator": "Element separator used for classes when using the BEM filter", "emmetPreferencesBemModifierSeparator": "Modifier separator used for classes when using the BEM filter", "emmetPreferencesFilterCommentBefore": "A definition of comment that should be placed before matched element when comment filter is applied.", @@ -54,5 +54,5 @@ "emmetPreferencesCssOProperties": "Comma separated CSS properties that get the 'o' vendor prefix when used in Emmet abbreviation that starts with `-`. Set to empty string to always avoid the 'o' prefix.", "emmetPreferencesCssMsProperties": "Comma separated CSS properties that get the 'ms' vendor prefix when used in Emmet abbreviation that starts with `-`. Set to empty string to always avoid the 'ms' prefix.", "emmetPreferencesCssFuzzySearchMinScore": "The minimum score (from 0 to 1) that fuzzy-matched abbreviation should achieve. Lower values may produce many false-positive matches, higher values may reduce possible matches.", - "emmetOptimizeStylesheetParsing": "When set to false, the whole file is parsed to determine if current position is valid for expanding Emmet abbreviations. When set to true, only the content around the current position in css/scss/less files is parsed." + "emmetOptimizeStylesheetParsing": "When set to `false`, the whole file is parsed to determine if current position is valid for expanding Emmet abbreviations. When set to `true`, only the content around the current position in css/scss/less files is parsed." } diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index 5ec3f401a70..26444af31e5 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -498,6 +498,12 @@ export function isValidLocationForEmmetAbbreviation(document: vscode.TextDocumen i--; continue; } + // Fix for https://github.com/Microsoft/vscode/issues/55411 + // A space is not a valid character right after < in a tag name. + if (/\s/.test(char) && textToBackTrack[i] === startAngle) { + i--; + continue; + } if (char !== startAngle && char !== endAngle) { continue; } diff --git a/extensions/emmet/src/test/abbreviationAction.test.ts b/extensions/emmet/src/test/abbreviationAction.test.ts index 14286654d05..b5fb87d8b07 100644 --- a/extensions/emmet/src/test/abbreviationAction.test.ts +++ b/extensions/emmet/src/test/abbreviationAction.test.ts @@ -466,6 +466,16 @@ suite('Tests for jsx, xml and xsl', () => { }); }); + test('Expand abbreviation with condition containing less than sign for jsx', () => { + return withRandomFileEditor('if (foo < 10) { span.bar', 'javascriptreact', (editor, doc) => { + editor.selection = new Selection(0, 27, 0, 27); + return expandEmmetAbbreviation({ language: 'javascriptreact' }).then(() => { + assert.equal(editor.document.getText(), 'if (foo < 10) { '); + return Promise.resolve(); + }); + }); + }); + test('No expanding text inside open tag in completion list (jsx)', () => { return testNoCompletion('jsx', htmlContents, new Selection(2, 4, 2, 4)); }); diff --git a/extensions/emmet/yarn.lock b/extensions/emmet/yarn.lock index 040417ab779..e910402265e 100644 --- a/extensions/emmet/yarn.lock +++ b/extensions/emmet/yarn.lock @@ -2115,26 +2115,18 @@ vinyl@~2.0.1: remove-trailing-separator "^1.0.1" replace-ext "^1.0.0" -vscode-emmet-helper@^1.2.10: - version "1.2.10" - resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-1.2.10.tgz#c4e08c721fa379f57e53c6bdf2843d4b3c830d16" +vscode-emmet-helper@^1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-1.2.11.tgz#4de78223666bf917eb6dc4b225b6c40f6901950c" dependencies: "@emmetio/extract-abbreviation" "0.1.6" jsonc-parser "^1.0.0" vscode-languageserver-types "^3.6.0-next.1" -vscode-languageserver-types@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.5.0.tgz#e48d79962f0b8e02de955e3f524908e2b19c0374" - vscode-languageserver-types@^3.6.0-next.1: version "3.6.0-next.1" resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.6.0-next.1.tgz#98e488d3f87b666b4ee1a3d89f0023e246d358f3" -vscode-nls@3.2.4: - version "3.2.4" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398" - vscode@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.0.1.tgz#3d161200615fe2af1d92ddc650751159411a513b" diff --git a/extensions/extension-editing/package.nls.json b/extensions/extension-editing/package.nls.json index 9265e6a3c9d..263fb6bc870 100644 --- a/extensions/extension-editing/package.nls.json +++ b/extensions/extension-editing/package.nls.json @@ -1,4 +1,4 @@ { - "displayName": "Package File Editing", - "description": "Provides IntelliSense for VS Code extension points and linting capabilities in package.json files." + "displayName": "Extension Authoring", + "description": "Provides linting capabilities for authoring extensions." } \ No newline at end of file diff --git a/extensions/extension-editing/src/extensionLinter.ts b/extensions/extension-editing/src/extensionLinter.ts index 0e486252598..2724038bdf7 100644 --- a/extensions/extension-editing/src/extensionLinter.ts +++ b/extensions/extension-editing/src/extensionLinter.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; import * as path from 'path'; import * as nls from 'vscode-nls'; @@ -264,13 +263,16 @@ export class ExtensionLinter { } private async loadPackageJson(folder: Uri) { + if (folder.scheme === 'git') { // #36236 + return undefined; + } const file = folder.with({ path: path.posix.join(folder.path, 'package.json') }); - const exists = await fileExists(file.fsPath); - if (!exists) { + try { + const document = await workspace.openTextDocument(file); + return parseTree(document.getText()); + } catch (err) { return undefined; } - const document = await workspace.openTextDocument(file); - return parseTree(document.getText()); } private packageJsonChanged(folder: Uri) { @@ -338,20 +340,6 @@ function endsWith(haystack: string, needle: string): boolean { } } -function fileExists(path: string): Promise { - return new Promise((resolve, reject) => { - fs.lstat(path, (err, stats) => { - if (!err) { - resolve(true); - } else if (err.code === 'ENOENT') { - resolve(false); - } else { - reject(err); - } - }); - }); -} - function parseUri(src: string) { try { return Uri.parse(src); diff --git a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json b/extensions/fsharp/syntaxes/fsharp.tmLanguage.json index 84356109cde..4b280955153 100644 --- a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json +++ b/extensions/fsharp/syntaxes/fsharp.tmLanguage.json @@ -4,19 +4,19 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/ionide/ionide-fsgrammar/commit/bd8d1225f93894a50bc8da6f5a76409b024d3d22", + "version": "https://github.com/ionide/ionide-fsgrammar/commit/67c9f45ebbbd5a12d89ffad1661caf8452f1d552", "name": "fsharp", "scopeName": "source.fsharp", "patterns": [ + { + "include": "#compiler_directives" + }, { "include": "#comments" }, { "include": "#constants" }, - { - "include": "#structure" - }, { "include": "#strings" }, @@ -30,10 +30,10 @@ "include": "#definition" }, { - "include": "#attributes" + "include": "#abstract_definition" }, { - "include": "#method_calls" + "include": "#attributes" }, { "include": "#modules" @@ -44,6 +44,9 @@ { "include": "#du_declaration" }, + { + "include": "#record_declaration" + }, { "include": "#keywords" }, @@ -58,6 +61,96 @@ } ], "repository": { + "generic_declaration": { + "patterns": [ + { + "match": "([^<>,])", + "captures": { + "1": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "begin": "(<)", + "end": "(>)", + "beginCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "patterns": [ + { + "match": "([^<>,])", + "captures": { + "1": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "include": "#generic_declaration" + } + ] + }, + { + "include": "#keywords" + } + ] + }, + "record_signature": { + "patterns": [ + { + "match": "[[:alpha:]0-9'`^_ ]+(=)([[:alpha:]0-9'`^_ ]+)", + "captures": { + "1": { + "name": "keyword.symbol.fsharp" + }, + "2": { + "name": "variable.parameter.fsharp" + } + } + }, + { + "begin": "({)", + "end": "(})", + "beginCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "patterns": [ + { + "match": "[[:alpha:]0-9'`^_ ]+(=)([[:alpha:]0-9'`^_ ]+)", + "captures": { + "1": { + "name": "keyword.symbol.fsharp" + }, + "2": { + "name": "variable.parameter.fsharp" + } + } + }, + { + "include": "#record_signature" + } + ] + }, + { + "include": "#keywords" + } + ] + }, "anonymous_functions": { "patterns": [ { @@ -66,15 +159,32 @@ "end": "(->)", "beginCaptures": { "1": { - "name": "keyword.other.function-definition.fsharp" + "name": "keyword.fsharp" } }, "endCaptures": { "1": { - "name": "keyword.other.fsharp" + "name": "keyword.fsharp" } }, "patterns": [ + { + "include": "#comments" + }, + { + "include": "#member_declaration" + }, + { + "match": "(:)(\\s*([?[:alpha:]0-9'`<>^._ ]+))*", + "captures": { + "1": { + "name": "keyword.symbol.fsharp" + }, + "2": { + "name": "entity.name.type.fsharp" + } + } + }, { "include": "#variables" } @@ -87,7 +197,7 @@ { "name": "support.function.attribute.fsharp", "begin": "\\[\\<", - "end": "\\>\\]", + "end": "\\>\\]|\\]", "patterns": [ { "include": "$self" @@ -178,57 +288,189 @@ } ] }, + "abstract_definition": { + "name": "abstract.definition.fsharp", + "begin": "\\b(abstract)\\s+(member)?(\\s+\\[\\<.*\\>\\])?\\s*([_[:alpha:]0-9,\\._`\\s]+)(:)", + "end": "\\s*(with)\\b|=|$", + "beginCaptures": { + "1": { + "name": "keyword.fsharp" + }, + "2": { + "name": "keyword.fsharp" + }, + "3": { + "name": "support.function.attribute.fsharp" + }, + "5": { + "name": "keyword.fsharp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.fsharp" + } + }, + "patterns": [ + { + "include": "#comments" + }, + { + "include": "#common_declaration" + }, + { + "match": "\\?{0,1}([[:alpha:]0-9'`^._ ]+)\\s*(:)(\\s*([?[:alpha:]0-9'`^._ ]+)){0,1}", + "captures": { + "1": { + "name": "variable.parameter.fsharp" + }, + "2": { + "name": "keyword.symbol.fsharp" + }, + "3": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "match": "(?!with|get|set\\b)\\b([\\w0-9'`^._]+)", + "comments": "Here we need the \\w modifier in order to check that the words isn't blacklisted", + "captures": { + "1": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "include": "#keywords" + } + ] + }, "definition": { "patterns": [ { "name": "binding.fsharp", - "begin": "\\b(val mutable|val|let mutable|let inline|let|member|static member|override|let!)(\\s+rec|mutable)?(\\s+\\[\\<.*\\>\\])?\\s*(private|internal|public)?\\s+(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9,\\._]|(?<=,)\\s)*|``[_[:alpha:]]([_[:alpha:]0-9,\\._`\\s]|(?<=,)\\s)*)?", - "end": "((``.*``)|(with)\\b|=|$)", + "begin": "\\b(val mutable|val|let mutable|let inline|let|member val|member|static 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": { - "name": "keyword.other.binding.fsharp" + "name": "keyword.fsharp" }, "2": { - "name": "keyword.other.function-recursive.fsharp" + "name": "keyword.fsharp" }, "3": { "name": "support.function.attribute.fsharp" }, "4": { - "name": "keyword.other.access.fsharp" + "name": "keyword.fsharp" }, "5": { - "name": "variable.other.binding.fsharp" + "name": "variable.fsharp" } }, "endCaptures": { "1": { - "name": "keyword.other.fsharp" - }, - "2": { - "name": "variable.other.binding.fsharp" - }, - "3": { - "name": "keyword.other.fsharp" + "name": "keyword.fsharp" } }, "patterns": [ { - "include": "#variables" + "include": "#comments" }, { - "include": "#member_declaration" - }, - { - "match": "(:)(\\s*([?[:alpha:]0-9'<>^._ ]+))*", + "match": "(:)\\s*(\\()?\\s*(([?[:alpha:]0-9'`^._ ]+))*", "captures": { "1": { - "name": "keyword.other.fsharp" + "name": "keyword.symbol.fsharp" + }, + "2": { + "name": "keyword.symbol.fsharp" + }, + "3": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "match": "(\\*\\s*\\()\\s*(([?[:alpha:]0-9'`^._ ]+))*", + "captures": { + "1": { + "name": "keyword.symbol.fsharp" }, "2": { "name": "entity.name.type.fsharp" } } + }, + { + "match": "(->)\\s*(\\()?\\s*([?[:alpha:]0-9'`^._ ]+)*", + "captures": { + "1": { + "name": "keyword.symbol.fsharp" + }, + "2": { + "name": "keyword.symbol.fsharp" + }, + "3": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "match": "(\\*)(\\s*([?[:alpha:]0-9'`^._ ]+))*", + "captures": { + "1": { + "name": "keyword.symbol.fsharp" + }, + "2": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "begin": "(<)", + "end": "(>)", + "beginCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "patterns": [ + { + "include": "#generic_declaration" + } + ] + }, + { + "begin": "({)", + "end": "(})", + "beginCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "patterns": [ + { + "include": "#record_signature" + } + ] + }, + { + "include": "#variables" + }, + { + "include": "#keywords" } ] } @@ -242,23 +484,26 @@ "end": "$|(\\|)", "beginCaptures": { "1": { - "name": "keyword.other.fsharp" + "name": "keyword.fsharp" } }, "endCaptures": { "1": { - "name": "keyword.other.fsharp" + "name": "keyword.symbol.fsharp" } }, "patterns": [ { - "match": "([[:alpha:]0-9'`<>^._]+)\\s*(:)\\s*([[:alpha:]0-9'`<>^._]+)", + "include": "#comments" + }, + { + "match": "([[:alpha:]0-9'`<>^._]+|``[[:alpha:]0-9' <>^._]+``)\\s*(:)\\s*([[:alpha:]0-9'`<>^._]+|``[[:alpha:]0-9' <>^._]+``)", "captures": { "1": { "name": "variable.parameter.fsharp" }, "2": { - "name": "keyword.other.fsharp" + "name": "keyword.symbol.fsharp" }, "3": { "name": "entity.name.type.fsharp" @@ -266,17 +511,7 @@ } }, { - "match": "([[:alpha:]0-9'`<>^._]+)", - "captures": { - "1": { - "name": "entity.name.type.fsharp" - } - } - }, - { - "begin": "\\(", - "end": "\\)", - "match": "([[:alpha:]0-9'`<>^._]+)", + "match": "([[:alpha:]0-9'`^._]+)|``([[:alpha:]0-9'^._ ]+)``", "captures": { "1": { "name": "entity.name.type.fsharp" @@ -293,17 +528,12 @@ "keywords": { "patterns": [ { - "name": "keyword.other.fsharp", + "name": "keyword.fsharp", "match": "\\b(private|to|public|internal|function|yield!|yield|class|exception|match|delegate|of|new|in|as|if|then|else|elif|for|begin|end|inherit|do|let\\!|return\\!|return|interface|with|abstract|property|union|enum|member|try|finally|and|when|use|use\\!|struct|while|mutable)(?!')\\b" }, { - "name": "meta.preprocessor.fsharp", - "begin": "^\\s*#\\s*(light)\\b", - "end": "(\\s|$)" - }, - { - "name": "keyword.other.fsharp", - "match": "(&&&|\\|\\|\\||\\^\\^\\^|~~~|<<<|>>>|\\|>|\\->|\\<\\-|:>|:\\?>|:|\\[|\\]|\\;|<>|=|@|\\|\\||&&|{|}|\\||_|\\.\\.|\\+|\\-|\\*|\\/|\\^|\\!|\\>|\\>\\=|\\>\\>|\\<|\\<\\=|\\<\\<)" + "name": "keyword.symbol.fsharp", + "match": "(&&&|\\|\\|\\||\\^\\^\\^|~~~|<<<|>>>|\\|>|\\->|\\<\\-|:>|:\\?>|:|\\[|\\]|\\;|<>|=|@|\\|\\||&&|{|}|\\||_|\\.\\.|\\,|\\+|\\-|\\*|\\/|\\^|\\!|\\>|\\>\\=|\\>\\>|\\<|\\<\\=|\\(|\\)|\\<\\<)" } ] }, @@ -312,18 +542,23 @@ { "name": "entity.name.section.fsharp", "begin": "\\b(namespace|module)\\s*(public|internal|private)?\\s+([[:alpha:]][[:alpha:]0-9'_. ]*)", - "end": "(\\s|$)", + "end": "(\\s?=|\\s|$)", "beginCaptures": { "1": { - "name": "keyword.other.fsharp" + "name": "keyword.fsharp" }, "2": { - "name": "keyword.other.fsharp" + "name": "keyword.fsharp" }, "3": { "name": "entity.name.section.fsharp" } }, + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, "patterns": [ { "name": "entity.name.section.fsharp", @@ -345,7 +580,7 @@ "end": "(\\s|$)", "beginCaptures": { "1": { - "name": "keyword.other.fsharp" + "name": "keyword.fsharp" }, "2": { "name": "entity.name.section.fsharp" @@ -372,7 +607,7 @@ "end": "(\\s|$)", "beginCaptures": { "1": { - "name": "keyword.other.namespace-definition.fsharp" + "name": "keyword.fsharp" }, "2": { "name": "entity.name.type.namespace.fsharp" @@ -485,7 +720,7 @@ "match": "(%0?-?(\\d+)?((a|t)|(\\.\\d+)?(f|F|e|E|g|G|M)|(b|c|s|d|i|x|X|o)|(s|b|O)|(\\+?A)))", "captures": { "1": { - "name": "keyword.other.format.specifier.fsharp" + "name": "keyword.format.specifier.fsharp" } } } @@ -499,29 +734,132 @@ }, { "name": "variable.parameter.fsharp", - "match": "[[:alpha:]'_]\\w*" + "match": "[[:alpha:]0-9'`<>^._ ]\\w*" + } + ] + }, + "common_declaration": { + "patterns": [ + { + "begin": "\\s*(->)\\s*([[:alpha:]0-9'`^._ ]+)(<)", + "end": "(>)", + "beginCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + }, + "2": { + "name": "entity.name.type.fsharp" + }, + "3": { + "name": "keyword.symbol.fsharp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "patterns": [ + { + "match": "([[:alpha:]0-9'`^._ ]+)", + "captures": { + "1": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "include": "#keywords" + } + ] + }, + { + "match": "\\s*(->)\\s*([[:alpha:]0-9'`^._ ]+)", + "captures": { + "1": { + "name": "keyword.symbol.fsharp" + }, + "2": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "begin": "\\?{0,1}([[:alpha:]0-9'`^._ ]+)\\s*(:)(\\s*([?[:alpha:]0-9'`^._ ]+)(<))", + "end": "(>)", + "beginCaptures": { + "1": { + "name": "variable.parameter.fsharp" + }, + "2": { + "name": "keyword.symbol.fsharp" + }, + "3": { + "name": "keyword.symbol.fsharp" + }, + "4": { + "name": "entity.name.type.fsharp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "patterns": [ + { + "match": "([[:alpha:]0-9'`^._ ]+)", + "captures": { + "1": { + "name": "entity.name.type.fsharp" + } + } + }, + { + "include": "#keywords" + } + ] } ] }, "member_declaration": { "patterns": [ { - "begin": "\\(", - "end": "\\)", + "begin": "(\\()", + "end": "(\\))", + "beginCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "endCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, "patterns": [ { - "match": "\\?{0,1}([[:alpha:]0-9'`<>^._]+)\\s*(:{0,1})(\\s*([?[:alpha:]0-9'<>^._ ]+)){0,1}", + "include": "#comments" + }, + { + "include": "#common_declaration" + }, + { + "match": "\\?{0,1}([[:alpha:]0-9'`^._ ]+)\\s*(:{0,1})(\\s*([?[:alpha:]0-9'`<>^._ ]+)){0,1}", "captures": { "1": { "name": "variable.parameter.fsharp" }, "2": { - "name": "keyword.other.fsharp" + "name": "keyword.symbol.fsharp" }, "3": { "name": "entity.name.type.fsharp" } } + }, + { + "include": "#keywords" } ] } @@ -550,45 +888,210 @@ "patterns": [ { "name": "record.fsharp", - "begin": "(type)[\\s]+(private|internal|public)?[\\s]*([[:alpha:]0-9'<>^:,._]+)[\\s]?(private|internal|public)?[\\s]*", - "end": "[\\s]*((with)|((as) ([[:alpha:]0-9']+))|(=)|[\\n=]|(\\(\\)))", + "begin": "\\b(type)[\\s]+(private|internal|public)?(\\s*\\[\\<.*\\>\\])?[\\s]*([[:alpha:]0-9'`^:,._]+|``[[:alpha:]0-9'`^:,._ ]+``)(<)", + "end": "\\s*((with)|((as)\\s*([[:alpha:]0-9']+))|(=)|[\\n=]|(\\(\\)))", "beginCaptures": { "1": { - "name": "keyword.other.fsharp" + "name": "keyword.fsharp" }, "2": { - "name": "keyword.other.fsharp" + "name": "keyword.fsharp" }, "3": { - "name": "entity.name.type.fsharp" + "name": "support.function.attribute.fsharp" }, "4": { - "name": "keyword.other.fsharp" + "name": "entity.name.type.fsharp" + }, + "5": { + "name": "keyword.symbol.fsharp" } }, "endCaptures": { "2": { - "name": "keyword.other.fsharp" + "name": "keyword.fsharp" }, "3": { - "name": "keyword.other.fsharp" + "name": "keyword.fsharp" }, "4": { - "name": "keyword.other.fsharp" + "name": "keyword.fsharp" }, "5": { "name": "variable.parameter.fsharp" }, "6": { - "name": "keyword.other.fsharp" + "name": "keyword.symbol.fsharp" }, "7": { "name": "constant.language.unit.fsharp" } }, "patterns": [ + { + "include": "#comments" + }, + { + "match": "\\s*(>)\\s*(private|internal|public)?", + "captures": { + "1": { + "name": "keyword.symbol.fsharp" + }, + "2": { + "name": "keyword.fsharp" + } + } + }, + { + "match": "([[:alpha:]0-9'`^._ ]+)", + "captures": { + "1": { + "name": "entity.name.type.fsharp" + } + } + }, { "include": "#member_declaration" + }, + { + "include": "#keywords" + } + ] + }, + { + "name": "record.fsharp", + "begin": "\\b(type)[\\s]+(private|internal|public)?(\\s*\\[\\<.*\\>\\])?[\\s]*([[:alpha:]0-9'`^:,._]+<(.*)>|[[:alpha:]0-9'^:,._]+|``[[:alpha:]0-9'`^:,._ ]+``)[\\s]?(private|internal|public)?[\\s]*", + "end": "\\s*((with)|((as)\\s*([[:alpha:]0-9']+))|(=)|[\\n=]|(\\(\\)))", + "beginCaptures": { + "1": { + "name": "keyword.fsharp" + }, + "2": { + "name": "keyword.fsharp" + }, + "3": { + "name": "support.function.attribute.fsharp" + }, + "4": { + "name": "entity.name.type.fsharp" + }, + "6": { + "name": "keyword.fsharp" + } + }, + "endCaptures": { + "2": { + "name": "keyword.fsharp" + }, + "3": { + "name": "keyword.fsharp" + }, + "4": { + "name": "keyword.fsharp" + }, + "5": { + "name": "variable.parameter.fsharp" + }, + "6": { + "name": "keyword.symbol.fsharp" + }, + "7": { + "name": "constant.language.unit.fsharp" + } + }, + "patterns": [ + { + "include": "#comments" + }, + { + "include": "#member_declaration" + } + ] + } + ] + }, + "record_declaration": { + "patterns": [ + { + "begin": "(\\{)", + "beginCaptures": { + "1": { + "name": "keyword.symbol.fsharp" + } + }, + "end": "(?<=\\})", + "patterns": [ + { + "include": "#comments" + }, + { + "begin": "(((mutable)\\s[[:alpha:]]+)|[[:alpha:]0-9'`<>^._]*)\\s*((?=", - "t": "source.fsharp keyword.other.fsharp", + "t": "source.fsharp keyword.symbol.fsharp", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -925,7 +1046,7 @@ }, { "c": "override", - "t": "source.fsharp binding.fsharp keyword.other.binding.fsharp", + "t": "source.fsharp binding.fsharp keyword.fsharp", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -947,7 +1068,7 @@ }, { "c": "this.ToString", - "t": "source.fsharp binding.fsharp variable.other.binding.fsharp", + "t": "source.fsharp binding.fsharp variable.fsharp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -958,13 +1079,13 @@ }, { "c": " ", - "t": "source.fsharp binding.fsharp", + "t": "source.fsharp binding.fsharp variable.parameter.fsharp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" + "hc_black": "variable: #9CDCFE" } }, { @@ -991,7 +1112,7 @@ }, { "c": "=", - "t": "source.fsharp binding.fsharp keyword.other.fsharp", + "t": "source.fsharp binding.fsharp keyword.fsharp", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -1057,7 +1178,7 @@ }, { "c": "+", - "t": "source.fsharp keyword.other.fsharp", + "t": "source.fsharp keyword.symbol.fsharp", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -1079,7 +1200,7 @@ }, { "c": "+", - "t": "source.fsharp keyword.other.fsharp", + "t": "source.fsharp keyword.symbol.fsharp", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -1145,7 +1266,7 @@ }, { "c": "+", - "t": "source.fsharp keyword.other.fsharp", + "t": "source.fsharp keyword.symbol.fsharp", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -1211,7 +1332,7 @@ }, { "c": "+", - "t": "source.fsharp keyword.other.fsharp", + "t": "source.fsharp keyword.symbol.fsharp", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -1221,7 +1342,51 @@ } }, { - "c": " (string)internalAge", + "c": " ", + "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": "string", + "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": "internalAge", "t": "source.fsharp", "r": { "dark_plus": "default: #D4D4D4", diff --git a/extensions/git/package.json b/extensions/git/package.json index e455ec699ad..0eba9ba5c94 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -885,7 +885,7 @@ "string", "null" ], - "description": "%config.path%", + "markdownDescription": "%config.path%", "default": null, "scope": "application" }, @@ -900,6 +900,12 @@ "subFolders", "openEditors" ], + "enumDescriptions": [ + "%config.autoRepositoryDetection.true%", + "%config.autoRepositoryDetection.false%", + "%config.autoRepositoryDetection.subFolders%", + "%config.autoRepositoryDetection.openEditors%" + ], "description": "%config.autoRepositoryDetection%", "default": true }, @@ -911,7 +917,10 @@ "git.autofetch": { "type": "boolean", "description": "%config.autofetch%", - "default": false + "default": false, + "tags": [ + "usesOnlineServices" + ] }, "git.confirmSync": { "type": "boolean", @@ -925,6 +934,11 @@ "tracked", "off" ], + "enumDescriptions": [ + "%config.countBadge.all%", + "%config.countBadge.tracked%", + "%config.countBadge.off%" + ], "description": "%config.countBadge%", "default": "all" }, @@ -936,7 +950,13 @@ "tags", "remote" ], - "description": "%config.checkoutType%", + "enumDescriptions": [ + "%config.checkoutType.all%", + "%config.checkoutType.local%", + "%config.checkoutType.tags%", + "%config.checkoutType.remote%" + ], + "markdownDescription": "%config.checkoutType%", "default": "all" }, "git.ignoreLegacyWarning": { @@ -1042,7 +1062,7 @@ "id": "gitDecoration.modifiedResourceForeground", "description": "%colors.modified%", "defaults": { - "light": "#a76e12", + "light": "#895503", "dark": "#E2C08D", "highContrast": "#E2C08D" } @@ -1060,7 +1080,7 @@ "id": "gitDecoration.untrackedResourceForeground", "description": "%colors.untracked%", "defaults": { - "light": "#019001", + "light": "#018101", "dark": "#73C991", "highContrast": "#73C991" } @@ -1177,7 +1197,7 @@ "file-type": "^7.2.0", "iconv-lite": "0.4.19", "jschardet": "^1.6.0", - "vscode-extension-telemetry": "0.0.17", + "vscode-extension-telemetry": "0.0.18", "vscode-nls": "^3.2.4", "which": "^1.3.0" }, diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 7dc623f9c8e..bc4c812d75b 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -50,23 +50,34 @@ "command.stash": "Stash", "command.stashPop": "Pop Stash...", "command.stashPopLatest": "Pop Latest Stash", - "config.enabled": "Whether git is enabled", - "config.path": "Path to the git executable", + "config.enabled": "Whether git is enabled.", + "config.path": "Path and filename of the git executable, e.g. `C:\\Program Files\\Git\\bin\\git.exe` (Windows).", "config.autoRepositoryDetection": "Configures when repositories should be automatically detected.", - "config.autorefresh": "Whether auto refreshing is enabled", - "config.autofetch": "Whether auto fetching is enabled", - "config.enableLongCommitWarning": "Whether long commit messages should be warned about", - "config.confirmSync": "Confirm before synchronizing git repositories", - "config.countBadge": "Controls the git badge counter. `all` counts all changes. `tracked` counts only the tracked changes. `off` turns it off.", - "config.checkoutType": "Controls what type of branches are listed when running `Checkout to...`. `all` shows all refs, `local` shows only the local branches, `tags` shows only tags and `remote` shows only remote branches.", - "config.ignoreLegacyWarning": "Ignores the legacy Git warning", - "config.ignoreMissingGitWarning": "Ignores the warning when Git is missing", - "config.ignoreLimitWarning": "Ignores the warning when there are too many changes in a repository", - "config.defaultCloneDirectory": "The default location where to clone a git repository", + "config.autoRepositoryDetection.true": "Scan for both subfolders of the current opened folder and parent folders of open files.", + "config.autoRepositoryDetection.false": "Disable automatic repository scanning.", + "config.autoRepositoryDetection.subFolders": "Scan for subfolders of the currently opened folder.", + "config.autoRepositoryDetection.openEditors": "Scan for parent folders of open files.", + "config.autorefresh": "Whether auto refreshing is enabled.", + "config.autofetch": "Whether auto fetching is enabled.", + "config.enableLongCommitWarning": "Whether long commit messages should be warned about.", + "config.confirmSync": "Confirm before synchronizing git repositories.", + "config.countBadge": "Controls the git badge counter.", + "config.countBadge.all": "Count all changes.", + "config.countBadge.tracked": "Count only tracked changes.", + "config.countBadge.off": "Turn off counter.", + "config.checkoutType": "Controls what type of branches are listed when running `Checkout to...`.", + "config.checkoutType.all": "Show all references.", + "config.checkoutType.local": "Show only local branches.", + "config.checkoutType.tags": "Show only tags.", + "config.checkoutType.remote": "Show only remote branches.", + "config.ignoreLegacyWarning": "Ignores the legacy Git warning.", + "config.ignoreMissingGitWarning": "Ignores the warning when Git is missing.", + "config.ignoreLimitWarning": "Ignores the warning when there are too many changes in a repository.", + "config.defaultCloneDirectory": "The default location to clone a git repository.", "config.enableSmartCommit": "Commit all changes when there are no staged changes.", "config.enableCommitSigning": "Enables commit signing with GPG.", "config.discardAllScope": "Controls what changes are discarded by the `Discard all changes` command. `all` discards all changes. `tracked` discards only tracked files. `prompt` shows a prompt dialog every time the action is run.", - "config.decorations.enabled": "Controls if Git contributes colors and badges to the explorer and the open editors view.", + "config.decorations.enabled": "Controls whether Git contributes colors and badges to the explorer and the open editors view.", "config.promptToSaveFilesBeforeCommit": "Controls whether Git should check for unsaved files before committing.", "config.showInlineOpenFileAction": "Controls whether to show an inline Open File action in the Git changes view.", "config.showPushSuccessNotification": "Controls whether to show a notification when a push is successful.", @@ -74,8 +85,8 @@ "config.detectSubmodules": "Controls whether to automatically detect git submodules.", "colors.added": "Color for added resources.", "config.detectSubmodulesLimit": "Controls the limit of git submodules detected.", - "config.alwaysSignOff": "Controls the signoff flag for all commits", - "config.ignoredRepositories": "List of git repositories to ignore", + "config.alwaysSignOff": "Controls the signoff flag for all commits.", + "config.ignoredRepositories": "List of git repositories to ignore.", "colors.modified": "Color for modified resources.", "colors.deleted": "Color for deleted resources.", "colors.untracked": "Color for untracked resources.", diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 19c3cbdbe59..2e1c115c14f 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -1021,8 +1021,8 @@ export class CommandCenter { if (unsavedTextDocuments.length > 0) { const message = unsavedTextDocuments.length === 1 - ? localize('unsaved files single', "The following file is unsaved: {0}.\n\nWould you like to save it before comitting?", path.basename(unsavedTextDocuments[0].uri.fsPath)) - : localize('unsaved files', "There are {0} unsaved files.\n\nWould you like to save them before comitting?", unsavedTextDocuments.length); + ? localize('unsaved files single', "The following file is unsaved: {0}.\n\nWould you like to save it before committing?", path.basename(unsavedTextDocuments[0].uri.fsPath)) + : localize('unsaved files', "There are {0} unsaved files.\n\nWould you like to save them before committing?", unsavedTextDocuments.length); const saveAndCommit = localize('save and commit', "Save All & Commit"); const commit = localize('commit', "Commit Anyway"); const pick = await window.showWarningMessage(message, { modal: true }, saveAndCommit, commit); diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index a6d79456ed2..a360264a394 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -345,7 +345,7 @@ function getGitErrorCode(stderr: string): string | undefined { return GitErrorCodes.RepositoryIsLocked; } else if (/Authentication failed/.test(stderr)) { return GitErrorCodes.AuthenticationFailed; - } else if (/Not a git repository/.test(stderr)) { + } else if (/Not a git repository/i.test(stderr)) { return GitErrorCodes.NotAGitRepository; } else if (/bad config file/.test(stderr)) { return GitErrorCodes.BadConfigFile; diff --git a/extensions/git/syntaxes/git-rebase.tmLanguage.json b/extensions/git/syntaxes/git-rebase.tmLanguage.json index 3ee481bede1..a2c116bd09b 100644 --- a/extensions/git/syntaxes/git-rebase.tmLanguage.json +++ b/extensions/git/syntaxes/git-rebase.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/textmate/git.tmbundle/commit/d1db42c2d71948662098183a6df519fb53a7a15b", + "version": "https://github.com/textmate/git.tmbundle/commit/3f6ad2138200db14b57a090ecb2d2e733275ca3e", "name": "Git Rebase Message", "scopeName": "text.git-rebase", "patterns": [ @@ -29,7 +29,23 @@ "name": "meta.commit-message.git-rebase" } }, - "match": "^\\s*(pick|p|reword|r|edit|e|squash|s|fixup|f|exec|x|drop|d)\\s+([0-9a-f]+)\\s+(.*)$", + "match": "^\\s*(pick|p|reword|r|edit|e|squash|s|fixup|f|drop|d)\\s+([0-9a-f]+)\\s+(.*)$", + "name": "meta.commit-command.git-rebase" + }, + { + "captures": { + "1": { + "name": "support.function.git-rebase" + }, + "2": { + "patterns": [ + { + "include": "source.shell" + } + ] + } + }, + "match": "^\\s*(exec|x)\\s+(.*)$", "name": "meta.commit-command.git-rebase" } ] diff --git a/extensions/git/test/colorize-results/COMMIT_EDITMSG.json b/extensions/git/test/colorize-results/COMMIT_EDITMSG.json index b5457404c8e..508e3ee199c 100644 --- a/extensions/git/test/colorize-results/COMMIT_EDITMSG.json +++ b/extensions/git/test/colorize-results/COMMIT_EDITMSG.json @@ -25,9 +25,9 @@ "c": "#", "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -36,9 +36,9 @@ "c": " Please enter the commit message for your changes. Lines starting", "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -47,9 +47,9 @@ "c": "#", "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -58,9 +58,9 @@ "c": " with '#' will be ignored, and an empty message aborts the commit.", "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -69,9 +69,9 @@ "c": "#", "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -80,9 +80,9 @@ "c": " On branch master", "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -91,9 +91,9 @@ "c": "#", "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -102,9 +102,9 @@ "c": " Your branch is up-to-date with 'origin/master'.", "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -113,9 +113,9 @@ "c": "#", "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -124,9 +124,9 @@ "c": "#", "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -135,9 +135,9 @@ "c": " Changes to be committed:", "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -146,9 +146,9 @@ "c": "#", "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -157,9 +157,9 @@ "c": "\t", "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -179,9 +179,9 @@ "c": "#", "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -190,9 +190,9 @@ "c": "\t", "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -212,9 +212,9 @@ "c": "#", "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -223,9 +223,9 @@ "c": "\t", "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -245,9 +245,9 @@ "c": "#", "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/git/test/colorize-results/git-rebase-todo.json b/extensions/git/test/colorize-results/git-rebase-todo.json index 87781fdbe05..d21bab17094 100644 --- a/extensions/git/test/colorize-results/git-rebase-todo.json +++ b/extensions/git/test/colorize-results/git-rebase-todo.json @@ -388,9 +388,9 @@ "c": "#", "t": "text.git-rebase comment.line.number-sign.git-rebase punctuation.definition.comment.git-rebase", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -399,9 +399,9 @@ "c": " Commands:", "t": "text.git-rebase comment.line.number-sign.git-rebase", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -410,9 +410,9 @@ "c": "#", "t": "text.git-rebase comment.line.number-sign.git-rebase punctuation.definition.comment.git-rebase", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -421,9 +421,9 @@ "c": " p, pick = use commit", "t": "text.git-rebase comment.line.number-sign.git-rebase", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -432,9 +432,9 @@ "c": "#", "t": "text.git-rebase comment.line.number-sign.git-rebase punctuation.definition.comment.git-rebase", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -443,9 +443,9 @@ "c": " r, reword = use commit, but edit the commit message", "t": "text.git-rebase comment.line.number-sign.git-rebase", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -454,9 +454,9 @@ "c": "#", "t": "text.git-rebase comment.line.number-sign.git-rebase punctuation.definition.comment.git-rebase", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -465,9 +465,9 @@ "c": " e, edit = use commit, but stop for amending", "t": "text.git-rebase comment.line.number-sign.git-rebase", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -476,9 +476,9 @@ "c": "#", "t": "text.git-rebase comment.line.number-sign.git-rebase punctuation.definition.comment.git-rebase", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -487,9 +487,9 @@ "c": " s, squash = use commit, but meld into previous commit", "t": "text.git-rebase comment.line.number-sign.git-rebase", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -498,9 +498,9 @@ "c": "#", "t": "text.git-rebase comment.line.number-sign.git-rebase punctuation.definition.comment.git-rebase", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -509,9 +509,9 @@ "c": " f, fixup = like \"squash\", but discard this commit's log message", "t": "text.git-rebase comment.line.number-sign.git-rebase", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -520,9 +520,9 @@ "c": "#", "t": "text.git-rebase comment.line.number-sign.git-rebase punctuation.definition.comment.git-rebase", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -531,9 +531,9 @@ "c": " x, exec = run command (the rest of the line) using shell", "t": "text.git-rebase comment.line.number-sign.git-rebase", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index d8a4d64e743..497e26b21d6 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -257,9 +257,9 @@ supports-color@3.1.2: dependencies: has-flag "^1.0.0" -vscode-extension-telemetry@0.0.17: - version "0.0.17" - resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.17.tgz#15123e7edb34e7b9724b6056f54a869bbb922cb7" +vscode-extension-telemetry@0.0.18: + version "0.0.18" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.18.tgz#602ba20d8c71453aa34533a291e7638f6e5c0327" dependencies: applicationinsights "1.0.1" diff --git a/extensions/go/test/colorize-results/test-13777_go.json b/extensions/go/test/colorize-results/test-13777_go.json index 7012da53227..ba7b1bd76fc 100644 --- a/extensions/go/test/colorize-results/test-13777_go.json +++ b/extensions/go/test/colorize-results/test-13777_go.json @@ -80,9 +80,9 @@ "c": "//", "t": "source.go comment.line.double-slash.go punctuation.definition.comment.go", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -91,9 +91,9 @@ "c": " ( comments after var are now green )", "t": "source.go comment.line.double-slash.go", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/go/test/colorize-results/test_go.json b/extensions/go/test/colorize-results/test_go.json index 6916ec49cd2..14cd6ef4230 100644 --- a/extensions/go/test/colorize-results/test_go.json +++ b/extensions/go/test/colorize-results/test_go.json @@ -927,9 +927,9 @@ "c": "//", "t": "source.go comment.line.double-slash.go punctuation.definition.comment.go", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -938,9 +938,9 @@ "c": " create virtual machine", "t": "source.go comment.line.double-slash.go", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/groovy/test/colorize-results/test_groovy.json b/extensions/groovy/test/colorize-results/test_groovy.json index 8d1191aa367..13aa74462a8 100644 --- a/extensions/groovy/test/colorize-results/test_groovy.json +++ b/extensions/groovy/test/colorize-results/test_groovy.json @@ -3,9 +3,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -14,9 +14,9 @@ "c": " Hello World", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -80,9 +80,9 @@ "c": "/*", "t": "source.groovy comment.block.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -91,9 +91,9 @@ "c": " Variables:", "t": "source.groovy comment.block.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -102,9 +102,9 @@ "c": " You can assign values to variables for later use", "t": "source.groovy comment.block.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -113,9 +113,9 @@ "c": "*/", "t": "source.groovy comment.block.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -542,9 +542,9 @@ "c": "/*", "t": "source.groovy comment.block.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -553,9 +553,9 @@ "c": " Collections and maps", "t": "source.groovy comment.block.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -564,9 +564,9 @@ "c": "*/", "t": "source.groovy comment.block.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -575,9 +575,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -586,9 +586,9 @@ "c": "Creating an empty list", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -685,9 +685,9 @@ "c": "/*", "t": "source.groovy comment.block.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -696,9 +696,9 @@ "c": "** Adding a elements to the list **", "t": "source.groovy comment.block.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -707,9 +707,9 @@ "c": "*/", "t": "source.groovy comment.block.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -718,9 +718,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -729,9 +729,9 @@ "c": " As with Java", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -828,9 +828,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -839,9 +839,9 @@ "c": " Left shift adds, and returns the list", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -916,9 +916,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -927,9 +927,9 @@ "c": " Add multiple elements", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1092,9 +1092,9 @@ "c": "/*", "t": "source.groovy comment.block.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1103,9 +1103,9 @@ "c": "** Removing elements from the list **", "t": "source.groovy comment.block.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1114,9 +1114,9 @@ "c": "*/", "t": "source.groovy comment.block.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1125,9 +1125,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1136,9 +1136,9 @@ "c": " As with Java", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1235,9 +1235,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1246,9 +1246,9 @@ "c": " Subtraction works also", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1345,9 +1345,9 @@ "c": "/*", "t": "source.groovy comment.block.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1356,9 +1356,9 @@ "c": "** Iterating Lists **", "t": "source.groovy comment.block.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1367,9 +1367,9 @@ "c": "*/", "t": "source.groovy comment.block.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1378,9 +1378,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1389,9 +1389,9 @@ "c": " Iterate over elements of a list", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1741,9 +1741,9 @@ "c": "/*", "t": "source.groovy comment.block.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1752,9 +1752,9 @@ "c": "** Checking List contents **", "t": "source.groovy comment.block.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1763,9 +1763,9 @@ "c": "*/", "t": "source.groovy comment.block.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1774,9 +1774,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1785,9 +1785,9 @@ "c": "Evaluate if a list contains element(s) (boolean)", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1928,9 +1928,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1939,9 +1939,9 @@ "c": " Or", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2049,9 +2049,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2060,9 +2060,9 @@ "c": " To sort without mutating original, you can do:", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2181,9 +2181,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2192,9 +2192,9 @@ "c": "Replace all elements in the list", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2379,9 +2379,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2390,9 +2390,9 @@ "c": "Shuffle a list", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2533,9 +2533,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2544,9 +2544,9 @@ "c": "Clear a list", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2610,9 +2610,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2621,9 +2621,9 @@ "c": "Creating an empty map", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2731,9 +2731,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2742,9 +2742,9 @@ "c": "Add values", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3215,9 +3215,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3226,9 +3226,9 @@ "c": "Iterate over elements of a map", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3644,9 +3644,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3655,9 +3655,9 @@ "c": "Evaluate if a map contains a key", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3765,9 +3765,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3776,9 +3776,9 @@ "c": "Get the keys of a map", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3919,9 +3919,9 @@ "c": "//", "t": "source.groovy meta.definition.class.groovy meta.class.body.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3930,9 +3930,9 @@ "c": " read only property", "t": "source.groovy meta.definition.class.groovy meta.class.body.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -4084,9 +4084,9 @@ "c": "//", "t": "source.groovy meta.definition.class.groovy meta.class.body.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -4095,9 +4095,9 @@ "c": " read only property with public getter and protected setter", "t": "source.groovy meta.definition.class.groovy meta.class.body.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -4370,9 +4370,9 @@ "c": "//", "t": "source.groovy meta.definition.class.groovy meta.class.body.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -4381,9 +4381,9 @@ "c": " dynamically typed property", "t": "source.groovy meta.definition.class.groovy meta.class.body.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -4447,9 +4447,9 @@ "c": "/*", "t": "source.groovy comment.block.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -4458,9 +4458,9 @@ "c": " Logical Branching and Looping", "t": "source.groovy comment.block.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -4469,9 +4469,9 @@ "c": "*/", "t": "source.groovy comment.block.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -4480,9 +4480,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -4491,9 +4491,9 @@ "c": "Groovy supports the usual if - else syntax", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -4964,9 +4964,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -4975,9 +4975,9 @@ "c": "Groovy also supports the ternary operator:", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -5371,9 +5371,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -5382,9 +5382,9 @@ "c": "Groovy supports 'The Elvis Operator' too!", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -5393,9 +5393,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -5404,9 +5404,9 @@ "c": "Instead of using the ternary operator:", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -5569,9 +5569,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -5580,9 +5580,9 @@ "c": "We can write it:", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -5701,9 +5701,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -5712,9 +5712,9 @@ "c": "For loop", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -5723,9 +5723,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -5734,9 +5734,9 @@ "c": "Iterate over a range", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -5987,9 +5987,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -5998,9 +5998,9 @@ "c": "Iterate over a list", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -6262,9 +6262,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -6273,9 +6273,9 @@ "c": "Iterate over an array", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -6548,9 +6548,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -6559,9 +6559,9 @@ "c": "Iterate over a map", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -7384,9 +7384,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -7395,9 +7395,9 @@ "c": " = to technologies.collect { it?.toUpperCase() }", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -8682,9 +8682,9 @@ "c": "//", "t": "source.groovy meta.definition.variable.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -8693,9 +8693,9 @@ "c": " simulate some time consuming processing", "t": "source.groovy meta.definition.variable.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -9375,9 +9375,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -9386,9 +9386,9 @@ "c": "Another example:", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -9980,9 +9980,9 @@ "c": "//", "t": "source.groovy comment.line.double-slash.groovy punctuation.definition.comment.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -9991,9 +9991,9 @@ "c": "CompileStatic example:", "t": "source.groovy comment.line.double-slash.groovy", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/handlebars/test/colorize-results/test_hbs.json b/extensions/handlebars/test/colorize-results/test_hbs.json index 8774d4f3885..f818a57b51e 100644 --- a/extensions/handlebars/test/colorize-results/test_hbs.json +++ b/extensions/handlebars/test/colorize-results/test_hbs.json @@ -1059,9 +1059,9 @@ "c": "{{!-- only output author name if an author exists --}}", "t": "text.html.handlebars comment.block.handlebars", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/html-language-features/client/src/htmlMain.ts b/extensions/html-language-features/client/src/htmlMain.ts index f463ff2ba8a..68f8b881d3b 100644 --- a/extensions/html-language-features/client/src/htmlMain.ts +++ b/extensions/html-language-features/client/src/htmlMain.ts @@ -8,14 +8,12 @@ import * as path from 'path'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { languages, ExtensionContext, IndentAction, Position, TextDocument, Range, CompletionItem, CompletionItemKind, SnippetString, FoldingRangeKind, FoldingRange, FoldingContext } from 'vscode'; -import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, RequestType, TextDocumentPositionParams, Disposable, CancellationToken } from 'vscode-languageclient'; +import { languages, ExtensionContext, IndentAction, Position, TextDocument, Range, CompletionItem, CompletionItemKind, SnippetString } from 'vscode'; +import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, RequestType, TextDocumentPositionParams } from 'vscode-languageclient'; import { EMPTY_ELEMENTS } from './htmlEmptyTagsShared'; import { activateTagClosing } from './tagClosing'; import TelemetryReporter from 'vscode-extension-telemetry'; -import { FoldingRangeRequest, FoldingRangeRequestParam, FoldingRangeClientCapabilities, FoldingRangeKind as LSFoldingRangeKind } from 'vscode-languageserver-protocol-foldingprovider'; - namespace TagCloseRequest { export const type: RequestType = new RequestType('html/tag'); } @@ -64,21 +62,6 @@ export function activate(context: ExtensionContext) { // Create the language client and start the client. let client = new LanguageClient('html', localize('htmlserver.name', 'HTML Language Server'), serverOptions, clientOptions); client.registerProposedFeatures(); - client.registerFeature({ - fillClientCapabilities(capabilities: FoldingRangeClientCapabilities): void { - let textDocumentCap = capabilities.textDocument; - if (!textDocumentCap) { - textDocumentCap = capabilities.textDocument = {}; - } - textDocumentCap.foldingRange = { - dynamicRegistration: false, - rangeLimit: 5000, - lineFoldingOnly: true - }; - }, - initialize(capabilities, documentSelector): void { - } - }); let disposable = client.start(); toDispose.push(disposable); @@ -96,7 +79,6 @@ export function activate(context: ExtensionContext) { } }); toDispose.push(disposable); - toDispose.push(initFoldingProvider()); }); languages.setLanguageConfiguration('html', { @@ -172,38 +154,6 @@ export function activate(context: ExtensionContext) { return null; } }); - - function initFoldingProvider(): Disposable { - function getKind(kind: string | undefined): FoldingRangeKind | undefined { - if (kind) { - switch (kind) { - case LSFoldingRangeKind.Comment: - return FoldingRangeKind.Comment; - case LSFoldingRangeKind.Imports: - return FoldingRangeKind.Imports; - case LSFoldingRangeKind.Region: - return FoldingRangeKind.Region; - } - } - return void 0; - } - return languages.registerFoldingRangeProvider('html', { - provideFoldingRanges(document: TextDocument, context: FoldingContext, token: CancellationToken) { - const param: FoldingRangeRequestParam = { - textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document) - }; - return client.sendRequest(FoldingRangeRequest.type, param, token).then(ranges => { - if (Array.isArray(ranges)) { - return ranges.map(r => new FoldingRange(r.startLine, r.endLine, getKind(r.kind))); - } - return null; - }, error => { - client.logFailedRequest(FoldingRangeRequest.type, error); - return null; - }); - } - }); - } } function getPackageInfo(context: ExtensionContext): IPackageInfo | null { diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 9ac18e38fbb..586b1684cda 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -125,13 +125,13 @@ "html.suggest.angular1": { "type": "boolean", "scope": "resource", - "default": true, + "default": false, "description": "%html.suggest.angular1.desc%" }, "html.suggest.ionic": { "type": "boolean", "scope": "resource", - "default": true, + "default": false, "description": "%html.suggest.ionic.desc%" }, "html.suggest.html5": { @@ -173,9 +173,8 @@ } }, "dependencies": { - "vscode-extension-telemetry": "0.0.17", - "vscode-languageclient": "^4.1.4", - "vscode-languageserver-protocol-foldingprovider": "^2.0.1", + "vscode-extension-telemetry": "0.0.18", + "vscode-languageclient": "^4.4.0", "vscode-nls": "^3.2.4" }, "devDependencies": { diff --git a/extensions/html-language-features/package.nls.json b/extensions/html-language-features/package.nls.json index 73c94b4acf6..077075780c3 100644 --- a/extensions/html-language-features/package.nls.json +++ b/extensions/html-language-features/package.nls.json @@ -1,12 +1,12 @@ { "displayName": "HTML Language Features", "description": "Provides rich language support for HTML, Razor, and Handlebar files", - "html.format.enable.desc": "Enable/disable default HTML formatter", + "html.format.enable.desc": "Enable/disable default HTML formatter.", "html.format.wrapLineLength.desc": "Maximum amount of characters per line (0 = disable).", "html.format.unformatted.desc": "List of tags, comma separated, that shouldn't be reformatted. 'null' defaults to all tags listed at https://www.w3.org/TR/html5/dom.html#phrasing-content.", "html.format.contentUnformatted.desc": "List of tags, comma separated, where the content shouldn't be reformatted. 'null' defaults to the 'pre' tag.", "html.format.indentInnerHtml.desc": "Indent and sections.", - "html.format.preserveNewLines.desc": "Whether existing line breaks before elements should be preserved. Only works before elements, not inside tags or for text.", + "html.format.preserveNewLines.desc": "Controls whether existing line breaks before elements should be preserved. Only works before elements, not inside tags or for text.", "html.format.maxPreserveNewLines.desc": "Maximum number of line breaks to be preserved in one chunk. Use 'null' for unlimited.", "html.format.indentHandlebars.desc": "Format and indent {{#foo}} and {{/foo}}.", "html.format.endWithNewline.desc": "End with a newline.", @@ -16,11 +16,11 @@ "html.format.wrapAttributes.force": "Wrap each attribute except first.", "html.format.wrapAttributes.forcealign": "Wrap each attribute except first and keep aligned.", "html.format.wrapAttributes.forcemultiline": "Wrap each attribute.", - "html.suggest.angular1.desc": "Configures if the built-in HTML language support suggests Angular V1 tags and properties.", - "html.suggest.ionic.desc": "Configures if the built-in HTML language support suggests Ionic tags, properties and values.", - "html.suggest.html5.desc":"Configures if the built-in HTML language support suggests HTML5 tags, properties and values.", + "html.suggest.angular1.desc": "Controls whether the built-in HTML language support suggests Angular V1 tags and properties.", + "html.suggest.ionic.desc": "Controls whether the built-in HTML language support suggests Ionic tags, properties and values.", + "html.suggest.html5.desc": "Controls whether the built-in HTML language support suggests HTML5 tags, properties and values.", "html.trace.server.desc": "Traces the communication between VS Code and the HTML language server.", - "html.validate.scripts": "Configures if the built-in HTML language support validates embedded scripts.", - "html.validate.styles": "Configures if the built-in HTML language support validates embedded styles.", + "html.validate.scripts": "Controls whether the built-in HTML language support validates embedded scripts.", + "html.validate.styles": "Controls whether the built-in HTML language support validates embedded styles.", "html.autoClosingTags": "Enable/disable autoclosing of HTML tags." } \ No newline at end of file diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index e9586c7ad34..5db8caf8ea8 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -8,13 +8,12 @@ "node": "*" }, "dependencies": { - "vscode-css-languageservice": "^3.0.9-next.20", - "vscode-html-languageservice": "^2.1.3-next.5", - "vscode-languageserver": "^4.1.3", - "vscode-languageserver-protocol-foldingprovider": "^2.0.1", - "vscode-languageserver-types": "^3.7.2", + "vscode-css-languageservice": "^3.0.9", + "vscode-html-languageservice": "^2.1.3", + "vscode-languageserver": "^4.4.0", + "vscode-languageserver-types": "^3.10.0", "vscode-nls": "^3.2.4", - "vscode-uri": "^1.0.3" + "vscode-uri": "^1.0.5" }, "devDependencies": { "@types/mocha": "2.2.33", diff --git a/extensions/html-language-features/server/src/htmlServerMain.ts b/extensions/html-language-features/server/src/htmlServerMain.ts index b4e4dc827bd..ffe5c18df41 100644 --- a/extensions/html-language-features/server/src/htmlServerMain.ts +++ b/extensions/html-language-features/server/src/htmlServerMain.ts @@ -19,7 +19,6 @@ import { getDocumentContext } from './utils/documentContext'; import uri from 'vscode-uri'; import { formatError, runSafe, runSafeAsync } from './utils/runner'; -import { FoldingRangeRequest, FoldingRangeServerCapabilities } from 'vscode-languageserver-protocol-foldingprovider'; import { getFoldingRanges } from './modes/htmlFolding'; namespace TagCloseRequest { @@ -119,7 +118,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { scopedSettingsSupport = getClientCapability('workspace.configuration', false); workspaceFoldersSupport = getClientCapability('workspace.workspaceFolders', false); foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); - const capabilities: ServerCapabilities & FoldingRangeServerCapabilities = { + const capabilities: ServerCapabilities = { // Tell the client that the server works in FULL text document sync mode textDocumentSync: documents.syncKind, completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['.', ':', '<', '"', '=', '/'] } : undefined, @@ -441,7 +440,7 @@ connection.onRequest(TagCloseRequest.type, (params, token) => { }, null, `Error while computing tag close actions for ${params.textDocument.uri}`, token); }); -connection.onRequest(FoldingRangeRequest.type, (params, token) => { +connection.onFoldingRanges((params, token) => { return runSafe(() => { const document = documents.get(params.textDocument.uri); if (document) { diff --git a/extensions/html-language-features/server/src/modes/htmlFolding.ts b/extensions/html-language-features/server/src/modes/htmlFolding.ts index c36b27ad3bf..aa63a24cf9a 100644 --- a/extensions/html-language-features/server/src/modes/htmlFolding.ts +++ b/extensions/html-language-features/server/src/modes/htmlFolding.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; import { TextDocument, CancellationToken, Position, Range } from 'vscode-languageserver'; -import { FoldingRange } from 'vscode-languageserver-protocol-foldingprovider'; +import { FoldingRange } from 'vscode-languageserver-types'; import { LanguageModes } from './languageModes'; export function getFoldingRanges(languageModes: LanguageModes, document: TextDocument, maxRanges: number | undefined, cancellationToken: CancellationToken | null): FoldingRange[] { diff --git a/extensions/html-language-features/server/src/modes/htmlMode.ts b/extensions/html-language-features/server/src/modes/htmlMode.ts index cd3edbaed4b..29fb01d4cb8 100644 --- a/extensions/html-language-features/server/src/modes/htmlMode.ts +++ b/extensions/html-language-features/server/src/modes/htmlMode.ts @@ -6,10 +6,8 @@ import { getLanguageModelCache } from '../languageModelCache'; import { LanguageService as HTMLLanguageService, HTMLDocument, DocumentContext, FormattingOptions, HTMLFormatConfiguration } from 'vscode-html-languageservice'; -import { TextDocument, Position, Range, CompletionItem } from 'vscode-languageserver-types'; +import { TextDocument, Position, Range, CompletionItem, FoldingRange } from 'vscode-languageserver-types'; import { LanguageMode, Workspace } from './languageModes'; - -import { FoldingRange } from 'vscode-languageserver-protocol-foldingprovider'; import { getPathCompletionParticipant } from './pathCompletion'; export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: Workspace): LanguageMode { diff --git a/extensions/html-language-features/server/src/modes/javascriptMode.ts b/extensions/html-language-features/server/src/modes/javascriptMode.ts index 0c124f2d468..4148761e42f 100644 --- a/extensions/html-language-features/server/src/modes/javascriptMode.ts +++ b/extensions/html-language-features/server/src/modes/javascriptMode.ts @@ -5,14 +5,17 @@ 'use strict'; import { LanguageModelCache, getLanguageModelCache } from '../languageModelCache'; -import { SymbolInformation, SymbolKind, CompletionItem, Location, SignatureHelp, SignatureInformation, ParameterInformation, Definition, TextEdit, TextDocument, Diagnostic, DiagnosticSeverity, Range, CompletionItemKind, Hover, MarkedString, DocumentHighlight, DocumentHighlightKind, CompletionList, Position, FormattingOptions } from 'vscode-languageserver-types'; +import { + SymbolInformation, SymbolKind, CompletionItem, Location, SignatureHelp, SignatureInformation, ParameterInformation, + Definition, TextEdit, TextDocument, Diagnostic, DiagnosticSeverity, Range, CompletionItemKind, Hover, MarkedString, + DocumentHighlight, DocumentHighlightKind, CompletionList, Position, FormattingOptions, FoldingRange, FoldingRangeKind +} from 'vscode-languageserver-types'; import { LanguageMode, Settings, Workspace } from './languageModes'; import { getWordAtText, startsWith, isWhitespaceOnly, repeat } from '../utils/strings'; import { HTMLDocumentRegions } from './embeddedSupport'; import * as ts from 'typescript'; import { join } from 'path'; -import { FoldingRange, FoldingRangeKind } from 'vscode-languageserver-protocol-foldingprovider'; const FILE_NAME = 'vscode://javascript/1'; // the same 'file' is used for all contents const JQUERY_D_TS = join(__dirname, '../../lib/jquery.d.ts'); diff --git a/extensions/html-language-features/server/src/modes/languageModes.ts b/extensions/html-language-features/server/src/modes/languageModes.ts index 282c4f75ef4..6fcba8d24dc 100644 --- a/extensions/html-language-features/server/src/modes/languageModes.ts +++ b/extensions/html-language-features/server/src/modes/languageModes.ts @@ -7,10 +7,9 @@ import { getLanguageService as getHTMLLanguageService, DocumentContext } from 'vscode-html-languageservice'; import { CompletionItem, Location, SignatureHelp, Definition, TextEdit, TextDocument, Diagnostic, DocumentLink, Range, - Hover, DocumentHighlight, CompletionList, Position, FormattingOptions, SymbolInformation + Hover, DocumentHighlight, CompletionList, Position, FormattingOptions, SymbolInformation, FoldingRange } from 'vscode-languageserver-types'; import { ColorInformation, ColorPresentation, Color, WorkspaceFolder } from 'vscode-languageserver'; -import { FoldingRange } from 'vscode-languageserver-protocol-foldingprovider'; import { getLanguageModelCache, LanguageModelCache } from '../languageModelCache'; import { getDocumentRegions, HTMLDocumentRegions } from './embeddedSupport'; diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index 32e736c53c2..0fb9f113a5d 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -194,66 +194,55 @@ supports-color@5.4.0: dependencies: has-flag "^3.0.0" -vscode-css-languageservice@^3.0.9-next.20: - version "3.0.9-next.20" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.9-next.20.tgz#8229aee66aa877929af5d2fd81a21731b415c92e" +vscode-css-languageservice@^3.0.9: + version "3.0.9" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.9.tgz#770471350120c5bcf6918632a125638fc0ece3be" dependencies: - vscode-languageserver-types "^3.7.2" - vscode-nls "^3.2.2" + vscode-languageserver-types "^3.10.0" + vscode-nls "^3.2.4" -vscode-html-languageservice@^2.1.3-next.5: - version "2.1.3-next.5" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-2.1.3-next.5.tgz#cfbf4ffed96845ad13999d572ce0b5c2aeee84af" +vscode-html-languageservice@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-2.1.3.tgz#c999c39e37adc632be8003a5e82075cf75dbb9bc" dependencies: - vscode-languageserver-types "^3.7.2" - vscode-nls "^3.2.2" - vscode-uri "^1.0.3" + vscode-languageserver-types "^3.10.0" + vscode-nls "^3.2.4" + vscode-uri "^1.0.5" vscode-jsonrpc@^3.6.2: version "3.6.2" resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.6.2.tgz#3b5eef691159a15556ecc500e9a8a0dd143470c8" -vscode-languageserver-protocol-foldingprovider@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol-foldingprovider/-/vscode-languageserver-protocol-foldingprovider-2.0.1.tgz#051d0d9e58d1b79dc4681acd48f21797f5515bfd" - dependencies: - vscode-languageserver-protocol "^3.7.2" - vscode-languageserver-types "^3.7.2" - -vscode-languageserver-protocol@^3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.7.2.tgz#df58621c032139010888b6a9ddc969423f9ba9d6" +vscode-languageserver-protocol@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.10.0.tgz#f8dcdf987687f64a26e7c32d498fc781a0e886dc" dependencies: vscode-jsonrpc "^3.6.2" - vscode-languageserver-types "^3.7.2" + vscode-languageserver-types "^3.10.0" -vscode-languageserver-types@^3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.7.2.tgz#aad8846f8e3e27962648554de5a8417e358f34eb" +vscode-languageserver-types@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.10.0.tgz#944e5308f3b36a3f372c766f1a344e903ec9c389" -vscode-languageserver@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-4.1.3.tgz#937d37c955b6b9c2409388413cd6f54d1eb9fe7d" +vscode-languageserver@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-4.4.0.tgz#b6e8b37a739ccb629d92f3635f0099d191c856fa" dependencies: - vscode-languageserver-protocol "^3.7.2" - vscode-uri "^1.0.1" - -vscode-nls@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.2.tgz#3817eca5b985c2393de325197cf4e15eb2aa5350" + vscode-languageserver-protocol "^3.10.0" + vscode-uri "^1.0.3" vscode-nls@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398" -vscode-uri@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.1.tgz#11a86befeac3c4aa3ec08623651a3c81a6d0bbc8" - vscode-uri@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.3.tgz#631bdbf716dccab0e65291a8dc25c23232085a52" +vscode-uri@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.5.tgz#3b899a8ef71c37f3054d79bdbdda31c7bf36f20d" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" diff --git a/extensions/html-language-features/yarn.lock b/extensions/html-language-features/yarn.lock index 1e7214749f2..a33a4a08108 100644 --- a/extensions/html-language-features/yarn.lock +++ b/extensions/html-language-features/yarn.lock @@ -28,9 +28,9 @@ semver@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" -vscode-extension-telemetry@0.0.17: - version "0.0.17" - resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.17.tgz#15123e7edb34e7b9724b6056f54a869bbb922cb7" +vscode-extension-telemetry@0.0.18: + version "0.0.18" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.18.tgz#602ba20d8c71453aa34533a291e7638f6e5c0327" dependencies: applicationinsights "1.0.1" @@ -38,29 +38,22 @@ vscode-jsonrpc@^3.6.2: version "3.6.2" resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.6.2.tgz#3b5eef691159a15556ecc500e9a8a0dd143470c8" -vscode-languageclient@^4.1.4: - version "4.1.4" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-4.1.4.tgz#fff1a6bca4714835dca7fce35bc4ce81442fdf2c" +vscode-languageclient@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-4.4.0.tgz#b05868f6477b6f0c9910b24daae4f3e8c4b65902" dependencies: - vscode-languageserver-protocol "^3.7.2" + vscode-languageserver-protocol "^3.10.0" -vscode-languageserver-protocol-foldingprovider@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol-foldingprovider/-/vscode-languageserver-protocol-foldingprovider-2.0.1.tgz#051d0d9e58d1b79dc4681acd48f21797f5515bfd" - dependencies: - vscode-languageserver-protocol "^3.7.2" - vscode-languageserver-types "^3.7.2" - -vscode-languageserver-protocol@^3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.7.2.tgz#df58621c032139010888b6a9ddc969423f9ba9d6" +vscode-languageserver-protocol@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.10.0.tgz#f8dcdf987687f64a26e7c32d498fc781a0e886dc" dependencies: vscode-jsonrpc "^3.6.2" - vscode-languageserver-types "^3.7.2" + vscode-languageserver-types "^3.10.0" -vscode-languageserver-types@^3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.7.2.tgz#aad8846f8e3e27962648554de5a8417e358f34eb" +vscode-languageserver-types@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.10.0.tgz#944e5308f3b36a3f372c766f1a344e903ec9c389" vscode-nls@^3.2.4: version "3.2.4" diff --git a/extensions/html/syntaxes/html.tmLanguage.json b/extensions/html/syntaxes/html.tmLanguage.json index 6b9b1f58e7d..31f584840f6 100644 --- a/extensions/html/syntaxes/html.tmLanguage.json +++ b/extensions/html/syntaxes/html.tmLanguage.json @@ -4,12 +4,12 @@ "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/textmate/html.tmbundle/commit/a723f08ebd49c67c22aca08dd8f17d0bf836ec93", + "version": "https://github.com/textmate/html.tmbundle/commit/6a6fb2967e2f562a634fca97d18018104d428f1c", "name": "HTML", "scopeName": "text.html.basic", "injections": { - "R:text.html - (comment.block, text.html source)": { - "comment": "Use R: to ensure this matches after any other injections.", + "R:text.html - (comment.block, text.html meta.embedded, meta.tag.*.*.html, meta.tag.*.*.*.html, meta.tag.*.*.*.*.html)": { + "comment": "Uses R: to ensure this matches after any other injections.", "patterns": [ { "match": "<", @@ -19,38 +19,6 @@ } }, "patterns": [ - { - "begin": "(<)([a-zA-Z][a-zA-Z0-9:-]*)(?=[^>]*>)", - "beginCaptures": { - "1": { - "name": "punctuation.definition.tag.html" - }, - "2": { - "name": "entity.name.tag.html" - } - }, - "end": "(>(<)/)(\\2)(>)", - "endCaptures": { - "1": { - "name": "punctuation.definition.tag.html" - }, - "2": { - "name": "meta.scope.between-tag-pair.html" - }, - "3": { - "name": "entity.name.tag.html" - }, - "4": { - "name": "punctuation.definition.tag.html" - } - }, - "name": "meta.tag.any.html", - "patterns": [ - { - "include": "#tag-stuff" - } - ] - }, { "begin": "(<\\?)(xml)", "captures": { @@ -58,481 +26,463 @@ "name": "punctuation.definition.tag.html" }, "2": { - "name": "entity.name.tag.xml.html" + "name": "entity.name.tag.html" } }, "end": "(\\?>)", - "name": "meta.tag.preprocessor.xml.html", + "name": "meta.tag.metadata.processing.xml.html", "patterns": [ { - "include": "#tag-generic-attribute" - }, - { - "include": "#string-double-quoted" - }, - { - "include": "#string-single-quoted" + "include": "#attribute" } ] }, { + "include": "#comment" + }, + { + "begin": "", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.metadata.doctype.html", + "patterns": [ + { + "match": "\\G(?i:DOCTYPE)", + "name": "entity.name.tag.html" + }, + { + "begin": "\"", + "end": "\"", + "name": "string.quoted.double.html" + }, + { + "match": "[^\\s>]+", + "name": "entity.other.attribute-name.html" + } + ] + }, + { + "include": "#cdata" + }, + { + "include": "#tags-valid" + }, + { + "include": "#tags-invalid" + }, + { + "include": "#entities" + } + ], + "repository": { + "attribute": { + "patterns": [ + { + "begin": "(s(hape|cope|t(ep|art)|ize(s)?|p(ellcheck|an)|elected|lot|andbox|rc(set|doc|lang)?)|h(ttp-equiv|i(dden|gh)|e(ight|aders)|ref(lang)?)|n(o(nce|validate|module)|ame)|c(h(ecked|arset)|ite|o(nt(ent(editable)?|rols)|ords|l(s(pan)?|or))|lass|rossorigin)|t(ype(mustmatch)?|itle|a(rget|bindex)|ranslate)|i(s(map)?|n(tegrity|putmode)|tem(scope|type|id|prop|ref)|d)|op(timum|en)|d(i(sabled|r(name)?)|ownload|e(coding|f(er|ault))|at(etime|a)|raggable)|usemap|p(ing|oster|la(ysinline|ceholder)|attern|reload)|enctype|value|kind|for(m(novalidate|target|enctype|action|method)?)?|w(idth|rap)|l(ist|o(op|w)|a(ng|bel))|a(s(ync)?|c(ce(sskey|pt(-charset)?)|tion)|uto(c(omplete|apitalize)|play|focus)|l(t|low(usermedia|paymentrequest|fullscreen))|bbr)|r(ows(pan)?|e(versed|quired|ferrerpolicy|l|adonly))|m(in(length)?|u(ted|ltiple)|e(thod|dia)|a(nifest|x(length)?)))(?![\\w:-])", + "beginCaptures": { + "0": { + "name": "entity.other.attribute-name.html" + } + }, + "comment": "HTML5 attributes, not event handlers", + "end": "(?=\\s*+[^=\\s])", + "name": "meta.attribute.$1.html", + "patterns": [ + { + "include": "#attribute-interior" + } + ] + }, + { + "begin": "style(?![\\w:-])", + "beginCaptures": { + "0": { + "name": "entity.other.attribute-name.html" + } + }, + "comment": "HTML5 style attribute", + "end": "(?=\\s*+[^=\\s])", + "name": "meta.attribute.style.html", + "patterns": [ + { + "begin": "=", + "beginCaptures": { + "0": { + "name": "punctuation.separator.key-value.html" + } + }, + "end": "(?<=[^\\s=])(?!\\s*=)|(?=/?>)", + "patterns": [ + { + "begin": "(?=[^\\s=<>`/]|/(?!>))", + "end": "(?!\\G)", + "name": "meta.embedded.line.css", + "patterns": [ + { + "captures": { + "0": { + "name": "source.css" + } + }, + "match": "([^\\s\"'=<>`/]|/(?!>))+", + "name": "string.unquoted.html" + }, + { + "begin": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.html" + } + }, + "contentName": "source.css", + "end": "(\")", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.html" + }, + "1": { + "name": "source.css" + } + }, + "name": "string.quoted.double.html", + "patterns": [ + { + "include": "#entities" + } + ] + }, + { + "begin": "'", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.html" + } + }, + "contentName": "source.css", + "end": "(')", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.html" + }, + "1": { + "name": "source.css" + } + }, + "name": "string.quoted.single.html", + "patterns": [ + { + "include": "#entities" + } + ] + } + ] + }, + { + "match": "=", + "name": "invalid.illegal.unexpected-equals-sign.html" + } + ] + } + ] + }, + { + "begin": "on(s(croll|t(orage|alled)|u(spend|bmit)|e(curitypolicyviolation|ek(ing|ed)|lect))|hashchange|c(hange|o(ntextmenu|py)|u(t|echange)|l(ick|ose)|an(cel|play(through)?))|t(imeupdate|oggle)|in(put|valid)|o(nline|ffline)|d(urationchange|r(op|ag(start|over|e(n(ter|d)|xit)|leave)?)|blclick)|un(handledrejection|load)|p(opstate|lay(ing)?|a(ste|use|ge(show|hide))|rogress)|e(nded|rror|mptied)|volumechange|key(down|up|press)|focus|w(heel|aiting)|l(oad(start|e(nd|d(data|metadata)))?|anguagechange)|a(uxclick|fterprint|bort)|r(e(s(ize|et)|jectionhandled)|atechange)|m(ouse(o(ut|ver)|down|up|enter|leave|move)|essage(error)?)|b(efore(unload|print)|lur))(?![\\w:-])", + "beginCaptures": { + "0": { + "name": "entity.other.attribute-name.html" + } + }, + "comment": "HTML5 attributes, event handlers", + "end": "(?=\\s*+[^=\\s])", + "name": "meta.attribute.event-handler.$1.html", + "patterns": [ + { + "begin": "=", + "beginCaptures": { + "0": { + "name": "punctuation.separator.key-value.html" + } + }, + "end": "(?<=[^\\s=])(?!\\s*=)|(?=/?>)", + "patterns": [ + { + "begin": "(?=[^\\s=<>`/]|/(?!>))", + "end": "(?!\\G)", + "name": "meta.embedded.line.js", + "patterns": [ + { + "captures": { + "0": { + "name": "source.js" + }, + "1": { + "patterns": [ + { + "include": "source.js" + } + ] + } + }, + "match": "(([^\\s\"'=<>`/]|/(?!>))+)", + "name": "string.unquoted.html" + }, + { + "begin": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.html" + } + }, + "contentName": "source.js", + "end": "(\")", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.html" + }, + "1": { + "name": "source.js" + } + }, + "name": "string.quoted.double.html", + "patterns": [ + { + "captures": { + "0": { + "patterns": [ + { + "include": "source.js" + } + ] + } + }, + "match": "[^\\n\"]+" + } + ] + }, + { + "begin": "'", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.html" + } + }, + "contentName": "source.js", + "end": "(')", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.html" + }, + "1": { + "name": "source.js" + } + }, + "name": "string.quoted.single.html", + "patterns": [ + { + "captures": { + "0": { + "patterns": [ + { + "include": "source.js" + } + ] + } + }, + "match": "[^\\n']+" + } + ] + } + ] + }, + { + "match": "=", + "name": "invalid.illegal.unexpected-equals-sign.html" + } + ] + } + ] + }, + { + "begin": "(data-[a-z\\-]+)(?![\\w:-])", + "beginCaptures": { + "0": { + "name": "entity.other.attribute-name.html" + } + }, + "comment": "HTML5 attributes, data-*", + "end": "(?=\\s*+[^=\\s])", + "name": "meta.attribute.data-x.$1.html", + "patterns": [ + { + "include": "#attribute-interior" + } + ] + }, + { + "begin": "(align|bgcolor|border)(?![\\w:-])", + "beginCaptures": { + "0": { + "name": "invalid.deprecated.entity.other.attribute-name.html" + } + }, + "comment": "HTML attributes, deprecated", + "end": "(?=\\s*+[^=\\s])", + "name": "meta.attribute.$1.html", + "patterns": [ + { + "include": "#attribute-interior" + } + ] + }, + { + "begin": "([^\\x{0020}\"'<>/=\\x{0000}-\\x{001F}\\x{007F}-\\x{009F}\\x{FDD0}-\\x{FDEF}\\x{FFFE}\\x{FFFF}\\x{1FFFE}\\x{1FFFF}\\x{2FFFE}\\x{2FFFF}\\x{3FFFE}\\x{3FFFF}\\x{4FFFE}\\x{4FFFF}\\x{5FFFE}\\x{5FFFF}\\x{6FFFE}\\x{6FFFF}\\x{7FFFE}\\x{7FFFF}\\x{8FFFE}\\x{8FFFF}\\x{9FFFE}\\x{9FFFF}\\x{AFFFE}\\x{AFFFF}\\x{BFFFE}\\x{BFFFF}\\x{CFFFE}\\x{CFFFF}\\x{DFFFE}\\x{DFFFF}\\x{EFFFE}\\x{EFFFF}\\x{FFFFE}\\x{FFFFF}\\x{10FFFE}\\x{10FFFF}]+)", + "beginCaptures": { + "0": { + "name": "entity.other.attribute-name.html" + } + }, + "comment": "Anything else that is valid", + "end": "(?=\\s*+[^=\\s])", + "name": "meta.attribute.unrecognized.$1.html", + "patterns": [ + { + "include": "#attribute-interior" + } + ] + }, + { + "match": "[^\\s>]+", + "name": "invalid.illegal.character-not-allowed-here.html" + } + ] + }, + "attribute-interior": { + "patterns": [ + { + "begin": "=", + "beginCaptures": { + "0": { + "name": "punctuation.separator.key-value.html" + } + }, + "end": "(?<=[^\\s=])(?!\\s*=)|(?=/?>)", + "patterns": [ + { + "match": "([^\\s\"'=<>`/]|/(?!>))+", + "name": "string.unquoted.html" + }, + { + "begin": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.html" + } + }, + "end": "\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.html" + } + }, + "name": "string.quoted.double.html", + "patterns": [ + { + "include": "#entities" + } + ] + }, + { + "begin": "'", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.html" + } + }, + "end": "'", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.html" + } + }, + "name": "string.quoted.single.html", + "patterns": [ + { + "include": "#entities" + } + ] + }, + { + "match": "=", + "name": "invalid.illegal.unexpected-equals-sign.html" + } + ] + } + ] + }, + "cdata": { + "begin": "", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.metadata.cdata.html" + }, + "comment": { "begin": "", "name": "comment.block.html", "patterns": [ { - "match": "--", - "name": "invalid.illegal.bad-comments-or-CDATA.html" + "match": "\\G-?>", + "name": "invalid.illegal.characters-not-allowed-here.html" }, { - "include": "#embedded-code" - } - ] - }, - { - "begin": "", - "name": "meta.tag.sgml.html", - "patterns": [ - { - "begin": "(?i:DOCTYPE)", - "captures": { - "1": { - "name": "entity.name.tag.doctype.html" - } - }, - "end": "(?=>)", - "name": "meta.tag.sgml.doctype.html", - "patterns": [ - { - "match": "\"[^\">]*\"", - "name": "string.quoted.double.doctype.identifiers-and-DTDs.html" - } - ] + "match": ")", + "name": "invalid.illegal.characters-not-allowed-here.html" }, { - "begin": "\\[CDATA\\[", - "end": "]](?=>)", - "name": "constant.other.inline-data.html" - }, - { - "match": "(\\s*)(?!--|>)\\S(\\s*)", - "name": "invalid.illegal.bad-comments-or-CDATA.html" - } - ] - }, - { - "include": "#embedded-code" - }, - { - "begin": "(^[ \\t]+)?(?=<(?i:style))", - "beginCaptures": { - "1": { - "name": "punctuation.whitespace.embedded.leading.html" - } - }, - "end": "(?!\\G)([ \\t]*$\\n?)?", - "endCaptures": { - "1": { - "name": "punctuation.whitespace.embedded.trailing.html" - } - }, - "patterns": [ - { - "begin": "(<)((?i:style))\\b", - "beginCaptures": { - "0": { - "name": "meta.tag.metadata.style.html" - }, - "1": { - "name": "punctuation.definition.tag.begin.html" - }, - "2": { - "name": "entity.name.tag.html" - } - }, - "end": "(/>)|((<)/)((?i:style))(>)", - "endCaptures": { - "0": { - "name": "meta.tag.metadata.style.html" - }, - "1": { - "name": "punctuation.definition.tag.end.html" - }, - "2": { - "name": "punctuation.definition.tag.begin.html" - }, - "3": { - "name": "source.css" - }, - "4": { - "name": "entity.name.tag.html" - }, - "5": { - "name": "punctuation.definition.tag.end.html" - } - }, - "name": "meta.embedded.block.html", - "patterns": [ - { - "begin": "\\G", - "captures": { - "1": { - "name": "punctuation.definition.tag.end.html" - } - }, - "end": "(?=/>)|(>)", - "name": "meta.tag.metadata.style.html", - "patterns": [ - { - "include": "#tag-stuff" - } - ] - }, - { - "begin": "(?!\\G)", - "end": "(?=)|(/)((?i:script))(>)", - "endCaptures": { - "0": { - "name": "meta.tag.metadata.script.html" - }, - "1": { - "name": "punctuation.definition.tag.end.html" - }, - "2": { - "name": "punctuation.definition.tag.begin.html" - }, - "3": { - "name": "entity.name.tag.html" - }, - "4": { - "name": "punctuation.definition.tag.end.html" - } - }, - "name": "meta.embedded.block.html", - "patterns": [ - { - "begin": "\\G", - "end": "(?=/>|/)", - "patterns": [ - { - "begin": "(>)", - "beginCaptures": { - "0": { - "name": "meta.tag.metadata.script.html" - }, - "1": { - "name": "punctuation.definition.tag.end.html" - } - }, - "end": "((<))(?=/(?i:script))", - "endCaptures": { - "0": { - "name": "meta.tag.metadata.script.html" - }, - "1": { - "name": "punctuation.definition.tag.begin.html" - }, - "2": { - "name": "source.js" - } - }, - "patterns": [ - { - "begin": "\\G", - "end": "(?=|type(?=[\\s=])(?!\\s*=\\s*('|\"|)(text/(javascript|ecmascript|babel)|application/((x-)?javascript|ecmascript|babel)|module)[\\s\"'>])))", - "name": "meta.tag.metadata.script.html", - "patterns": [ - { - "include": "#tag-stuff" - } - ] - }, - { - "begin": "(?=(?i:type\\s*=\\s*('|\"|)(text/(x-handlebars|(x-(handlebars-)?|ng-)?template|html)[\\s\"'>])))", - "end": "((<))(?=/(?i:script))", - "endCaptures": { - "0": { - "name": "meta.tag.metadata.script.html" - }, - "1": { - "name": "punctuation.definition.tag.begin.html" - }, - "2": { - "name": "text.html.basic" - } - }, - "patterns": [ - { - "begin": "\\G", - "end": "(>)|(?=/>)", - "endCaptures": { - "1": { - "name": "punctuation.definition.tag.end.html" - } - }, - "name": "meta.tag.metadata.script.html", - "patterns": [ - { - "include": "#tag-stuff" - } - ] - }, - { - "begin": "(?!\\G)", - "end": "(?=)|(?=/>)", - "endCaptures": { - "1": { - "name": "punctuation.definition.tag.end.html" - } - }, - "name": "meta.tag.metadata.script.html", - "patterns": [ - { - "include": "#tag-stuff" - } - ] - }, - { - "begin": "(?!\\G)", - "end": "(?=)", - "name": "meta.tag.structure.any.html", - "patterns": [ - { - "include": "#tag-stuff" - } - ] - }, - { - "begin": "()", - "endCaptures": { - "1": { - "name": "punctuation.definition.tag.end.html" - } - }, - "name": "meta.tag.block.any.html", - "patterns": [ - { - "include": "#tag-stuff" - } - ] - }, - { - "begin": "()", - "endCaptures": { - "1": { - "name": "punctuation.definition.tag.end.html" - } - }, - "name": "meta.tag.inline.any.html", - "patterns": [ - { - "include": "#tag-stuff" - } - ] - }, - { - "begin": "()", - "endCaptures": { - "1": { - "name": "punctuation.definition.tag.end.html" - } - }, - "name": "meta.tag.other.html", - "patterns": [ - { - "include": "#tag-stuff" - } - ] - }, - { - "include": "#entities" - }, - { - "match": "<>", - "name": "invalid.illegal.incomplete.html" - } - ], - "repository": { - "embedded-code": { - "patterns": [ - { - "include": "#smarty" - }, - { - "include": "#python" + "match": "--!>", + "name": "invalid.illegal.characters-not-allowed-here.html" } ] }, "entities": { "patterns": [ + { + "captures": { + "1": { + "name": "punctuation.definition.entity.html" + }, + "912": { + "name": "punctuation.definition.entity.html" + } + }, + "comment": "Yes this is a bit ridiculous, there are quite a lot of these", + "match": "(?x)\n\t\t\t\t\t\t(&)\t(?=[a-zA-Z])\n\t\t\t\t\t\t(\n\t\t\t\t\t\t\t(a(s(ymp(eq)?|cr|t)|n(d(slope|d|v|and)?|g(s(t|ph)|zarr|e|le|rt(vb(d)?)?|msd(a(h|c|d|e|f|a|g|b))?)?)|c(y|irc|d|ute|E)?|tilde|o(pf|gon)|uml|p(id|os|prox(eq)?|e|E|acir)?|elig|f(r)?|w(conint|int)|l(pha|e(ph|fsym))|acute|ring|grave|m(p|a(cr|lg))|breve)|A(s(sign|cr)|nd|MP|c(y|irc)|tilde|o(pf|gon)|uml|pplyFunction|fr|Elig|lpha|acute|ring|grave|macr|breve))\n\t\t\t\t\t\t | (B(scr|cy|opf|umpeq|e(cause|ta|rnoullis)|fr|a(ckslash|r(v|wed))|reve)|b(s(cr|im(e)?|ol(hsub|b)?|emi)|n(ot|e(quiv)?)|c(y|ong)|ig(s(tar|qcup)|c(irc|up|ap)|triangle(down|up)|o(times|dot|plus)|uplus|vee|wedge)|o(t(tom)?|pf|wtie|x(h(d|u|D|U)?|times|H(d|u|D|U)?|d(R|l|r|L)|u(R|l|r|L)|plus|D(R|l|r|L)|v(R|h|H|l|r|L)?|U(R|l|r|L)|V(R|h|H|l|r|L)?|minus|box))|Not|dquo|u(ll(et)?|mp(e(q)?|E)?)|prime|e(caus(e)?|t(h|ween|a)|psi|rnou|mptyv)|karow|fr|l(ock|k(1(2|4)|34)|a(nk|ck(square|triangle(down|left|right)?|lozenge)))|a(ck(sim(eq)?|cong|prime|epsilon)|r(vee|wed(ge)?))|r(eve|vbar)|brk(tbrk)?))\n\t\t\t\t\t\t | (c(s(cr|u(p(e)?|b(e)?))|h(cy|i|eck(mark)?)|ylcty|c(irc|ups(sm)?|edil|a(ps|ron))|tdot|ir(scir|c(eq|le(d(R|circ|S|dash|ast)|arrow(left|right)))?|e|fnint|E|mid)?|o(n(int|g(dot)?)|p(y(sr)?|f|rod)|lon(e(q)?)?|m(p(fn|le(xes|ment))?|ma(t)?))|dot|u(darr(l|r)|p(s|c(up|ap)|or|dot|brcap)?|e(sc|pr)|vee|wed|larr(p)?|r(vearrow(left|right)|ly(eq(succ|prec)|vee|wedge)|arr(m)?|ren))|e(nt(erdot)?|dil|mptyv)|fr|w(conint|int)|lubs(uit)?|a(cute|p(s|c(up|ap)|dot|and|brcup)?|r(on|et))|r(oss|arr))|C(scr|hi|c(irc|onint|edil|aron)|ircle(Minus|Times|Dot|Plus)|Hcy|o(n(tourIntegral|int|gruent)|unterClockwiseContourIntegral|p(f|roduct)|lon(e)?)|dot|up(Cap)?|OPY|e(nterDot|dilla)|fr|lo(seCurly(DoubleQuote|Quote)|ckwiseContourIntegral)|a(yleys|cute|p(italDifferentialD)?)|ross))\n\t\t\t\t\t\t | (d(s(c(y|r)|trok|ol)|har(l|r)|c(y|aron)|t(dot|ri(f)?)|i(sin|e|v(ide(ontimes)?|onx)?|am(s|ond(suit)?)?|gamma)|Har|z(cy|igrarr)|o(t(square|plus|eq(dot)?|minus)?|ublebarwedge|pf|wn(harpoon(left|right)|downarrows|arrow)|llar)|d(otseq|a(rr|gger))?|u(har|arr)|jcy|e(lta|g|mptyv)|f(isht|r)|wangle|lc(orn|rop)|a(sh(v)?|leth|rr|gger)|r(c(orn|rop)|bkarow)|b(karow|lac)|Arr)|D(s(cr|trok)|c(y|aron)|Scy|i(fferentialD|a(critical(Grave|Tilde|Do(t|ubleAcute)|Acute)|mond))|o(t(Dot|Equal)?|uble(Right(Tee|Arrow)|ContourIntegral|Do(t|wnArrow)|Up(DownArrow|Arrow)|VerticalBar|L(ong(RightArrow|Left(RightArrow|Arrow))|eft(RightArrow|Tee|Arrow)))|pf|wn(Right(TeeVector|Vector(Bar)?)|Breve|Tee(Arrow)?|arrow|Left(RightVector|TeeVector|Vector(Bar)?)|Arrow(Bar|UpArrow)?))|Zcy|el(ta)?|D(otrahd)?|Jcy|fr|a(shv|rr|gger)))\n\t\t\t\t\t\t | (e(s(cr|im|dot)|n(sp|g)|c(y|ir(c)?|olon|aron)|t(h|a)|o(pf|gon)|dot|u(ro|ml)|p(si(v|lon)?|lus|ar(sl)?)|e|D(ot|Dot)|q(s(im|lant(less|gtr))|c(irc|olon)|u(iv(DD)?|est|als)|vparsl)|f(Dot|r)|l(s(dot)?|inters|l)?|a(ster|cute)|r(Dot|arr)|g(s(dot)?|rave)?|x(cl|ist|p(onentiale|ectation))|m(sp(1(3|4))?|pty(set|v)?|acr))|E(s(cr|im)|c(y|irc|aron)|ta|o(pf|gon)|NG|dot|uml|TH|psilon|qu(ilibrium|al(Tilde)?)|fr|lement|acute|grave|x(ists|ponentialE)|m(pty(SmallSquare|VerySmallSquare)|acr)))\n\t\t\t\t\t\t | (f(scr|nof|cy|ilig|o(pf|r(k(v)?|all))|jlig|partint|emale|f(ilig|l(ig|lig)|r)|l(tns|lig|at)|allingdotseq|r(own|a(sl|c(1(2|8|3|4|5|6)|78|2(3|5)|3(8|4|5)|45|5(8|6)))))|F(scr|cy|illed(SmallSquare|VerySmallSquare)|o(uriertrf|pf|rAll)|fr))\n\t\t\t\t\t\t | (G(scr|c(y|irc|edil)|t|opf|dot|T|Jcy|fr|amma(d)?|reater(Greater|SlantEqual|Tilde|Equal(Less)?|FullEqual|Less)|g|breve)|g(s(cr|im(e|l)?)|n(sim|e(q(q)?)?|E|ap(prox)?)|c(y|irc)|t(c(c|ir)|dot|quest|lPar|r(sim|dot|eq(qless|less)|less|a(pprox|rr)))?|imel|opf|dot|jcy|e(s(cc|dot(o(l)?)?|l(es)?)?|q(slant|q)?|l)?|v(nE|ertneqq)|fr|E(l)?|l(j|E|a)?|a(cute|p|mma(d)?)|rave|g(g)?|breve))\n\t\t\t\t\t\t | (h(s(cr|trok|lash)|y(phen|bull)|circ|o(ok(leftarrow|rightarrow)|pf|arr|rbar|mtht)|e(llip|arts(uit)?|rcon)|ks(earow|warow)|fr|a(irsp|lf|r(dcy|r(cir|w)?)|milt)|bar|Arr)|H(s(cr|trok)|circ|ilbertSpace|o(pf|rizontalLine)|ump(DownHump|Equal)|fr|a(cek|t)|ARDcy))\n\t\t\t\t\t\t | (i(s(cr|in(s(v)?|dot|v|E)?)|n(care|t(cal|prod|e(rcal|gers)|larhk)?|odot|fin(tie)?)?|c(y|irc)?|t(ilde)?|i(nfin|i(nt|int)|ota)?|o(cy|ta|pf|gon)|u(kcy|ml)|jlig|prod|e(cy|xcl)|quest|f(f|r)|acute|grave|m(of|ped|a(cr|th|g(part|e|line))))|I(scr|n(t(e(rsection|gral))?|visible(Comma|Times))|c(y|irc)|tilde|o(ta|pf|gon)|dot|u(kcy|ml)|Ocy|Jlig|fr|Ecy|acute|grave|m(plies|a(cr|ginaryI))?))\n\t\t\t\t\t\t | (j(s(cr|ercy)|c(y|irc)|opf|ukcy|fr|math)|J(s(cr|ercy)|c(y|irc)|opf|ukcy|fr))\n\t\t\t\t\t\t | (k(scr|hcy|c(y|edil)|opf|jcy|fr|appa(v)?|green)|K(scr|c(y|edil)|Hcy|opf|Jcy|fr|appa))\n\t\t\t\t\t\t | (l(s(h|cr|trok|im(e|g)?|q(uo(r)?|b)|aquo)|h(ar(d|u(l)?)|blk)|n(sim|e(q(q)?)?|E|ap(prox)?)|c(y|ub|e(il|dil)|aron)|Barr|t(hree|c(c|ir)|imes|dot|quest|larr|r(i(e|f)?|Par))?|Har|o(ng(left(arrow|rightarrow)|rightarrow|mapsto)|times|z(enge|f)?|oparrow(left|right)|p(f|lus|ar)|w(ast|bar)|a(ng|rr)|brk)|d(sh|ca|quo(r)?|r(dhar|ushar))|ur(dshar|uhar)|jcy|par(lt)?|e(s(s(sim|dot|eq(qgtr|gtr)|approx|gtr)|cc|dot(o(r)?)?|g(es)?)?|q(slant|q)?|ft(harpoon(down|up)|threetimes|leftarrows|arrow(tail)?|right(squigarrow|harpoons|arrow(s)?))|g)?|v(nE|ertneqq)|f(isht|loor|r)|E(g)?|l(hard|corner|tri|arr)?|a(ng(d|le)?|cute|t(e(s)?|ail)?|p|emptyv|quo|rr(sim|hk|tl|pl|fs|lp|b(fs)?)?|gran|mbda)|r(har(d)?|corner|tri|arr|m)|g(E)?|m(idot|oust(ache)?)|b(arr|r(k(sl(d|u)|e)|ac(e|k))|brk)|A(tail|arr|rr))|L(s(h|cr|trok)|c(y|edil|aron)|t|o(ng(RightArrow|left(arrow|rightarrow)|rightarrow|Left(RightArrow|Arrow))|pf|wer(RightArrow|LeftArrow))|T|e(ss(Greater|SlantEqual|Tilde|EqualGreater|FullEqual|Less)|ft(Right(Vector|Arrow)|Ceiling|T(ee(Vector|Arrow)?|riangle(Bar|Equal)?)|Do(ubleBracket|wn(TeeVector|Vector(Bar)?))|Up(TeeVector|DownVector|Vector(Bar)?)|Vector(Bar)?|arrow|rightarrow|Floor|A(ngleBracket|rrow(RightArrow|Bar)?)))|Jcy|fr|l(eftarrow)?|a(ng|cute|placetrf|rr|mbda)|midot))\n\t\t\t\t\t\t | (M(scr|cy|inusPlus|opf|u|e(diumSpace|llintrf)|fr|ap)|m(s(cr|tpos)|ho|nplus|c(y|omma)|i(nus(d(u)?|b)?|cro|d(cir|dot|ast)?)|o(dels|pf)|dash|u(ltimap|map)?|p|easuredangle|DDot|fr|l(cp|dr)|a(cr|p(sto(down|up|left)?)?|l(t(ese)?|e)|rker)))\n\t\t\t\t\t\t | (n(s(hort(parallel|mid)|c(cue|e|r)?|im(e(q)?)?|u(cc(eq)?|p(set(eq(q)?)?|e|E)?|b(set(eq(q)?)?|e|E)?)|par|qsu(pe|be)|mid)|Rightarrow|h(par|arr|Arr)|G(t(v)?|g)|c(y|ong(dot)?|up|edil|a(p|ron))|t(ilde|lg|riangle(left(eq)?|right(eq)?)|gl)|i(s(d)?|v)?|o(t(ni(v(c|a|b))?|in(dot|v(c|a|b)|E)?)?|pf)|dash|u(m(sp|ero)?)?|jcy|p(olint|ar(sl|t|allel)?|r(cue|e(c(eq)?)?)?)|e(s(im|ear)|dot|quiv|ar(hk|r(ow)?)|xist(s)?|Arr)?|v(sim|infin|Harr|dash|Dash|l(t(rie)?|e|Arr)|ap|r(trie|Arr)|g(t|e))|fr|w(near|ar(hk|r(ow)?)|Arr)|V(dash|Dash)|l(sim|t(ri(e)?)?|dr|e(s(s)?|q(slant|q)?|ft(arrow|rightarrow))?|E|arr|Arr)|a(ng|cute|tur(al(s)?)?|p(id|os|prox|E)?|bla)|r(tri(e)?|ightarrow|arr(c|w)?|Arr)|g(sim|t(r)?|e(s|q(slant|q)?)?|E)|mid|L(t(v)?|eft(arrow|rightarrow)|l)|b(sp|ump(e)?))|N(scr|c(y|edil|aron)|tilde|o(nBreakingSpace|Break|t(R(ightTriangle(Bar|Equal)?|everseElement)|Greater(Greater|SlantEqual|Tilde|Equal|FullEqual|Less)?|S(u(cceeds(SlantEqual|Tilde|Equal)?|perset(Equal)?|bset(Equal)?)|quareSu(perset(Equal)?|bset(Equal)?))|Hump(DownHump|Equal)|Nested(GreaterGreater|LessLess)|C(ongruent|upCap)|Tilde(Tilde|Equal|FullEqual)?|DoubleVerticalBar|Precedes(SlantEqual|Equal)?|E(qual(Tilde)?|lement|xists)|VerticalBar|Le(ss(Greater|SlantEqual|Tilde|Equal|Less)?|ftTriangle(Bar|Equal)?))?|pf)|u|e(sted(GreaterGreater|LessLess)|wLine|gative(MediumSpace|Thi(nSpace|ckSpace)|VeryThinSpace))|Jcy|fr|acute))\n\t\t\t\t\t\t | (o(s(cr|ol|lash)|h(m|bar)|c(y|ir(c)?)|ti(lde|mes(as)?)|S|int|opf|d(sold|iv|ot|ash|blac)|uml|p(erp|lus|ar)|elig|vbar|f(cir|r)|l(c(ir|ross)|t|ine|arr)|a(st|cute)|r(slope|igof|or|d(er(of)?|f|m)?|v|arr)?|g(t|on|rave)|m(i(nus|cron|d)|ega|acr))|O(s(cr|lash)|c(y|irc)|ti(lde|mes)|opf|dblac|uml|penCurly(DoubleQuote|Quote)|ver(B(ar|rac(e|ket))|Parenthesis)|fr|Elig|acute|r|grave|m(icron|ega|acr)))\n\t\t\t\t\t\t | (p(s(cr|i)|h(i(v)?|one|mmat)|cy|i(tchfork|v)?|o(intint|und|pf)|uncsp|er(cnt|tenk|iod|p|mil)|fr|l(us(sim|cir|two|d(o|u)|e|acir|mn|b)?|an(ck(h)?|kv))|ar(s(im|l)|t|a(llel)?)?|r(sim|n(sim|E|ap)|cue|ime(s)?|o(d|p(to)?|f(surf|line|alar))|urel|e(c(sim|n(sim|eqq|approx)|curlyeq|eq|approx)?)?|E|ap)?|m)|P(s(cr|i)|hi|cy|i|o(incareplane|pf)|fr|lusMinus|artialD|r(ime|o(duct|portion(al)?)|ecedes(SlantEqual|Tilde|Equal)?)?))\n\t\t\t\t\t\t | (q(scr|int|opf|u(ot|est(eq)?|at(int|ernions))|prime|fr)|Q(scr|opf|UOT|fr))\n\t\t\t\t\t\t | (R(s(h|cr)|ho|c(y|edil|aron)|Barr|ight(Ceiling|T(ee(Vector|Arrow)?|riangle(Bar|Equal)?)|Do(ubleBracket|wn(TeeVector|Vector(Bar)?))|Up(TeeVector|DownVector|Vector(Bar)?)|Vector(Bar)?|arrow|Floor|A(ngleBracket|rrow(Bar|LeftArrow)?))|o(undImplies|pf)|uleDelayed|e(verse(UpEquilibrium|E(quilibrium|lement)))?|fr|EG|a(ng|cute|rr(tl)?)|rightarrow)|r(s(h|cr|q(uo(r)?|b)|aquo)|h(o(v)?|ar(d|u(l)?))|nmid|c(y|ub|e(il|dil)|aron)|Barr|t(hree|imes|ri(e|f|ltri)?)|i(singdotseq|ng|ght(squigarrow|harpoon(down|up)|threetimes|left(harpoons|arrows)|arrow(tail)?|rightarrows))|Har|o(times|p(f|lus|ar)|a(ng|rr)|brk)|d(sh|ca|quo(r)?|ldhar)|uluhar|p(polint|ar(gt)?)|e(ct|al(s|ine|part)?|g)|f(isht|loor|r)|l(har|arr|m)|a(ng(d|e|le)?|c(ute|e)|t(io(nals)?|ail)|dic|emptyv|quo|rr(sim|hk|c|tl|pl|fs|w|lp|ap|b(fs)?)?)|rarr|x|moust(ache)?|b(arr|r(k(sl(d|u)|e)|ac(e|k))|brk)|A(tail|arr|rr)))\n\t\t\t\t\t\t | (s(s(cr|tarf|etmn|mile)|h(y|c(hcy|y)|ort(parallel|mid)|arp)|c(sim|y|n(sim|E|ap)|cue|irc|polint|e(dil)?|E|a(p|ron))?|t(ar(f)?|r(ns|aight(phi|epsilon)))|i(gma(v|f)?|m(ne|dot|plus|e(q)?|l(E)?|rarr|g(E)?)?)|zlig|o(pf|ftcy|l(b(ar)?)?)|dot(e|b)?|u(ng|cc(sim|n(sim|eqq|approx)|curlyeq|eq|approx)?|p(s(im|u(p|b)|et(neq(q)?|eq(q)?)?)|hs(ol|ub)|1|n(e|E)|2|d(sub|ot)|3|plus|e(dot)?|E|larr|mult)?|m|b(s(im|u(p|b)|et(neq(q)?|eq(q)?)?)|n(e|E)|dot|plus|e(dot)?|E|rarr|mult)?)|pa(des(uit)?|r)|e(swar|ct|tm(n|inus)|ar(hk|r(ow)?)|xt|mi|Arr)|q(su(p(set(eq)?|e)?|b(set(eq)?|e)?)|c(up(s)?|ap(s)?)|u(f|ar(e|f))?)|fr(own)?|w(nwar|ar(hk|r(ow)?)|Arr)|larr|acute|rarr|m(t(e(s)?)?|i(d|le)|eparsl|a(shp|llsetminus))|bquo)|S(scr|hort(RightArrow|DownArrow|UpArrow|LeftArrow)|c(y|irc|edil|aron)?|tar|igma|H(cy|CHcy)|opf|u(c(hThat|ceeds(SlantEqual|Tilde|Equal)?)|p(set|erset(Equal)?)?|m|b(set(Equal)?)?)|OFTcy|q(uare(Su(perset(Equal)?|bset(Equal)?)|Intersection|Union)?|rt)|fr|acute|mallCircle))\n\t\t\t\t\t\t | (t(s(hcy|c(y|r)|trok)|h(i(nsp|ck(sim|approx))|orn|e(ta(sym|v)?|re(4|fore))|k(sim|ap))|c(y|edil|aron)|i(nt|lde|mes(d|b(ar)?)?)|o(sa|p(cir|f(ork)?|bot)?|ea)|dot|prime|elrec|fr|w(ixt|ohead(leftarrow|rightarrow))|a(u|rget)|r(i(sb|time|dot|plus|e|angle(down|q|left(eq)?|right(eq)?)?|minus)|pezium|ade)|brk)|T(s(cr|trok)|RADE|h(i(nSpace|ckSpace)|e(ta|refore))|c(y|edil|aron)|S(cy|Hcy)|ilde(Tilde|Equal|FullEqual)?|HORN|opf|fr|a(u|b)|ripleDot))\n\t\t\t\t\t\t | (u(scr|h(ar(l|r)|blk)|c(y|irc)|t(ilde|dot|ri(f)?)|Har|o(pf|gon)|d(har|arr|blac)|u(arr|ml)|p(si(h|lon)?|harpoon(left|right)|downarrow|uparrows|lus|arrow)|f(isht|r)|wangle|l(c(orn(er)?|rop)|tri)|a(cute|rr)|r(c(orn(er)?|rop)|tri|ing)|grave|m(l|acr)|br(cy|eve)|Arr)|U(scr|n(ion(Plus)?|der(B(ar|rac(e|ket))|Parenthesis))|c(y|irc)|tilde|o(pf|gon)|dblac|uml|p(si(lon)?|downarrow|Tee(Arrow)?|per(RightArrow|LeftArrow)|DownArrow|Equilibrium|arrow|Arrow(Bar|DownArrow)?)|fr|a(cute|rr(ocir)?)|ring|grave|macr|br(cy|eve)))\n\t\t\t\t\t\t | (v(s(cr|u(pn(e|E)|bn(e|E)))|nsu(p|b)|cy|Bar(v)?|zigzag|opf|dash|prop|e(e(eq|bar)?|llip|r(t|bar))|Dash|fr|ltri|a(ngrt|r(s(igma|u(psetneq(q)?|bsetneq(q)?))|nothing|t(heta|riangle(left|right))|p(hi|i|ropto)|epsilon|kappa|r(ho)?))|rtri|Arr)|V(scr|cy|opf|dash(l)?|e(e|r(yThinSpace|t(ical(Bar|Separator|Tilde|Line))?|bar))|Dash|vdash|fr|bar))\n\t\t\t\t\t\t | (w(scr|circ|opf|p|e(ierp|d(ge(q)?|bar))|fr|r(eath)?)|W(scr|circ|opf|edge|fr))\n\t\t\t\t\t\t | (X(scr|i|opf|fr)|x(s(cr|qcup)|h(arr|Arr)|nis|c(irc|up|ap)|i|o(time|dot|p(f|lus))|dtri|u(tri|plus)|vee|fr|wedge|l(arr|Arr)|r(arr|Arr)|map))\n\t\t\t\t\t\t | (y(scr|c(y|irc)|icy|opf|u(cy|ml)|en|fr|ac(y|ute))|Y(scr|c(y|irc)|opf|uml|Icy|Ucy|fr|acute|Acy))\n\t\t\t\t\t\t | (z(scr|hcy|c(y|aron)|igrarr|opf|dot|e(ta|etrf)|fr|w(nj|j)|acute)|Z(scr|c(y|aron)|Hcy|opf|dot|e(ta|roWidthSpace)|fr|acute))\n\t\t\t\t\t\t)\n\t\t\t\t\t\t(;)\n\t\t\t\t\t", + "name": "constant.character.entity.named.$2.html" + }, { "captures": { "1": { @@ -542,199 +492,2073 @@ "name": "punctuation.definition.entity.html" } }, - "match": "(&)([a-zA-Z0-9]+|#[0-9]+|#[xX][0-9a-fA-F]+)(;)", - "name": "constant.character.entity.html" + "match": "(&)#[0-9]+(;)", + "name": "constant.character.entity.numeric.decimal.html" }, { - "match": "&", - "name": "invalid.illegal.bad-ampersand.html" - } - ] - }, - "python": { - "begin": "(?:^\\s*)<\\?python(?!.*\\?>)", - "end": "\\?>(?:\\s*$\\n)?", - "name": "source.python.embedded.html", - "patterns": [ - { - "include": "source.python" - } - ] - }, - "smarty": { - "patterns": [ - { - "begin": "(\\{(literal)\\})", "captures": { "1": { - "name": "source.smarty.embedded.html" + "name": "punctuation.definition.entity.html" + }, + "3": { + "name": "punctuation.definition.entity.html" + } + }, + "match": "(&)#[xX][0-9a-fA-F]+(;)", + "name": "constant.character.entity.numeric.hexadecimal.html" + }, + { + "match": "&(?=[a-zA-Z0-9]+;)", + "name": "invalid.illegal.ambiguous-ampersand.html" + } + ] + }, + "math": { + "patterns": [ + { + "begin": "(?i)(<)(math)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?", + "beginCaptures": { + "0": { + "name": "meta.tag.structure.$2.start.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" }, "2": { - "name": "support.function.built-in.smarty" + "name": "entity.name.tag.html" + }, + "3": { + "patterns": [ + { + "include": "#attribute" + } + ] + }, + "5": { + "name": "punctuation.definition.tag.end.html" } }, - "end": "(\\{/(literal)\\})" - }, - { - "begin": "{{|{", - "disabled": 1, - "end": "}}|}", - "name": "source.smarty.embedded.html", - "patterns": [ - { - "include": "source.smarty" - } - ] - } - ] - }, - "string-double-quoted": { - "begin": "\"", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.html" - } - }, - "end": "\"", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.html" - } - }, - "name": "string.quoted.double.html", - "patterns": [ - { - "include": "#embedded-code" - }, - { - "include": "#entities" - } - ] - }, - "string-single-quoted": { - "begin": "'", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.html" - } - }, - "end": "'", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.html" - } - }, - "name": "string.quoted.single.html", - "patterns": [ - { - "include": "#embedded-code" - }, - { - "include": "#entities" - } - ] - }, - "tag-generic-attribute": { - "match": "(?<=[^=])\\b([a-zA-Z0-9:-]+)", - "name": "entity.other.attribute-name.html" - }, - "tag-id-attribute": { - "begin": "\\b(id)\\b\\s*(=)", - "captures": { - "1": { - "name": "entity.other.attribute-name.id.html" - }, - "2": { - "name": "punctuation.separator.key-value.html" - } - }, - "end": "(?!\\G)(?<='|\"|[^\\s<>/])", - "name": "meta.attribute-with-value.id.html", - "patterns": [ - { - "begin": "\"", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.html" - } - }, - "contentName": "meta.toc-list.id.html", - "end": "\"", + "end": "(?i)()", "endCaptures": { "0": { - "name": "punctuation.definition.string.end.html" + "name": "meta.tag.structure.$2.end.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "name": "punctuation.definition.tag.end.html" } }, - "name": "string.quoted.double.html", + "name": "meta.element.structure.$2.html", "patterns": [ { - "include": "#embedded-code" + "begin": "(?)\\G", + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.structure.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] }, { - "include": "#entities" + "include": "#tags" } ] - }, - { - "begin": "'", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.html" - } - }, - "contentName": "meta.toc-list.id.html", - "end": "'", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.html" - } - }, - "name": "string.quoted.single.html", - "patterns": [ - { - "include": "#embedded-code" - }, - { - "include": "#entities" - } - ] - }, - { - "captures": { - "0": { - "name": "meta.toc-list.id.html" - } - }, - "match": "(?<==)(?:[^\\s<>/'\"]|/(?!>))+", - "name": "string.unquoted.html" } - ] + ], + "repository": { + "attribute": { + "patterns": [ + { + "begin": "(s(hift|ymmetric|cript(sizemultiplier|level|minsize)|t(ackalign|retchy)|ide|u(pscriptshift|bscriptshift)|e(parator(s)?|lection)|rc)|h(eight|ref)|n(otation|umalign)|c(haralign|olumn(spa(n|cing)|width|lines|align)|lose|rossout)|i(n(dent(shift(first|last)?|target|align(first|last)?)|fixlinebreakstyle)|d)|o(pen|verflow)|d(i(splay(style)?|r)|e(nomalign|cimalpoint|pth))|position|e(dge|qual(columns|rows))|voffset|f(orm|ence|rame(spacing)?)|width|l(space|ine(thickness|leading|break(style|multchar)?)|o(ngdivstyle|cation)|ength|quote|argeop)|a(c(cent(under)?|tiontype)|l(t(text|img(-(height|valign|width))?)|ign(mentscope)?))|r(space|ow(spa(n|cing)|lines|align)|quote)|groupalign|x(link:href|mlns)|m(in(size|labelspacing)|ovablelimits|a(th(size|color|variant|background)|xsize))|bevelled)(?![\\w:-])", + "beginCaptures": { + "0": { + "name": "entity.other.attribute-name.html" + } + }, + "end": "(?=\\s*+[^=\\s])", + "name": "meta.attribute.$1.html", + "patterns": [ + { + "include": "#attribute-interior" + } + ] + }, + { + "begin": "([^\\x{0020}\"'<>/=\\x{0000}-\\x{001F}\\x{007F}-\\x{009F}\\x{FDD0}-\\x{FDEF}\\x{FFFE}\\x{FFFF}\\x{1FFFE}\\x{1FFFF}\\x{2FFFE}\\x{2FFFF}\\x{3FFFE}\\x{3FFFF}\\x{4FFFE}\\x{4FFFF}\\x{5FFFE}\\x{5FFFF}\\x{6FFFE}\\x{6FFFF}\\x{7FFFE}\\x{7FFFF}\\x{8FFFE}\\x{8FFFF}\\x{9FFFE}\\x{9FFFF}\\x{AFFFE}\\x{AFFFF}\\x{BFFFE}\\x{BFFFF}\\x{CFFFE}\\x{CFFFF}\\x{DFFFE}\\x{DFFFF}\\x{EFFFE}\\x{EFFFF}\\x{FFFFE}\\x{FFFFF}\\x{10FFFE}\\x{10FFFF}]+)", + "beginCaptures": { + "0": { + "name": "entity.other.attribute-name.html" + } + }, + "comment": "Anything else that is valid", + "end": "(?=\\s*+[^=\\s])", + "name": "meta.attribute.unrecognized.$1.html", + "patterns": [ + { + "include": "#attribute-interior" + } + ] + }, + { + "match": "[^\\s>]+", + "name": "invalid.illegal.character-not-allowed-here.html" + } + ] + }, + "tags": { + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#cdata" + }, + { + "captures": { + "0": { + "name": "meta.tag.structure.math.$2.void.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "patterns": [ + { + "include": "#attribute" + } + ] + }, + "5": { + "name": "punctuation.definition.tag.end.html" + } + }, + "match": "(?i)(<)(annotation|annotation-xml|semantics|menclose|merror|mfenced|mfrac|mpadded|mphantom|mroot|mrow|msqrt|mstyle|mmultiscripts|mover|mprescripts|msub|msubsup|msup|munder|munderover|none|mlabeledtr|mtable|mtd|mtr|mlongdiv|mscarries|mscarry|msgroup|msline|msrow|mstack|maction)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(/>))", + "name": "meta.element.structure.math.$2.html" + }, + { + "begin": "(?i)(<)(annotation|annotation-xml|semantics|menclose|merror|mfenced|mfrac|mpadded|mphantom|mroot|mrow|msqrt|mstyle|mmultiscripts|mover|mprescripts|msub|msubsup|msup|munder|munderover|none|mlabeledtr|mtable|mtd|mtr|mlongdiv|mscarries|mscarry|msgroup|msline|msrow|mstack|maction)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?", + "beginCaptures": { + "0": { + "name": "meta.tag.structure.math.$2.start.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "patterns": [ + { + "include": "#attribute" + } + ] + }, + "5": { + "name": "punctuation.definition.tag.end.html" + } + }, + "end": "(?i)()|(/>)|(?=)\\G", + "end": "(?=/>)|>", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.structure.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "include": "#tags" + } + ] + }, + { + "captures": { + "0": { + "name": "meta.tag.inline.math.$2.void.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "patterns": [ + { + "include": "#attribute" + } + ] + }, + "5": { + "name": "punctuation.definition.tag.end.html" + } + }, + "match": "(?i)(<)(mi|mn|mo|ms|mspace|mtext|maligngroup|malignmark)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(/>))", + "name": "meta.element.inline.math.$2.html" + }, + { + "begin": "(?i)(<)(mi|mn|mo|ms|mspace|mtext|maligngroup|malignmark)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?", + "beginCaptures": { + "0": { + "name": "meta.tag.inline.math.$2.start.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "patterns": [ + { + "include": "#attribute" + } + ] + }, + "5": { + "name": "punctuation.definition.tag.end.html" + } + }, + "end": "(?i)()|(/>)|(?=)\\G", + "end": "(?=/>)|>", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.inline.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "include": "#tags" + } + ] + }, + { + "captures": { + "0": { + "name": "meta.tag.object.math.$2.void.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "patterns": [ + { + "include": "#attribute" + } + ] + }, + "5": { + "name": "punctuation.definition.tag.end.html" + } + }, + "match": "(?i)(<)(mglyph)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(/>))", + "name": "meta.element.object.math.$2.html" + }, + { + "begin": "(?i)(<)(mglyph)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?", + "beginCaptures": { + "0": { + "name": "meta.tag.object.math.$2.start.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "patterns": [ + { + "include": "#attribute" + } + ] + }, + "5": { + "name": "punctuation.definition.tag.end.html" + } + }, + "end": "(?i)()|(/>)|(?=)\\G", + "end": "(?=/>)|>", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.object.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "include": "#tags" + } + ] + }, + { + "captures": { + "0": { + "name": "meta.tag.other.invalid.void.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "name": "invalid.illegal.unrecognized-tag.html" + }, + "4": { + "patterns": [ + { + "include": "#attribute" + } + ] + }, + "6": { + "name": "punctuation.definition.tag.end.html" + } + }, + "match": "(?i)(<)(([\\w:]+))(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(/>))", + "name": "meta.element.other.invalid.html" + }, + { + "begin": "(?i)(<)((\\w[^\\s>]*))(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?", + "beginCaptures": { + "0": { + "name": "meta.tag.other.invalid.start.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "name": "invalid.illegal.unrecognized-tag.html" + }, + "4": { + "patterns": [ + { + "include": "#attribute" + } + ] + }, + "6": { + "name": "punctuation.definition.tag.end.html" + } + }, + "end": "(?i)()|(/>)|(?=)\\G", + "end": "(?=/>)|>", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.other.invalid.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "include": "#tags" + } + ] + }, + { + "include": "#tags-invalid" + } + ] + } + } }, - "tag-stuff": { + "svg": { "patterns": [ { - "include": "#tag-id-attribute" + "begin": "(?i)(<)(svg)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?", + "beginCaptures": { + "0": { + "name": "meta.tag.structure.$2.start.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "patterns": [ + { + "include": "#attribute" + } + ] + }, + "5": { + "name": "punctuation.definition.tag.end.html" + } + }, + "end": "(?i)()", + "endCaptures": { + "0": { + "name": "meta.tag.structure.$2.end.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.element.structure.$2.html", + "patterns": [ + { + "begin": "(?)\\G", + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.structure.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "include": "#tags" + } + ] + } + ], + "repository": { + "attribute": { + "patterns": [ + { + "begin": "(s(hape-rendering|ystemLanguage|cale|t(yle|itchTiles|op-(color|opacity)|dDeviation|em(h|v)|artOffset|r(i(ng|kethrough-(thickness|position))|oke(-(opacity|dash(offset|array)|width|line(cap|join)|miterlimit))?))|urfaceScale|p(e(cular(Constant|Exponent)|ed)|acing|readMethod)|eed|lope)|h(oriz-(origin-x|adv-x)|eight|anging|ref(lang)?)|y(1|2|ChannelSelector)?|n(umOctaves|ame)|c(y|o(ntentS(criptType|tyleType)|lor(-(interpolation(-filters)?|profile|rendering))?)|ursor|l(ip(-(path|rule)|PathUnits)?|ass)|a(p-height|lcMode)|x)|t(ype|o|ext(-(decoration|anchor|rendering)|Length)|a(rget(X|Y)?|b(index|leValues))|ransform)|i(n(tercept|2)?|d(eographic)?|mage-rendering)|z(oomAndPan)?|o(p(erator|acity)|ver(flow|line-(thickness|position))|ffset|r(i(ent(ation)?|gin)|der))|d(y|i(splay|visor|ffuseConstant|rection)|ominant-baseline|ur|e(scent|celerate)|x)?|u(1|n(i(code(-(range|bidi))?|ts-per-em)|derline-(thickness|position))|2)|p(ing|oint(s(At(X|Y|Z))?|er-events)|a(nose-1|t(h(Length)?|tern(ContentUnits|Transform|Units))|int-order)|r(imitiveUnits|eserveA(spectRatio|lpha)))|e(n(d|able-background)|dgeMode|levation|x(ternalResourcesRequired|ponent))|v(i(sibility|ew(Box|Target))|-(hanging|ideographic|alphabetic|mathematical)|e(ctor-effect|r(sion|t-(origin-(y|x)|adv-y)))|alues)|k(1|2|3|e(y(Splines|Times|Points)|rn(ing|el(Matrix|UnitLength)))|4)?|f(y|il(ter(Res|Units)?|l(-(opacity|rule))?)|o(nt-(s(t(yle|retch)|ize(-adjust)?)|variant|family|weight)|rmat)|lood-(color|opacity)|r(om)?|x)|w(idth(s)?|ord-spacing|riting-mode)|l(i(ghting-color|mitingConeAngle)|ocal|e(ngthAdjust|tter-spacing)|ang)|a(scent|cc(umulate|ent-height)|ttribute(Name|Type)|zimuth|dditive|utoReverse|l(ignment-baseline|phabetic|lowReorder)|rabic-form|mplitude)|r(y|otate|e(s(tart|ult)|ndering-intent|peat(Count|Dur)|quired(Extensions|Features)|f(X|Y|errerPolicy)|l)|adius|x)?|g(1|2|lyph(Ref|-(name|orientation-(horizontal|vertical)))|radient(Transform|Units))|x(1|2|ChannelSelector|-height|link:(show|href|t(ype|itle)|a(ctuate|rcrole)|role)|ml:(space|lang|base))?|m(in|ode|e(thod|dia)|a(sk(ContentUnits|Units)?|thematical|rker(Height|-(start|end|mid)|Units|Width)|x))|b(y|ias|egin|ase(Profile|line-shift|Frequency)|box))(?![\\w:-])", + "beginCaptures": { + "0": { + "name": "entity.other.attribute-name.html" + } + }, + "end": "(?=\\s*+[^=\\s])", + "name": "meta.attribute.$1.html", + "patterns": [ + { + "include": "#attribute-interior" + } + ] + }, + { + "begin": "([^\\x{0020}\"'<>/=\\x{0000}-\\x{001F}\\x{007F}-\\x{009F}\\x{FDD0}-\\x{FDEF}\\x{FFFE}\\x{FFFF}\\x{1FFFE}\\x{1FFFF}\\x{2FFFE}\\x{2FFFF}\\x{3FFFE}\\x{3FFFF}\\x{4FFFE}\\x{4FFFF}\\x{5FFFE}\\x{5FFFF}\\x{6FFFE}\\x{6FFFF}\\x{7FFFE}\\x{7FFFF}\\x{8FFFE}\\x{8FFFF}\\x{9FFFE}\\x{9FFFF}\\x{AFFFE}\\x{AFFFF}\\x{BFFFE}\\x{BFFFF}\\x{CFFFE}\\x{CFFFF}\\x{DFFFE}\\x{DFFFF}\\x{EFFFE}\\x{EFFFF}\\x{FFFFE}\\x{FFFFF}\\x{10FFFE}\\x{10FFFF}]+)", + "beginCaptures": { + "0": { + "name": "entity.other.attribute-name.html" + } + }, + "comment": "Anything else that is valid", + "end": "(?=\\s*+[^=\\s])", + "name": "meta.attribute.unrecognized.$1.html", + "patterns": [ + { + "include": "#attribute-interior" + } + ] + }, + { + "match": "[^\\s>]+", + "name": "invalid.illegal.character-not-allowed-here.html" + } + ] }, + "tags": { + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#cdata" + }, + { + "captures": { + "0": { + "name": "meta.tag.metadata.svg.$2.void.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "patterns": [ + { + "include": "#attribute" + } + ] + }, + "5": { + "name": "punctuation.definition.tag.end.html" + } + }, + "match": "(?i)(<)(color-profile|desc|metadata|script|style|title)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(/>))", + "name": "meta.element.metadata.svg.$2.html" + }, + { + "begin": "(?i)(<)(color-profile|desc|metadata|script|style|title)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?", + "beginCaptures": { + "0": { + "name": "meta.tag.metadata.svg.$2.start.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "patterns": [ + { + "include": "#attribute" + } + ] + }, + "5": { + "name": "punctuation.definition.tag.end.html" + } + }, + "end": "(?i)()|(/>)|(?=)\\G", + "end": "(?=/>)|>", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.metadata.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "include": "#tags" + } + ] + }, + { + "captures": { + "0": { + "name": "meta.tag.structure.svg.$2.void.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "patterns": [ + { + "include": "#attribute" + } + ] + }, + "5": { + "name": "punctuation.definition.tag.end.html" + } + }, + "match": "(?i)(<)(animateMotion|clipPath|defs|feComponentTransfer|feDiffuseLighting|feMerge|feSpecularLighting|filter|g|hatch|linearGradient|marker|mask|mesh|meshgradient|meshpatch|meshrow|pattern|radialGradient|switch|text|textPath)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(/>))", + "name": "meta.element.structure.svg.$2.html" + }, + { + "begin": "(?i)(<)(animateMotion|clipPath|defs|feComponentTransfer|feDiffuseLighting|feMerge|feSpecularLighting|filter|g|hatch|linearGradient|marker|mask|mesh|meshgradient|meshpatch|meshrow|pattern|radialGradient|switch|text|textPath)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?", + "beginCaptures": { + "0": { + "name": "meta.tag.structure.svg.$2.start.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "patterns": [ + { + "include": "#attribute" + } + ] + }, + "5": { + "name": "punctuation.definition.tag.end.html" + } + }, + "end": "(?i)()|(/>)|(?=)\\G", + "end": "(?=/>)|>", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.structure.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "include": "#tags" + } + ] + }, + { + "captures": { + "0": { + "name": "meta.tag.inline.svg.$2.void.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "patterns": [ + { + "include": "#attribute" + } + ] + }, + "5": { + "name": "punctuation.definition.tag.end.html" + } + }, + "match": "(?i)(<)(a|animate|discard|feBlend|feColorMatrix|feComposite|feConvolveMatrix|feDisplacementMap|feDistantLight|feDropShadow|feFlood|feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feMergeNode|feMorphology|feOffset|fePointLight|feSpotLight|feTile|feTurbulence|hatchPath|mpath|set|solidcolor|stop|tspan)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(/>))", + "name": "meta.element.inline.svg.$2.html" + }, + { + "begin": "(?i)(<)(a|animate|discard|feBlend|feColorMatrix|feComposite|feConvolveMatrix|feDisplacementMap|feDistantLight|feDropShadow|feFlood|feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feMergeNode|feMorphology|feOffset|fePointLight|feSpotLight|feTile|feTurbulence|hatchPath|mpath|set|solidcolor|stop|tspan)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?", + "beginCaptures": { + "0": { + "name": "meta.tag.inline.svg.$2.start.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "patterns": [ + { + "include": "#attribute" + } + ] + }, + "5": { + "name": "punctuation.definition.tag.end.html" + } + }, + "end": "(?i)()|(/>)|(?=)\\G", + "end": "(?=/>)|>", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.inline.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "include": "#tags" + } + ] + }, + { + "captures": { + "0": { + "name": "meta.tag.object.svg.$2.void.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "patterns": [ + { + "include": "#attribute" + } + ] + }, + "5": { + "name": "punctuation.definition.tag.end.html" + } + }, + "match": "(?i)(<)(circle|ellipse|feImage|foreignObject|image|line|path|polygon|polyline|rect|symbol|use|view)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(/>))", + "name": "meta.element.object.svg.$2.html" + }, + { + "begin": "(?i)(<)(a|circle|ellipse|feImage|foreignObject|image|line|path|polygon|polyline|rect|symbol|use|view)(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?", + "beginCaptures": { + "0": { + "name": "meta.tag.object.svg.$2.start.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "patterns": [ + { + "include": "#attribute" + } + ] + }, + "5": { + "name": "punctuation.definition.tag.end.html" + } + }, + "end": "(?i)()|(/>)|(?=)\\G", + "end": "(?=/>)|>", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.object.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "include": "#tags" + } + ] + }, + { + "captures": { + "0": { + "name": "meta.tag.other.svg.$2.void.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "name": "invalid.deprecated.html" + }, + "4": { + "patterns": [ + { + "include": "#attribute" + } + ] + }, + "6": { + "name": "punctuation.definition.tag.end.html" + } + }, + "match": "(?i)(<)((altGlyph|altGlyphDef|altGlyphItem|animateColor|animateTransform|cursor|font|font-face|font-face-format|font-face-name|font-face-src|font-face-uri|glyph|glyphRef|hkern|missing-glyph|tref|vkern))(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(/>))", + "name": "meta.element.other.svg.$2.html" + }, + { + "begin": "(?i)(<)((altGlyph|altGlyphDef|altGlyphItem|animateColor|animateTransform|cursor|font|font-face|font-face-format|font-face-name|font-face-src|font-face-uri|glyph|glyphRef|hkern|missing-glyph|tref|vkern))(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?", + "beginCaptures": { + "0": { + "name": "meta.tag.other.svg.$2.start.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "name": "invalid.deprecated.html" + }, + "4": { + "patterns": [ + { + "include": "#attribute" + } + ] + }, + "6": { + "name": "punctuation.definition.tag.end.html" + } + }, + "end": "(?i)()|(/>)|(?=)\\G", + "end": "(?=/>)|>", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.other.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "include": "#tags" + } + ] + }, + { + "captures": { + "0": { + "name": "meta.tag.other.invalid.void.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "name": "invalid.illegal.unrecognized-tag.html" + }, + "4": { + "patterns": [ + { + "include": "#attribute" + } + ] + }, + "6": { + "name": "punctuation.definition.tag.end.html" + } + }, + "match": "(?i)(<)(([\\w:]+))(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(/>))", + "name": "meta.element.other.invalid.html" + }, + { + "begin": "(?i)(<)((\\w[^\\s>]*))(?=\\s|/?>)(?:(([^\"'>]|\"[^\"]*\"|'[^']*')*)(>))?", + "beginCaptures": { + "0": { + "name": "meta.tag.other.invalid.start.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "name": "invalid.illegal.unrecognized-tag.html" + }, + "4": { + "patterns": [ + { + "include": "#attribute" + } + ] + }, + "6": { + "name": "punctuation.definition.tag.end.html" + } + }, + "end": "(?i)()|(/>)|(?=)\\G", + "end": "(?=/>)|>", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.other.invalid.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "include": "#tags" + } + ] + }, + { + "include": "#tags-invalid" + } + ] + } + } + }, + "tags-invalid": { + "patterns": [ { - "include": "#tag-generic-attribute" - }, - { - "include": "#string-double-quoted" - }, - { - "include": "#string-single-quoted" - }, - { - "include": "#embedded-code" - }, - { - "include": "#unquoted-attribute" + "begin": "(]*))(?)", + "endCaptures": { + "1": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.other.$2.html", + "patterns": [ + { + "include": "#attribute" + } + ] } ] }, - "unquoted-attribute": { - "match": "(?<==)(?:[^\\s<>/'\"]|/(?!>))+", - "name": "string.unquoted.html" + "tags-valid": { + "patterns": [ + { + "begin": "(^[ \\t]+)?(?=<(?i:style)\\b(?!-))", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.embedded.leading.html" + } + }, + "end": "(?!\\G)([ \\t]*$\\n?)?", + "endCaptures": { + "1": { + "name": "punctuation.whitespace.embedded.trailing.html" + } + }, + "patterns": [ + { + "begin": "(?i)(<)(style)(?=\\s|/?>)", + "beginCaptures": { + "0": { + "name": "meta.tag.metadata.style.start.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + } + }, + "end": "(?i)((<)/)(style)\\s*(>)", + "endCaptures": { + "0": { + "name": "meta.tag.metadata.style.end.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "source.css" + }, + "3": { + "name": "entity.name.tag.html" + }, + "4": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.embedded.block.html", + "patterns": [ + { + "begin": "\\G", + "captures": { + "1": { + "name": "punctuation.definition.tag.end.html" + } + }, + "end": "(>)", + "name": "meta.tag.metadata.style.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?!\\G)", + "end": "(?=)", + "endCaptures": { + "0": { + "name": "meta.tag.metadata.script.end.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.embedded.block.html", + "patterns": [ + { + "begin": "\\G", + "end": "(?=/)", + "patterns": [ + { + "begin": "(>)", + "beginCaptures": { + "0": { + "name": "meta.tag.metadata.script.start.html" + }, + "1": { + "name": "punctuation.definition.tag.end.html" + } + }, + "end": "((<))(?=/(?i:script))", + "endCaptures": { + "0": { + "name": "meta.tag.metadata.script.end.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "source.js" + } + }, + "patterns": [ + { + "begin": "\\G", + "end": "(?=\t\t\t\t\t\t\t\t\t\t\t# Tag without type attribute\n\t\t\t\t\t\t\t\t\t\t\t\t | type(?=[\\s=])\n\t\t\t\t\t\t\t\t\t\t\t\t \t(?!\\s*=\\s*\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t''\t\t\t\t\t\t\t\t# Empty\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t | \"\"\t\t\t\t\t\t\t\t\t# Values\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t | ('|\"|)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttext/\t\t\t\t\t\t\t# Text mime-types\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tjavascript(1\\.[0-5])?\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t | x-javascript\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t | jscript\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t | livescript\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t | (x-)?ecmascript\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t | babel\t\t\t\t\t\t# Javascript variant currently\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \t\t\t\t\t\t\t\t# recognized as such\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \t)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t | application/\t\t\t\t\t# Application mime-types\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(x-)?javascript\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t | (x-)?ecmascript\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t | module\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t \t)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[\\s\"'>]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t)", + "name": "meta.tag.metadata.script.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?ix:\n\t\t\t\t\t\t\t\t\t\t\t\t(?=\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype\\s*=\\s*\n\t\t\t\t\t\t\t\t\t\t\t\t\t('|\"|)\n\t\t\t\t\t\t\t\t\t\t\t\t\ttext/\n\t\t\t\t\t\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tx-handlebars\n\t\t\t\t\t\t\t\t\t\t\t\t\t | (x-(handlebars-)?|ng-)?template\n\t\t\t\t\t\t\t\t\t\t\t\t\t | html\n\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\t[\\s\"'>]\n\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t)", + "end": "((<))(?=/(?i:script))", + "endCaptures": { + "0": { + "name": "meta.tag.metadata.script.end.html" + }, + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "text.html.basic" + } + }, + "patterns": [ + { + "begin": "\\G", + "end": "(>)", + "endCaptures": { + "1": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.metadata.script.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?!\\G)", + "end": "(?=)", + "endCaptures": { + "1": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.metadata.script.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?!\\G)", + "end": "(?=)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + } + }, + "end": "/?>", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.metadata.$2.void.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?i)(<)(noscript|title)(?=\\s|/?>)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.metadata.$2.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?i)()", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.metadata.$2.end.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?i)(<)(col|hr|input)(?=\\s|/?>)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + } + }, + "end": "/?>", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.structure.$2.void.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?i)(<)(address|article|aside|blockquote|body|button|caption|colgroup|datalist|dd|details|dialog|div|dl|dt|fieldset|figcaption|figure|footer|form|head|header|hgroup|html|h[1-6]|label|legend|li|main|map|menu|meter|nav|ol|optgroup|option|output|p|pre|progress|section|select|slot|summary|table|tbody|td|template|textarea|tfoot|th|thead|tr|ul)(?=\\s|/?>)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.structure.$2.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?i)()", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.structure.$2.end.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?i)(<)(area|br|wbr)(?=\\s|/?>)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + } + }, + "end": "/?>", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.inline.$2.void.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?i)(<)(a|abbr|b|bdi|bdo|cite|code|data|del|dfn|em|i|ins|kbd|mark|q|rp|rt|ruby|s|samp|small|span|strong|sub|sup|time|u|var)(?=\\s|/?>)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.inline.$2.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?i)()", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.inline.$2.end.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?i)(<)(embed|img|param|source|track)(?=\\s|/?>)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + } + }, + "end": "/?>", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.object.$2.void.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?i)(<)(audio|canvas|iframe|object|picture|video)(?=\\s|/?>)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.object.$2.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?i)()", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.object.$2.end.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?i)(<)((basefont|isindex))(?=\\s|/?>)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "name": "invalid.deprecated.html" + } + }, + "end": "/?>", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.metadata.$2.void.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?i)(<)((center|frameset|noembed|noframes))(?=\\s|/?>)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "name": "invalid.deprecated.html" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.structure.$2.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?i)()", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "name": "invalid.deprecated.html" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.structure.$2.end.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?i)(<)((acronym|big|blink|font|strike|tt|xmp))(?=\\s|/?>)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "name": "invalid.deprecated.html" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.inline.$2.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?i)()", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "name": "invalid.deprecated.html" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.inline.$2.end.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?i)(<)((frame))(?=\\s|/?>)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "name": "invalid.deprecated.html" + } + }, + "end": "/?>", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.object.$2.void.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?i)(<)((applet))(?=\\s|/?>)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "name": "invalid.deprecated.html" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.object.$2.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?i)()", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "name": "invalid.deprecated.html" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.object.$2.end.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?i)(<)((dir|keygen|listing|menuitem|plaintext|spacer))(?=\\s|/?>)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "name": "invalid.illegal.no-longer-supported.html" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.other.$2.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "(?i)()", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + }, + "3": { + "name": "invalid.illegal.no-longer-supported.html" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.other.$2.end.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "include": "#math" + }, + { + "include": "#svg" + }, + { + "begin": "(<)([a-zA-Z][.0-9_a-zA-Z\\x{00B7}\\x{00C0}-\\x{00D6}\\x{00D8}-\\x{00F6}\\x{00F8}-\\x{037D}\\x{037F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{203F}-\\x{2040}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}]*-[\\-.0-9_a-zA-Z\\x{00B7}\\x{00C0}-\\x{00D6}\\x{00D8}-\\x{00F6}\\x{00F8}-\\x{037D}\\x{037F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{203F}-\\x{2040}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\x{10000}-\\x{EFFFF}]*)(?=\\s|/?>)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + } + }, + "end": "/?>", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.custom.start.html", + "patterns": [ + { + "include": "#attribute" + } + ] + }, + { + "begin": "()", + "beginCaptures": { + "1": { + "name": "punctuation.definition.tag.begin.html" + }, + "2": { + "name": "entity.name.tag.html" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.html" + } + }, + "name": "meta.tag.custom.end.html", + "patterns": [ + { + "include": "#attribute" + } + ] + } + ] } } } \ No newline at end of file diff --git a/extensions/html/test/colorize-results/12750_html.json b/extensions/html/test/colorize-results/12750_html.json index a89b03aa84f..389ca104ac5 100644 --- a/extensions/html/test/colorize-results/12750_html.json +++ b/extensions/html/test/colorize-results/12750_html.json @@ -1,7 +1,7 @@ [ { "c": "<", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -12,7 +12,7 @@ }, { "c": "script", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html entity.name.tag.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -23,7 +23,7 @@ }, { "c": " ", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -34,7 +34,7 @@ }, { "c": "type", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html entity.other.attribute-name.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -45,7 +45,7 @@ }, { "c": "=", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html punctuation.separator.key-value.html", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -56,7 +56,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -67,7 +67,7 @@ }, { "c": "text/javascript", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html string.quoted.double.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -78,7 +78,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -89,7 +89,7 @@ }, { "c": ">", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -210,7 +210,7 @@ }, { "c": "<", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html source.js", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -221,7 +221,7 @@ }, { "c": "/", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -232,7 +232,7 @@ }, { "c": "script", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html entity.name.tag.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -243,7 +243,7 @@ }, { "c": ">", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -254,7 +254,7 @@ }, { "c": "<", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -265,7 +265,7 @@ }, { "c": "script", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html entity.name.tag.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -276,7 +276,7 @@ }, { "c": ">", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -397,7 +397,7 @@ }, { "c": "<", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html source.js", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -408,7 +408,7 @@ }, { "c": "/", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -419,7 +419,7 @@ }, { "c": "script", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html entity.name.tag.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -430,7 +430,7 @@ }, { "c": ">", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", diff --git a/extensions/html/test/colorize-results/13448_html.json b/extensions/html/test/colorize-results/13448_html.json index 1a442d66c0b..2be7fc0e698 100644 --- a/extensions/html/test/colorize-results/13448_html.json +++ b/extensions/html/test/colorize-results/13448_html.json @@ -1,7 +1,7 @@ [ { "c": "<", - "t": "text.html.basic meta.tag.other.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.tag.custom.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -12,7 +12,7 @@ }, { "c": "ion-view", - "t": "text.html.basic meta.tag.other.html entity.name.tag.other.html", + "t": "text.html.basic meta.tag.custom.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -23,7 +23,7 @@ }, { "c": ">", - "t": "text.html.basic meta.tag.other.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.tag.custom.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -34,7 +34,7 @@ }, { "c": "<", - "t": "text.html.basic meta.tag.other.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.tag.custom.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -45,7 +45,7 @@ }, { "c": "button-view", - "t": "text.html.basic meta.tag.other.html entity.name.tag.other.html", + "t": "text.html.basic meta.tag.custom.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -56,7 +56,7 @@ }, { "c": "/>", - "t": "text.html.basic meta.tag.other.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.tag.custom.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -67,7 +67,7 @@ }, { "c": "<", - "t": "text.html.basic meta.tag.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.custom.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -78,7 +78,7 @@ }, { "c": "font-face", - "t": "text.html.basic meta.tag.any.html entity.name.tag.html", + "t": "text.html.basic meta.tag.custom.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -89,51 +89,7 @@ }, { "c": ">", - "t": "text.html.basic meta.tag.any.html punctuation.definition.tag.html", - "r": { - "dark_plus": "punctuation.definition.tag: #808080", - "light_plus": "punctuation.definition.tag: #800000", - "dark_vs": "punctuation.definition.tag: #808080", - "light_vs": "punctuation.definition.tag: #800000", - "hc_black": "punctuation.definition.tag: #808080" - } - }, - { - "c": "<", - "t": "text.html.basic meta.tag.any.html punctuation.definition.tag.html meta.scope.between-tag-pair.html", - "r": { - "dark_plus": "punctuation.definition.tag: #808080", - "light_plus": "punctuation.definition.tag: #800000", - "dark_vs": "punctuation.definition.tag: #808080", - "light_vs": "punctuation.definition.tag: #800000", - "hc_black": "punctuation.definition.tag: #808080" - } - }, - { - "c": "/", - "t": "text.html.basic meta.tag.any.html punctuation.definition.tag.html", - "r": { - "dark_plus": "punctuation.definition.tag: #808080", - "light_plus": "punctuation.definition.tag: #800000", - "dark_vs": "punctuation.definition.tag: #808080", - "light_vs": "punctuation.definition.tag: #800000", - "hc_black": "punctuation.definition.tag: #808080" - } - }, - { - "c": "font-face", - "t": "text.html.basic meta.tag.any.html entity.name.tag.html", - "r": { - "dark_plus": "entity.name.tag: #569CD6", - "light_plus": "entity.name.tag: #800000", - "dark_vs": "entity.name.tag: #569CD6", - "light_vs": "entity.name.tag: #800000", - "hc_black": "entity.name.tag: #569CD6" - } - }, - { - "c": ">", - "t": "text.html.basic meta.tag.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.custom.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -144,7 +100,7 @@ }, { "c": "", - "t": "text.html.basic meta.tag.other.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.tag.custom.end.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "", + "t": "text.html.basic meta.tag.custom.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", diff --git a/extensions/html/test/colorize-results/25920_html.json b/extensions/html/test/colorize-results/25920_html.json index 16b71acbe88..81f4d3c143b 100644 --- a/extensions/html/test/colorize-results/25920_html.json +++ b/extensions/html/test/colorize-results/25920_html.json @@ -1,7 +1,7 @@ [ { "c": "<", - "t": "text.html.basic meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.structure.html.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -12,7 +12,7 @@ }, { "c": "html", - "t": "text.html.basic meta.tag.structure.any.html entity.name.tag.structure.any.html", + "t": "text.html.basic meta.tag.structure.html.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -23,7 +23,7 @@ }, { "c": ">", - "t": "text.html.basic meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.structure.html.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -34,7 +34,7 @@ }, { "c": "<", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -45,7 +45,7 @@ }, { "c": "script", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html entity.name.tag.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -56,7 +56,7 @@ }, { "c": " ", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -67,7 +67,7 @@ }, { "c": "type", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html entity.other.attribute-name.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -78,7 +78,7 @@ }, { "c": "=", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html punctuation.separator.key-value.html", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -89,7 +89,7 @@ }, { "c": "'", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html string.quoted.single.html punctuation.definition.string.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html string.quoted.single.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.html: #0000FF", @@ -100,7 +100,7 @@ }, { "c": "text/html", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html string.quoted.single.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html string.quoted.single.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.html: #0000FF", @@ -111,7 +111,7 @@ }, { "c": "'", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html string.quoted.single.html punctuation.definition.string.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html string.quoted.single.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.html: #0000FF", @@ -122,7 +122,7 @@ }, { "c": ">", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -144,7 +144,7 @@ }, { "c": "<", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.structure.div.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -155,7 +155,7 @@ }, { "c": "div", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html entity.name.tag.html", + "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.structure.div.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -166,7 +166,7 @@ }, { "c": " ", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html", + "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.structure.div.start.html", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -177,7 +177,7 @@ }, { "c": "class", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html entity.other.attribute-name.html", + "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.structure.div.start.html meta.attribute.class.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -188,7 +188,7 @@ }, { "c": "=", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html", + "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.structure.div.start.html meta.attribute.class.html punctuation.separator.key-value.html", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -199,7 +199,7 @@ }, { "c": "'", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html string.quoted.single.html punctuation.definition.string.begin.html", + "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.structure.div.start.html meta.attribute.class.html string.quoted.single.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.html: #0000FF", @@ -210,7 +210,7 @@ }, { "c": "foo", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html string.quoted.single.html", + "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.structure.div.start.html meta.attribute.class.html string.quoted.single.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.html: #0000FF", @@ -221,7 +221,7 @@ }, { "c": "'", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html string.quoted.single.html punctuation.definition.string.end.html", + "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.structure.div.start.html meta.attribute.class.html string.quoted.single.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.html: #0000FF", @@ -232,7 +232,7 @@ }, { "c": ">", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.structure.div.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -242,19 +242,8 @@ } }, { - "c": "<", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html punctuation.definition.tag.html meta.scope.between-tag-pair.html", - "r": { - "dark_plus": "punctuation.definition.tag: #808080", - "light_plus": "punctuation.definition.tag: #800000", - "dark_vs": "punctuation.definition.tag: #808080", - "light_vs": "punctuation.definition.tag: #800000", - "hc_black": "punctuation.definition.tag: #808080" - } - }, - { - "c": "/", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html punctuation.definition.tag.html", + "c": "", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.structure.div.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -287,7 +276,7 @@ }, { "c": "<", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html text.html.basic", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html text.html.basic", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -298,7 +287,7 @@ }, { "c": "/", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -309,7 +298,7 @@ }, { "c": "script", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html entity.name.tag.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -320,7 +309,7 @@ }, { "c": ">", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -331,7 +320,7 @@ }, { "c": "<", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -342,7 +331,7 @@ }, { "c": "script", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html entity.name.tag.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -353,7 +342,7 @@ }, { "c": " ", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -364,7 +353,7 @@ }, { "c": "type", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html entity.other.attribute-name.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -375,7 +364,7 @@ }, { "c": "=", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html punctuation.separator.key-value.html", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -386,7 +375,7 @@ }, { "c": "'", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html string.quoted.single.html punctuation.definition.string.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html string.quoted.single.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.html: #0000FF", @@ -397,7 +386,7 @@ }, { "c": "module", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html string.quoted.single.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html string.quoted.single.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.html: #0000FF", @@ -408,7 +397,7 @@ }, { "c": "'", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html string.quoted.single.html punctuation.definition.string.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html string.quoted.single.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.html: #0000FF", @@ -419,7 +408,7 @@ }, { "c": ">", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -529,7 +518,7 @@ }, { "c": "<", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html source.js", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -540,7 +529,7 @@ }, { "c": "/", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -551,7 +540,7 @@ }, { "c": "script", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html entity.name.tag.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -562,7 +551,7 @@ }, { "c": ">", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -573,7 +562,7 @@ }, { "c": "<", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -584,7 +573,7 @@ }, { "c": "script", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html entity.name.tag.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -595,7 +584,7 @@ }, { "c": " ", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -606,7 +595,7 @@ }, { "c": "type", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html entity.other.attribute-name.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -617,7 +606,7 @@ }, { "c": "=", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html punctuation.separator.key-value.html", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -628,7 +617,7 @@ }, { "c": "'", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html string.quoted.single.html punctuation.definition.string.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html string.quoted.single.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.html: #0000FF", @@ -639,7 +628,7 @@ }, { "c": "text/ng-template", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html string.quoted.single.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html string.quoted.single.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.html: #0000FF", @@ -650,7 +639,7 @@ }, { "c": "'", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html string.quoted.single.html punctuation.definition.string.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html string.quoted.single.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.html: #0000FF", @@ -661,7 +650,7 @@ }, { "c": ">", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -683,7 +672,7 @@ }, { "c": "<", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.structure.div.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -694,7 +683,7 @@ }, { "c": "div", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html entity.name.tag.html", + "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.structure.div.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -705,7 +694,7 @@ }, { "c": " ", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html", + "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.structure.div.start.html", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -716,7 +705,7 @@ }, { "c": "class", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html entity.other.attribute-name.html", + "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.structure.div.start.html meta.attribute.class.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -727,7 +716,7 @@ }, { "c": "=", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html", + "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.structure.div.start.html meta.attribute.class.html punctuation.separator.key-value.html", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -738,7 +727,7 @@ }, { "c": "'", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html string.quoted.single.html punctuation.definition.string.begin.html", + "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.structure.div.start.html meta.attribute.class.html string.quoted.single.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.html: #0000FF", @@ -749,7 +738,7 @@ }, { "c": "foo", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html string.quoted.single.html", + "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.structure.div.start.html meta.attribute.class.html string.quoted.single.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.html: #0000FF", @@ -760,7 +749,7 @@ }, { "c": "'", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html string.quoted.single.html punctuation.definition.string.end.html", + "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.structure.div.start.html meta.attribute.class.html string.quoted.single.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.html: #0000FF", @@ -771,7 +760,7 @@ }, { "c": ">", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.structure.div.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -781,19 +770,8 @@ } }, { - "c": "<", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html punctuation.definition.tag.html meta.scope.between-tag-pair.html", - "r": { - "dark_plus": "punctuation.definition.tag: #808080", - "light_plus": "punctuation.definition.tag: #800000", - "dark_vs": "punctuation.definition.tag: #808080", - "light_vs": "punctuation.definition.tag: #800000", - "hc_black": "punctuation.definition.tag: #808080" - } - }, - { - "c": "/", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html punctuation.definition.tag.html", + "c": "", - "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.embedded.block.html text.html.basic meta.tag.structure.div.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -826,7 +804,7 @@ }, { "c": "<", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html text.html.basic", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html text.html.basic", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -837,7 +815,7 @@ }, { "c": "/", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -848,7 +826,7 @@ }, { "c": "script", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html entity.name.tag.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -859,7 +837,7 @@ }, { "c": ">", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -870,7 +848,7 @@ }, { "c": "<", - "t": "text.html.basic meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.structure.body.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -881,7 +859,7 @@ }, { "c": "body", - "t": "text.html.basic meta.tag.structure.any.html entity.name.tag.structure.any.html", + "t": "text.html.basic meta.tag.structure.body.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -892,7 +870,7 @@ }, { "c": " ", - "t": "text.html.basic meta.tag.structure.any.html", + "t": "text.html.basic meta.tag.structure.body.start.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -903,7 +881,7 @@ }, { "c": "class", - "t": "text.html.basic meta.tag.structure.any.html entity.other.attribute-name.html", + "t": "text.html.basic meta.tag.structure.body.start.html meta.attribute.class.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -914,7 +892,7 @@ }, { "c": "=", - "t": "text.html.basic meta.tag.structure.any.html", + "t": "text.html.basic meta.tag.structure.body.start.html meta.attribute.class.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -925,7 +903,7 @@ }, { "c": "'", - "t": "text.html.basic meta.tag.structure.any.html string.quoted.single.html punctuation.definition.string.begin.html", + "t": "text.html.basic meta.tag.structure.body.start.html meta.attribute.class.html string.quoted.single.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.html: #0000FF", @@ -936,7 +914,7 @@ }, { "c": "bar", - "t": "text.html.basic meta.tag.structure.any.html string.quoted.single.html", + "t": "text.html.basic meta.tag.structure.body.start.html meta.attribute.class.html string.quoted.single.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.html: #0000FF", @@ -947,7 +925,7 @@ }, { "c": "'", - "t": "text.html.basic meta.tag.structure.any.html string.quoted.single.html punctuation.definition.string.end.html", + "t": "text.html.basic meta.tag.structure.body.start.html meta.attribute.class.html string.quoted.single.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.html: #0000FF", @@ -958,7 +936,7 @@ }, { "c": ">", - "t": "text.html.basic meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.structure.body.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -969,7 +947,7 @@ }, { "c": "", - "t": "text.html.basic meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.structure.body.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1002,7 +980,7 @@ }, { "c": "", - "t": "text.html.basic meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.structure.html.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", diff --git a/extensions/html/test/colorize-results/test_html.json b/extensions/html/test/colorize-results/test_html.json index a1da5d2c393..20ca7fac857 100644 --- a/extensions/html/test/colorize-results/test_html.json +++ b/extensions/html/test/colorize-results/test_html.json @@ -1,7 +1,7 @@ [ { "c": "<", - "t": "text.html.basic meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.structure.html.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -12,7 +12,7 @@ }, { "c": "html", - "t": "text.html.basic meta.tag.structure.any.html entity.name.tag.structure.any.html", + "t": "text.html.basic meta.tag.structure.html.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -23,7 +23,7 @@ }, { "c": ">", - "t": "text.html.basic meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.structure.html.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -34,7 +34,7 @@ }, { "c": "<", - "t": "text.html.basic meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.structure.head.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -45,7 +45,7 @@ }, { "c": "head", - "t": "text.html.basic meta.tag.structure.any.html entity.name.tag.structure.any.html", + "t": "text.html.basic meta.tag.structure.head.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -56,7 +56,7 @@ }, { "c": ">", - "t": "text.html.basic meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.structure.head.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -78,7 +78,7 @@ }, { "c": "<", - "t": "text.html.basic meta.tag.inline.any.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.tag.metadata.meta.void.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -89,7 +89,7 @@ }, { "c": "meta", - "t": "text.html.basic meta.tag.inline.any.html entity.name.tag.inline.any.html", + "t": "text.html.basic meta.tag.metadata.meta.void.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -100,7 +100,7 @@ }, { "c": " ", - "t": "text.html.basic meta.tag.inline.any.html", + "t": "text.html.basic meta.tag.metadata.meta.void.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -111,7 +111,7 @@ }, { "c": "charset", - "t": "text.html.basic meta.tag.inline.any.html entity.other.attribute-name.html", + "t": "text.html.basic meta.tag.metadata.meta.void.html meta.attribute.charset.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -122,7 +122,7 @@ }, { "c": "=", - "t": "text.html.basic meta.tag.inline.any.html", + "t": "text.html.basic meta.tag.metadata.meta.void.html meta.attribute.charset.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -133,7 +133,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.basic meta.tag.metadata.meta.void.html meta.attribute.charset.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -144,7 +144,7 @@ }, { "c": "utf-8", - "t": "text.html.basic meta.tag.inline.any.html string.quoted.double.html", + "t": "text.html.basic meta.tag.metadata.meta.void.html meta.attribute.charset.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -155,7 +155,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.basic meta.tag.metadata.meta.void.html meta.attribute.charset.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -166,7 +166,7 @@ }, { "c": ">", - "t": "text.html.basic meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.tag.metadata.meta.void.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -188,7 +188,7 @@ }, { "c": "<", - "t": "text.html.basic meta.tag.inline.any.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.tag.metadata.title.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -199,7 +199,7 @@ }, { "c": "title", - "t": "text.html.basic meta.tag.inline.any.html entity.name.tag.inline.any.html", + "t": "text.html.basic meta.tag.metadata.title.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -210,7 +210,7 @@ }, { "c": ">", - "t": "text.html.basic meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.tag.metadata.title.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -232,7 +232,7 @@ }, { "c": "", - "t": "text.html.basic meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.tag.metadata.title.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -276,7 +276,7 @@ }, { "c": "<", - "t": "text.html.basic meta.tag.inline.any.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.tag.metadata.link.void.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -287,7 +287,7 @@ }, { "c": "link", - "t": "text.html.basic meta.tag.inline.any.html entity.name.tag.inline.any.html", + "t": "text.html.basic meta.tag.metadata.link.void.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -298,7 +298,7 @@ }, { "c": " ", - "t": "text.html.basic meta.tag.inline.any.html", + "t": "text.html.basic meta.tag.metadata.link.void.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -309,7 +309,7 @@ }, { "c": "href", - "t": "text.html.basic meta.tag.inline.any.html entity.other.attribute-name.html", + "t": "text.html.basic meta.tag.metadata.link.void.html meta.attribute.href.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -320,7 +320,7 @@ }, { "c": "=", - "t": "text.html.basic meta.tag.inline.any.html", + "t": "text.html.basic meta.tag.metadata.link.void.html meta.attribute.href.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -331,7 +331,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.basic meta.tag.metadata.link.void.html meta.attribute.href.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -342,7 +342,7 @@ }, { "c": "https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.css", - "t": "text.html.basic meta.tag.inline.any.html string.quoted.double.html", + "t": "text.html.basic meta.tag.metadata.link.void.html meta.attribute.href.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -353,7 +353,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.basic meta.tag.metadata.link.void.html meta.attribute.href.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -364,7 +364,7 @@ }, { "c": " ", - "t": "text.html.basic meta.tag.inline.any.html", + "t": "text.html.basic meta.tag.metadata.link.void.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -375,7 +375,7 @@ }, { "c": "rel", - "t": "text.html.basic meta.tag.inline.any.html entity.other.attribute-name.html", + "t": "text.html.basic meta.tag.metadata.link.void.html meta.attribute.rel.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -386,7 +386,7 @@ }, { "c": "=", - "t": "text.html.basic meta.tag.inline.any.html", + "t": "text.html.basic meta.tag.metadata.link.void.html meta.attribute.rel.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -397,7 +397,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.basic meta.tag.metadata.link.void.html meta.attribute.rel.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -408,7 +408,7 @@ }, { "c": "stylesheet", - "t": "text.html.basic meta.tag.inline.any.html string.quoted.double.html", + "t": "text.html.basic meta.tag.metadata.link.void.html meta.attribute.rel.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -419,7 +419,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.basic meta.tag.metadata.link.void.html meta.attribute.rel.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -429,8 +429,19 @@ } }, { - "c": " />", - "t": "text.html.basic meta.tag.inline.any.html punctuation.definition.tag.end.html", + "c": " ", + "t": "text.html.basic meta.tag.metadata.link.void.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "/>", + "t": "text.html.basic meta.tag.metadata.link.void.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -452,7 +463,7 @@ }, { "c": "<", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -463,7 +474,7 @@ }, { "c": "style", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.html entity.name.tag.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -474,7 +485,7 @@ }, { "c": " ", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.start.html", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -485,7 +496,7 @@ }, { "c": "type", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.html entity.other.attribute-name.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.start.html meta.attribute.type.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -496,7 +507,7 @@ }, { "c": "=", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.start.html meta.attribute.type.html punctuation.separator.key-value.html", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -507,7 +518,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.start.html meta.attribute.type.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -518,7 +529,7 @@ }, { "c": "text/css", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.html string.quoted.double.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.start.html meta.attribute.type.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -529,7 +540,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.start.html meta.attribute.type.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -540,7 +551,7 @@ }, { "c": ">", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -771,7 +782,7 @@ }, { "c": "<", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.html punctuation.definition.tag.begin.html source.css", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.end.html punctuation.definition.tag.begin.html source.css", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -782,7 +793,7 @@ }, { "c": "/", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -793,7 +804,7 @@ }, { "c": "style", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.html entity.name.tag.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -804,7 +815,7 @@ }, { "c": ">", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.style.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -815,7 +826,7 @@ }, { "c": "", - "t": "text.html.basic meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.structure.head.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -848,7 +859,7 @@ }, { "c": "<", - "t": "text.html.basic meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.structure.body.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -859,7 +870,7 @@ }, { "c": "body", - "t": "text.html.basic meta.tag.structure.any.html entity.name.tag.structure.any.html", + "t": "text.html.basic meta.tag.structure.body.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -870,7 +881,7 @@ }, { "c": ">", - "t": "text.html.basic meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.structure.body.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -892,7 +903,7 @@ }, { "c": "<", - "t": "text.html.basic meta.tag.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.structure.div.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -903,7 +914,7 @@ }, { "c": "div", - "t": "text.html.basic meta.tag.any.html entity.name.tag.html", + "t": "text.html.basic meta.tag.structure.div.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -914,7 +925,7 @@ }, { "c": " ", - "t": "text.html.basic meta.tag.any.html", + "t": "text.html.basic meta.tag.structure.div.start.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -925,7 +936,7 @@ }, { "c": "id", - "t": "text.html.basic meta.tag.any.html meta.attribute-with-value.id.html entity.other.attribute-name.id.html", + "t": "text.html.basic meta.tag.structure.div.start.html meta.attribute.id.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -936,7 +947,7 @@ }, { "c": "=", - "t": "text.html.basic meta.tag.any.html meta.attribute-with-value.id.html punctuation.separator.key-value.html", + "t": "text.html.basic meta.tag.structure.div.start.html meta.attribute.id.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -947,7 +958,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.tag.any.html meta.attribute-with-value.id.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.basic meta.tag.structure.div.start.html meta.attribute.id.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -958,7 +969,7 @@ }, { "c": "mocha", - "t": "text.html.basic meta.tag.any.html meta.attribute-with-value.id.html string.quoted.double.html meta.toc-list.id.html", + "t": "text.html.basic meta.tag.structure.div.start.html meta.attribute.id.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -969,7 +980,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.tag.any.html meta.attribute-with-value.id.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.basic meta.tag.structure.div.start.html meta.attribute.id.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -980,7 +991,7 @@ }, { "c": ">", - "t": "text.html.basic meta.tag.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.structure.div.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -990,19 +1001,8 @@ } }, { - "c": "<", - "t": "text.html.basic meta.tag.any.html punctuation.definition.tag.html meta.scope.between-tag-pair.html", - "r": { - "dark_plus": "punctuation.definition.tag: #808080", - "light_plus": "punctuation.definition.tag: #800000", - "dark_vs": "punctuation.definition.tag: #808080", - "light_vs": "punctuation.definition.tag: #800000", - "hc_black": "punctuation.definition.tag: #808080" - } - }, - { - "c": "/", - "t": "text.html.basic meta.tag.any.html punctuation.definition.tag.html", + "c": "", - "t": "text.html.basic meta.tag.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.structure.div.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1048,9 +1048,9 @@ "c": "", "t": "text.html.basic comment.block.html punctuation.definition.comment.html", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1101,7 +1101,7 @@ }, { "c": "<", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1112,7 +1112,7 @@ }, { "c": "script", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html entity.name.tag.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1123,7 +1123,7 @@ }, { "c": " ", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -1134,7 +1134,7 @@ }, { "c": "src", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html entity.other.attribute-name.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.src.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -1145,7 +1145,7 @@ }, { "c": "=", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.src.html punctuation.separator.key-value.html", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -1156,7 +1156,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.src.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -1167,7 +1167,7 @@ }, { "c": "/out/vs/loader.js", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html string.quoted.double.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.src.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -1178,7 +1178,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.src.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -1189,7 +1189,7 @@ }, { "c": ">", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1200,7 +1200,7 @@ }, { "c": "<", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html source.js", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1211,7 +1211,7 @@ }, { "c": "/", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1222,7 +1222,7 @@ }, { "c": "script", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html entity.name.tag.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1233,7 +1233,7 @@ }, { "c": ">", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1255,7 +1255,7 @@ }, { "c": "<", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1266,7 +1266,7 @@ }, { "c": "script", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html entity.name.tag.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1277,7 +1277,7 @@ }, { "c": " ", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -1288,7 +1288,7 @@ }, { "c": "src", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html entity.other.attribute-name.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.src.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -1299,7 +1299,7 @@ }, { "c": "=", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.src.html punctuation.separator.key-value.html", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -1310,7 +1310,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.src.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -1321,7 +1321,7 @@ }, { "c": "https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.js", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html string.quoted.double.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.src.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -1332,7 +1332,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.src.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -1343,7 +1343,7 @@ }, { "c": ">", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1354,7 +1354,7 @@ }, { "c": "<", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html source.js", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1365,7 +1365,7 @@ }, { "c": "/", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1376,7 +1376,7 @@ }, { "c": "script", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html entity.name.tag.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1387,7 +1387,7 @@ }, { "c": ">", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1409,7 +1409,7 @@ }, { "c": "<", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1420,7 +1420,7 @@ }, { "c": "script", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html entity.name.tag.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1431,7 +1431,7 @@ }, { "c": ">", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2212,7 +2212,7 @@ }, { "c": "<", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html source.js", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2223,7 +2223,7 @@ }, { "c": "/", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2234,7 +2234,7 @@ }, { "c": "script", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html entity.name.tag.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -2245,7 +2245,7 @@ }, { "c": ">", - "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2267,7 +2267,7 @@ }, { "c": "<", - "t": "text.html.basic meta.tag.block.any.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.tag.structure.div.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2278,7 +2278,7 @@ }, { "c": "div", - "t": "text.html.basic meta.tag.block.any.html entity.name.tag.block.any.html", + "t": "text.html.basic meta.tag.structure.div.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -2289,7 +2289,7 @@ }, { "c": " ", - "t": "text.html.basic meta.tag.block.any.html", + "t": "text.html.basic meta.tag.structure.div.start.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2300,7 +2300,7 @@ }, { "c": "class", - "t": "text.html.basic meta.tag.block.any.html entity.other.attribute-name.html", + "t": "text.html.basic meta.tag.structure.div.start.html meta.attribute.class.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -2311,7 +2311,7 @@ }, { "c": "=", - "t": "text.html.basic meta.tag.block.any.html", + "t": "text.html.basic meta.tag.structure.div.start.html meta.attribute.class.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2322,7 +2322,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.tag.block.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.basic meta.tag.structure.div.start.html meta.attribute.class.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2333,7 +2333,7 @@ }, { "c": "js-stale-session-flash stale-session-flash flash flash-warn flash-banner hidden", - "t": "text.html.basic meta.tag.block.any.html string.quoted.double.html", + "t": "text.html.basic meta.tag.structure.div.start.html meta.attribute.class.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2344,7 +2344,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.tag.block.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.basic meta.tag.structure.div.start.html meta.attribute.class.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2355,7 +2355,7 @@ }, { "c": ">", - "t": "text.html.basic meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.tag.structure.div.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2377,7 +2377,7 @@ }, { "c": "<", - "t": "text.html.basic meta.tag.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.inline.span.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2388,7 +2388,7 @@ }, { "c": "span", - "t": "text.html.basic meta.tag.any.html entity.name.tag.html", + "t": "text.html.basic meta.tag.inline.span.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -2399,7 +2399,7 @@ }, { "c": " ", - "t": "text.html.basic meta.tag.any.html", + "t": "text.html.basic meta.tag.inline.span.start.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2410,7 +2410,7 @@ }, { "c": "class", - "t": "text.html.basic meta.tag.any.html entity.other.attribute-name.html", + "t": "text.html.basic meta.tag.inline.span.start.html meta.attribute.class.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -2421,7 +2421,7 @@ }, { "c": "=", - "t": "text.html.basic meta.tag.any.html", + "t": "text.html.basic meta.tag.inline.span.start.html meta.attribute.class.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2432,7 +2432,7 @@ }, { "c": "octicon", - "t": "text.html.basic meta.tag.any.html string.unquoted.html", + "t": "text.html.basic meta.tag.inline.span.start.html meta.attribute.class.html string.unquoted.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.html: #0000FF", @@ -2443,7 +2443,7 @@ }, { "c": ">", - "t": "text.html.basic meta.tag.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.inline.span.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2453,19 +2453,8 @@ } }, { - "c": "<", - "t": "text.html.basic meta.tag.any.html punctuation.definition.tag.html meta.scope.between-tag-pair.html", - "r": { - "dark_plus": "punctuation.definition.tag: #808080", - "light_plus": "punctuation.definition.tag: #800000", - "dark_vs": "punctuation.definition.tag: #808080", - "light_vs": "punctuation.definition.tag: #800000", - "hc_black": "punctuation.definition.tag: #808080" - } - }, - { - "c": "/", - "t": "text.html.basic meta.tag.any.html punctuation.definition.tag.html", + "c": "", - "t": "text.html.basic meta.tag.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.inline.span.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2509,7 +2498,7 @@ }, { "c": "<", - "t": "text.html.basic meta.tag.inline.any.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.tag.inline.span.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2520,7 +2509,7 @@ }, { "c": "span", - "t": "text.html.basic meta.tag.inline.any.html entity.name.tag.inline.any.html", + "t": "text.html.basic meta.tag.inline.span.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -2531,7 +2520,7 @@ }, { "c": " ", - "t": "text.html.basic meta.tag.inline.any.html", + "t": "text.html.basic meta.tag.inline.span.start.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2542,7 +2531,7 @@ }, { "c": "class", - "t": "text.html.basic meta.tag.inline.any.html entity.other.attribute-name.html", + "t": "text.html.basic meta.tag.inline.span.start.html meta.attribute.class.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -2553,7 +2542,7 @@ }, { "c": "=", - "t": "text.html.basic meta.tag.inline.any.html", + "t": "text.html.basic meta.tag.inline.span.start.html meta.attribute.class.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2564,7 +2553,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.basic meta.tag.inline.span.start.html meta.attribute.class.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2575,7 +2564,7 @@ }, { "c": "signed-in-tab-flash", - "t": "text.html.basic meta.tag.inline.any.html string.quoted.double.html", + "t": "text.html.basic meta.tag.inline.span.start.html meta.attribute.class.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2586,7 +2575,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.basic meta.tag.inline.span.start.html meta.attribute.class.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2597,7 +2586,7 @@ }, { "c": ">", - "t": "text.html.basic meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.tag.inline.span.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2619,7 +2608,7 @@ }, { "c": "<", - "t": "text.html.basic meta.tag.inline.any.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.tag.inline.a.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2630,7 +2619,7 @@ }, { "c": "a", - "t": "text.html.basic meta.tag.inline.any.html entity.name.tag.inline.any.html", + "t": "text.html.basic meta.tag.inline.a.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -2641,7 +2630,7 @@ }, { "c": " ", - "t": "text.html.basic meta.tag.inline.any.html", + "t": "text.html.basic meta.tag.inline.a.start.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2652,7 +2641,7 @@ }, { "c": "href", - "t": "text.html.basic meta.tag.inline.any.html entity.other.attribute-name.html", + "t": "text.html.basic meta.tag.inline.a.start.html meta.attribute.href.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -2663,7 +2652,7 @@ }, { "c": "=", - "t": "text.html.basic meta.tag.inline.any.html", + "t": "text.html.basic meta.tag.inline.a.start.html meta.attribute.href.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2674,7 +2663,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.basic meta.tag.inline.a.start.html meta.attribute.href.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2685,7 +2674,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.basic meta.tag.inline.a.start.html meta.attribute.href.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2696,7 +2685,7 @@ }, { "c": ">", - "t": "text.html.basic meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.tag.inline.a.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2718,7 +2707,7 @@ }, { "c": "", - "t": "text.html.basic meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.tag.inline.a.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2762,7 +2751,7 @@ }, { "c": "", - "t": "text.html.basic meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.tag.inline.span.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2806,7 +2795,7 @@ }, { "c": "<", - "t": "text.html.basic meta.tag.inline.any.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.tag.inline.span.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2817,7 +2806,7 @@ }, { "c": "span", - "t": "text.html.basic meta.tag.inline.any.html entity.name.tag.inline.any.html", + "t": "text.html.basic meta.tag.inline.span.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -2828,7 +2817,7 @@ }, { "c": " ", - "t": "text.html.basic meta.tag.inline.any.html", + "t": "text.html.basic meta.tag.inline.span.start.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2839,7 +2828,7 @@ }, { "c": "class", - "t": "text.html.basic meta.tag.inline.any.html entity.other.attribute-name.html", + "t": "text.html.basic meta.tag.inline.span.start.html meta.attribute.class.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -2850,7 +2839,7 @@ }, { "c": "=", - "t": "text.html.basic meta.tag.inline.any.html", + "t": "text.html.basic meta.tag.inline.span.start.html meta.attribute.class.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2861,7 +2850,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.basic meta.tag.inline.span.start.html meta.attribute.class.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2872,7 +2861,7 @@ }, { "c": "signed-out-tab-flash", - "t": "text.html.basic meta.tag.inline.any.html string.quoted.double.html", + "t": "text.html.basic meta.tag.inline.span.start.html meta.attribute.class.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2883,7 +2872,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.basic meta.tag.inline.span.start.html meta.attribute.class.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2894,7 +2883,7 @@ }, { "c": ">", - "t": "text.html.basic meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.tag.inline.span.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2916,7 +2905,7 @@ }, { "c": "<", - "t": "text.html.basic meta.tag.inline.any.html punctuation.definition.tag.begin.html", + "t": "text.html.basic meta.tag.inline.a.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2927,7 +2916,7 @@ }, { "c": "a", - "t": "text.html.basic meta.tag.inline.any.html entity.name.tag.inline.any.html", + "t": "text.html.basic meta.tag.inline.a.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -2938,7 +2927,7 @@ }, { "c": " ", - "t": "text.html.basic meta.tag.inline.any.html", + "t": "text.html.basic meta.tag.inline.a.start.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2949,7 +2938,7 @@ }, { "c": "href", - "t": "text.html.basic meta.tag.inline.any.html entity.other.attribute-name.html", + "t": "text.html.basic meta.tag.inline.a.start.html meta.attribute.href.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -2960,7 +2949,7 @@ }, { "c": "=", - "t": "text.html.basic meta.tag.inline.any.html", + "t": "text.html.basic meta.tag.inline.a.start.html meta.attribute.href.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2971,7 +2960,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.basic meta.tag.inline.a.start.html meta.attribute.href.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2982,7 +2971,7 @@ }, { "c": "\"", - "t": "text.html.basic meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.basic meta.tag.inline.a.start.html meta.attribute.href.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2993,7 +2982,7 @@ }, { "c": ">", - "t": "text.html.basic meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.tag.inline.a.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3015,7 +3004,7 @@ }, { "c": "", - "t": "text.html.basic meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.tag.inline.a.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3059,7 +3048,7 @@ }, { "c": "", - "t": "text.html.basic meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.tag.inline.span.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3103,7 +3092,7 @@ }, { "c": "", - "t": "text.html.basic meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.basic meta.tag.structure.div.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3136,7 +3125,7 @@ }, { "c": "", - "t": "text.html.basic meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.structure.body.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3169,7 +3158,7 @@ }, { "c": "", - "t": "text.html.basic meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.basic meta.tag.structure.html.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", diff --git a/extensions/ini/test/colorize-results/test_ini.json b/extensions/ini/test/colorize-results/test_ini.json index 5b001c68246..39c089f5cef 100644 --- a/extensions/ini/test/colorize-results/test_ini.json +++ b/extensions/ini/test/colorize-results/test_ini.json @@ -3,9 +3,9 @@ "c": ";", "t": "source.ini comment.line.semicolon.ini punctuation.definition.comment.ini", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -14,9 +14,9 @@ "c": " last modified 1 April 2001 by John Doe", "t": "source.ini comment.line.semicolon.ini", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -157,9 +157,9 @@ "c": ";", "t": "source.ini comment.line.semicolon.ini punctuation.definition.comment.ini", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -168,9 +168,9 @@ "c": " use IP address in case network name resolution is not working", "t": "source.ini comment.line.semicolon.ini", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/java/syntaxes/java.tmLanguage.json b/extensions/java/syntaxes/java.tmLanguage.json index b629f66dc70..d4477107155 100644 --- a/extensions/java/syntaxes/java.tmLanguage.json +++ b/extensions/java/syntaxes/java.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/atom/language-java/commit/b9f1a853a69184363b0fb15f52da07c9660bf730", + "version": "https://github.com/atom/language-java/commit/f213dfac7dc3726170046c8f0c174c9e9f13e4ce", "name": "Java", "scopeName": "source.java", "patterns": [ @@ -183,6 +183,25 @@ } ] }, + "anonymous-block-and-instance-initializer": { + "begin": "{", + "beginCaptures": { + "0": { + "name": "punctuation.section.block.begin.bracket.curly.java" + } + }, + "end": "}", + "endCaptures": { + "0": { + "name": "punctuation.section.block.end.bracket.curly.java" + } + }, + "patterns": [ + { + "include": "#code" + } + ] + }, "anonymous-classes-and-new": { "begin": "\\bnew\\b", "beginCaptures": { @@ -370,36 +389,6 @@ } ] }, - "anonymous-block-and-instance-initializer": { - "begin": "{", - "beginCaptures": { - "0": { - "name": "punctuation.section.block.begin.bracket.curly.java" - } - }, - "end": "}", - "endCaptures": { - "0": { - "name": "punctuation.section.block.end.bracket.curly.java" - } - }, - "patterns": [ - { - "include": "#code" - } - ] - }, - "static-initializer": { - "patterns": [ - { - "include": "#anonymous-block-and-instance-initializer" - }, - { - "match": "static", - "name": "storage.modifier.java" - } - ] - }, "code": { "patterns": [ { @@ -450,15 +439,15 @@ { "include": "#function-call" }, + { + "include": "#variables" + }, { "include": "#objects" }, { "include": "#properties" }, - { - "include": "#variables" - }, { "include": "#strings" }, @@ -594,161 +583,6 @@ } ] }, - "try-catch-finally": { - "patterns": [ - { - "begin": "\\btry\\b", - "beginCaptures": { - "0": { - "name": "keyword.control.try.java" - } - }, - "end": "}", - "endCaptures": { - "0": { - "name": "punctuation.section.try.end.bracket.curly.java" - } - }, - "name": "meta.try.java", - "patterns": [ - { - "begin": "\\(", - "beginCaptures": { - "0": { - "name": "punctuation.section.try.resources.begin.bracket.round.java" - } - }, - "end": "\\)", - "endCaptures": { - "0": { - "name": "punctuation.section.try.resources.end.bracket.round.java" - } - }, - "name": "meta.try.resources.java", - "patterns": [ - { - "include": "#code" - } - ] - }, - { - "begin": "{", - "beginCaptures": { - "0": { - "name": "punctuation.section.try.begin.bracket.curly.java" - } - }, - "end": "(?=})", - "contentName": "meta.try.body.java", - "patterns": [ - { - "include": "#code" - } - ] - } - ] - }, - { - "begin": "\\b(catch)\\b\\s*(?=\\(\\s*[^\\s]+\\s*[^)]+\\))", - "beginCaptures": { - "1": { - "name": "keyword.control.catch.java" - } - }, - "end": "}", - "endCaptures": { - "0": { - "name": "punctuation.section.catch.end.bracket.curly.java" - } - }, - "name": "meta.catch.java", - "patterns": [ - { - "begin": "\\(", - "beginCaptures": { - "0": { - "name": "punctuation.definition.parameters.begin.bracket.round.java" - } - }, - "end": "\\)", - "endCaptures": { - "0": { - "name": "punctuation.definition.parameters.end.bracket.round.java" - } - }, - "contentName": "meta.catch.parameters.java", - "patterns": [ - { - "include": "#comments" - }, - { - "match": "\\|", - "name": "punctuation.catch.separator.java" - }, - { - "match": "([a-zA-Z$_][\\.a-zA-Z0-9$_]*)\\s*(\\w+)?", - "captures": { - "1": { - "name": "storage.type.java" - }, - "2": { - "name": "variable.parameter.java" - } - } - } - ] - }, - { - "begin": "{", - "beginCaptures": { - "0": { - "name": "punctuation.section.catch.begin.bracket.curly.java" - } - }, - "end": "(?=})", - "contentName": "meta.catch.body.java", - "patterns": [ - { - "include": "#code" - } - ] - } - ] - }, - { - "begin": "\\bfinally\\b", - "beginCaptures": { - "0": { - "name": "keyword.control.finally.java" - } - }, - "end": "}", - "endCaptures": { - "0": { - "name": "punctuation.section.finally.end.bracket.curly.java" - } - }, - "name": "meta.finally.java", - "patterns": [ - { - "begin": "{", - "beginCaptures": { - "0": { - "name": "punctuation.section.finally.begin.bracket.curly.java" - } - }, - "end": "(?=})", - "contentName": "meta.finally.body.java", - "patterns": [ - { - "include": "#code" - } - ] - } - ] - } - ] - }, "constants-and-special-vars": { "patterns": [ { @@ -765,66 +599,6 @@ } ] }, - "generics": { - "begin": "<", - "beginCaptures": { - "0": { - "name": "punctuation.bracket.angle.java" - } - }, - "end": ">", - "endCaptures": { - "0": { - "name": "punctuation.bracket.angle.java" - } - }, - "patterns": [ - { - "match": "\\b(extends|super)\\b", - "name": "storage.modifier.$1.java" - }, - { - "match": "(?", + "endCaptures": { + "0": { + "name": "punctuation.bracket.angle.java" + } + }, + "patterns": [ + { + "match": "\\b(extends|super)\\b", + "name": "storage.modifier.$1.java" + }, + { + "match": "(?)|(?!;)", - "patterns": [ - { - "include": "#generics" - } - ] - }, - { - "match": "\\b(?:[A-Z]\\w*\\s*(\\.)\\s*)*[A-Z]\\w*\\b((?=\\s*[A-Za-z$_\\n])|(?=\\s*\\.\\.\\.))", - "name": "storage.type.java", + "match": "\\b((?:[A-Za-z]\\w*\\s*\\.\\s*)*[A-Z]\\w*)\\s*(?=<)", "captures": { "1": { - "name": "punctuation.separator.period.java" + "patterns": [ + { + "match": "[A-Za-z]\\w*", + "name": "storage.type.java" + }, + { + "match": "\\.", + "name": "punctuation.separator.period.java" + } + ] + } + } + }, + { + "match": "\\b((?:[A-Za-z]\\w*\\s*\\.\\s*)*[A-Z]\\w*)\\b((?=\\s*[A-Za-z$_\\n])|(?=\\s*\\.\\.\\.))", + "captures": { + "1": { + "patterns": [ + { + "match": "[A-Za-z]\\w*", + "name": "storage.type.java" + }, + { + "match": "\\.", + "name": "punctuation.separator.period.java" + } + ] } } } @@ -1352,6 +1221,17 @@ } ] }, + "static-initializer": { + "patterns": [ + { + "include": "#anonymous-block-and-instance-initializer" + }, + { + "match": "static", + "name": "storage.modifier.java" + } + ] + }, "storage-modifiers": { "match": "\\b(public|private|protected|static|final|native|synchronized|abstract|threadsafe|transient|volatile|default|strictfp)\\b", "name": "storage.modifier.java" @@ -1422,9 +1302,164 @@ } ] }, + "try-catch-finally": { + "patterns": [ + { + "begin": "\\btry\\b", + "beginCaptures": { + "0": { + "name": "keyword.control.try.java" + } + }, + "end": "}", + "endCaptures": { + "0": { + "name": "punctuation.section.try.end.bracket.curly.java" + } + }, + "name": "meta.try.java", + "patterns": [ + { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.section.try.resources.begin.bracket.round.java" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.try.resources.end.bracket.round.java" + } + }, + "name": "meta.try.resources.java", + "patterns": [ + { + "include": "#code" + } + ] + }, + { + "begin": "{", + "beginCaptures": { + "0": { + "name": "punctuation.section.try.begin.bracket.curly.java" + } + }, + "end": "(?=})", + "contentName": "meta.try.body.java", + "patterns": [ + { + "include": "#code" + } + ] + } + ] + }, + { + "begin": "\\b(catch)\\b\\s*(?=\\(\\s*[^\\s]+\\s*[^)]+\\))", + "beginCaptures": { + "1": { + "name": "keyword.control.catch.java" + } + }, + "end": "}", + "endCaptures": { + "0": { + "name": "punctuation.section.catch.end.bracket.curly.java" + } + }, + "name": "meta.catch.java", + "patterns": [ + { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.definition.parameters.begin.bracket.round.java" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.parameters.end.bracket.round.java" + } + }, + "contentName": "meta.catch.parameters.java", + "patterns": [ + { + "include": "#comments" + }, + { + "match": "\\|", + "name": "punctuation.catch.separator.java" + }, + { + "match": "([a-zA-Z$_][\\.a-zA-Z0-9$_]*)\\s*(\\w+)?", + "captures": { + "1": { + "name": "storage.type.java" + }, + "2": { + "name": "variable.parameter.java" + } + } + } + ] + }, + { + "begin": "{", + "beginCaptures": { + "0": { + "name": "punctuation.section.catch.begin.bracket.curly.java" + } + }, + "end": "(?=})", + "contentName": "meta.catch.body.java", + "patterns": [ + { + "include": "#code" + } + ] + } + ] + }, + { + "begin": "\\bfinally\\b", + "beginCaptures": { + "0": { + "name": "keyword.control.finally.java" + } + }, + "end": "}", + "endCaptures": { + "0": { + "name": "punctuation.section.finally.end.bracket.curly.java" + } + }, + "name": "meta.finally.java", + "patterns": [ + { + "begin": "{", + "beginCaptures": { + "0": { + "name": "punctuation.section.finally.begin.bracket.curly.java" + } + }, + "end": "(?=})", + "contentName": "meta.finally.body.java", + "patterns": [ + { + "include": "#code" + } + ] + } + ] + } + ] + }, "variables": { "begin": "(?x)\n(?=\n (\n (void|boolean|byte|char|short|int|float|long|double)\n |\n (?>(\\w+\\.)*[A-Z]+\\w*) # e.g. `javax.ws.rs.Response`, or `String`\n )\n (\n <[\\w<>,\\.?\\s\\[\\]]*> # e.g. `HashMap`, or `List`\n )?\n (\n (\\[\\])* # int[][]\n )?\n \\s+\n [A-Za-z_$][\\w$]* # At least one identifier after space\n ([\\w\\[\\],$][\\w\\[\\],\\s]*)? # possibly primitive array or additional identifiers\n \\s*(=|;)\n)", - "end": "(?=;)", + "end": "(?=\\=|;)", "name": "meta.definition.variable.java", "patterns": [ { @@ -1438,42 +1473,10 @@ { "include": "#all-types" }, - { - "begin": "=", - "beginCaptures": { - "0": { - "name": "keyword.operator.assignment.java" - } - }, - "end": "(?=;)", - "patterns": [ - { - "include": "#code" - } - ] - }, { "include": "#code" } ] - }, - "member-variables": { - "begin": "(?=private|protected|public|native|synchronized|abstract|threadsafe|transient|static|final)", - "end": "(?=;)", - "patterns": [ - { - "include": "#storage-modifiers" - }, - { - "include": "#variables" - }, - { - "include": "#primitive-arrays" - }, - { - "include": "#object-types" - } - ] } } } \ No newline at end of file diff --git a/extensions/java/test/colorize-results/basic_java.json b/extensions/java/test/colorize-results/basic_java.json index 1ef3a8ff324..ce736eb21d2 100644 --- a/extensions/java/test/colorize-results/basic_java.json +++ b/extensions/java/test/colorize-results/basic_java.json @@ -245,9 +245,9 @@ "c": "/*", "t": "source.java comment.block.java punctuation.definition.comment.java", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -256,9 +256,9 @@ "c": " * Multi line comment", "t": "source.java comment.block.java", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -267,9 +267,9 @@ "c": " ", "t": "source.java comment.block.java", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -278,9 +278,9 @@ "c": "*/", "t": "source.java comment.block.java punctuation.definition.comment.java", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -443,9 +443,9 @@ "c": "\t/**", "t": "source.java meta.class.java meta.class.body.java comment.block.javadoc.java punctuation.definition.comment.java", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -454,9 +454,9 @@ "c": "\t *

Note:

Hello", "t": "source.java meta.class.java meta.class.body.java comment.block.javadoc.java", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -465,9 +465,9 @@ "c": "\t * ", "t": "source.java meta.class.java meta.class.body.java comment.block.javadoc.java", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -487,9 +487,9 @@ "c": " ", "t": "source.java meta.class.java meta.class.body.java comment.block.javadoc.java", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -500,7 +500,7 @@ "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "variable: #9CDCFE" } @@ -509,9 +509,9 @@ "c": "\t ", "t": "source.java meta.class.java meta.class.body.java comment.block.javadoc.java", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -520,9 +520,9 @@ "c": "*/", "t": "source.java meta.class.java meta.class.body.java comment.block.javadoc.java punctuation.definition.comment.java", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -727,7 +727,7 @@ }, { "c": "=", - "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.definition.variable.java keyword.operator.assignment.java", + "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java keyword.operator.assignment.java", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -738,7 +738,7 @@ }, { "c": " ", - "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.definition.variable.java", + "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -749,7 +749,7 @@ }, { "c": "0.0", - "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.definition.variable.java constant.numeric.decimal.java", + "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java constant.numeric.decimal.java", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -826,7 +826,7 @@ }, { "c": "=", - "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.definition.variable.java keyword.operator.assignment.java", + "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java keyword.operator.assignment.java", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -837,7 +837,7 @@ }, { "c": " ", - "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.definition.variable.java", + "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -848,7 +848,7 @@ }, { "c": "10e3", - "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.definition.variable.java constant.numeric.decimal.java", + "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java constant.numeric.decimal.java", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -925,7 +925,7 @@ }, { "c": "=", - "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.definition.variable.java keyword.operator.assignment.java", + "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java keyword.operator.assignment.java", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -936,7 +936,7 @@ }, { "c": " ", - "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.definition.variable.java", + "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -947,7 +947,7 @@ }, { "c": "134l", - "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.definition.variable.java constant.numeric.decimal.java", + "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java constant.numeric.decimal.java", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1004,9 +1004,9 @@ "c": "/*", "t": "source.java meta.class.java meta.class.body.java comment.block.java punctuation.definition.comment.java", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1015,9 +1015,9 @@ "c": "\t * multiline comment", "t": "source.java meta.class.java meta.class.body.java comment.block.java", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1026,9 +1026,9 @@ "c": "\t ", "t": "source.java meta.class.java meta.class.body.java comment.block.java", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1037,9 +1037,9 @@ "c": "*/", "t": "source.java meta.class.java meta.class.body.java comment.block.java punctuation.definition.comment.java", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1398,7 +1398,7 @@ }, { "c": "=", - "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.definition.variable.java keyword.operator.assignment.java", + "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java keyword.operator.assignment.java", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1409,7 +1409,7 @@ }, { "c": " ", - "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.definition.variable.java", + "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1420,7 +1420,7 @@ }, { "c": "0", - "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.definition.variable.java constant.numeric.decimal.java", + "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java constant.numeric.decimal.java", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -1829,9 +1829,9 @@ "c": "//", "t": "source.java meta.class.java meta.class.body.java comment.line.double-slash.java punctuation.definition.comment.java", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1840,9 +1840,9 @@ "c": "single line comment", "t": "source.java meta.class.java meta.class.body.java comment.line.double-slash.java", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2047,7 +2047,7 @@ }, { "c": "=", - "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.definition.variable.java keyword.operator.assignment.java", + "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java keyword.operator.assignment.java", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2058,7 +2058,7 @@ }, { "c": " ", - "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.definition.variable.java", + "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2069,7 +2069,7 @@ }, { "c": "0x5", - "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.definition.variable.java constant.numeric.hex.java", + "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java constant.numeric.hex.java", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #09885A", @@ -2179,7 +2179,7 @@ }, { "c": "=", - "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.definition.variable.java keyword.operator.assignment.java", + "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java keyword.operator.assignment.java", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2190,7 +2190,7 @@ }, { "c": " ", - "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.definition.variable.java", + "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2201,7 +2201,7 @@ }, { "c": "new", - "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.definition.variable.java keyword.control.new.java", + "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java keyword.control.new.java", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -2212,7 +2212,7 @@ }, { "c": " ", - "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.definition.variable.java", + "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2223,7 +2223,7 @@ }, { "c": "Vector", - "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.definition.variable.java meta.function-call.java entity.name.function.java", + "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.function-call.java entity.name.function.java", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -2234,7 +2234,7 @@ }, { "c": "(", - "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.definition.variable.java meta.function-call.java punctuation.definition.parameters.begin.bracket.round.java", + "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.function-call.java punctuation.definition.parameters.begin.bracket.round.java", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2245,7 +2245,7 @@ }, { "c": ")", - "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.definition.variable.java meta.function-call.java punctuation.definition.parameters.end.bracket.round.java", + "t": "source.java meta.class.java meta.class.body.java meta.method.java meta.method.body.java meta.function-call.java punctuation.definition.parameters.end.bracket.round.java", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index 4774b176fd2..2b6ae46fe6d 100644 --- a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/d7df3e324468b6535af67573d2956f9a852aa586", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/858d33f03943e4ec040e81cbabbc3f7892157c18", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -285,55 +285,112 @@ ] }, "var-expr": { - "name": "meta.var.expr.js", - "begin": "(?)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.js entity.name.function.js" @@ -386,6 +443,40 @@ } ] }, + "var-single-const": { + "patterns": [ + { + "name": "meta.var-single-variable.expr.js", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "beginCaptures": { + "1": { + "name": "meta.definition.variable.js variable.other.constant.js entity.name.function.js" + } + }, + "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", + "patterns": [ + { + "include": "#var-single-variable-type-annotation" + } + ] + }, + { + "name": "meta.var-single-variable.expr.js", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)", + "beginCaptures": { + "1": { + "name": "meta.definition.variable.js variable.other.constant.js" + } + }, + "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", + "patterns": [ + { + "include": "#var-single-variable-type-annotation" + } + ] + } + ] + }, "var-single-variable-type-annotation": { "patterns": [ { @@ -435,6 +526,42 @@ } ] }, + "destructuring-const": { + "patterns": [ + { + "name": "meta.object-binding-pattern-variable.js", + "begin": "(?)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.js" @@ -813,7 +1051,7 @@ "include": "#comment" }, { - "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\?)?(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\?)?(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.js entity.name.function.js" @@ -1594,7 +1832,7 @@ "name": "storage.type.namespace.js" } }, - "end": "(?<=\\})|(?=;|\\babstract\\b|\\basync\\b|\\bclass\\b|\\bconst\\b|\\bdeclare\\b|\\benum\\b|\\bexport\\b|\\bfunction\\b|\\bimport\\b|\\binterface\\b|\\blet\\b|\\bmodule\\b|\\bnamespace\\b|\\breturn\\b|\\btype\\b|\\bvar\\b)", + "end": "(?<=\\})|(?=;|^\\s*abstract\\b|^\\s*async\\b|^\\s*class\\b|^\\s*const\\b|^\\s*declare\\b|^\\s*enum\\b|^\\s*export\\b|^\\s*function\\b|^\\s*import\\b|^\\s*interface\\b|^\\s*let\\b|^\\s*module\\b|^\\s*namespace\\b|^\\s*return\\b|^\\s*type\\b|^\\s*var\\b)", "patterns": [ { "include": "#comment" @@ -1628,7 +1866,7 @@ "name": "entity.name.type.alias.js" } }, - "end": "(?=\\}|;|\\babstract\\b|\\basync\\b|\\bclass\\b|\\bconst\\b|\\bdeclare\\b|\\benum\\b|\\bexport\\b|\\bfunction\\b|\\bimport\\b|\\binterface\\b|\\blet\\b|\\bmodule\\b|\\bnamespace\\b|\\breturn\\b|\\btype\\b|\\bvar\\b)", + "end": "(?=\\}|;|^\\s*abstract\\b|^\\s*async\\b|^\\s*class\\b|^\\s*const\\b|^\\s*declare\\b|^\\s*enum\\b|^\\s*export\\b|^\\s*function\\b|^\\s*import\\b|^\\s*interface\\b|^\\s*let\\b|^\\s*module\\b|^\\s*namespace\\b|^\\s*return\\b|^\\s*type\\b|^\\s*var\\b)", "patterns": [ { "include": "#comment" @@ -1643,7 +1881,7 @@ "name": "keyword.operator.assignment.js" } }, - "end": "(?=\\}|;|\\babstract\\b|\\basync\\b|\\bclass\\b|\\bconst\\b|\\bdeclare\\b|\\benum\\b|\\bexport\\b|\\bfunction\\b|\\bimport\\b|\\binterface\\b|\\blet\\b|\\bmodule\\b|\\bnamespace\\b|\\breturn\\b|\\btype\\b|\\bvar\\b)", + "end": "(?=\\}|;|^\\s*abstract\\b|^\\s*async\\b|^\\s*class\\b|^\\s*const\\b|^\\s*declare\\b|^\\s*enum\\b|^\\s*export\\b|^\\s*function\\b|^\\s*import\\b|^\\s*interface\\b|^\\s*let\\b|^\\s*module\\b|^\\s*namespace\\b|^\\s*return\\b|^\\s*type\\b|^\\s*var\\b)", "patterns": [ { "include": "#type" @@ -1807,7 +2045,7 @@ "name": "keyword.control.default.js" } }, - "end": "(?=$|;|\\babstract\\b|\\basync\\b|\\bclass\\b|\\bconst\\b|\\bdeclare\\b|\\benum\\b|\\bexport\\b|\\bfunction\\b|\\bimport\\b|\\binterface\\b|\\blet\\b|\\bmodule\\b|\\bnamespace\\b|\\breturn\\b|\\btype\\b|\\bvar\\b)", + "end": "(?=$|;|^\\s*abstract\\b|^\\s*async\\b|^\\s*class\\b|^\\s*const\\b|^\\s*declare\\b|^\\s*enum\\b|^\\s*export\\b|^\\s*function\\b|^\\s*import\\b|^\\s*interface\\b|^\\s*let\\b|^\\s*module\\b|^\\s*namespace\\b|^\\s*return\\b|^\\s*type\\b|^\\s*var\\b)", "patterns": [ { "include": "#expression" @@ -1822,7 +2060,7 @@ "name": "keyword.control.export.js" } }, - "end": "(?=$|;|\\babstract\\b|\\basync\\b|\\bclass\\b|\\bconst\\b|\\bdeclare\\b|\\benum\\b|\\bexport\\b|\\bfunction\\b|\\bimport\\b|\\binterface\\b|\\blet\\b|\\bmodule\\b|\\bnamespace\\b|\\breturn\\b|\\btype\\b|\\bvar\\b)", + "end": "(?=$|;|^\\s*abstract\\b|^\\s*async\\b|^\\s*class\\b|^\\s*const\\b|^\\s*declare\\b|^\\s*enum\\b|^\\s*export\\b|^\\s*function\\b|^\\s*import\\b|^\\s*interface\\b|^\\s*let\\b|^\\s*module\\b|^\\s*namespace\\b|^\\s*return\\b|^\\s*type\\b|^\\s*var\\b)", "patterns": [ { "include": "#import-export-declaration" @@ -2167,7 +2405,7 @@ }, { "name": "meta.object.member.js", - "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=:\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=:\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.js" @@ -2412,7 +2650,7 @@ ] }, { - "begin": "(?<=[(=,]|=>)\\s*(async)?(?=\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?\\(\\s*$)", + "begin": "(?<=[(=,]|=>)\\s*(async)?(?=\\s*((((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?\\()|(<))\\s*$)", "beginCaptures": { "1": { "name": "storage.modifier.async.js" @@ -3004,7 +3242,7 @@ "include": "#object-identifiers" }, { - "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.js" @@ -3474,7 +3712,7 @@ "include": "#destructuring-parameter" }, { - "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)))", + "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))))", "captures": { "1": { "name": "storage.modifier.js" @@ -3846,7 +4084,7 @@ "patterns": [ { "name": "string.regexp.js", - "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "1": { "name": "punctuation.definition.string.begin.js" @@ -3869,7 +4107,7 @@ }, { "name": "string.regexp.js", - "begin": "(?:*]|&&|\\|\\||\\?|^return|[^\\._$[:alnum:]]return|^default|[^\\._$[:alnum:]]default|^)\\s*(?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?))", - "end": "(?!(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?))", + "begin": "(?:*]|&&|\\|\\||\\?|^await|[^\\._$[:alnum:]]await|^return|[^\\._$[:alnum:]]return|^default|[^\\._$[:alnum:]]default|^yield|[^\\._$[:alnum:]]yield|^)\\s*(?=(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?))", + "end": "(?!(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?))", "patterns": [ { "include": "#jsx-tag-without-attributes" @@ -4618,8 +4856,8 @@ }, "jsx-tag-without-attributes": { "name": "meta.tag.without-attributes.js", - "begin": "(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?)", - "end": "()", + "begin": "(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?)", + "end": "()", "beginCaptures": { "1": { "name": "punctuation.definition.tag.begin.js" @@ -4668,8 +4906,8 @@ ] }, "jsx-tag-in-expression": { - "begin": "(?x)\n (?:*]|&&|\\|\\||\\?|^return|[^\\._$[:alnum:]]return|^default|[^\\._$[:alnum:]]default|^)\\s*\n (?!<\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s+[^=>])|,)) # look ahead is not type parameter of arrow\n (?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?))", - "end": "(?!(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?))", + "begin": "(?x)\n (?:*]|&&|\\|\\||\\?|^await|[^\\._$[:alnum:]]await|^return|[^\\._$[:alnum:]]return|^default|[^\\._$[:alnum:]]default|^yield|[^\\._$[:alnum:]]yield|^)\\s*\n (?!<\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s+[^=>])|,)) # look ahead is not type parameter of arrow\n (?=(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?))", + "end": "(?!(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?))", "patterns": [ { "include": "#jsx-tag" @@ -4678,8 +4916,8 @@ }, "jsx-tag": { "name": "meta.tag.js", - "begin": "(?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?))", - "end": "(/>)|(?:())", + "begin": "(?=(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?))", + "end": "(/>)|(?:())", "endCaptures": { "1": { "name": "punctuation.definition.tag.end.js" @@ -4705,7 +4943,7 @@ }, "patterns": [ { - "begin": "(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?)", + "begin": "(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?)", "beginCaptures": { "1": { "name": "punctuation.definition.tag.begin.js" @@ -4838,7 +5076,7 @@ ] }, "jsx-tag-attribute-name": { - "match": "(?x)\n \\s*\n (?:([_$a-zA-Z][-$\\w.]*)(:))?\n ([_$a-zA-Z][-$\\w]*)\n (?=\\s|=|/?>|/\\*|//)", + "match": "(?x)\n \\s*\n (?:([_$[:alpha:]][-$[:alnum:].]*)(:))?\n ([_$[:alpha:]][-$[:alnum:]]*)\n (?=\\s|=|/?>|/\\*|//)", "captures": { "1": { "name": "entity.other.attribute-name.namespace.js" diff --git a/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json b/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json index 5aebe044bdc..15eeb04d7fc 100644 --- a/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/d7df3e324468b6535af67573d2956f9a852aa586", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/858d33f03943e4ec040e81cbabbc3f7892157c18", "name": "JavaScript (with React support)", "scopeName": "source.js.jsx", "patterns": [ @@ -285,55 +285,112 @@ ] }, "var-expr": { - "name": "meta.var.expr.js.jsx", - "begin": "(?)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.js.jsx entity.name.function.js.jsx" @@ -386,6 +443,40 @@ } ] }, + "var-single-const": { + "patterns": [ + { + "name": "meta.var-single-variable.expr.js.jsx", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "beginCaptures": { + "1": { + "name": "meta.definition.variable.js.jsx variable.other.constant.js.jsx entity.name.function.js.jsx" + } + }, + "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", + "patterns": [ + { + "include": "#var-single-variable-type-annotation" + } + ] + }, + { + "name": "meta.var-single-variable.expr.js.jsx", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)", + "beginCaptures": { + "1": { + "name": "meta.definition.variable.js.jsx variable.other.constant.js.jsx" + } + }, + "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", + "patterns": [ + { + "include": "#var-single-variable-type-annotation" + } + ] + } + ] + }, "var-single-variable-type-annotation": { "patterns": [ { @@ -435,6 +526,42 @@ } ] }, + "destructuring-const": { + "patterns": [ + { + "name": "meta.object-binding-pattern-variable.js.jsx", + "begin": "(?)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.js.jsx" @@ -813,7 +1051,7 @@ "include": "#comment" }, { - "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\?)?(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\?)?(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.js.jsx entity.name.function.js.jsx" @@ -1594,7 +1832,7 @@ "name": "storage.type.namespace.js.jsx" } }, - "end": "(?<=\\})|(?=;|\\babstract\\b|\\basync\\b|\\bclass\\b|\\bconst\\b|\\bdeclare\\b|\\benum\\b|\\bexport\\b|\\bfunction\\b|\\bimport\\b|\\binterface\\b|\\blet\\b|\\bmodule\\b|\\bnamespace\\b|\\breturn\\b|\\btype\\b|\\bvar\\b)", + "end": "(?<=\\})|(?=;|^\\s*abstract\\b|^\\s*async\\b|^\\s*class\\b|^\\s*const\\b|^\\s*declare\\b|^\\s*enum\\b|^\\s*export\\b|^\\s*function\\b|^\\s*import\\b|^\\s*interface\\b|^\\s*let\\b|^\\s*module\\b|^\\s*namespace\\b|^\\s*return\\b|^\\s*type\\b|^\\s*var\\b)", "patterns": [ { "include": "#comment" @@ -1628,7 +1866,7 @@ "name": "entity.name.type.alias.js.jsx" } }, - "end": "(?=\\}|;|\\babstract\\b|\\basync\\b|\\bclass\\b|\\bconst\\b|\\bdeclare\\b|\\benum\\b|\\bexport\\b|\\bfunction\\b|\\bimport\\b|\\binterface\\b|\\blet\\b|\\bmodule\\b|\\bnamespace\\b|\\breturn\\b|\\btype\\b|\\bvar\\b)", + "end": "(?=\\}|;|^\\s*abstract\\b|^\\s*async\\b|^\\s*class\\b|^\\s*const\\b|^\\s*declare\\b|^\\s*enum\\b|^\\s*export\\b|^\\s*function\\b|^\\s*import\\b|^\\s*interface\\b|^\\s*let\\b|^\\s*module\\b|^\\s*namespace\\b|^\\s*return\\b|^\\s*type\\b|^\\s*var\\b)", "patterns": [ { "include": "#comment" @@ -1643,7 +1881,7 @@ "name": "keyword.operator.assignment.js.jsx" } }, - "end": "(?=\\}|;|\\babstract\\b|\\basync\\b|\\bclass\\b|\\bconst\\b|\\bdeclare\\b|\\benum\\b|\\bexport\\b|\\bfunction\\b|\\bimport\\b|\\binterface\\b|\\blet\\b|\\bmodule\\b|\\bnamespace\\b|\\breturn\\b|\\btype\\b|\\bvar\\b)", + "end": "(?=\\}|;|^\\s*abstract\\b|^\\s*async\\b|^\\s*class\\b|^\\s*const\\b|^\\s*declare\\b|^\\s*enum\\b|^\\s*export\\b|^\\s*function\\b|^\\s*import\\b|^\\s*interface\\b|^\\s*let\\b|^\\s*module\\b|^\\s*namespace\\b|^\\s*return\\b|^\\s*type\\b|^\\s*var\\b)", "patterns": [ { "include": "#type" @@ -1807,7 +2045,7 @@ "name": "keyword.control.default.js.jsx" } }, - "end": "(?=$|;|\\babstract\\b|\\basync\\b|\\bclass\\b|\\bconst\\b|\\bdeclare\\b|\\benum\\b|\\bexport\\b|\\bfunction\\b|\\bimport\\b|\\binterface\\b|\\blet\\b|\\bmodule\\b|\\bnamespace\\b|\\breturn\\b|\\btype\\b|\\bvar\\b)", + "end": "(?=$|;|^\\s*abstract\\b|^\\s*async\\b|^\\s*class\\b|^\\s*const\\b|^\\s*declare\\b|^\\s*enum\\b|^\\s*export\\b|^\\s*function\\b|^\\s*import\\b|^\\s*interface\\b|^\\s*let\\b|^\\s*module\\b|^\\s*namespace\\b|^\\s*return\\b|^\\s*type\\b|^\\s*var\\b)", "patterns": [ { "include": "#expression" @@ -1822,7 +2060,7 @@ "name": "keyword.control.export.js.jsx" } }, - "end": "(?=$|;|\\babstract\\b|\\basync\\b|\\bclass\\b|\\bconst\\b|\\bdeclare\\b|\\benum\\b|\\bexport\\b|\\bfunction\\b|\\bimport\\b|\\binterface\\b|\\blet\\b|\\bmodule\\b|\\bnamespace\\b|\\breturn\\b|\\btype\\b|\\bvar\\b)", + "end": "(?=$|;|^\\s*abstract\\b|^\\s*async\\b|^\\s*class\\b|^\\s*const\\b|^\\s*declare\\b|^\\s*enum\\b|^\\s*export\\b|^\\s*function\\b|^\\s*import\\b|^\\s*interface\\b|^\\s*let\\b|^\\s*module\\b|^\\s*namespace\\b|^\\s*return\\b|^\\s*type\\b|^\\s*var\\b)", "patterns": [ { "include": "#import-export-declaration" @@ -2167,7 +2405,7 @@ }, { "name": "meta.object.member.js.jsx", - "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=:\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=:\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.js.jsx" @@ -2412,7 +2650,7 @@ ] }, { - "begin": "(?<=[(=,]|=>)\\s*(async)?(?=\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?\\(\\s*$)", + "begin": "(?<=[(=,]|=>)\\s*(async)?(?=\\s*((((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?\\()|(<))\\s*$)", "beginCaptures": { "1": { "name": "storage.modifier.async.js.jsx" @@ -3004,7 +3242,7 @@ "include": "#object-identifiers" }, { - "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.js.jsx" @@ -3474,7 +3712,7 @@ "include": "#destructuring-parameter" }, { - "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)))", + "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))))", "captures": { "1": { "name": "storage.modifier.js.jsx" @@ -3846,7 +4084,7 @@ "patterns": [ { "name": "string.regexp.js.jsx", - "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "1": { "name": "punctuation.definition.string.begin.js.jsx" @@ -3869,7 +4107,7 @@ }, { "name": "string.regexp.js.jsx", - "begin": "(?:*]|&&|\\|\\||\\?|^return|[^\\._$[:alnum:]]return|^default|[^\\._$[:alnum:]]default|^)\\s*(?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?))", - "end": "(?!(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?))", + "begin": "(?:*]|&&|\\|\\||\\?|^await|[^\\._$[:alnum:]]await|^return|[^\\._$[:alnum:]]return|^default|[^\\._$[:alnum:]]default|^yield|[^\\._$[:alnum:]]yield|^)\\s*(?=(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?))", + "end": "(?!(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?))", "patterns": [ { "include": "#jsx-tag-without-attributes" @@ -4618,8 +4856,8 @@ }, "jsx-tag-without-attributes": { "name": "meta.tag.without-attributes.js.jsx", - "begin": "(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?)", - "end": "()", + "begin": "(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?)", + "end": "()", "beginCaptures": { "1": { "name": "punctuation.definition.tag.begin.js.jsx" @@ -4668,8 +4906,8 @@ ] }, "jsx-tag-in-expression": { - "begin": "(?x)\n (?:*]|&&|\\|\\||\\?|^return|[^\\._$[:alnum:]]return|^default|[^\\._$[:alnum:]]default|^)\\s*\n (?!<\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s+[^=>])|,)) # look ahead is not type parameter of arrow\n (?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?))", - "end": "(?!(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?))", + "begin": "(?x)\n (?:*]|&&|\\|\\||\\?|^await|[^\\._$[:alnum:]]await|^return|[^\\._$[:alnum:]]return|^default|[^\\._$[:alnum:]]default|^yield|[^\\._$[:alnum:]]yield|^)\\s*\n (?!<\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s+[^=>])|,)) # look ahead is not type parameter of arrow\n (?=(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?))", + "end": "(?!(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?))", "patterns": [ { "include": "#jsx-tag" @@ -4678,8 +4916,8 @@ }, "jsx-tag": { "name": "meta.tag.js.jsx", - "begin": "(?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?))", - "end": "(/>)|(?:())", + "begin": "(?=(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?))", + "end": "(/>)|(?:())", "endCaptures": { "1": { "name": "punctuation.definition.tag.end.js.jsx" @@ -4705,7 +4943,7 @@ }, "patterns": [ { - "begin": "(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?)", + "begin": "(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?)", "beginCaptures": { "1": { "name": "punctuation.definition.tag.begin.js.jsx" @@ -4838,7 +5076,7 @@ ] }, "jsx-tag-attribute-name": { - "match": "(?x)\n \\s*\n (?:([_$a-zA-Z][-$\\w.]*)(:))?\n ([_$a-zA-Z][-$\\w]*)\n (?=\\s|=|/?>|/\\*|//)", + "match": "(?x)\n \\s*\n (?:([_$[:alpha:]][-$[:alnum:].]*)(:))?\n ([_$[:alpha:]][-$[:alnum:]]*)\n (?=\\s|=|/?>|/\\*|//)", "captures": { "1": { "name": "entity.other.attribute-name.namespace.js.jsx" diff --git a/extensions/javascript/test/colorize-results/test_js.json b/extensions/javascript/test/colorize-results/test_js.json index 4a05d177de3..6ba00939cdf 100644 --- a/extensions/javascript/test/colorize-results/test_js.json +++ b/extensions/javascript/test/colorize-results/test_js.json @@ -3,9 +3,9 @@ "c": "/*", "t": "source.js comment.block.js punctuation.definition.comment.js", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -14,9 +14,9 @@ "c": "---------------------------------------------------------------------------------------------", "t": "source.js comment.block.js", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -25,9 +25,9 @@ "c": " * Copyright (c) Microsoft Corporation. All rights reserved.", "t": "source.js comment.block.js", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -36,9 +36,9 @@ "c": " * Licensed under the MIT License. See License.txt in the project root for license information.", "t": "source.js comment.block.js", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -47,9 +47,9 @@ "c": " *--------------------------------------------------------------------------------------------", "t": "source.js comment.block.js", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -58,9 +58,9 @@ "c": "*/", "t": "source.js comment.block.js punctuation.definition.comment.js", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/javascript/test/colorize-results/test_jsx.json b/extensions/javascript/test/colorize-results/test_jsx.json index a375d5d7b9d..cdd8b8cdfa4 100644 --- a/extensions/javascript/test/colorize-results/test_jsx.json +++ b/extensions/javascript/test/colorize-results/test_jsx.json @@ -520,9 +520,9 @@ "c": "//", "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx comment.line.double-slash.js.jsx punctuation.definition.comment.js.jsx", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -531,9 +531,9 @@ "c": " Prevent following the link.", "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx comment.line.double-slash.js.jsx", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -619,9 +619,9 @@ "c": "//", "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx comment.line.double-slash.js.jsx punctuation.definition.comment.js.jsx", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -630,9 +630,9 @@ "c": " Invert the chosen default.", "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx comment.line.double-slash.js.jsx", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -652,9 +652,9 @@ "c": "//", "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx comment.line.double-slash.js.jsx punctuation.definition.comment.js.jsx", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -663,9 +663,9 @@ "c": " This will trigger an intelligent re-render of the component.", "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx comment.line.double-slash.js.jsx", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1037,9 +1037,9 @@ "c": "//", "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx comment.line.double-slash.js.jsx punctuation.definition.comment.js.jsx", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1048,9 +1048,9 @@ "c": " Default to the default message.", "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx comment.line.double-slash.js.jsx", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1213,9 +1213,9 @@ "c": "//", "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx comment.line.double-slash.js.jsx punctuation.definition.comment.js.jsx", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1224,9 +1224,9 @@ "c": " If toggled, show the alternate message.", "t": "source.js.jsx meta.var.expr.js.jsx meta.objectliteral.js.jsx meta.object.member.js.jsx meta.function.expression.js.jsx meta.block.js.jsx comment.line.double-slash.js.jsx", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/json-language-features/client/src/jsonMain.ts b/extensions/json-language-features/client/src/jsonMain.ts index b2fc5658eb5..9040304a4f8 100644 --- a/extensions/json-language-features/client/src/jsonMain.ts +++ b/extensions/json-language-features/client/src/jsonMain.ts @@ -8,12 +8,10 @@ import * as path from 'path'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { workspace, languages, ExtensionContext, extensions, Uri, LanguageConfiguration, TextDocument, FoldingRangeKind, FoldingRange, Disposable, FoldingContext } from 'vscode'; -import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification, CancellationToken } from 'vscode-languageclient'; +import { workspace, languages, ExtensionContext, extensions, Uri, LanguageConfiguration } from 'vscode'; +import { LanguageClient, LanguageClientOptions, RequestType, ServerOptions, TransportKind, NotificationType, DidChangeConfigurationNotification } from 'vscode-languageclient'; import TelemetryReporter from 'vscode-extension-telemetry'; -import { FoldingRangeRequest, FoldingRangeRequestParam, FoldingRangeClientCapabilities, FoldingRangeKind as LSFoldingRangeKind } from 'vscode-languageserver-protocol-foldingprovider'; - import { hash } from './utils/hash'; namespace VSCodeContentRequest { @@ -97,21 +95,6 @@ export function activate(context: ExtensionContext) { // Create the language client and start the client. let client = new LanguageClient('json', localize('jsonserver.name', 'JSON Language Server'), serverOptions, clientOptions); client.registerProposedFeatures(); - client.registerFeature({ - fillClientCapabilities(capabilities: FoldingRangeClientCapabilities): void { - let textDocumentCap = capabilities.textDocument; - if (!textDocumentCap) { - textDocumentCap = capabilities.textDocument = {}; - } - textDocumentCap.foldingRange = { - dynamicRegistration: false, - rangeLimit: 5000, - lineFoldingOnly: true - }; - }, - initialize(capabilities, documentSelector): void { - } - }); let disposable = client.start(); toDispose.push(disposable); @@ -141,8 +124,6 @@ export function activate(context: ExtensionContext) { toDispose.push(workspace.onDidCloseTextDocument(d => handleContentChange(d.uri))); client.sendNotification(SchemaAssociationNotification.type, getSchemaAssociation(context)); - - toDispose.push(initFoldingProvider()); }); let languageConfiguration: LanguageConfiguration = { @@ -154,38 +135,6 @@ export function activate(context: ExtensionContext) { }; languages.setLanguageConfiguration('json', languageConfiguration); languages.setLanguageConfiguration('jsonc', languageConfiguration); - - function initFoldingProvider(): Disposable { - function getKind(kind: string | undefined): FoldingRangeKind | undefined { - if (kind) { - switch (kind) { - case LSFoldingRangeKind.Comment: - return FoldingRangeKind.Comment; - case LSFoldingRangeKind.Imports: - return FoldingRangeKind.Imports; - case LSFoldingRangeKind.Region: - return FoldingRangeKind.Region; - } - } - return void 0; - } - return languages.registerFoldingRangeProvider(documentSelector, { - provideFoldingRanges(document: TextDocument, context: FoldingContext, token: CancellationToken) { - const param: FoldingRangeRequestParam = { - textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document) - }; - return client.sendRequest(FoldingRangeRequest.type, param, token).then(ranges => { - if (Array.isArray(ranges)) { - return ranges.map(r => new FoldingRange(r.startLine, r.endLine, getKind(r.kind))); - } - return null; - }, error => { - client.logFailedRequest(FoldingRangeRequest.type, error); - return null; - }); - } - }); - } } export function deactivate(): Promise { diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index 8ea4b2f3d21..f814f5e747d 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -100,9 +100,8 @@ } }, "dependencies": { - "vscode-extension-telemetry": "0.0.17", - "vscode-languageclient": "^4.1.4", - "vscode-languageserver-protocol-foldingprovider": "^2.0.1", + "vscode-extension-telemetry": "0.0.18", + "vscode-languageclient": "^4.4.0", "vscode-nls": "^3.2.4" }, "devDependencies": { diff --git a/extensions/json-language-features/package.nls.json b/extensions/json-language-features/package.nls.json index f9d52e8ebcf..943de414e15 100644 --- a/extensions/json-language-features/package.nls.json +++ b/extensions/json-language-features/package.nls.json @@ -6,7 +6,7 @@ "json.schemas.fileMatch.desc": "An array of file patterns to match against when resolving JSON files to schemas.", "json.schemas.fileMatch.item.desc": "A file pattern that can contain '*' to match against when resolving JSON files to schemas.", "json.schemas.schema.desc": "The schema definition for the given URL. The schema only needs to be provided to avoid accesses to the schema URL.", - "json.format.enable.desc": "Enable/disable default JSON formatter (requires restart)", + "json.format.enable.desc": "Enable/disable default JSON formatter", "json.tracing.desc": "Traces the communication between VS Code and the JSON language server.", "json.colorDecorators.enable.desc": "Enables or disables color decorators", "json.colorDecorators.enable.deprecationMessage": "The setting `json.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`." diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index 2f67cd91401..e894d848722 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -11,13 +11,12 @@ "vscode-json-languageserver": "./bin/vscode-json-languageserver" }, "dependencies": { - "jsonc-parser": "^2.0.0-next.1", - "request-light": "^0.2.2", - "vscode-json-languageservice": "^3.1.2", - "vscode-languageserver": "^4.1.3", - "vscode-languageserver-protocol-foldingprovider": "^2.0.1", + "jsonc-parser": "^2.0.1", + "request-light": "^0.2.3", + "vscode-json-languageservice": "^3.1.4", + "vscode-languageserver": "^4.4.0", "vscode-nls": "^3.2.4", - "vscode-uri": "^1.0.3" + "vscode-uri": "^1.0.5" }, "devDependencies": { "@types/mocha": "2.2.33", diff --git a/extensions/json-language-features/server/src/jsonServerMain.ts b/extensions/json-language-features/server/src/jsonServerMain.ts index 2fa7619d1ff..8db215a72e9 100644 --- a/extensions/json-language-features/server/src/jsonServerMain.ts +++ b/extensions/json-language-features/server/src/jsonServerMain.ts @@ -19,8 +19,6 @@ import { formatError, runSafe, runSafeAsync } from './utils/runner'; import { JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration } from 'vscode-json-languageservice'; import { getLanguageModelCache } from './languageModelCache'; -import { FoldingRangeRequest, FoldingRangeServerCapabilities } from 'vscode-languageserver-protocol-foldingprovider'; - interface ISchemaAssociations { [pattern: string]: string[]; } @@ -81,7 +79,7 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { clientSnippetSupport = getClientCapability('textDocument.completion.completionItem.snippetSupport', false); clientDynamicRegisterSupport = getClientCapability('workspace.symbol.dynamicRegistration', false); foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); - const capabilities: ServerCapabilities & FoldingRangeServerCapabilities = { + const capabilities: ServerCapabilities = { // Tell the client that the server works in FULL text document sync mode textDocumentSync: documents.syncKind, completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['"', ':'] } : void 0, @@ -382,7 +380,7 @@ connection.onColorPresentation((params, token) => { }, [], `Error while computing color presentations for ${params.textDocument.uri}`, token); }); -connection.onRequest(FoldingRangeRequest.type, (params, token) => { +connection.onFoldingRanges((params, token) => { return runSafe(() => { const document = documents.get(params.textDocument.uri); if (document) { diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index d9b5875b9bc..8948926b0bd 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -16,13 +16,7 @@ agent-base@4, agent-base@^4.1.0: dependencies: es6-promisify "^5.0.0" -debug@2: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - dependencies: - ms "2.0.0" - -debug@^3.1.0: +debug@3.1.0, debug@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" dependencies: @@ -38,24 +32,20 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" -http-proxy-agent@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.0.0.tgz#46482a2f0523a4d6082551709f469cb3e4a85ff4" +http-proxy-agent@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" dependencies: agent-base "4" - debug "2" + debug "3.1.0" -https-proxy-agent@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.1.1.tgz#a7ce4382a1ba8266ee848578778122d491260fd9" +https-proxy-agent@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0" dependencies: agent-base "^4.1.0" debug "^3.1.0" -jsonc-parser@^2.0.0-next.1: - version "2.0.0-next.1" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.0-next.1.tgz#445a824f765a96abfbb286d759a9b1d226b18088" - jsonc-parser@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.1.tgz#9d23cd2709714fff508a1a6679d82135bee1ae60" @@ -64,68 +54,53 @@ ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" -request-light@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.2.2.tgz#53e48af32ad1514e45221ea5ece5ce782720f712" +request-light@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.2.3.tgz#a18635ec6dd92f8705c019c42ef645f684d94f7e" dependencies: - http-proxy-agent "2.0.0" - https-proxy-agent "2.1.1" - vscode-nls "^2.0.2" + http-proxy-agent "^2.1.0" + https-proxy-agent "^2.2.1" + vscode-nls "^3.2.2" -vscode-json-languageservice@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.1.2.tgz#5c70fc32ad389e6da48452e7b0187ea5e70f68bf" +vscode-json-languageservice@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.1.4.tgz#72e84e2754ad117f9e8d36876c1a66fe16234235" dependencies: jsonc-parser "^2.0.1" - vscode-languageserver-types "^3.7.2" - vscode-nls "^3.2.2" - vscode-uri "^1.0.3" + vscode-languageserver-types "^3.10.0" + vscode-nls "^3.2.4" + vscode-uri "^1.0.5" vscode-jsonrpc@^3.6.2: version "3.6.2" resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.6.2.tgz#3b5eef691159a15556ecc500e9a8a0dd143470c8" -vscode-languageserver-protocol-foldingprovider@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol-foldingprovider/-/vscode-languageserver-protocol-foldingprovider-2.0.1.tgz#051d0d9e58d1b79dc4681acd48f21797f5515bfd" - dependencies: - vscode-languageserver-protocol "^3.7.2" - vscode-languageserver-types "^3.7.2" - -vscode-languageserver-protocol@^3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.7.2.tgz#df58621c032139010888b6a9ddc969423f9ba9d6" +vscode-languageserver-protocol@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.10.0.tgz#f8dcdf987687f64a26e7c32d498fc781a0e886dc" dependencies: vscode-jsonrpc "^3.6.2" - vscode-languageserver-types "^3.7.2" + vscode-languageserver-types "^3.10.0" -vscode-languageserver-types@^3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.7.2.tgz#aad8846f8e3e27962648554de5a8417e358f34eb" +vscode-languageserver-types@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.10.0.tgz#944e5308f3b36a3f372c766f1a344e903ec9c389" -vscode-languageserver@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-4.1.3.tgz#937d37c955b6b9c2409388413cd6f54d1eb9fe7d" +vscode-languageserver@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-4.4.0.tgz#b6e8b37a739ccb629d92f3635f0099d191c856fa" dependencies: - vscode-languageserver-protocol "^3.7.2" - vscode-uri "^1.0.1" + vscode-languageserver-protocol "^3.10.0" + vscode-uri "^1.0.3" -vscode-nls@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-2.0.2.tgz#808522380844b8ad153499af5c3b03921aea02da" - -vscode-nls@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.2.tgz#3817eca5b985c2393de325197cf4e15eb2aa5350" - -vscode-nls@^3.2.4: +vscode-nls@^3.2.2, vscode-nls@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.4.tgz#2166b4183c8aea884d20727f5449e62be69fd398" -vscode-uri@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.1.tgz#11a86befeac3c4aa3ec08623651a3c81a6d0bbc8" - vscode-uri@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.3.tgz#631bdbf716dccab0e65291a8dc25c23232085a52" + +vscode-uri@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.5.tgz#3b899a8ef71c37f3054d79bdbdda31c7bf36f20d" diff --git a/extensions/json-language-features/yarn.lock b/extensions/json-language-features/yarn.lock index 1e7214749f2..a33a4a08108 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-language-features/yarn.lock @@ -28,9 +28,9 @@ semver@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" -vscode-extension-telemetry@0.0.17: - version "0.0.17" - resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.17.tgz#15123e7edb34e7b9724b6056f54a869bbb922cb7" +vscode-extension-telemetry@0.0.18: + version "0.0.18" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.18.tgz#602ba20d8c71453aa34533a291e7638f6e5c0327" dependencies: applicationinsights "1.0.1" @@ -38,29 +38,22 @@ vscode-jsonrpc@^3.6.2: version "3.6.2" resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.6.2.tgz#3b5eef691159a15556ecc500e9a8a0dd143470c8" -vscode-languageclient@^4.1.4: - version "4.1.4" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-4.1.4.tgz#fff1a6bca4714835dca7fce35bc4ce81442fdf2c" +vscode-languageclient@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-4.4.0.tgz#b05868f6477b6f0c9910b24daae4f3e8c4b65902" dependencies: - vscode-languageserver-protocol "^3.7.2" + vscode-languageserver-protocol "^3.10.0" -vscode-languageserver-protocol-foldingprovider@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol-foldingprovider/-/vscode-languageserver-protocol-foldingprovider-2.0.1.tgz#051d0d9e58d1b79dc4681acd48f21797f5515bfd" - dependencies: - vscode-languageserver-protocol "^3.7.2" - vscode-languageserver-types "^3.7.2" - -vscode-languageserver-protocol@^3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.7.2.tgz#df58621c032139010888b6a9ddc969423f9ba9d6" +vscode-languageserver-protocol@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.10.0.tgz#f8dcdf987687f64a26e7c32d498fc781a0e886dc" dependencies: vscode-jsonrpc "^3.6.2" - vscode-languageserver-types "^3.7.2" + vscode-languageserver-types "^3.10.0" -vscode-languageserver-types@^3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.7.2.tgz#aad8846f8e3e27962648554de5a8417e358f34eb" +vscode-languageserver-types@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.10.0.tgz#944e5308f3b36a3f372c766f1a344e903ec9c389" vscode-nls@^3.2.4: version "3.2.4" diff --git a/extensions/json/test/colorize-results/test_json.json b/extensions/json/test/colorize-results/test_json.json index 19641069faa..75561c366d7 100644 --- a/extensions/json/test/colorize-results/test_json.json +++ b/extensions/json/test/colorize-results/test_json.json @@ -25,9 +25,9 @@ "c": "//", "t": "source.json meta.structure.dictionary.json comment.line.double-slash.js punctuation.definition.comment.json", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -36,9 +36,9 @@ "c": " a comment", "t": "source.json meta.structure.dictionary.json comment.line.double-slash.js", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/less/package.nls.json b/extensions/less/package.nls.json index 7010e123b8e..bad671bf059 100644 --- a/extensions/less/package.nls.json +++ b/extensions/less/package.nls.json @@ -1,4 +1,4 @@ { "displayName": "Less Language Basics", - "description": "Provides syntax highlighting, bracket matching and folding Less files." + "description": "Provides syntax highlighting, bracket matching and folding in Less files." } \ No newline at end of file diff --git a/extensions/log/test/colorize-results/test_log.json b/extensions/log/test/colorize-results/test_log.json index 0b295cad127..2057a5b8764 100644 --- a/extensions/log/test/colorize-results/test_log.json +++ b/extensions/log/test/colorize-results/test_log.json @@ -14,9 +14,9 @@ "c": "2017-12-21", "t": "text.log comment log.date", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -36,9 +36,9 @@ "c": "12:47:29.584", "t": "text.log comment log.date", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -80,9 +80,9 @@ "c": "2017-12-21", "t": "text.log comment log.date", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -102,9 +102,9 @@ "c": "12:47:29.614", "t": "text.log comment log.date", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -135,9 +135,9 @@ "c": "2017-12-21", "t": "text.log comment log.date", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -157,9 +157,9 @@ "c": "12:47:29.632", "t": "text.log comment log.date", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -190,9 +190,9 @@ "c": "2017-12-21", "t": "text.log comment log.date", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -212,9 +212,9 @@ "c": "12:47:29.636", "t": "text.log comment log.date", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -245,9 +245,9 @@ "c": "2017-12-21", "t": "text.log comment log.date", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -267,9 +267,9 @@ "c": "12:47:32.164", "t": "text.log comment log.date", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -311,9 +311,9 @@ "c": "2017-12-21", "t": "text.log comment log.date", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -333,9 +333,9 @@ "c": "12:47:33.122", "t": "text.log comment log.date", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -377,9 +377,9 @@ "c": "2017-12-21", "t": "text.log comment log.date", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -399,9 +399,9 @@ "c": "12:47:34.249", "t": "text.log comment log.date", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -443,9 +443,9 @@ "c": "2017-12-21", "t": "text.log comment log.date", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -465,9 +465,9 @@ "c": "12:47:48.078", "t": "text.log comment log.date", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -509,9 +509,9 @@ "c": "2017-12-21", "t": "text.log comment log.date", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -531,9 +531,9 @@ "c": "12:47:49.294", "t": "text.log comment log.date", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/lua/syntaxes/lua.tmLanguage.json b/extensions/lua/syntaxes/lua.tmLanguage.json index 8adc657f806..c2b31570531 100644 --- a/extensions/lua/syntaxes/lua.tmLanguage.json +++ b/extensions/lua/syntaxes/lua.tmLanguage.json @@ -4,23 +4,25 @@ "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/textmate/lua.tmbundle/commit/42da2c6ff5d86c068f72520f856190f413911a80", + "version": "https://github.com/textmate/lua.tmbundle/commit/8ae5641365b28f697121ba1133890e8d81f5b00e", "name": "Lua", "scopeName": "source.lua", - "comment": "Lua Syntax: version 0.8", "patterns": [ { - "begin": "\\b((local\\b)\\s+)?(function)\\s*(\\s+[a-zA-Z_][a-zA-Z0-9_]*(\\.[a-zA-Z_][a-zA-Z0-9_]*)*(:[a-zA-Z_][a-zA-Z0-9_]*)?\\s*)?(\\()", + "begin": "\\b(?:(local)\\s+)?(function)\\s*(?:\\s+([a-zA-Z_][a-zA-Z0-9_]*(?:([\\.:])[a-zA-Z_][a-zA-Z0-9_]*)?)\\s*)?(\\()", "beginCaptures": { "1": { "name": "storage.modifier.local.lua" }, - "3": { + "2": { "name": "keyword.control.lua" }, - "4": { + "3": { "name": "entity.name.function.lua" }, + "4": { + "name": "punctuation.separator.parameter.lua" + }, "5": { "name": "punctuation.definition.parameters.begin.lua" } @@ -36,6 +38,10 @@ { "match": "[a-zA-Z_][a-zA-Z0-9_]*", "name": "variable.parameter.function.lua" + }, + { + "match": ",", + "name": "punctuation.separator.arguments.lua" } ] }, diff --git a/extensions/lua/test/colorize-results/test_lua.json b/extensions/lua/test/colorize-results/test_lua.json index bc041f3ac30..21c3d794e5b 100644 --- a/extensions/lua/test/colorize-results/test_lua.json +++ b/extensions/lua/test/colorize-results/test_lua.json @@ -14,9 +14,9 @@ "c": "--", "t": "source.lua comment.line.double-dash.lua punctuation.definition.comment.lua", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -25,9 +25,9 @@ "c": " defines a factorial function", "t": "source.lua comment.line.double-dash.lua", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -55,7 +55,18 @@ } }, { - "c": " fact ", + "c": " ", + "t": "source.lua meta.function.lua", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "fact", "t": "source.lua meta.function.lua entity.name.function.lua", "r": { "dark_plus": "entity.name.function: #DCDCAA", @@ -66,7 +77,7 @@ } }, { - "c": "(", + "c": " ", "t": "source.lua meta.function.lua", "r": { "dark_plus": "default: #D4D4D4", @@ -76,6 +87,17 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "(", + "t": "source.lua meta.function.lua punctuation.definition.parameters.begin.lua", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": "n", "t": "source.lua meta.function.lua variable.parameter.function.lua", @@ -586,9 +608,9 @@ "c": "--", "t": "source.lua comment.line.double-dash.lua punctuation.definition.comment.lua", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -597,9 +619,9 @@ "c": " read a number", "t": "source.lua comment.line.double-dash.lua", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/make/package.json b/extensions/make/package.json index d57f0077caa..f66762ba800 100644 --- a/extensions/make/package.json +++ b/extensions/make/package.json @@ -4,29 +4,47 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", - "engines": { "vscode": "*" }, + "engines": { + "vscode": "*" + }, "scripts": { "update-grammar": "node ../../build/npm/update-grammar.js fadeevab/make.tmbundle Syntaxes/Makefile.plist ./syntaxes/make.tmLanguage.json" }, "contributes": { - - "languages": [{ - "id": "makefile", - "aliases": ["Makefile", "makefile"], - "extensions": [ ".mk" ], - "filenames": [ "Makefile", "makefile", "GNUmakefile", "OCamlMakefile" ], - "firstLine": "^#!\\s*/usr/bin/make", - "configuration": "./language-configuration.json" - }], - "grammars": [{ - "language": "makefile", - "scopeName": "source.makefile", - "path": "./syntaxes/make.tmLanguage.json" - }], + "languages": [ + { + "id": "makefile", + "aliases": [ + "Makefile", + "makefile" + ], + "extensions": [ + ".mk" + ], + "filenames": [ + "Makefile", + "makefile", + "GNUmakefile", + "OCamlMakefile" + ], + "firstLine": "^#!\\s*/usr/bin/make", + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "makefile", + "scopeName": "source.makefile", + "path": "./syntaxes/make.tmLanguage.json", + "tokenTypes": { + "string.interpolated": "other" + } + } + ], "configurationDefaults": { "[makefile]": { "editor.insertSpaces": false } } } -} +} \ No newline at end of file diff --git a/extensions/make/syntaxes/make.tmLanguage.json b/extensions/make/syntaxes/make.tmLanguage.json index 505336ac862..acfd8adea23 100644 --- a/extensions/make/syntaxes/make.tmLanguage.json +++ b/extensions/make/syntaxes/make.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/fadeevab/make.tmbundle/commit/43e1a67476dea3ddefbb4f0ee7901834b31b8bee", + "version": "https://github.com/fadeevab/make.tmbundle/commit/d94d403d6d31623763a4ff86b656886fa699ef60", "name": "Makefile", "scopeName": "source.makefile", "patterns": [ @@ -257,7 +257,7 @@ } ] }, - "interpolation": { + "shell-interpolation": { "begin": "(?=`)", "end": "(?!\\G)", "name": "meta.embedded.line.shell", @@ -288,18 +288,6 @@ } ] }, - "braces-interpolation": { - "begin": "\\(", - "end": "\\)", - "patterns": [ - { - "include": "#variables" - }, - { - "include": "#braces-interpolation" - } - ] - }, "recipe": { "begin": "^(?!\\t)([^:]*)(:)(?!\\=)", "beginCaptures": { @@ -404,6 +392,40 @@ { "include": "#comment" }, + { + "include": "#variables" + }, + { + "include": "#shell-interpolation" + } + ] + }, + "interpolation": { + "patterns": [ + { + "include": "#parentheses-interpolation" + }, + { + "include": "#braces-interpolation" + } + ] + }, + "parentheses-interpolation": { + "begin": "\\(", + "end": "\\)", + "patterns": [ + { + "include": "#variables" + }, + { + "include": "#interpolation" + } + ] + }, + "braces-interpolation": { + "begin": "{", + "end": "}", + "patterns": [ { "include": "#variables" }, @@ -415,11 +437,28 @@ "variables": { "patterns": [ { - "match": "\\$[^\\(\\)]", - "name": "variable.language.makefile" + "include": "#simple-variable" }, { - "begin": "(\\$|(?<=\\$))\\(", + "include": "#variable-parentheses" + }, + { + "include": "#variable-braces" + } + ] + }, + "simple-variable": { + "patterns": [ + { + "match": "\\$[^(){}]", + "name": "variable.language.makefile" + } + ] + }, + "variable-parentheses": { + "patterns": [ + { + "begin": "\\$\\(", "captures": { "0": { "name": "punctuation.definition.variable.makefile" @@ -432,64 +471,199 @@ "include": "#variables" }, { - "match": "(?<=\\()(MAKEFILES|VPATH|SHELL|MAKESHELL|MAKE|MAKELEVEL|MAKEFLAGS|MAKECMDGOALS|CURDIR|SUFFIXES|\\.LIBPATTERNS)(?=\\s*\\))", - "name": "variable.language.makefile" + "include": "#builtin-variable-parentheses" }, { - "begin": "(?<=\\()(subst|patsubst|strip|findstring|filter(-out)?|sort|word(list)?|firstword|lastword|dir|notdir|suffix|basename|addsuffix|addprefix|join|wildcard|realpath|abspath|info|error|warning|shell|foreach|if|or|and|call|eval|value|file|guile)\\s", - "beginCaptures": { - "1": { - "name": "support.function.$1.makefile" - } - }, - "end": "(?=\\)|((?= 40800) + +ok := ok +$(info Braces {} in parentheses ({}): ${ok}) +${info Parentheses () in braces {()}: $(ok)} + +ifeq ("${ok}", "skip") + $(ok))} + ${ok}}) +endif diff --git a/extensions/make/test/colorize-results/makefile.json b/extensions/make/test/colorize-results/makefile.json index 03fbc6814fb..1e2cee1cb1c 100644 --- a/extensions/make/test/colorize-results/makefile.json +++ b/extensions/make/test/colorize-results/makefile.json @@ -300,9 +300,9 @@ "c": "#", "t": "source.makefile meta.scope.target.makefile meta.scope.prerequisites.makefile comment.line.number-sign.makefile punctuation.definition.comment.makefile", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -311,9 +311,9 @@ "c": " This is a long ", "t": "source.makefile meta.scope.target.makefile meta.scope.prerequisites.makefile comment.line.number-sign.makefile", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -324,7 +324,7 @@ "r": { "dark_plus": "constant.character.escape: #D7BA7D", "light_plus": "constant.character.escape: #FF0000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "constant.character: #569CD6" } @@ -333,9 +333,9 @@ "c": " comment inside prerequisites.", "t": "source.makefile meta.scope.target.makefile meta.scope.prerequisites.makefile comment.line.number-sign.makefile", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -355,9 +355,9 @@ "c": "#", "t": "source.makefile comment.line.number-sign.makefile punctuation.definition.comment.makefile", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -366,9 +366,9 @@ "c": " There are a building steps ", "t": "source.makefile comment.line.number-sign.makefile", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -379,7 +379,7 @@ "r": { "dark_plus": "constant.character.escape: #D7BA7D", "light_plus": "constant.character.escape: #FF0000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "constant.character: #569CD6" } @@ -388,9 +388,9 @@ "c": "\tbelow. And the tab is at the beginning of this line.", "t": "source.makefile comment.line.number-sign.makefile", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -692,6 +692,94 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "all", + "t": "source.makefile meta.scope.target.makefile entity.name.function.target.makefile", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA" + } + }, + { + "c": ":", + "t": "source.makefile meta.scope.target.makefile punctuation.separator.key-value.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t", + "t": "source.makefile punctuation.whitespace.comment.leading.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.makefile comment.line.number-sign.makefile punctuation.definition.comment.makefile", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " \"$$\" in a shell means to escape makefile's variable substitution.", + "t": "source.makefile comment.line.number-sign.makefile", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "\tsome_shell_var=", + "t": "source.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$$", + "t": "source.makefile variable.language.makefile", + "r": { + "dark_plus": "variable.language: #569CD6", + "light_plus": "variable.language: #0000FF", + "dark_vs": "variable.language: #569CD6", + "light_vs": "variable.language: #0000FF", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "(sed -nre 's/some regex with (group)/\\1/p')", + "t": "source.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": "define", "t": "source.makefile meta.scope.conditional.makefile keyword.control.define.makefile", @@ -1572,6 +1660,545 @@ "hc_black": "string: #CE9178" } }, + { + "c": "endif", + "t": "source.makefile meta.scope.conditional.makefile keyword.control.endif.makefile", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": "CXXVER_GE480", + "t": "source.makefile variable.other.makefile", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":=", + "t": "source.makefile punctuation.separator.key-value.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$(", + "t": "source.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "shell", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile support.function.shell.makefile", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " expr `", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "$(", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "CXX", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile variable.other.makefile", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " -dumpversion | sed -e 's/\\.\\([0-9][0-9]\\)/\\1/g' -e 's/\\.\\([0-9]\\)/0\\1/g' -e 's/^[0-9]\\{3,4\\}", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "$$", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile variable.language.makefile", + "r": { + "dark_plus": "variable.language: #569CD6", + "light_plus": "variable.language: #0000FF", + "dark_vs": "variable.language: #569CD6", + "light_vs": "variable.language: #0000FF", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "/&00/'` \\>= 40800", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "ok", + "t": "source.makefile variable.other.makefile", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": " ", + "t": "source.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": ":=", + "t": "source.makefile punctuation.separator.key-value.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ok", + "t": "source.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$(", + "t": "source.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "info", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile support.function.info.makefile", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " Braces {} in parentheses ({}): ", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "${", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "ok", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile variable.other.makefile", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "}", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "${", + "t": "source.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "info", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile support.function.info.makefile", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " Parentheses () in braces {()}: ", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "$(", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "ok", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile variable.other.makefile", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "}", + "t": "source.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "ifeq", + "t": "source.makefile meta.scope.conditional.makefile keyword.control.ifeq.makefile", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": " (\"", + "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "${", + "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "ok", + "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile string.interpolated.makefile variable.other.makefile", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "}", + "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "\", \"skip\")", + "t": "source.makefile meta.scope.conditional.makefile meta.scope.condition.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.makefile meta.scope.conditional.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$(", + "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "ok", + "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile variable.other.makefile", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")}", + "t": "source.makefile meta.scope.conditional.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": " ", + "t": "source.makefile meta.scope.conditional.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "${", + "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "ok", + "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile variable.other.makefile", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "}", + "t": "source.makefile meta.scope.conditional.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "})", + "t": "source.makefile meta.scope.conditional.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": "endif", "t": "source.makefile meta.scope.conditional.makefile keyword.control.endif.makefile", diff --git a/extensions/markdown-basics/language-configuration.json b/extensions/markdown-basics/language-configuration.json index 23a25ede9cc..ccddf061e75 100644 --- a/extensions/markdown-basics/language-configuration.json +++ b/extensions/markdown-basics/language-configuration.json @@ -36,7 +36,9 @@ "surroundingPairs": [ ["(", ")"], ["[", "]"], - ["`", "`"] + ["`", "`"], + ["_", "_"], + ["*", "*"] ], "folding": { "offSide": true, diff --git a/extensions/markdown-basics/package.json b/extensions/markdown-basics/package.json index a091ad8ea29..dd0b3f5a87d 100644 --- a/extensions/markdown-basics/package.json +++ b/extensions/markdown-basics/package.json @@ -19,7 +19,8 @@ ".md", ".mdown", ".markdown", - ".markdn" + ".markdn", + ".workbook" ], "configuration": "./language-configuration.json" } diff --git a/extensions/markdown-basics/test/colorize-results/test-33886_md.json b/extensions/markdown-basics/test/colorize-results/test-33886_md.json index 185d172e8af..179172a5738 100644 --- a/extensions/markdown-basics/test/colorize-results/test-33886_md.json +++ b/extensions/markdown-basics/test/colorize-results/test-33886_md.json @@ -34,7 +34,7 @@ }, { "c": "<", - "t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.begin.html", + "t": "text.html.markdown meta.tag.structure.pre.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -45,7 +45,7 @@ }, { "c": "pre", - "t": "text.html.markdown meta.tag.block.any.html entity.name.tag.block.any.html", + "t": "text.html.markdown meta.tag.structure.pre.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -56,7 +56,7 @@ }, { "c": ">", - "t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.tag.structure.pre.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -67,7 +67,7 @@ }, { "c": "<", - "t": "text.html.markdown meta.tag.inline.any.html punctuation.definition.tag.begin.html", + "t": "text.html.markdown meta.tag.inline.code.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -78,7 +78,7 @@ }, { "c": "code", - "t": "text.html.markdown meta.tag.inline.any.html entity.name.tag.inline.any.html", + "t": "text.html.markdown meta.tag.inline.code.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -89,7 +89,7 @@ }, { "c": ">", - "t": "text.html.markdown meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.tag.inline.code.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -111,7 +111,7 @@ }, { "c": "", - "t": "text.html.markdown meta.paragraph.markdown meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.paragraph.markdown meta.tag.inline.code.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -144,7 +144,7 @@ }, { "c": "", - "t": "text.html.markdown meta.paragraph.markdown meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.paragraph.markdown meta.tag.structure.pre.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -210,7 +210,7 @@ }, { "c": "<", - "t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.begin.html", + "t": "text.html.markdown meta.tag.structure.pre.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -221,7 +221,7 @@ }, { "c": "pre", - "t": "text.html.markdown meta.tag.block.any.html entity.name.tag.block.any.html", + "t": "text.html.markdown meta.tag.structure.pre.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -232,7 +232,7 @@ }, { "c": ">", - "t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.tag.structure.pre.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -265,7 +265,7 @@ }, { "c": "", - "t": "text.html.markdown meta.paragraph.markdown meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.paragraph.markdown meta.tag.structure.pre.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", diff --git a/extensions/markdown-basics/test/colorize-results/test_md.json b/extensions/markdown-basics/test/colorize-results/test_md.json index eb09a71c815..dc9ce77ffe7 100644 --- a/extensions/markdown-basics/test/colorize-results/test_md.json +++ b/extensions/markdown-basics/test/colorize-results/test_md.json @@ -300,9 +300,9 @@ "c": "", "t": "text.html.markdown comment.block.html punctuation.definition.comment.html", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } }, { "c": "<", - "t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.begin.html", + "t": "text.html.markdown meta.tag.structure.div.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -342,7 +342,7 @@ }, { "c": "div", - "t": "text.html.markdown meta.tag.block.any.html entity.name.tag.block.any.html", + "t": "text.html.markdown meta.tag.structure.div.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -353,7 +353,7 @@ }, { "c": " ", - "t": "text.html.markdown meta.tag.block.any.html", + "t": "text.html.markdown meta.tag.structure.div.start.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -364,7 +364,7 @@ }, { "c": "class", - "t": "text.html.markdown meta.tag.block.any.html entity.other.attribute-name.html", + "t": "text.html.markdown meta.tag.structure.div.start.html meta.attribute.class.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -375,7 +375,7 @@ }, { "c": "=", - "t": "text.html.markdown meta.tag.block.any.html", + "t": "text.html.markdown meta.tag.structure.div.start.html meta.attribute.class.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -386,7 +386,7 @@ }, { "c": "\"", - "t": "text.html.markdown meta.tag.block.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.markdown meta.tag.structure.div.start.html meta.attribute.class.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -397,7 +397,7 @@ }, { "c": "custom-class", - "t": "text.html.markdown meta.tag.block.any.html string.quoted.double.html", + "t": "text.html.markdown meta.tag.structure.div.start.html meta.attribute.class.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -408,7 +408,7 @@ }, { "c": "\"", - "t": "text.html.markdown meta.tag.block.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.markdown meta.tag.structure.div.start.html meta.attribute.class.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -419,7 +419,7 @@ }, { "c": " ", - "t": "text.html.markdown meta.tag.block.any.html", + "t": "text.html.markdown meta.tag.structure.div.start.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -430,7 +430,7 @@ }, { "c": "markdown", - "t": "text.html.markdown meta.tag.block.any.html entity.other.attribute-name.html", + "t": "text.html.markdown meta.tag.structure.div.start.html meta.attribute.unrecognized.markdown.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -441,7 +441,7 @@ }, { "c": "=", - "t": "text.html.markdown meta.tag.block.any.html", + "t": "text.html.markdown meta.tag.structure.div.start.html meta.attribute.unrecognized.markdown.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -452,7 +452,7 @@ }, { "c": "\"", - "t": "text.html.markdown meta.tag.block.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.markdown meta.tag.structure.div.start.html meta.attribute.unrecognized.markdown.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -463,7 +463,7 @@ }, { "c": "1", - "t": "text.html.markdown meta.tag.block.any.html string.quoted.double.html", + "t": "text.html.markdown meta.tag.structure.div.start.html meta.attribute.unrecognized.markdown.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -474,7 +474,7 @@ }, { "c": "\"", - "t": "text.html.markdown meta.tag.block.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.markdown meta.tag.structure.div.start.html meta.attribute.unrecognized.markdown.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -485,7 +485,7 @@ }, { "c": ">", - "t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.tag.structure.div.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -507,7 +507,7 @@ }, { "c": "<", - "t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.begin.html", + "t": "text.html.markdown meta.tag.structure.div.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -518,7 +518,7 @@ }, { "c": "div", - "t": "text.html.markdown meta.tag.block.any.html entity.name.tag.block.any.html", + "t": "text.html.markdown meta.tag.structure.div.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -529,7 +529,7 @@ }, { "c": ">", - "t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.tag.structure.div.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -562,7 +562,7 @@ }, { "c": "", - "t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.tag.structure.div.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -606,7 +606,7 @@ }, { "c": "<", - "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -617,7 +617,7 @@ }, { "c": "script", - "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.html entity.name.tag.html", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -628,7 +628,7 @@ }, { "c": " ", - "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.html", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.start.html", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -639,7 +639,7 @@ }, { "c": "type", - "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.html entity.other.attribute-name.html", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -650,7 +650,7 @@ }, { "c": "=", - "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.html", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html punctuation.separator.key-value.html", "r": { "dark_plus": "meta.embedded: #D4D4D4", "light_plus": "meta.embedded: #000000", @@ -661,7 +661,7 @@ }, { "c": "'", - "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.html string.quoted.single.html punctuation.definition.string.begin.html", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html string.quoted.single.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.html: #0000FF", @@ -672,7 +672,7 @@ }, { "c": "text/x-koka", - "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.html string.quoted.single.html", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html string.quoted.single.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.html: #0000FF", @@ -683,7 +683,7 @@ }, { "c": "'", - "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.html string.quoted.single.html punctuation.definition.string.end.html", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.start.html meta.attribute.type.html string.quoted.single.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.html: #0000FF", @@ -694,7 +694,7 @@ }, { "c": ">", - "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -727,7 +727,7 @@ }, { "c": "", - "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -770,29 +770,7 @@ } }, { - "c": " and a ", - "t": "text.html.markdown", - "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF" - } - }, - { - "c": "&", - "t": "text.html.markdown invalid.illegal.bad-ampersand.html", - "r": { - "dark_plus": "invalid: #F44747", - "light_plus": "invalid: #CD3131", - "dark_vs": "invalid: #F44747", - "light_vs": "invalid: #CD3131", - "hc_black": "invalid: #F44747" - } - }, - { - "c": " ", + "c": " and a & ", "t": "text.html.markdown", "r": { "dark_plus": "default: #D4D4D4", @@ -804,7 +782,7 @@ }, { "c": "<", - "t": "text.html.markdown meta.tag.inline.any.html punctuation.definition.tag.begin.html", + "t": "text.html.markdown meta.tag.inline.b.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -815,7 +793,7 @@ }, { "c": "b", - "t": "text.html.markdown meta.tag.inline.any.html entity.name.tag.inline.any.html", + "t": "text.html.markdown meta.tag.inline.b.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -826,7 +804,7 @@ }, { "c": " ", - "t": "text.html.markdown meta.tag.inline.any.html", + "t": "text.html.markdown meta.tag.inline.b.start.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -837,7 +815,7 @@ }, { "c": "class", - "t": "text.html.markdown meta.tag.inline.any.html entity.other.attribute-name.html", + "t": "text.html.markdown meta.tag.inline.b.start.html meta.attribute.class.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -848,7 +826,7 @@ }, { "c": "=", - "t": "text.html.markdown meta.tag.inline.any.html", + "t": "text.html.markdown meta.tag.inline.b.start.html meta.attribute.class.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -859,7 +837,7 @@ }, { "c": "\"", - "t": "text.html.markdown meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.markdown meta.tag.inline.b.start.html meta.attribute.class.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -870,7 +848,7 @@ }, { "c": "bold", - "t": "text.html.markdown meta.tag.inline.any.html string.quoted.double.html", + "t": "text.html.markdown meta.tag.inline.b.start.html meta.attribute.class.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -881,7 +859,7 @@ }, { "c": "\"", - "t": "text.html.markdown meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.markdown meta.tag.inline.b.start.html meta.attribute.class.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -892,7 +870,7 @@ }, { "c": ">", - "t": "text.html.markdown meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.tag.inline.b.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -914,7 +892,7 @@ }, { "c": "", - "t": "text.html.markdown meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.tag.inline.b.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -969,7 +947,7 @@ }, { "c": "<", - "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.html punctuation.definition.tag.begin.html", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -980,7 +958,7 @@ }, { "c": "style", - "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.html entity.name.tag.html", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -991,7 +969,7 @@ }, { "c": ">", - "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1156,7 +1134,7 @@ }, { "c": "<", - "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.html punctuation.definition.tag.begin.html source.css", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.end.html punctuation.definition.tag.begin.html source.css", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1167,7 +1145,7 @@ }, { "c": "/", - "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.html punctuation.definition.tag.begin.html", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1178,7 +1156,7 @@ }, { "c": "style", - "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.html entity.name.tag.html", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1189,7 +1167,7 @@ }, { "c": ">", - "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.embedded.block.html meta.tag.metadata.style.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1200,7 +1178,7 @@ }, { "c": "", - "t": "text.html.markdown meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.tag.structure.div.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1620,9 +1598,9 @@ "c": ">", "t": "text.html.markdown markup.quote.markdown beginning.punctuation.definition.quote.markdown", "r": { - "dark_plus": "beginning.punctuation.definition.quote.markdown: #608B4E", + "dark_plus": "beginning.punctuation.definition.quote.markdown: #6A9955", "light_plus": "beginning.punctuation.definition.quote.markdown: #0451A5", - "dark_vs": "beginning.punctuation.definition.quote.markdown: #608B4E", + "dark_vs": "beginning.punctuation.definition.quote.markdown: #6A9955", "light_vs": "beginning.punctuation.definition.quote.markdown: #0451A5", "hc_black": "default: #FFFFFF" } @@ -1653,9 +1631,9 @@ "c": ">", "t": "text.html.markdown markup.quote.markdown beginning.punctuation.definition.quote.markdown", "r": { - "dark_plus": "beginning.punctuation.definition.quote.markdown: #608B4E", + "dark_plus": "beginning.punctuation.definition.quote.markdown: #6A9955", "light_plus": "beginning.punctuation.definition.quote.markdown: #0451A5", - "dark_vs": "beginning.punctuation.definition.quote.markdown: #608B4E", + "dark_vs": "beginning.punctuation.definition.quote.markdown: #6A9955", "light_vs": "beginning.punctuation.definition.quote.markdown: #0451A5", "hc_black": "default: #FFFFFF" } @@ -1664,9 +1642,9 @@ "c": ">", "t": "text.html.markdown markup.quote.markdown markup.quote.markdown beginning.punctuation.definition.quote.markdown", "r": { - "dark_plus": "beginning.punctuation.definition.quote.markdown: #608B4E", + "dark_plus": "beginning.punctuation.definition.quote.markdown: #6A9955", "light_plus": "beginning.punctuation.definition.quote.markdown: #0451A5", - "dark_vs": "beginning.punctuation.definition.quote.markdown: #608B4E", + "dark_vs": "beginning.punctuation.definition.quote.markdown: #6A9955", "light_vs": "beginning.punctuation.definition.quote.markdown: #0451A5", "hc_black": "default: #FFFFFF" } @@ -1741,9 +1719,9 @@ "c": ">", "t": "text.html.markdown markup.list.numbered.markdown markup.quote.markdown beginning.punctuation.definition.quote.markdown", "r": { - "dark_plus": "beginning.punctuation.definition.quote.markdown: #608B4E", + "dark_plus": "beginning.punctuation.definition.quote.markdown: #6A9955", "light_plus": "beginning.punctuation.definition.quote.markdown: #0451A5", - "dark_vs": "beginning.punctuation.definition.quote.markdown: #608B4E", + "dark_vs": "beginning.punctuation.definition.quote.markdown: #6A9955", "light_vs": "beginning.punctuation.definition.quote.markdown: #0451A5", "hc_black": "default: #FFFFFF" } @@ -2520,7 +2498,7 @@ }, { "c": "<", - "t": "text.html.markdown meta.paragraph.markdown meta.tag.inline.any.html punctuation.definition.tag.begin.html", + "t": "text.html.markdown meta.paragraph.markdown meta.tag.inline.abbr.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2531,7 +2509,7 @@ }, { "c": "abbr", - "t": "text.html.markdown meta.paragraph.markdown meta.tag.inline.any.html entity.name.tag.inline.any.html", + "t": "text.html.markdown meta.paragraph.markdown meta.tag.inline.abbr.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -2542,7 +2520,7 @@ }, { "c": ">", - "t": "text.html.markdown meta.paragraph.markdown meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.markdown meta.paragraph.markdown meta.tag.inline.abbr.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 984967f637f..b49638d9707 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -295,7 +295,7 @@ "highlight.js": "9.12.0", "markdown-it": "^8.4.1", "markdown-it-named-headers": "0.0.4", - "vscode-extension-telemetry": "0.0.17", + "vscode-extension-telemetry": "0.0.18", "vscode-nls": "^3.2.4" }, "devDependencies": { diff --git a/extensions/markdown-language-features/src/extension.ts b/extensions/markdown-language-features/src/extension.ts index a9d95e36ce8..7786db20216 100644 --- a/extensions/markdown-language-features/src/extension.ts +++ b/extensions/markdown-language-features/src/extension.ts @@ -24,7 +24,7 @@ export function activate(context: vscode.ExtensionContext) { const telemetryReporter = loadDefaultTelemetryReporter(); context.subscriptions.push(telemetryReporter); - const contributions = getMarkdownExtensionContributions(); + const contributions = getMarkdownExtensionContributions(context); const cspArbiter = new ExtensionContentSecurityPolicyArbiter(context.globalState, context.workspaceState); const engine = new MarkdownEngine(contributions, githubSlugifier); diff --git a/extensions/markdown-language-features/src/features/foldingProvider.ts b/extensions/markdown-language-features/src/features/foldingProvider.ts index 236908a1f1f..5f0d6480ff3 100644 --- a/extensions/markdown-language-features/src/features/foldingProvider.ts +++ b/extensions/markdown-language-features/src/features/foldingProvider.ts @@ -7,6 +7,7 @@ import * as vscode from 'vscode'; import { MarkdownEngine } from '../markdownEngine'; import { TableOfContentsProvider } from '../tableOfContentsProvider'; +import { Token } from 'markdown-it'; const rangeLimit = 5000; @@ -16,15 +17,44 @@ export default class MarkdownFoldingProvider implements vscode.FoldingRangeProvi private readonly engine: MarkdownEngine ) { } + private async getRegions(document: vscode.TextDocument): Promise { + + const isStartRegion = (t: string) => /^\s*/.test(t); + const isEndRegion = (t: string) => /^\s*/.test(t); + + const isRegionMarker = (token: Token) => token.type === 'html_block' && + (isStartRegion(token.content) || isEndRegion(token.content)); + + + const tokens = await this.engine.parse(document.uri, document.getText()); + const regionMarkers = tokens.filter(isRegionMarker) + .map(token => ({ line: token.map[0], isStart: isStartRegion(token.content) })); + + const nestingStack: { line: number, isStart: boolean }[] = []; + return regionMarkers + .map(marker => { + if (marker.isStart) { + nestingStack.push(marker); + } else if (nestingStack.length && nestingStack[nestingStack.length - 1].isStart) { + return new vscode.FoldingRange(nestingStack.pop()!.line, marker.line, vscode.FoldingRangeKind.Region); + } else { + // noop: invalid nesting (i.e. [end, start] or [start, end, end]) + } + return null; + }) + .filter((region: vscode.FoldingRange | null): region is vscode.FoldingRange => !!region); + } + public async provideFoldingRanges( document: vscode.TextDocument, _: vscode.FoldingContext, _token: vscode.CancellationToken ): Promise { const tocProvider = new TableOfContentsProvider(this.engine, document); - let toc = await tocProvider.getToc(); - if (toc.length > rangeLimit) { - toc = toc.slice(0, rangeLimit); + let [regions, toc] = await Promise.all([this.getRegions(document), tocProvider.getToc()]); + + if (toc.length > rangeLimit - regions.length) { + toc = toc.slice(0, rangeLimit - regions.length); } const foldingRanges = toc.map((entry, startIndex) => { @@ -44,7 +74,6 @@ export default class MarkdownFoldingProvider implements vscode.FoldingRangeProvi typeof end === 'number' ? end : document.lineCount - 1); }); - - return foldingRanges; + return [...regions, ...foldingRanges]; } } \ No newline at end of file diff --git a/extensions/markdown-language-features/src/features/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index 2f4993fa13a..f15efe24098 100644 --- a/extensions/markdown-language-features/src/features/preview.ts +++ b/extensions/markdown-language-features/src/features/preview.ts @@ -271,6 +271,14 @@ export class MarkdownPreview { this.editor.title = MarkdownPreview.getPreviewTitle(this._resource, this._locked); } + private get iconPath() { + const root = path.join(this._contributions.extensionPath, 'media'); + return { + light: vscode.Uri.file(path.join(root, 'Preview.svg')), + dark: vscode.Uri.file(path.join(root, 'Preview_inverse.svg')) + }; + } + private isPreviewOf(resource: vscode.Uri): boolean { return this._resource.fsPath === resource.fsPath; } @@ -327,6 +335,7 @@ export class MarkdownPreview { const content = await this._contentProvider.provideTextDocumentContent(document, this._previewConfigurations, this.line, this.state); if (this._resource === resource) { this.editor.title = MarkdownPreview.getPreviewTitle(this._resource, this._locked); + this.editor.iconPath = this.iconPath; this.editor.webview.options = MarkdownPreview.getWebviewOptions(resource, this._contributions); this.editor.webview.html = content; } diff --git a/extensions/markdown-language-features/src/markdownExtensions.ts b/extensions/markdown-language-features/src/markdownExtensions.ts index a9ef207cf38..55c8d76e7a4 100644 --- a/extensions/markdown-language-features/src/markdownExtensions.ts +++ b/extensions/markdown-language-features/src/markdownExtensions.ts @@ -26,6 +26,7 @@ const resolveExtensionResources = (extension: vscode.Extension, resourcePat }; export interface MarkdownContributions { + readonly extensionPath: string; readonly previewScripts: vscode.Uri[]; readonly previewStyles: vscode.Uri[]; readonly markdownItPlugins: Thenable<(md: any) => any>[]; @@ -40,6 +41,10 @@ class MarkdownExtensionContributions implements MarkdownContributions { private _loaded = false; + public constructor( + public readonly extensionPath: string, + ) { } + public get previewScripts(): vscode.Uri[] { this.ensureLoaded(); return this._scripts; @@ -111,6 +116,6 @@ class MarkdownExtensionContributions implements MarkdownContributions { } } -export function getMarkdownExtensionContributions(): MarkdownContributions { - return new MarkdownExtensionContributions(); +export function getMarkdownExtensionContributions(context: vscode.ExtensionContext): MarkdownContributions { + return new MarkdownExtensionContributions(context.extensionPath); } \ No newline at end of file diff --git a/extensions/markdown-language-features/src/slugify.ts b/extensions/markdown-language-features/src/slugify.ts index c3e167e89e0..4bc3cee42ce 100644 --- a/extensions/markdown-language-features/src/slugify.ts +++ b/extensions/markdown-language-features/src/slugify.ts @@ -23,7 +23,7 @@ export const githubSlugifier: Slugifier = new class implements Slugifier { heading.trim() .toLowerCase() .replace(/\s+/g, '-') // Replace whitespace with - - .replace(/[\]\[\!\'\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~\`]/g, '') // Remove known puctuators + .replace(/[\]\[\!\'\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~\`。,、;:?!…—·ˉ¨‘’“”々~‖∶"'`|〃〔〕〈〉《》「」『』.〖〗【】()[]{}]/g, '') // Remove known puctuators .replace(/^\-+/, '') // Remove leading - .replace(/\-+$/, '') // Remove trailing - ); diff --git a/extensions/markdown-language-features/src/test/engine.ts b/extensions/markdown-language-features/src/test/engine.ts index 860bafad7cc..a1834e057a2 100644 --- a/extensions/markdown-language-features/src/test/engine.ts +++ b/extensions/markdown-language-features/src/test/engine.ts @@ -9,6 +9,7 @@ import { MarkdownContributions } from '../markdownExtensions'; import { githubSlugifier } from '../slugify'; const emptyContributions = new class implements MarkdownContributions { + readonly extensionPath = ''; readonly previewScripts: vscode.Uri[] = []; readonly previewStyles: vscode.Uri[] = []; readonly previewResourceRoots: vscode.Uri[] = []; diff --git a/extensions/markdown-language-features/src/test/foldingProvider.test.ts b/extensions/markdown-language-features/src/test/foldingProvider.test.ts index 44c570d64d4..cd3b82d599e 100644 --- a/extensions/markdown-language-features/src/test/foldingProvider.test.ts +++ b/extensions/markdown-language-features/src/test/foldingProvider.test.ts @@ -78,6 +78,31 @@ y`); assert.strictEqual(firstFold.end, 2); }); + test('Should fold nested markers', async () => { + const folds = await getFoldsForDocument(`a + +b + +b.a + +b + +b.b + +b + +a`); + assert.strictEqual(folds.length, 3); + const [outer, first, second] = folds.sort((a, b) => a.start - b.start); + + assert.strictEqual(outer.start, 1); + assert.strictEqual(outer.end, 11); + assert.strictEqual(first.start, 3); + assert.strictEqual(first.end, 5); + assert.strictEqual(second.start, 7); + assert.strictEqual(second.end, 9); + }); + }); diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index c44b9715579..a2e69ddb6b8 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -5456,9 +5456,9 @@ vm-browserify@0.0.4: dependencies: indexof "0.0.1" -vscode-extension-telemetry@0.0.17: - version "0.0.17" - resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.17.tgz#15123e7edb34e7b9724b6056f54a869bbb922cb7" +vscode-extension-telemetry@0.0.18: + version "0.0.18" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.18.tgz#602ba20d8c71453aa34533a291e7638f6e5c0327" dependencies: applicationinsights "1.0.1" diff --git a/extensions/merge-conflict/package.nls.json b/extensions/merge-conflict/package.nls.json index 66076bd7a87..ed7430674cb 100644 --- a/extensions/merge-conflict/package.nls.json +++ b/extensions/merge-conflict/package.nls.json @@ -13,6 +13,6 @@ "command.previous": "Previous Conflict", "command.compare": "Compare Current Conflict", "config.title": "Merge Conflict", - "config.codeLensEnabled": "Enable/disable merge conflict block CodeLens within editor", - "config.decoratorsEnabled": "Enable/disable merge conflict decorators within editor" + "config.codeLensEnabled": "Create a Code Lens for merge conflict blocks within editor.", + "config.decoratorsEnabled": "Create decorators for merge conflict blocks within editor." } \ No newline at end of file diff --git a/extensions/npm/.vscode/tasks.json b/extensions/npm/.vscode/tasks.json index 0a411c1c867..b7c8a281635 100644 --- a/extensions/npm/.vscode/tasks.json +++ b/extensions/npm/.vscode/tasks.json @@ -1,8 +1,10 @@ { - "version": "0.1.0", + "version": "2.0.0", "command": "npm", - "isShellCommand": true, - "showOutput": "silent", + "type": "shell", + "presentation": { + "reveal": "silent", + }, "args": ["run", "compile"], "isBackground": true, "problemMatcher": "$tsc-watch" diff --git a/extensions/npm/README.md b/extensions/npm/README.md index 0b3961ff716..a24a7d69d6e 100644 --- a/extensions/npm/README.md +++ b/extensions/npm/README.md @@ -4,6 +4,8 @@ ## Features +### Task Running + This extension supports running npm scripts defined in the `package.json` as [tasks](https://code.visualstudio.com/docs/editor/tasks). Scripts with the name 'build', 'compile', or 'watch' are treated as build tasks. @@ -11,6 +13,19 @@ To run scripts as tasks, use the **Tasks** menu. For more information about auto detection of Tasks, see the [documentation](https://code.visualstudio.com/Docs/editor/tasks#_task-autodetection). +### Script Explorer + +The Npm Script Explorer shows the npm scripts found in your workspace. The explorer view is enabled by the setting `npm.enableScriptExplorer`. A script can be opened, run, or debug from the explorer. + +### Run Scripts from the Editor + +The extension supports to run the selected script as a task when editing the `package.json`file. You can either run a script from +the hover shown on a script or using the command `Run Selected Npm Script`. + +### Others + +The extension fetches data from https://registry.npmjs/org and https://registry.bower.io to provide auto-completion and information on hover features on npm dependencies. + ## Settings - `npm.autoDetect` - Enable detecting scripts as tasks, the default is `on`. @@ -19,3 +34,5 @@ For more information about auto detection of Tasks, see the [documentation](http - `npm.exclude` - Glob patterns for folders that should be excluded from automatic script detection. The pattern is matched against the **absolute path** of the package.json. For example, to exclude all test folders use '**/test/**'. - `npm.enableScriptExplorer` - Enable an explorer view for npm scripts. - `npm.scriptExplorerAction` - The default click action: `open` or `run`, the default is `open`. +- `npm.scriptCodeLens.enable` - Enable/disable the code lenses to run a script, the default is `false`. + diff --git a/extensions/npm/package.json b/extensions/npm/package.json index df9c903fcb4..dc6a25a9dca 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -82,6 +82,10 @@ "light": "resources/light/refresh.svg", "dark": "resources/dark/refresh.svg" } + }, + { + "command": "npm.runSelectedScript", + "title": "%command.runSelectedScript%" } ], "menus": { @@ -105,6 +109,17 @@ { "command": "npm.runInstall", "when": "false" + }, + { + "command": "npm.runSelectedScript", + "when": "false" + } + ], + "editor/context": [ + { + "command": "npm.runSelectedScript", + "when": "resourceFilename == 'package.json'", + "group": "navigation@+1" } ], "view/title": [ @@ -176,7 +191,7 @@ "type": "boolean", "default": false, "scope": "resource", - "description": "%config.npm.runSilent%" + "markdownDescription": "%config.npm.runSilent%" }, "npm.packageManager": { "scope": "resource", @@ -211,9 +226,16 @@ "open", "run" ], - "description": "%config.npm.scriptExplorerAction%", + "markdownDescription": "%config.npm.scriptExplorerAction%", "scope": "window", "default": "open" + }, + "npm.fetchOnlinePackageInfo": { + "type": "boolean", + "description": "%config.npm.fetchOnlinePackageInfo%", + "default": true, + "scope": "window", + "tags": ["usesOnlineServices"] } } }, diff --git a/extensions/npm/package.nls.json b/extensions/npm/package.nls.json index 92665d5f65a..4a33bcc0bc3 100644 --- a/extensions/npm/package.nls.json +++ b/extensions/npm/package.nls.json @@ -1,12 +1,13 @@ { "description": "Extension to add task support for npm scripts.", "displayName": "Npm support for VS Code", - "config.npm.autoDetect": "Controls whether auto detection of npm scripts is on or off. Default is on.", + "config.npm.autoDetect": "Controls whether npm scripts should be automatically detected.", "config.npm.runSilent": "Run npm commands with the `--silent` option.", "config.npm.packageManager": "The package manager used to run scripts.", "config.npm.exclude": "Configure glob patterns for folders that should be excluded from automatic script detection.", "config.npm.enableScriptExplorer": "Enable an explorer view for npm scripts.", - "config.npm.scriptExplorerAction": "The default click action used in the scripts explorer: 'open' or 'run', the default is 'open'.", + "config.npm.scriptExplorerAction": "The default click action used in the scripts explorer: `open` or `run`, the default is `open`.", + "config.npm.fetchOnlinePackageInfo": "Fetch data from https://registry.npmjs/org and https://registry.bower.io to provide auto-completion and information on hover features on npm dependencies.", "npm.parseError": "Npm task detection: failed to parse the file {0}", "taskdef.script": "The npm script to customize.", "taskdef.path": "The path to the folder of the package.json file that provides the script. Can be omitted.", @@ -15,5 +16,6 @@ "command.run": "Run", "command.debug": "Debug", "command.openScript": "Open", - "command.runInstall": "Run Install" + "command.runInstall": "Run Install", + "command.runSelectedScript": "Run Script" } diff --git a/extensions/npm/src/commands.ts b/extensions/npm/src/commands.ts new file mode 100644 index 00000000000..509d536db92 --- /dev/null +++ b/extensions/npm/src/commands.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as vscode from 'vscode'; +import { + runScript, findScriptAtPosition +} from './tasks'; +import * as nls from 'vscode-nls'; + +const localize = nls.loadMessageBundle(); + +export function runSelectedScript() { + let editor = vscode.window.activeTextEditor; + if (!editor) { + return; + } + let document = editor.document; + let contents = document.getText(); + let selection = editor.selection; + let offset = document.offsetAt(selection.anchor); + + let script = findScriptAtPosition(contents, offset); + if (script) { + runScript(script, document); + } else { + let message = localize('noScriptFound', 'Could not find an npm script at the selection.'); + vscode.window.showErrorMessage(message); + } +} \ No newline at end of file diff --git a/extensions/npm/src/features/bowerJSONContribution.ts b/extensions/npm/src/features/bowerJSONContribution.ts index a00f347078c..038057bf2b2 100644 --- a/extensions/npm/src/features/bowerJSONContribution.ts +++ b/extensions/npm/src/features/bowerJSONContribution.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { MarkedString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString } from 'vscode'; -import { IJSONContribution, ISuggestionsCollector } from './jsonContributions'; +import { MarkedString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString, workspace } from 'vscode'; +import { IJSONContribution, ISuggestionsCollector, xhrDisabled } from './jsonContributions'; import { XHRRequest } from 'request-light'; import { Location } from 'jsonc-parser'; import { textToMarkedString } from './markedTextUtil'; @@ -25,7 +25,19 @@ export class BowerJSONContribution implements IJSONContribution { 'hui', 'bootstrap-languages', 'async', 'gulp', 'jquery-pjax', 'coffeescript', 'hammer.js', 'ace', 'leaflet', 'jquery-mobile', 'sweetalert', 'typeahead.js', 'soup', 'typehead.js', 'sails', 'codeigniter2']; - public constructor(private xhr: XHRRequest) { + private xhr: XHRRequest; + + public constructor(httprequestxhr: XHRRequest) { + + const getxhr = () => { + return workspace.getConfiguration('npm').get('fetchOnlinePackageInfo') === false ? xhrDisabled : httprequestxhr; + }; + this.xhr = getxhr(); + workspace.onDidChangeConfiguration((e) => { + if (e.affectsConfiguration('npm.fetchOnlinePackageInfo')) { + this.xhr = getxhr(); + } + }); } public getDocumentSelector(): DocumentSelector { diff --git a/extensions/npm/src/features/jsonContributions.ts b/extensions/npm/src/features/jsonContributions.ts index 81f8a66f2c2..a18bccf28ff 100644 --- a/extensions/npm/src/features/jsonContributions.ts +++ b/extensions/npm/src/features/jsonContributions.ts @@ -164,3 +164,5 @@ export class JSONCompletionItemProvider implements CompletionItemProvider { return nextToken === SyntaxKind.CloseBraceToken || nextToken === SyntaxKind.EOF; } } + +export const xhrDisabled = () => Promise.reject({ responseText: 'Use of online resources is disabled.' }); \ No newline at end of file diff --git a/extensions/npm/src/features/packageJSONContribution.ts b/extensions/npm/src/features/packageJSONContribution.ts index b060290eb51..1576ded6a4d 100644 --- a/extensions/npm/src/features/packageJSONContribution.ts +++ b/extensions/npm/src/features/packageJSONContribution.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { MarkedString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString } from 'vscode'; -import { IJSONContribution, ISuggestionsCollector } from './jsonContributions'; +import { MarkedString, CompletionItemKind, CompletionItem, DocumentSelector, SnippetString, workspace } from 'vscode'; +import { IJSONContribution, ISuggestionsCollector, xhrDisabled } from './jsonContributions'; import { XHRRequest } from 'request-light'; import { Location } from 'jsonc-parser'; import { textToMarkedString } from './markedTextUtil'; @@ -28,12 +28,22 @@ export class PackageJSONContribution implements IJSONContribution { 'jsdom', 'stylus', 'when', 'readable-stream', 'aws-sdk', 'concat-stream', 'chai', 'Thenable', 'wrench']; private knownScopes = ['@types', '@angular']; + private xhr: XHRRequest; public getDocumentSelector(): DocumentSelector { return [{ language: 'json', scheme: '*', pattern: '**/package.json' }]; } - public constructor(private xhr: XHRRequest) { + public constructor(httprequestxhr: XHRRequest) { + const getxhr = () => { + return workspace.getConfiguration('npm').get('fetchOnlinePackageInfo') === false ? xhrDisabled : httprequestxhr; + }; + this.xhr = getxhr(); + workspace.onDidChangeConfiguration((e) => { + if (e.affectsConfiguration('npm.fetchOnlinePackageInfo')) { + this.xhr = getxhr(); + } + }); } public collectDefaultSuggestions(_fileName: string, result: ISuggestionsCollector): Thenable { diff --git a/extensions/npm/src/main.ts b/extensions/npm/src/main.ts index 023fddbffb6..02e22490fd7 100644 --- a/extensions/npm/src/main.ts +++ b/extensions/npm/src/main.ts @@ -6,21 +6,22 @@ import * as httpRequest from 'request-light'; import * as vscode from 'vscode'; - import { addJSONProviders } from './features/jsonContributions'; import { NpmScriptsTreeDataProvider } from './npmView'; -import { provideNpmScripts, invalidateScriptsCache } from './tasks'; - -let taskProvider: vscode.Disposable | undefined; +import { invalidateTasksCache, NpmTaskProvider } from './tasks'; +import { invalidateHoverScriptsCache, NpmScriptHoverProvider } from './scriptHover'; +import { runSelectedScript } from './commands'; export async function activate(context: vscode.ExtensionContext): Promise { - taskProvider = registerTaskProvider(context); + const taskProvider = registerTaskProvider(context); const treeDataProvider = registerExplorer(context); + const hoverProvider = registerHoverProvider(context); + configureHttpRequest(); - vscode.workspace.onDidChangeConfiguration((e) => { + let d = vscode.workspace.onDidChangeConfiguration((e) => { configureHttpRequest(); if (e.affectsConfiguration('npm.exclude')) { - invalidateScriptsCache(); + invalidateTasksCache(); if (treeDataProvider) { treeDataProvider.refresh(); } @@ -31,26 +32,34 @@ export async function activate(context: vscode.ExtensionContext): Promise } } }); + context.subscriptions.push(d); + + d = vscode.workspace.onDidChangeTextDocument((e) => { + invalidateHoverScriptsCache(e.document); + }); + context.subscriptions.push(d); + context.subscriptions.push(vscode.commands.registerCommand('npm.runSelectedScript', runSelectedScript)); context.subscriptions.push(addJSONProviders(httpRequest.xhr)); } function registerTaskProvider(context: vscode.ExtensionContext): vscode.Disposable | undefined { + + function invalidateScriptCaches() { + invalidateHoverScriptsCache(); + invalidateTasksCache(); + } + if (vscode.workspace.workspaceFolders) { let watcher = vscode.workspace.createFileSystemWatcher('**/package.json'); - watcher.onDidChange((_e) => invalidateScriptsCache()); - watcher.onDidDelete((_e) => invalidateScriptsCache()); - watcher.onDidCreate((_e) => invalidateScriptsCache()); + watcher.onDidChange((_e) => invalidateScriptCaches()); + watcher.onDidDelete((_e) => invalidateScriptCaches()); + watcher.onDidCreate((_e) => invalidateScriptCaches()); context.subscriptions.push(watcher); - let provider: vscode.TaskProvider = { - provideTasks: async () => { - return provideNpmScripts(); - }, - resolveTask(_task: vscode.Task): vscode.Task | undefined { - return undefined; - } - }; - return vscode.workspace.registerTaskProvider('npm', provider); + let provider: vscode.TaskProvider = new NpmTaskProvider(context); + let disposable = vscode.workspace.registerTaskProvider('npm', provider); + context.subscriptions.push(disposable); + return disposable; } return undefined; } @@ -65,13 +74,24 @@ function registerExplorer(context: vscode.ExtensionContext): NpmScriptsTreeDataP return undefined; } +function registerHoverProvider(context: vscode.ExtensionContext): NpmScriptHoverProvider | undefined { + if (vscode.workspace.workspaceFolders) { + let npmSelector: vscode.DocumentSelector = { + language: 'json', + scheme: 'file', + pattern: '**/package.json' + }; + let provider = new NpmScriptHoverProvider(context); + context.subscriptions.push(vscode.languages.registerHoverProvider(npmSelector, provider)); + return provider; + } + return undefined; +} + function configureHttpRequest() { const httpSettings = vscode.workspace.getConfiguration('http'); httpRequest.configure(httpSettings.get('proxy', ''), httpSettings.get('proxyStrictSSL', true)); } export function deactivate(): void { - if (taskProvider) { - taskProvider.dispose(); - } } diff --git a/extensions/npm/src/npmView.ts b/extensions/npm/src/npmView.ts index 2bb290876a8..f737df23e1b 100644 --- a/extensions/npm/src/npmView.ts +++ b/extensions/npm/src/npmView.ts @@ -6,14 +6,14 @@ import * as path from 'path'; import { - DebugConfiguration, Event, EventEmitter, ExtensionContext, Task, + Event, EventEmitter, ExtensionContext, Task, TextDocument, ThemeIcon, TreeDataProvider, TreeItem, TreeItemCollapsibleState, Uri, - WorkspaceFolder, commands, debug, window, workspace, tasks, Selection, TaskGroup + WorkspaceFolder, commands, window, workspace, tasks, Selection, TaskGroup } from 'vscode'; import { visit, JSONVisitor } from 'jsonc-parser'; import { NpmTaskDefinition, getPackageJsonUriFromTask, getScripts, - isWorkspaceFolder, getPackageManager, getTaskName, createTask + isWorkspaceFolder, getTaskName, createTask, extractDebugArgFromScript, startDebugging } from './tasks'; import * as nls from 'vscode-nls'; @@ -162,25 +162,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { } private extractDebugArg(scripts: any, task: Task): [string, number] | undefined { - let script: string = scripts[task.name]; - - // matches --debug, --debug=1234, --debug-brk, debug-brk=1234, --inspect, - // --inspect=1234, --inspect-brk, --inspect-brk=1234, - // --inspect=localhost:1245, --inspect=127.0.0.1:1234, --inspect=[aa:1:0:0:0]:1234, --inspect=:1234 - let match = script.match(/--(inspect|debug)(-brk)?(=((\[[0-9a-fA-F:]*\]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[a-zA-Z0-9\.]*):)?(\d+))?/); - - if (match) { - if (match[6]) { - return [match[1], parseInt(match[6])]; - } - if (match[1] === 'inspect') { - return [match[1], 9229]; - } - if (match[1] === 'debug') { - return [match[1], 5858]; - } - } - return undefined; + return extractDebugArgFromScript(scripts[task.name]); } private async debugScript(script: NpmScript) { @@ -193,7 +175,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { return; } - let debugArg = await this.extractDebugArg(scripts, task); + let debugArg = this.extractDebugArg(scripts, task); if (!debugArg) { let message = localize('noDebugOptions', 'Could not launch "{0}" for debugging because the scripts lacks a node debug option, e.g. "--inspect-brk".', task.name); let learnMore = localize('learnMore', 'Learn More'); @@ -204,29 +186,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { } return; } - - let protocol = 'inspector'; - if (debugArg[0] === 'debug') { - protocol = 'legacy'; - } - - let packageManager = getPackageManager(script.getFolder()); - const config: DebugConfiguration = { - type: 'node', - request: 'launch', - name: `Debug ${task.name}`, - runtimeExecutable: packageManager, - runtimeArgs: [ - 'run-script', - task.name, - ], - port: debugArg[1], - protocol: protocol - }; - - if (isWorkspaceFolder(task.scope)) { - debug.startDebugging(task.scope, config); - } + startDebugging(task.name, debugArg[0], debugArg[1], script.getFolder()); } private scriptNotValid(task: Task) { diff --git a/extensions/npm/src/scriptHover.ts b/extensions/npm/src/scriptHover.ts new file mode 100644 index 00000000000..d81ceee0807 --- /dev/null +++ b/extensions/npm/src/scriptHover.ts @@ -0,0 +1,126 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { + ExtensionContext, TextDocument, commands, ProviderResult, CancellationToken, + workspace, tasks, Range, HoverProvider, Hover, Position, MarkdownString, Uri +} from 'vscode'; +import { + createTask, startDebugging, findAllScriptRanges, extractDebugArgFromScript +} from './tasks'; +import * as nls from 'vscode-nls'; + +const localize = nls.loadMessageBundle(); + +let cachedDocument: Uri | undefined = undefined; +let cachedScriptsMap: Map | undefined = undefined; + +export function invalidateHoverScriptsCache(document?: TextDocument) { + if (!document) { + cachedDocument = undefined; + return; + } + if (document.uri === cachedDocument) { + cachedDocument = undefined; + } +} + +export class NpmScriptHoverProvider implements HoverProvider { + private extensionContext: ExtensionContext; + + constructor(context: ExtensionContext) { + this.extensionContext = context; + context.subscriptions.push(commands.registerCommand('npm.runScriptFromHover', this.runScriptFromHover, this)); + context.subscriptions.push(commands.registerCommand('npm.debugScriptFromHover', this.debugScriptFromHover, this)); + } + + public provideHover(document: TextDocument, position: Position, _token: CancellationToken): ProviderResult { + let hover: Hover | undefined = undefined; + + if (!cachedDocument || cachedDocument.fsPath !== document.uri.fsPath) { + cachedScriptsMap = findAllScriptRanges(document.getText()); + cachedDocument = document.uri; + } + + cachedScriptsMap!.forEach((value, key) => { + let start = document.positionAt(value[0]); + let end = document.positionAt(value[0] + value[1]); + let range = new Range(start, end); + + if (range.contains(position)) { + let contents: MarkdownString = new MarkdownString(); + contents.isTrusted = true; + contents.appendMarkdown(this.createRunScriptMarkdown(key, document.uri)); + + let debugArgs = extractDebugArgFromScript(value[2]); + if (debugArgs) { + contents.appendMarkdown(this.createDebugScriptMarkdown(key, document.uri, debugArgs[0], debugArgs[1])); + } + hover = new Hover(contents); + } + }); + return hover; + } + + private createRunScriptMarkdown(script: string, documentUri: Uri): string { + let args = { + documentUri: documentUri, + script: script, + }; + return this.createMarkdownLink( + localize('runScript', 'Run Script'), + 'npm.runScriptFromHover', + args, + localize('runScript.tooltip', 'Run the script as a task') + ); + } + + private createDebugScriptMarkdown(script: string, documentUri: Uri, protocol: string, port: number): string { + let args = { + documentUri: documentUri, + script: script, + protocol: protocol, + port: port + }; + return this.createMarkdownLink( + localize('debugScript', 'Debug Script'), + 'npm.debugScriptFromHover', + args, + localize('debugScript.tooltip', 'Runs the script under the debugger'), + '|' + ); + } + + private createMarkdownLink(label: string, cmd: string, args: any, tooltip: string, separator?: string): string { + let encodedArgs = encodeURIComponent(JSON.stringify(args)); + let prefix = ''; + if (separator) { + prefix = ` ${separator} `; + } + return `${prefix}[${label}](command:${cmd}?${encodedArgs} "${tooltip}")`; + } + + public runScriptFromHover(args: any) { + let script = args.script; + let documentUri = args.documentUri; + let folder = workspace.getWorkspaceFolder(documentUri); + if (folder) { + let task = createTask(script, `run ${script}`, folder, documentUri); + tasks.executeTask(task); + } + } + + public debugScriptFromHover(args: any) { + let script = args.script; + let documentUri = args.documentUri; + let protocol = args.protocol; + let port = args.port; + let folder = workspace.getWorkspaceFolder(documentUri); + if (folder) { + startDebugging(script, protocol, port, folder); + } + } +} diff --git a/extensions/npm/src/tasks.ts b/extensions/npm/src/tasks.ts index 2c978a15267..189fd98baec 100644 --- a/extensions/npm/src/tasks.ts +++ b/extensions/npm/src/tasks.ts @@ -4,7 +4,10 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { TaskDefinition, Task, TaskGroup, WorkspaceFolder, RelativePattern, ShellExecution, Uri, workspace } from 'vscode'; +import { + TaskDefinition, Task, TaskGroup, WorkspaceFolder, RelativePattern, ShellExecution, Uri, workspace, + DebugConfiguration, debug, TaskProvider, ExtensionContext, TextDocument, tasks +} from 'vscode'; import * as path from 'path'; import * as fs from 'fs'; import * as minimatch from 'minimatch'; @@ -22,7 +25,23 @@ type AutoDetect = 'on' | 'off'; let cachedTasks: Task[] | undefined = undefined; -export function invalidateScriptsCache() { +export class NpmTaskProvider implements TaskProvider { + private extensionContext: ExtensionContext; + + constructor(context: ExtensionContext) { + this.extensionContext = context; + } + + public provideTasks() { + return provideNpmScripts(); + } + + public resolveTask(_task: Task): Task | undefined { + return undefined; + } +} + +export function invalidateTasksCache() { cachedTasks = undefined; } @@ -162,7 +181,7 @@ function isExcluded(folder: WorkspaceFolder, packageJsonUri: Uri) { } function isDebugScript(script: string): boolean { - let match = script.match(/--(inspect|debug)(-brk)?(=(\d*))?/); + let match = script.match(/--(inspect|debug)(-brk)?(=((\[[0-9a-fA-F:]*\]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[a-zA-Z0-9\.]*):)?(\d+))?/); return match !== null; } @@ -269,6 +288,61 @@ async function readFile(file: string): Promise { }); } +export function runScript(script: string, document: TextDocument) { + let uri = document.uri; + let folder = workspace.getWorkspaceFolder(uri); + if (folder) { + let task = createTask(script, `run ${script}`, folder, uri); + tasks.executeTask(task); + } +} + +export function extractDebugArgFromScript(scriptValue: string): [string, number] | undefined { + // matches --debug, --debug=1234, --debug-brk, debug-brk=1234, --inspect, + // --inspect=1234, --inspect-brk, --inspect-brk=1234, + // --inspect=localhost:1245, --inspect=127.0.0.1:1234, --inspect=[aa:1:0:0:0]:1234, --inspect=:1234 + let match = scriptValue.match(/--(inspect|debug)(-brk)?(=((\[[0-9a-fA-F:]*\]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[a-zA-Z0-9\.]*):)?(\d+))?/); + + if (match) { + if (match[6]) { + return [match[1], parseInt(match[6])]; + } + if (match[1] === 'inspect') { + return [match[1], 9229]; + } + if (match[1] === 'debug') { + return [match[1], 5858]; + } + } + return undefined; +} + +export function startDebugging(scriptName: string, protocol: string, port: number, folder: WorkspaceFolder) { + let p = 'inspector'; + if (protocol === 'debug') { + p = 'legacy'; + } + + let packageManager = getPackageManager(folder); + const config: DebugConfiguration = { + type: 'node', + request: 'launch', + name: `Debug ${scriptName}`, + runtimeExecutable: packageManager, + runtimeArgs: [ + 'run', + scriptName, + ], + port: port, + protocol: p + }; + + if (folder) { + debug.startDebugging(folder, config); + } +} + + export type StringMap = { [s: string]: string; }; async function findAllScripts(buffer: string): Promise { @@ -278,7 +352,6 @@ async function findAllScripts(buffer: string): Promise { let visitor: JSONVisitor = { onError(_error: ParseErrorCode, _offset: number, _length: number) { - // TODO: inform user about the parse error }, onObjectEnd() { if (inScripts) { @@ -304,6 +377,80 @@ async function findAllScripts(buffer: string): Promise { return scripts; } +export function findAllScriptRanges(buffer: string): Map { + var scripts: Map = new Map(); + let script: string | undefined = undefined; + let offset: number; + let length: number; + + let inScripts = false; + + let visitor: JSONVisitor = { + onError(_error: ParseErrorCode, _offset: number, _length: number) { + }, + onObjectEnd() { + if (inScripts) { + inScripts = false; + } + }, + onLiteralValue(value: any, _offset: number, _length: number) { + if (script) { + scripts.set(script, [offset, length, value]); + script = undefined; + } + }, + onObjectProperty(property: string, off: number, len: number) { + if (property === 'scripts') { + inScripts = true; + } + else if (inScripts) { + script = property; + offset = off; + length = len; + } + } + }; + visit(buffer, visitor); + return scripts; +} + +export function findScriptAtPosition(buffer: string, offset: number): string | undefined { + let script: string | undefined = undefined; + let inScripts = false; + let scriptStart: number | undefined; + let visitor: JSONVisitor = { + onError(_error: ParseErrorCode, _offset: number, _length: number) { + }, + onObjectEnd() { + if (inScripts) { + inScripts = false; + scriptStart = undefined; + } + }, + onLiteralValue(value: any, nodeOffset: number, nodeLength: number) { + if (inScripts && scriptStart) { + if (offset >= scriptStart && offset < nodeOffset + nodeLength) { + // found the script + inScripts = false; + } else { + script = undefined; + } + } + }, + onObjectProperty(property: string, nodeOffset: number, nodeLength: number) { + if (property === 'scripts') { + inScripts = true; + } + else if (inScripts) { + scriptStart = nodeOffset; + script = property; + } + } + }; + visit(buffer, visitor); + return script; +} + export async function getScripts(packageJsonUri: Uri): Promise { if (packageJsonUri.scheme !== 'file') { diff --git a/extensions/objective-c/test/colorize-results/test_m.json b/extensions/objective-c/test/colorize-results/test_m.json index ed1bda084e3..691fe71e9a5 100644 --- a/extensions/objective-c/test/colorize-results/test_m.json +++ b/extensions/objective-c/test/colorize-results/test_m.json @@ -3,9 +3,9 @@ "c": "//", "t": "source.objc comment.line.double-slash.cpp punctuation.definition.comment.cpp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -14,9 +14,9 @@ "c": "//", "t": "source.objc comment.line.double-slash.cpp punctuation.definition.comment.cpp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -25,9 +25,9 @@ "c": " Copyright (c) Microsoft Corporation. All rights reserved.", "t": "source.objc comment.line.double-slash.cpp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -36,9 +36,9 @@ "c": "//", "t": "source.objc comment.line.double-slash.cpp punctuation.definition.comment.cpp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -179,9 +179,9 @@ "c": "/*", "t": "source.objc comment.block.c punctuation.definition.comment.begin.c", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -190,9 +190,9 @@ "c": "\tMulti", "t": "source.objc comment.block.c", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -201,9 +201,9 @@ "c": "\tLine", "t": "source.objc comment.block.c", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -212,9 +212,9 @@ "c": "\tComments", "t": "source.objc comment.block.c", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -223,9 +223,9 @@ "c": "*/", "t": "source.objc comment.block.c punctuation.definition.comment.end.c", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2225,9 +2225,9 @@ "c": "//", "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c comment.line.double-slash.cpp punctuation.definition.comment.cpp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2236,9 +2236,9 @@ "c": " add a tap gesture recognizer", "t": "source.objc meta.implementation.objc meta.scope.implementation.objc meta.function-with-body.objc meta.block.c comment.line.double-slash.cpp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/package.json b/extensions/package.json index 7b4fd5a5bd0..8e143a8485e 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "3.0.1-insiders.20180713" + "typescript": "3.0.1" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/perl/test/colorize-results/test_pl.json b/extensions/perl/test/colorize-results/test_pl.json index 7e575aa4652..ecfd9660ad8 100644 --- a/extensions/perl/test/colorize-results/test_pl.json +++ b/extensions/perl/test/colorize-results/test_pl.json @@ -278,9 +278,9 @@ "c": "#", "t": "source.perl comment.line.number-sign.perl punctuation.definition.comment.perl", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -289,9 +289,9 @@ "c": " Check for that =.", "t": "source.perl comment.line.number-sign.perl", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1015,9 +1015,9 @@ "c": "#", "t": "source.perl comment.line.number-sign.perl punctuation.definition.comment.perl", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1026,9 +1026,9 @@ "c": "#", "t": "source.perl comment.line.number-sign.perl punctuation.definition.comment.perl", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1037,9 +1037,9 @@ "c": " This function opens and reads one file, and calls", "t": "source.perl comment.line.number-sign.perl", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1048,9 +1048,9 @@ "c": "#", "t": "source.perl comment.line.number-sign.perl punctuation.definition.comment.perl", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1059,9 +1059,9 @@ "c": " check_line to analyze each line. Call it with the", "t": "source.perl comment.line.number-sign.perl", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1070,9 +1070,9 @@ "c": "#", "t": "source.perl comment.line.number-sign.perl punctuation.definition.comment.perl", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1081,9 +1081,9 @@ "c": " file name.", "t": "source.perl comment.line.number-sign.perl", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1092,9 +1092,9 @@ "c": "#", "t": "source.perl comment.line.number-sign.perl punctuation.definition.comment.perl", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1939,9 +1939,9 @@ "c": "#", "t": "source.perl comment.line.number-sign.perl punctuation.definition.comment.perl", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1950,9 +1950,9 @@ "c": "#", "t": "source.perl comment.line.number-sign.perl punctuation.definition.comment.perl", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1961,9 +1961,9 @@ "c": " Go through the argument list and check each file", "t": "source.perl comment.line.number-sign.perl", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1972,9 +1972,9 @@ "c": "#", "t": "source.perl comment.line.number-sign.perl punctuation.definition.comment.perl", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/php-language-features/package.nls.json b/extensions/php-language-features/package.nls.json index c991103e41d..3920760d1d2 100644 --- a/extensions/php-language-features/package.nls.json +++ b/extensions/php-language-features/package.nls.json @@ -1,5 +1,5 @@ { - "configuration.suggest.basic": "Configures if the built-in PHP language suggestions are enabled. The support suggests PHP globals and variables.", + "configuration.suggest.basic": "Controls whether the built-in PHP language suggestions are enabled. The support suggests PHP globals and variables.", "configuration.validate.enable": "Enable/disable built-in PHP validation.", "configuration.validate.executablePath": "Points to the PHP executable.", "configuration.validate.run": "Whether the linter is run on save or on type.", diff --git a/extensions/php/test/colorize-results/issue-28354_php.json b/extensions/php/test/colorize-results/issue-28354_php.json index cc9924d3945..12e439430fb 100644 --- a/extensions/php/test/colorize-results/issue-28354_php.json +++ b/extensions/php/test/colorize-results/issue-28354_php.json @@ -1,7 +1,7 @@ [ { "c": "<", - "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html", + "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -12,7 +12,7 @@ }, { "c": "script", - "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.html entity.name.tag.html", + "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -23,7 +23,7 @@ }, { "c": ">", - "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.end.html", + "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -496,7 +496,7 @@ }, { "c": "<", - "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html source.js", + "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html source.js", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -507,7 +507,7 @@ }, { "c": "/", - "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.begin.html", + "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -518,7 +518,7 @@ }, { "c": "script", - "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.html entity.name.tag.html", + "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.end.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -529,7 +529,7 @@ }, { "c": ">", - "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.html punctuation.definition.tag.end.html", + "t": "text.html.php meta.embedded.block.html meta.tag.metadata.script.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", diff --git a/extensions/php/test/colorize-results/test_php.json b/extensions/php/test/colorize-results/test_php.json index 4cb58182b1a..277428ac11b 100644 --- a/extensions/php/test/colorize-results/test_php.json +++ b/extensions/php/test/colorize-results/test_php.json @@ -1,7 +1,7 @@ [ { "c": "<", - "t": "text.html.php meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.php meta.tag.structure.html.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -12,7 +12,7 @@ }, { "c": "html", - "t": "text.html.php meta.tag.structure.any.html entity.name.tag.structure.any.html", + "t": "text.html.php meta.tag.structure.html.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -23,7 +23,7 @@ }, { "c": ">", - "t": "text.html.php meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.php meta.tag.structure.html.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -34,7 +34,7 @@ }, { "c": "<", - "t": "text.html.php meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.php meta.tag.structure.head.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -45,7 +45,7 @@ }, { "c": "head", - "t": "text.html.php meta.tag.structure.any.html entity.name.tag.structure.any.html", + "t": "text.html.php meta.tag.structure.head.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -56,7 +56,7 @@ }, { "c": ">", - "t": "text.html.php meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.php meta.tag.structure.head.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -78,7 +78,7 @@ }, { "c": "<", - "t": "text.html.php meta.tag.inline.any.html punctuation.definition.tag.begin.html", + "t": "text.html.php meta.tag.metadata.title.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -89,7 +89,7 @@ }, { "c": "title", - "t": "text.html.php meta.tag.inline.any.html entity.name.tag.inline.any.html", + "t": "text.html.php meta.tag.metadata.title.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -100,7 +100,7 @@ }, { "c": ">", - "t": "text.html.php meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.php meta.tag.metadata.title.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -122,7 +122,7 @@ }, { "c": "", - "t": "text.html.php meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.php meta.tag.metadata.title.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -155,7 +155,7 @@ }, { "c": "", - "t": "text.html.php meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.php meta.tag.structure.head.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -188,7 +188,7 @@ }, { "c": "<", - "t": "text.html.php meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.php meta.tag.structure.body.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -199,7 +199,7 @@ }, { "c": "body", - "t": "text.html.php meta.tag.structure.any.html entity.name.tag.structure.any.html", + "t": "text.html.php meta.tag.structure.body.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -210,7 +210,7 @@ }, { "c": ">", - "t": "text.html.php meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.php meta.tag.structure.body.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -322,9 +322,9 @@ "c": "//", "t": "text.html.php meta.embedded.block.php source.php comment.line.double-slash.php punctuation.definition.comment.php", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -333,9 +333,9 @@ "c": " Code to be executed", "t": "text.html.php meta.embedded.block.php source.php comment.line.double-slash.php", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -377,9 +377,9 @@ "c": "/*", "t": "text.html.php meta.embedded.block.php source.php comment.block.php punctuation.definition.comment.php", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -388,9 +388,9 @@ "c": " Example PHP file", "t": "text.html.php meta.embedded.block.php source.php comment.block.php", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -399,9 +399,9 @@ "c": "\tmultiline comment", "t": "text.html.php meta.embedded.block.php source.php comment.block.php", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -410,9 +410,9 @@ "c": "\t", "t": "text.html.php meta.embedded.block.php source.php comment.block.php", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -421,9 +421,9 @@ "c": "*/", "t": "text.html.php meta.embedded.block.php source.php comment.block.php punctuation.definition.comment.php", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2654,9 +2654,9 @@ "c": "//", "t": "text.html.php meta.embedded.block.php source.php comment.line.double-slash.php punctuation.definition.comment.php", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2665,9 +2665,9 @@ "c": " display shuffled cards (EXAMPLE ONLY)", "t": "text.html.php meta.embedded.block.php source.php comment.line.double-slash.php", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3565,7 +3565,7 @@ }, { "c": "", - "t": "text.html.php meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.php meta.tag.structure.body.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3598,7 +3598,7 @@ }, { "c": "", - "t": "text.html.php meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.php meta.tag.structure.html.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", diff --git a/extensions/powershell/syntaxes/powershell.tmLanguage.json b/extensions/powershell/syntaxes/powershell.tmLanguage.json index ca3fd4d5a4d..0e3c0dbe84a 100644 --- a/extensions/powershell/syntaxes/powershell.tmLanguage.json +++ b/extensions/powershell/syntaxes/powershell.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/PowerShell/EditorSyntax/commit/146e421358945dbfbd24a9dcf56d759bdb0693db", + "version": "https://github.com/PowerShell/EditorSyntax/commit/472c9447da4e3160bef211d5e1a0c2dee3cce497", "name": "PowerShell", "scopeName": "source.powershell", "patterns": [ @@ -262,7 +262,7 @@ "name": "punctuation.definition.comment.powershell" } }, - "end": "$", + "end": "$\\n?", "name": "comment.line.powershell", "patterns": [ { diff --git a/extensions/powershell/test/colorize-results/test_ps1.json b/extensions/powershell/test/colorize-results/test_ps1.json index 15d677e1b95..fd82cda536b 100644 --- a/extensions/powershell/test/colorize-results/test_ps1.json +++ b/extensions/powershell/test/colorize-results/test_ps1.json @@ -3,9 +3,9 @@ "c": "#", "t": "source.powershell comment.line.powershell punctuation.definition.comment.powershell", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -14,9 +14,9 @@ "c": " Copyright Microsoft Corporation", "t": "source.powershell comment.line.powershell", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1774,9 +1774,9 @@ "c": "#", "t": "source.powershell comment.line.powershell punctuation.definition.comment.powershell", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1785,9 +1785,9 @@ "c": " PowerShell commands need elevation for dependencies installation and running tests", "t": "source.powershell comment.line.powershell", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/python/syntaxes/MagicPython.tmLanguage.json b/extensions/python/syntaxes/MagicPython.tmLanguage.json index 44a995bc046..fc32d74cec5 100644 --- a/extensions/python/syntaxes/MagicPython.tmLanguage.json +++ b/extensions/python/syntaxes/MagicPython.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/MagicStack/MagicPython/commit/b453f26ed856c9b16a053517c41207e3a72cc7d5", + "version": "https://github.com/MagicStack/MagicPython/commit/fb56c6a98d684e30bed1b0f9647e85741a48f914", "name": "MagicPython", "scopeName": "source.python", "patterns": [ @@ -97,7 +97,8 @@ }, "docstring-statement": { "begin": "^(?=\\s*[rR]?(\\'\\'\\'|\\\"\\\"\\\"|\\'|\\\"))", - "end": "(?<=\\'\\'\\'|\\\"\\\"\\\"|\\'|\\\")", + "comment": "the string either terminates correctly or by the beginning of a new line (this is for single line docstrings that aren't terminated) AND it's not followed by another docstring", + "end": "((?<=\\1)|^)(?!\\s*[rR]?(\\'\\'\\'|\\\"\\\"\\\"|\\'|\\\"))", "patterns": [ { "include": "#docstring" @@ -164,7 +165,7 @@ { "name": "string.quoted.docstring.single.python", "begin": "(\\'|\\\")", - "end": "(\\1)|((?=^]? [-+ ]? \\#?\n \\d* ,? (\\.\\d+)? [bcdeEfFgGnosxX%]? )?\n })\n )\n", + "name": "meta.format.brace.python", + "match": "(?x)\n (\n {{ | }}\n | (?:\n {\n \\w*? (\\.[[:alpha:]_]\\w*? | \\[[^\\]'\"]+\\])*?\n (![rsa])?\n ( : \\w? [<>=^]? [-+ ]? \\#?\n \\d* ,? (\\.\\d+)? [bcdeEfFgGnosxX%]? )?\n })\n )\n", "captures": { - "2": { - "name": "storage.type.format.python" + "1": { + "name": "constant.character.format.placeholder.other.python" }, "3": { "name": "storage.type.format.python" + }, + "4": { + "name": "storage.type.format.python" } } }, { - "name": "constant.character.format.placeholder.other.python", - "begin": "(?x)\n \\{\n \\w*? (\\.[[:alpha:]_]\\w*? | \\[[^\\]'\"]+\\])*?\n (![rsa])?\n (:)\n (?=[^'\"}\\n]*\\})\n", - "end": "\\}", - "beginCaptures": { - "2": { - "name": "storage.type.format.python" + "name": "meta.format.brace.python", + "match": "(?x)\n (\n {\n \\w*? (\\.[[:alpha:]_]\\w*? | \\[[^\\]'\"]+\\])*?\n (![rsa])?\n (:)\n (\n [^'\"{}\\n]+?\n |\n \\{ [^'\"}\\n]*? \\}\n )*\n }\n )\n", + "captures": { + "1": { + "name": "constant.character.format.placeholder.other.python" }, "3": { "name": "storage.type.format.python" + }, + "4": { + "name": "storage.type.format.python" } - }, - "patterns": [ - { - "match": "(?x) \\{ [^'\"}\\n]*? \\} (?=.*?\\})\n" - } - ] + } } ] }, @@ -4537,7 +4543,7 @@ }, "string-quoted-single-line": { "name": "string.quoted.single.python", - "begin": "(\\b[rR](?=[uU]))?([uU])?((['\"]))", + "begin": "(?:\\b([rR])(?=[uU]))?([uU])?((['\"]))", "end": "(\\3)|((?", - "t": "text.html.cshtml meta.tag.sgml.html punctuation.definition.tag.html", - "r": { - "dark_plus": "punctuation.definition.tag: #808080", - "light_plus": "punctuation.definition.tag: #800000", - "dark_vs": "punctuation.definition.tag: #808080", - "light_vs": "punctuation.definition.tag: #800000", - "hc_black": "punctuation.definition.tag: #808080" - } - }, - { - "c": "<", - "t": "text.html.cshtml meta.tag.structure.any.html punctuation.definition.tag.html", - "r": { - "dark_plus": "punctuation.definition.tag: #808080", - "light_plus": "punctuation.definition.tag: #800000", - "dark_vs": "punctuation.definition.tag: #808080", - "light_vs": "punctuation.definition.tag: #800000", - "hc_black": "punctuation.definition.tag: #808080" - } - }, - { - "c": "html", - "t": "text.html.cshtml meta.tag.structure.any.html entity.name.tag.structure.any.html", + "c": "DOCTYPE", + "t": "text.html.cshtml meta.tag.metadata.doctype.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1464,7 +1431,62 @@ }, { "c": " ", - "t": "text.html.cshtml meta.tag.structure.any.html", + "t": "text.html.cshtml meta.tag.metadata.doctype.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "html", + "t": "text.html.cshtml meta.tag.metadata.doctype.html entity.other.attribute-name.html", + "r": { + "dark_plus": "entity.other.attribute-name: #9CDCFE", + "light_plus": "entity.other.attribute-name: #FF0000", + "dark_vs": "entity.other.attribute-name: #9CDCFE", + "light_vs": "entity.other.attribute-name: #FF0000", + "hc_black": "entity.other.attribute-name: #9CDCFE" + } + }, + { + "c": ">", + "t": "text.html.cshtml meta.tag.metadata.doctype.html punctuation.definition.tag.end.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "<", + "t": "text.html.cshtml meta.tag.structure.html.start.html punctuation.definition.tag.begin.html", + "r": { + "dark_plus": "punctuation.definition.tag: #808080", + "light_plus": "punctuation.definition.tag: #800000", + "dark_vs": "punctuation.definition.tag: #808080", + "light_vs": "punctuation.definition.tag: #800000", + "hc_black": "punctuation.definition.tag: #808080" + } + }, + { + "c": "html", + "t": "text.html.cshtml meta.tag.structure.html.start.html entity.name.tag.html", + "r": { + "dark_plus": "entity.name.tag: #569CD6", + "light_plus": "entity.name.tag: #800000", + "dark_vs": "entity.name.tag: #569CD6", + "light_vs": "entity.name.tag: #800000", + "hc_black": "entity.name.tag: #569CD6" + } + }, + { + "c": " ", + "t": "text.html.cshtml meta.tag.structure.html.start.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1475,7 +1497,7 @@ }, { "c": "lang", - "t": "text.html.cshtml meta.tag.structure.any.html entity.other.attribute-name.html", + "t": "text.html.cshtml meta.tag.structure.html.start.html meta.attribute.lang.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -1486,7 +1508,7 @@ }, { "c": "=", - "t": "text.html.cshtml meta.tag.structure.any.html", + "t": "text.html.cshtml meta.tag.structure.html.start.html meta.attribute.lang.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1497,7 +1519,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.structure.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.cshtml meta.tag.structure.html.start.html meta.attribute.lang.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -1508,7 +1530,7 @@ }, { "c": "en", - "t": "text.html.cshtml meta.tag.structure.any.html string.quoted.double.html", + "t": "text.html.cshtml meta.tag.structure.html.start.html meta.attribute.lang.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -1519,7 +1541,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.structure.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.cshtml meta.tag.structure.html.start.html meta.attribute.lang.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -1530,7 +1552,7 @@ }, { "c": ">", - "t": "text.html.cshtml meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.cshtml meta.tag.structure.html.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1552,7 +1574,7 @@ }, { "c": "<", - "t": "text.html.cshtml meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.cshtml meta.tag.structure.head.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1563,7 +1585,7 @@ }, { "c": "head", - "t": "text.html.cshtml meta.tag.structure.any.html entity.name.tag.structure.any.html", + "t": "text.html.cshtml meta.tag.structure.head.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1574,7 +1596,7 @@ }, { "c": ">", - "t": "text.html.cshtml meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.cshtml meta.tag.structure.head.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1596,7 +1618,7 @@ }, { "c": "<", - "t": "text.html.cshtml meta.tag.inline.any.html punctuation.definition.tag.begin.html", + "t": "text.html.cshtml meta.tag.metadata.title.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1607,7 +1629,7 @@ }, { "c": "title", - "t": "text.html.cshtml meta.tag.inline.any.html entity.name.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.metadata.title.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1618,7 +1640,7 @@ }, { "c": ">", - "t": "text.html.cshtml meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.cshtml meta.tag.metadata.title.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1640,7 +1662,7 @@ }, { "c": "", - "t": "text.html.cshtml meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.cshtml meta.tag.metadata.title.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1684,7 +1706,7 @@ }, { "c": "<", - "t": "text.html.cshtml meta.tag.inline.any.html punctuation.definition.tag.begin.html", + "t": "text.html.cshtml meta.tag.metadata.meta.void.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1695,7 +1717,7 @@ }, { "c": "meta", - "t": "text.html.cshtml meta.tag.inline.any.html entity.name.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.metadata.meta.void.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1706,7 +1728,7 @@ }, { "c": " ", - "t": "text.html.cshtml meta.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.metadata.meta.void.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1717,7 +1739,7 @@ }, { "c": "charset", - "t": "text.html.cshtml meta.tag.inline.any.html entity.other.attribute-name.html", + "t": "text.html.cshtml meta.tag.metadata.meta.void.html meta.attribute.charset.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -1728,7 +1750,7 @@ }, { "c": "=", - "t": "text.html.cshtml meta.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.metadata.meta.void.html meta.attribute.charset.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1739,7 +1761,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.cshtml meta.tag.metadata.meta.void.html meta.attribute.charset.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -1750,7 +1772,7 @@ }, { "c": "utf-8", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html", + "t": "text.html.cshtml meta.tag.metadata.meta.void.html meta.attribute.charset.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -1761,7 +1783,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.cshtml meta.tag.metadata.meta.void.html meta.attribute.charset.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -1771,8 +1793,19 @@ } }, { - "c": " />", - "t": "text.html.cshtml meta.tag.inline.any.html punctuation.definition.tag.end.html", + "c": " ", + "t": "text.html.cshtml meta.tag.metadata.meta.void.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "/>", + "t": "text.html.cshtml meta.tag.metadata.meta.void.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1794,7 +1827,7 @@ }, { "c": "", - "t": "text.html.cshtml meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.cshtml meta.tag.structure.head.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1827,7 +1860,7 @@ }, { "c": "<", - "t": "text.html.cshtml meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.cshtml meta.tag.structure.body.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1838,7 +1871,7 @@ }, { "c": "body", - "t": "text.html.cshtml meta.tag.structure.any.html entity.name.tag.structure.any.html", + "t": "text.html.cshtml meta.tag.structure.body.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1849,7 +1882,7 @@ }, { "c": ">", - "t": "text.html.cshtml meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.cshtml meta.tag.structure.body.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1871,7 +1904,7 @@ }, { "c": "<", - "t": "text.html.cshtml meta.tag.block.any.html punctuation.definition.tag.begin.html", + "t": "text.html.cshtml meta.tag.structure.p.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1882,7 +1915,7 @@ }, { "c": "p", - "t": "text.html.cshtml meta.tag.block.any.html entity.name.tag.block.any.html", + "t": "text.html.cshtml meta.tag.structure.p.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1893,7 +1926,7 @@ }, { "c": ">", - "t": "text.html.cshtml meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.cshtml meta.tag.structure.p.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1915,7 +1948,7 @@ }, { "c": "<", - "t": "text.html.cshtml meta.tag.inline.any.html punctuation.definition.tag.begin.html", + "t": "text.html.cshtml meta.tag.inline.strong.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1926,7 +1959,7 @@ }, { "c": "strong", - "t": "text.html.cshtml meta.tag.inline.any.html entity.name.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.inline.strong.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1937,7 +1970,7 @@ }, { "c": ">", - "t": "text.html.cshtml meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.cshtml meta.tag.inline.strong.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -1959,7 +1992,7 @@ }, { "c": "", - "t": "text.html.cshtml meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.cshtml meta.tag.inline.strong.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2003,7 +2036,7 @@ }, { "c": "", - "t": "text.html.cshtml meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.cshtml meta.tag.structure.p.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2047,7 +2080,7 @@ }, { "c": "<", - "t": "text.html.cshtml meta.tag.block.any.html punctuation.definition.tag.begin.html", + "t": "text.html.cshtml meta.tag.structure.form.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2058,7 +2091,7 @@ }, { "c": "form", - "t": "text.html.cshtml meta.tag.block.any.html entity.name.tag.block.any.html", + "t": "text.html.cshtml meta.tag.structure.form.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -2069,7 +2102,7 @@ }, { "c": " ", - "t": "text.html.cshtml meta.tag.block.any.html", + "t": "text.html.cshtml meta.tag.structure.form.start.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2080,7 +2113,7 @@ }, { "c": "action", - "t": "text.html.cshtml meta.tag.block.any.html entity.other.attribute-name.html", + "t": "text.html.cshtml meta.tag.structure.form.start.html meta.attribute.action.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -2091,7 +2124,7 @@ }, { "c": "=", - "t": "text.html.cshtml meta.tag.block.any.html", + "t": "text.html.cshtml meta.tag.structure.form.start.html meta.attribute.action.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2102,7 +2135,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.block.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.cshtml meta.tag.structure.form.start.html meta.attribute.action.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2113,7 +2146,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.block.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.cshtml meta.tag.structure.form.start.html meta.attribute.action.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2124,7 +2157,7 @@ }, { "c": " ", - "t": "text.html.cshtml meta.tag.block.any.html", + "t": "text.html.cshtml meta.tag.structure.form.start.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2135,7 +2168,7 @@ }, { "c": "method", - "t": "text.html.cshtml meta.tag.block.any.html entity.other.attribute-name.html", + "t": "text.html.cshtml meta.tag.structure.form.start.html meta.attribute.method.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -2146,7 +2179,7 @@ }, { "c": "=", - "t": "text.html.cshtml meta.tag.block.any.html", + "t": "text.html.cshtml meta.tag.structure.form.start.html meta.attribute.method.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2157,7 +2190,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.block.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.cshtml meta.tag.structure.form.start.html meta.attribute.method.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2168,7 +2201,7 @@ }, { "c": "post", - "t": "text.html.cshtml meta.tag.block.any.html string.quoted.double.html", + "t": "text.html.cshtml meta.tag.structure.form.start.html meta.attribute.method.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2179,7 +2212,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.block.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.cshtml meta.tag.structure.form.start.html meta.attribute.method.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2190,7 +2223,7 @@ }, { "c": ">", - "t": "text.html.cshtml meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.cshtml meta.tag.structure.form.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2212,7 +2245,7 @@ }, { "c": "<", - "t": "text.html.cshtml meta.tag.block.any.html punctuation.definition.tag.begin.html", + "t": "text.html.cshtml meta.tag.structure.p.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2223,7 +2256,7 @@ }, { "c": "p", - "t": "text.html.cshtml meta.tag.block.any.html entity.name.tag.block.any.html", + "t": "text.html.cshtml meta.tag.structure.p.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -2234,7 +2267,7 @@ }, { "c": ">", - "t": "text.html.cshtml meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.cshtml meta.tag.structure.p.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2245,7 +2278,7 @@ }, { "c": "<", - "t": "text.html.cshtml meta.tag.inline.any.html punctuation.definition.tag.begin.html", + "t": "text.html.cshtml meta.tag.structure.label.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2256,7 +2289,7 @@ }, { "c": "label", - "t": "text.html.cshtml meta.tag.inline.any.html entity.name.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.structure.label.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -2267,7 +2300,7 @@ }, { "c": " ", - "t": "text.html.cshtml meta.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.structure.label.start.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2278,7 +2311,7 @@ }, { "c": "for", - "t": "text.html.cshtml meta.tag.inline.any.html entity.other.attribute-name.html", + "t": "text.html.cshtml meta.tag.structure.label.start.html meta.attribute.for.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -2289,7 +2322,7 @@ }, { "c": "=", - "t": "text.html.cshtml meta.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.structure.label.start.html meta.attribute.for.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2300,7 +2333,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.cshtml meta.tag.structure.label.start.html meta.attribute.for.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2311,7 +2344,7 @@ }, { "c": "text1", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html", + "t": "text.html.cshtml meta.tag.structure.label.start.html meta.attribute.for.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2322,7 +2355,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.cshtml meta.tag.structure.label.start.html meta.attribute.for.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2333,7 +2366,7 @@ }, { "c": ">", - "t": "text.html.cshtml meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.cshtml meta.tag.structure.label.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2355,7 +2388,7 @@ }, { "c": "", - "t": "text.html.cshtml meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.cshtml meta.tag.structure.label.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2399,7 +2432,7 @@ }, { "c": "<", - "t": "text.html.cshtml meta.tag.inline.any.html punctuation.definition.tag.begin.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2410,7 +2443,7 @@ }, { "c": "input", - "t": "text.html.cshtml meta.tag.inline.any.html entity.name.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -2421,7 +2454,7 @@ }, { "c": " ", - "t": "text.html.cshtml meta.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2432,7 +2465,7 @@ }, { "c": "type", - "t": "text.html.cshtml meta.tag.inline.any.html entity.other.attribute-name.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.type.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -2443,7 +2476,7 @@ }, { "c": "=", - "t": "text.html.cshtml meta.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.type.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2454,7 +2487,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.type.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2465,7 +2498,7 @@ }, { "c": "text", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.type.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2476,7 +2509,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.type.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2487,7 +2520,7 @@ }, { "c": " ", - "t": "text.html.cshtml meta.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2498,7 +2531,7 @@ }, { "c": "name", - "t": "text.html.cshtml meta.tag.inline.any.html entity.other.attribute-name.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.name.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -2509,7 +2542,7 @@ }, { "c": "=", - "t": "text.html.cshtml meta.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.name.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2520,7 +2553,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.name.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2531,7 +2564,7 @@ }, { "c": "text1", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.name.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2542,7 +2575,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.name.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2552,8 +2585,19 @@ } }, { - "c": " />", - "t": "text.html.cshtml meta.tag.inline.any.html punctuation.definition.tag.end.html", + "c": " ", + "t": "text.html.cshtml meta.tag.structure.input.void.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "/>", + "t": "text.html.cshtml meta.tag.structure.input.void.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2575,7 +2619,7 @@ }, { "c": "", - "t": "text.html.cshtml meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.cshtml meta.tag.structure.p.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2619,7 +2663,7 @@ }, { "c": "<", - "t": "text.html.cshtml meta.tag.block.any.html punctuation.definition.tag.begin.html", + "t": "text.html.cshtml meta.tag.structure.p.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2630,7 +2674,7 @@ }, { "c": "p", - "t": "text.html.cshtml meta.tag.block.any.html entity.name.tag.block.any.html", + "t": "text.html.cshtml meta.tag.structure.p.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -2641,7 +2685,7 @@ }, { "c": ">", - "t": "text.html.cshtml meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.cshtml meta.tag.structure.p.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2652,7 +2696,7 @@ }, { "c": "<", - "t": "text.html.cshtml meta.tag.inline.any.html punctuation.definition.tag.begin.html", + "t": "text.html.cshtml meta.tag.structure.label.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2663,7 +2707,7 @@ }, { "c": "label", - "t": "text.html.cshtml meta.tag.inline.any.html entity.name.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.structure.label.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -2674,7 +2718,7 @@ }, { "c": " ", - "t": "text.html.cshtml meta.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.structure.label.start.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2685,7 +2729,7 @@ }, { "c": "for", - "t": "text.html.cshtml meta.tag.inline.any.html entity.other.attribute-name.html", + "t": "text.html.cshtml meta.tag.structure.label.start.html meta.attribute.for.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -2696,7 +2740,7 @@ }, { "c": "=", - "t": "text.html.cshtml meta.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.structure.label.start.html meta.attribute.for.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2707,7 +2751,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.cshtml meta.tag.structure.label.start.html meta.attribute.for.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2718,7 +2762,7 @@ }, { "c": "text2", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html", + "t": "text.html.cshtml meta.tag.structure.label.start.html meta.attribute.for.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2729,7 +2773,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.cshtml meta.tag.structure.label.start.html meta.attribute.for.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2740,7 +2784,7 @@ }, { "c": ">", - "t": "text.html.cshtml meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.cshtml meta.tag.structure.label.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2762,7 +2806,7 @@ }, { "c": "", - "t": "text.html.cshtml meta.tag.inline.any.html punctuation.definition.tag.end.html", + "t": "text.html.cshtml meta.tag.structure.label.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2806,7 +2850,7 @@ }, { "c": "<", - "t": "text.html.cshtml meta.tag.inline.any.html punctuation.definition.tag.begin.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2817,7 +2861,7 @@ }, { "c": "input", - "t": "text.html.cshtml meta.tag.inline.any.html entity.name.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -2828,7 +2872,7 @@ }, { "c": " ", - "t": "text.html.cshtml meta.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2839,7 +2883,7 @@ }, { "c": "type", - "t": "text.html.cshtml meta.tag.inline.any.html entity.other.attribute-name.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.type.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -2850,7 +2894,7 @@ }, { "c": "=", - "t": "text.html.cshtml meta.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.type.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2861,7 +2905,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.type.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2872,7 +2916,7 @@ }, { "c": "text", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.type.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2883,7 +2927,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.type.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2894,7 +2938,7 @@ }, { "c": " ", - "t": "text.html.cshtml meta.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2905,7 +2949,7 @@ }, { "c": "name", - "t": "text.html.cshtml meta.tag.inline.any.html entity.other.attribute-name.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.name.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -2916,7 +2960,7 @@ }, { "c": "=", - "t": "text.html.cshtml meta.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.name.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2927,7 +2971,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.name.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2938,7 +2982,7 @@ }, { "c": "text2", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.name.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2949,7 +2993,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.name.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -2959,8 +3003,19 @@ } }, { - "c": " />", - "t": "text.html.cshtml meta.tag.inline.any.html punctuation.definition.tag.end.html", + "c": " ", + "t": "text.html.cshtml meta.tag.structure.input.void.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "/>", + "t": "text.html.cshtml meta.tag.structure.input.void.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -2982,7 +3037,7 @@ }, { "c": "", - "t": "text.html.cshtml meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.cshtml meta.tag.structure.p.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3026,7 +3081,7 @@ }, { "c": "<", - "t": "text.html.cshtml meta.tag.block.any.html punctuation.definition.tag.begin.html", + "t": "text.html.cshtml meta.tag.structure.p.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3037,7 +3092,7 @@ }, { "c": "p", - "t": "text.html.cshtml meta.tag.block.any.html entity.name.tag.block.any.html", + "t": "text.html.cshtml meta.tag.structure.p.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -3048,7 +3103,7 @@ }, { "c": ">", - "t": "text.html.cshtml meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.cshtml meta.tag.structure.p.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3059,7 +3114,7 @@ }, { "c": "<", - "t": "text.html.cshtml meta.tag.inline.any.html punctuation.definition.tag.begin.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3070,7 +3125,7 @@ }, { "c": "input", - "t": "text.html.cshtml meta.tag.inline.any.html entity.name.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -3081,7 +3136,7 @@ }, { "c": " ", - "t": "text.html.cshtml meta.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3092,7 +3147,7 @@ }, { "c": "type", - "t": "text.html.cshtml meta.tag.inline.any.html entity.other.attribute-name.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.type.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -3103,7 +3158,7 @@ }, { "c": "=", - "t": "text.html.cshtml meta.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.type.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3114,7 +3169,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.type.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -3125,7 +3180,7 @@ }, { "c": "submit", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.type.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -3136,7 +3191,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.type.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -3147,7 +3202,7 @@ }, { "c": " ", - "t": "text.html.cshtml meta.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3158,7 +3213,7 @@ }, { "c": "value", - "t": "text.html.cshtml meta.tag.inline.any.html entity.other.attribute-name.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.value.html entity.other.attribute-name.html", "r": { "dark_plus": "entity.other.attribute-name: #9CDCFE", "light_plus": "entity.other.attribute-name: #FF0000", @@ -3169,7 +3224,7 @@ }, { "c": "=", - "t": "text.html.cshtml meta.tag.inline.any.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.value.html punctuation.separator.key-value.html", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3180,7 +3235,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.begin.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.value.html string.quoted.double.html punctuation.definition.string.begin.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -3191,7 +3246,7 @@ }, { "c": "Add", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.value.html string.quoted.double.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -3202,7 +3257,7 @@ }, { "c": "\"", - "t": "text.html.cshtml meta.tag.inline.any.html string.quoted.double.html punctuation.definition.string.end.html", + "t": "text.html.cshtml meta.tag.structure.input.void.html meta.attribute.value.html string.quoted.double.html punctuation.definition.string.end.html", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.double.html: #0000FF", @@ -3212,8 +3267,19 @@ } }, { - "c": " />", - "t": "text.html.cshtml meta.tag.inline.any.html punctuation.definition.tag.end.html", + "c": " ", + "t": "text.html.cshtml meta.tag.structure.input.void.html", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "/>", + "t": "text.html.cshtml meta.tag.structure.input.void.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3224,7 +3290,7 @@ }, { "c": "", - "t": "text.html.cshtml meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.cshtml meta.tag.structure.p.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3268,7 +3334,7 @@ }, { "c": "", - "t": "text.html.cshtml meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.cshtml meta.tag.structure.form.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3334,7 +3400,7 @@ }, { "c": "<", - "t": "text.html.cshtml meta.tag.block.any.html punctuation.definition.tag.begin.html", + "t": "text.html.cshtml meta.tag.structure.p.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3345,7 +3411,7 @@ }, { "c": "p", - "t": "text.html.cshtml meta.tag.block.any.html entity.name.tag.block.any.html", + "t": "text.html.cshtml meta.tag.structure.p.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -3356,7 +3422,7 @@ }, { "c": ">", - "t": "text.html.cshtml meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.cshtml meta.tag.structure.p.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3400,7 +3466,7 @@ }, { "c": "<", - "t": "text.html.cshtml meta.tag.block.any.html punctuation.definition.tag.begin.html", + "t": "text.html.cshtml meta.tag.structure.p.start.html punctuation.definition.tag.begin.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3411,7 +3477,7 @@ }, { "c": "p", - "t": "text.html.cshtml meta.tag.block.any.html entity.name.tag.block.any.html", + "t": "text.html.cshtml meta.tag.structure.p.start.html entity.name.tag.html", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -3422,7 +3488,7 @@ }, { "c": ">", - "t": "text.html.cshtml meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.cshtml meta.tag.structure.p.start.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3510,7 +3576,7 @@ }, { "c": "", - "t": "text.html.cshtml meta.tag.block.any.html punctuation.definition.tag.end.html", + "t": "text.html.cshtml meta.tag.structure.p.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3565,7 +3631,7 @@ }, { "c": "", - "t": "text.html.cshtml meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.cshtml meta.tag.structure.body.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", @@ -3598,7 +3664,7 @@ }, { "c": "", - "t": "text.html.cshtml meta.tag.structure.any.html punctuation.definition.tag.html", + "t": "text.html.cshtml meta.tag.structure.html.end.html punctuation.definition.tag.end.html", "r": { "dark_plus": "punctuation.definition.tag: #808080", "light_plus": "punctuation.definition.tag: #800000", diff --git a/extensions/ruby/test/colorize-results/test_rb.json b/extensions/ruby/test/colorize-results/test_rb.json index 4a5fa89c87e..c53ee5970e2 100644 --- a/extensions/ruby/test/colorize-results/test_rb.json +++ b/extensions/ruby/test/colorize-results/test_rb.json @@ -3,9 +3,9 @@ "c": "#", "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -14,9 +14,9 @@ "c": " encoding: utf-8", "t": "source.ruby comment.line.number-sign.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -25,9 +25,9 @@ "c": "#", "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -36,9 +36,9 @@ "c": " Code generated by Microsoft (R) AutoRest Code Generator 0.16.0.0", "t": "source.ruby comment.line.number-sign.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -47,9 +47,9 @@ "c": "#", "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -58,9 +58,9 @@ "c": " Changes may cause incorrect behavior and will be lost if the code is", "t": "source.ruby comment.line.number-sign.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -69,9 +69,9 @@ "c": "#", "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -80,9 +80,9 @@ "c": " regenerated.", "t": "source.ruby comment.line.number-sign.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -179,9 +179,9 @@ "c": "#", "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -201,9 +201,9 @@ "c": "#", "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -212,9 +212,9 @@ "c": " A service client - single point of access to the REST API.", "t": "source.ruby comment.line.number-sign.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -234,9 +234,9 @@ "c": "#", "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -498,9 +498,9 @@ "c": "#", "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -509,9 +509,9 @@ "c": " @return job_collections", "t": "source.ruby comment.line.number-sign.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -586,9 +586,9 @@ "c": "#", "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -608,9 +608,9 @@ "c": "#", "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -619,9 +619,9 @@ "c": " Creates initializes a new instance of the SchedulerManagementClient class.", "t": "source.ruby comment.line.number-sign.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -641,9 +641,9 @@ "c": "#", "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -652,9 +652,9 @@ "c": " @param credentials [MsRest::ServiceClientCredentials] credentials to authorize HTTP requests made by the service client.", "t": "source.ruby comment.line.number-sign.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -674,9 +674,9 @@ "c": "#", "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -685,9 +685,9 @@ "c": " @param base_url [String] the base URI of the service.", "t": "source.ruby comment.line.number-sign.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -707,9 +707,9 @@ "c": "#", "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -718,9 +718,9 @@ "c": " @param options [Array] filters to be applied to the HTTP requests.", "t": "source.ruby comment.line.number-sign.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -740,9 +740,9 @@ "c": "#", "t": "source.ruby comment.line.number-sign.ruby punctuation.definition.comment.ruby", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/scss/test/colorize-results/test_scss.json b/extensions/scss/test/colorize-results/test_scss.json index 9528b311bae..497e8c13fd0 100644 --- a/extensions/scss/test/colorize-results/test_scss.json +++ b/extensions/scss/test/colorize-results/test_scss.json @@ -3,9 +3,9 @@ "c": "//", "t": "source.css.scss comment.line.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -14,9 +14,9 @@ "c": " snippets from the Sass documentation at http://sass-lang.com/", "t": "source.css.scss comment.line.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -25,9 +25,9 @@ "c": "/*", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -36,9 +36,9 @@ "c": " css stuff ", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -47,9 +47,9 @@ "c": "*/", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -58,9 +58,9 @@ "c": "/*", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -69,9 +69,9 @@ "c": " charset ", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -80,9 +80,9 @@ "c": "*/", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -168,9 +168,9 @@ "c": "/*", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -179,9 +179,9 @@ "c": " nested rules ", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -190,9 +190,9 @@ "c": "*/", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -773,9 +773,9 @@ "c": "/*", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -784,9 +784,9 @@ "c": " parent selector (&) ", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -795,9 +795,9 @@ "c": "*/", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1213,9 +1213,9 @@ "c": "/*", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1224,9 +1224,9 @@ "c": " nested properties ", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1235,9 +1235,9 @@ "c": "*/", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1642,9 +1642,9 @@ "c": "/*", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1653,9 +1653,9 @@ "c": " nesting conflicts ", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1664,9 +1664,9 @@ "c": "*/", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1785,9 +1785,9 @@ "c": "//", "t": "source.css.scss meta.property-list.scss meta.property-list.scss comment.line.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1796,9 +1796,9 @@ "c": " properties", "t": "source.css.scss meta.property-list.scss meta.property-list.scss comment.line.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1994,9 +1994,9 @@ "c": "//", "t": "source.css.scss meta.property-list.scss comment.line.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2005,9 +2005,9 @@ "c": " rule", "t": "source.css.scss meta.property-list.scss comment.line.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2093,9 +2093,9 @@ "c": "//", "t": "source.css.scss meta.property-list.scss meta.property-list.scss comment.line.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2104,9 +2104,9 @@ "c": " selector", "t": "source.css.scss meta.property-list.scss meta.property-list.scss comment.line.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2258,9 +2258,9 @@ "c": "//", "t": "source.css.scss meta.property-list.scss meta.property-value.scss comment.line.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2269,9 +2269,9 @@ "c": " selector", "t": "source.css.scss meta.property-list.scss meta.property-value.scss comment.line.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2368,9 +2368,9 @@ "c": "//", "t": "source.css.scss comment.line.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2379,9 +2379,9 @@ "c": " rule", "t": "source.css.scss comment.line.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2401,9 +2401,9 @@ "c": "/*", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2412,9 +2412,9 @@ "c": " extended comment syntax ", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2423,9 +2423,9 @@ "c": "*/", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2434,9 +2434,9 @@ "c": "/*", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2445,9 +2445,9 @@ "c": " This comment is", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2456,9 +2456,9 @@ "c": " * several lines long.", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2467,9 +2467,9 @@ "c": " * since it uses the CSS comment syntax,", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2478,9 +2478,9 @@ "c": " * it will appear in the CSS output. ", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2489,9 +2489,9 @@ "c": "*/", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2621,9 +2621,9 @@ "c": "//", "t": "source.css.scss comment.line.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2632,9 +2632,9 @@ "c": " These comments are only one line long each.", "t": "source.css.scss comment.line.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2643,9 +2643,9 @@ "c": "//", "t": "source.css.scss comment.line.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2654,9 +2654,9 @@ "c": " They won't appear in the CSS output,", "t": "source.css.scss comment.line.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2665,9 +2665,9 @@ "c": "//", "t": "source.css.scss comment.line.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2676,9 +2676,9 @@ "c": " since they use the single-line comment syntax.", "t": "source.css.scss comment.line.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2808,9 +2808,9 @@ "c": "/*", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2819,9 +2819,9 @@ "c": " variables ", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -2830,9 +2830,9 @@ "c": "*/", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3776,9 +3776,9 @@ "c": "/*", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3787,9 +3787,9 @@ "c": " variable declaration with whitespaces ", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3798,9 +3798,9 @@ "c": "*/", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3809,9 +3809,9 @@ "c": "//", "t": "source.css.scss comment.line.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -3820,9 +3820,9 @@ "c": " Set the color of your columns", "t": "source.css.scss comment.line.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -4051,9 +4051,9 @@ "c": "/*", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -4062,9 +4062,9 @@ "c": " operations", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -4073,9 +4073,9 @@ "c": "*/", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -5382,9 +5382,9 @@ "c": "/*", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -5393,9 +5393,9 @@ "c": " functions", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -5404,9 +5404,9 @@ "c": "*/", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -6086,9 +6086,9 @@ "c": "/*", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -6097,9 +6097,9 @@ "c": " @import ", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -6108,9 +6108,9 @@ "c": "*/", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -6636,9 +6636,9 @@ "c": "/*", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -6647,9 +6647,9 @@ "c": " @media ", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -6658,9 +6658,9 @@ "c": "*/", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -7076,9 +7076,9 @@ "c": "/*", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -7087,9 +7087,9 @@ "c": " @extend ", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -7098,9 +7098,9 @@ "c": "*/", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -7989,9 +7989,9 @@ "c": "/*", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -8000,9 +8000,9 @@ "c": " @debug and @warn ", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -8011,9 +8011,9 @@ "c": "*/", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -9166,9 +9166,9 @@ "c": "/*", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -9177,9 +9177,9 @@ "c": " control directives ", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -9188,9 +9188,9 @@ "c": "*/", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -9199,9 +9199,9 @@ "c": "/*", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -9210,9 +9210,9 @@ "c": " if statement ", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -9221,9 +9221,9 @@ "c": "*/", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -9980,9 +9980,9 @@ "c": "/*", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -9991,9 +9991,9 @@ "c": " if else statement ", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -10002,9 +10002,9 @@ "c": "*/", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -10420,9 +10420,9 @@ "c": "/*", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -10431,9 +10431,9 @@ "c": " for statement ", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -10442,9 +10442,9 @@ "c": "*/", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -10849,9 +10849,9 @@ "c": "/*", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -10860,9 +10860,9 @@ "c": " each statement ", "t": "source.css.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -10871,9 +10871,9 @@ "c": "*/", "t": "source.css.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -11278,9 +11278,9 @@ "c": "/*", "t": "source.css.scss meta.at-rule.each.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -11289,9 +11289,9 @@ "c": " while statement ", "t": "source.css.scss meta.at-rule.each.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -11300,9 +11300,9 @@ "c": "*/", "t": "source.css.scss meta.at-rule.each.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -11817,9 +11817,9 @@ "c": "/*", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -11828,9 +11828,9 @@ "c": " function with controlstatements ", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -11839,9 +11839,9 @@ "c": "*/", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -12752,9 +12752,9 @@ "c": "/*", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -12763,9 +12763,9 @@ "c": " @mixin simple", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -12774,9 +12774,9 @@ "c": "*/", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -13412,9 +13412,9 @@ "c": "/*", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -13423,9 +13423,9 @@ "c": " mixin with parameters ", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -13434,9 +13434,9 @@ "c": "*/", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -14039,9 +14039,9 @@ "c": "/*", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -14050,9 +14050,9 @@ "c": " mixin with varargs ", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -14061,9 +14061,9 @@ "c": "*/", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -14787,9 +14787,9 @@ "c": "/*", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -14798,9 +14798,9 @@ "c": " include with varargs ", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -14809,9 +14809,9 @@ "c": "*/", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -15458,9 +15458,9 @@ "c": "/*", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -15469,9 +15469,9 @@ "c": " include with body ", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -15480,9 +15480,9 @@ "c": "*/", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -16129,9 +16129,9 @@ "c": "/*", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -16140,9 +16140,9 @@ "c": " attributes ", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -16151,9 +16151,9 @@ "c": "*/", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -16382,9 +16382,9 @@ "c": "/*", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -16393,9 +16393,9 @@ "c": "page ", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -16404,9 +16404,9 @@ "c": "*/", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -16646,9 +16646,9 @@ "c": "/*", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -16657,9 +16657,9 @@ "c": " missing semicolons ", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -16668,9 +16668,9 @@ "c": "*/", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -17603,9 +17603,9 @@ "c": "/*", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -17614,9 +17614,9 @@ "c": " extend with interpolation variable ", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -17625,9 +17625,9 @@ "c": "*/", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -18351,9 +18351,9 @@ "c": "/*", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -18362,9 +18362,9 @@ "c": " css3: @font face ", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -18373,9 +18373,9 @@ "c": "*/", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -18637,9 +18637,9 @@ "c": "/*", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -18648,9 +18648,9 @@ "c": " rule names with variables ", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -18659,9 +18659,9 @@ "c": "*/", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -19209,9 +19209,9 @@ "c": "/*", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -19220,9 +19220,9 @@ "c": " keyframes ", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -19231,9 +19231,9 @@ "c": "*/", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -20518,9 +20518,9 @@ "c": "/*", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -20529,9 +20529,9 @@ "c": " string escaping ", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -20540,9 +20540,9 @@ "c": "*/", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -20760,9 +20760,9 @@ "c": "/*", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -20771,9 +20771,9 @@ "c": " a comment ", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -20782,9 +20782,9 @@ "c": "*/", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -20947,9 +20947,9 @@ "c": "/*", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -20958,9 +20958,9 @@ "c": " another comment ", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss comment.block.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -20969,9 +20969,9 @@ "c": "*/", "t": "source.css.scss meta.at-rule.each.scss meta.at-rule.while.scss meta.property-list.scss comment.block.scss punctuation.definition.comment.scss", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/search-rg/package.json b/extensions/search-rg/package.json index b149088fcf2..c4621670df8 100644 --- a/extensions/search-rg/package.json +++ b/extensions/search-rg/package.json @@ -13,7 +13,7 @@ }, "categories": [], "dependencies": { - "vscode-extension-telemetry": "0.0.15", + "vscode-extension-telemetry": "0.0.18", "vscode-nls": "^3.2.4", "vscode-ripgrep": "^1.0.1" }, @@ -24,6 +24,7 @@ }, "scripts": {}, "activationEvents": [ + "onSearch:file", "*" ], "main": "./out/extension", diff --git a/extensions/search-rg/src/cachedSearchProvider.ts b/extensions/search-rg/src/cachedSearchProvider.ts deleted file mode 100644 index ec28b6b6a81..00000000000 --- a/extensions/search-rg/src/cachedSearchProvider.ts +++ /dev/null @@ -1,234 +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 path from 'path'; -import * as vscode from 'vscode'; -import * as arrays from './common/arrays'; -import { compareItemsByScore, IItemAccessor, prepareQuery, ScorerCache } from './common/fileSearchScorer'; -import * as strings from './common/strings'; -import { joinPath } from './utils'; - -interface IProviderArgs { - query: vscode.FileSearchQuery; - options: vscode.FileSearchOptions; - progress: vscode.Progress; - token: vscode.CancellationToken; -} - -export interface IInternalFileSearchProvider { - provideFileSearchResults(options: vscode.FileSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable; -} - -export class CachedSearchProvider { - - private static readonly BATCH_SIZE = 512; - - private caches: { [cacheKey: string]: Cache; } = Object.create(null); - - provideFileSearchResults(provider: IInternalFileSearchProvider, query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { - const onResult = (result: IInternalFileMatch) => { - progress.report(joinPath(options.folder, result.relativePath)); - }; - - const providerArgs: IProviderArgs = { - query, options, progress, token - }; - - let sortedSearch = this.trySortedSearchFromCache(providerArgs, onResult); - if (!sortedSearch) { - const engineOpts = options.maxResults ? - { - ...options, - ...{ maxResults: 1e9 } - } : - options; - providerArgs.options = engineOpts; - - sortedSearch = this.doSortedSearch(providerArgs, provider); - } - - return sortedSearch.then(rawMatches => { - rawMatches.forEach(onResult); - }); - } - - private doSortedSearch(args: IProviderArgs, provider: IInternalFileSearchProvider): Promise { - const allResultsPromise = new Promise((c, e) => { - const results: IInternalFileMatch[] = []; - const onResult = (progress: IInternalFileMatch[]) => results.push(...progress); - - // TODO@roblou set maxResult = null - this.doSearch(args, provider, onResult, CachedSearchProvider.BATCH_SIZE) - .then(() => c(results), e); - }); - - let cache: Cache; - if (args.query.cacheKey) { - cache = this.getOrCreateCache(args.query.cacheKey); // TODO include folder in cache key - cache.resultsToSearchCache[args.query.pattern] = { finished: allResultsPromise }; - allResultsPromise.then(null, err => { - delete cache.resultsToSearchCache[args.query.pattern]; - }); - } - - return allResultsPromise.then(results => { - // TODO@roblou quickopen results are not scored until the first keypress - if (args.query.pattern) { - const scorerCache: ScorerCache = cache ? cache.scorerCache : Object.create(null); - return this.sortResults(args, results, scorerCache); - } else { - return results; - } - }); - } - - private getOrCreateCache(cacheKey: string): Cache { - const existing = this.caches[cacheKey]; - if (existing) { - return existing; - } - return this.caches[cacheKey] = new Cache(); - } - - private trySortedSearchFromCache(args: IProviderArgs, onResult: (result: IInternalFileMatch) => void): Promise { - const cache = args.query.cacheKey && this.caches[args.query.cacheKey]; - if (!cache) { - return undefined; - } - - const cached = this.getResultsFromCache(cache, args.query.pattern, onResult); - if (cached) { - return cached.then((results) => this.sortResults(args, results, cache.scorerCache)); - } - - return undefined; - } - - private sortResults(args: IProviderArgs, results: IInternalFileMatch[], scorerCache: ScorerCache): Promise { - // we use the same compare function that is used later when showing the results using fuzzy scoring - // this is very important because we are also limiting the number of results by config.maxResults - // and as such we want the top items to be included in this result set if the number of items - // exceeds config.maxResults. - const preparedQuery = prepareQuery(args.query.pattern); - const compare = (matchA: IInternalFileMatch, matchB: IInternalFileMatch) => compareItemsByScore(matchA, matchB, preparedQuery, true, FileMatchItemAccessor, scorerCache); - - return arrays.topAsync(results, compare, args.options.maxResults || 0, 10000); - } - - private getResultsFromCache(cache: Cache, searchValue: string, onResult: (results: IInternalFileMatch) => void): Promise { - // Find cache entries by prefix of search value - const hasPathSep = searchValue.indexOf(path.sep) >= 0; - let cached: CacheEntry; - let wasResolved: boolean; - for (let previousSearch in cache.resultsToSearchCache) { - // If we narrow down, we might be able to reuse the cached results - if (searchValue.startsWith(previousSearch)) { - if (hasPathSep && previousSearch.indexOf(path.sep) < 0) { - continue; // since a path character widens the search for potential more matches, require it in previous search too - } - - const c = cache.resultsToSearchCache[previousSearch]; - c.finished.then(() => { wasResolved = false; }); - cached = c; - wasResolved = true; - break; - } - } - - if (!cached) { - return null; - } - - return new Promise((c, e) => { - cached.finished.then(cachedEntries => { - const cacheFilterStartTime = Date.now(); - - // Pattern match on results - let results: IInternalFileMatch[] = []; - const normalizedSearchValueLowercase = strings.stripWildcards(searchValue).toLowerCase(); - for (let i = 0; i < cachedEntries.length; i++) { - let entry = cachedEntries[i]; - - // Check if this entry is a match for the search value - if (!strings.fuzzyContains(entry.relativePath, normalizedSearchValueLowercase)) { - continue; - } - - results.push(entry); - } - - c(results); - }, e); - }); - } - - private doSearch(args: IProviderArgs, provider: IInternalFileSearchProvider, onResult: (result: IInternalFileMatch[]) => void, batchSize: number): Promise { - return new Promise((c, e) => { - let batch: IInternalFileMatch[] = []; - const onProviderResult = (match: string) => { - if (match) { - const internalMatch: IInternalFileMatch = { - relativePath: match, - basename: path.basename(match) - }; - - batch.push(internalMatch); - if (batchSize > 0 && batch.length >= batchSize) { - onResult(batch); - batch = []; - } - } - }; - - provider.provideFileSearchResults(args.options, { report: onProviderResult }, args.token).then(() => { - if (batch.length) { - onResult(batch); - } - - c(); - }, error => { - if (batch.length) { - onResult(batch); - } - - e(error); - }); - }); - } - - public clearCache(cacheKey: string): Promise { - delete this.caches[cacheKey]; - return Promise.resolve(undefined); - } -} - -interface IInternalFileMatch { - relativePath?: string; // Not present for extraFiles or absolute path matches - basename: string; -} - -interface CacheEntry { - finished: Promise; -} - -class Cache { - public resultsToSearchCache: { [searchValue: string]: CacheEntry } = Object.create(null); - public scorerCache: ScorerCache = Object.create(null); -} - -const FileMatchItemAccessor = new class implements IItemAccessor { - - public getItemLabel(match: IInternalFileMatch): string { - return match.basename; // e.g. myFile.txt - } - - public getItemDescription(match: IInternalFileMatch): string { - return match.relativePath.substr(0, match.relativePath.length - match.basename.length - 1); // e.g. some/path/to/file - } - - public getItemPath(match: IInternalFileMatch): string { - return match.relativePath; // e.g. some/path/to/file/myFile.txt - } -}; diff --git a/extensions/search-rg/src/common/arrays.ts b/extensions/search-rg/src/common/arrays.ts deleted file mode 100644 index d06d185dfa5..00000000000 --- a/extensions/search-rg/src/common/arrays.ts +++ /dev/null @@ -1,75 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/** - * Asynchronous variant of `top()` allowing for splitting up work in batches between which the event loop can run. - * - * Returns the top N elements from the array. - * - * Faster than sorting the entire array when the array is a lot larger than N. - * - * @param array The unsorted array. - * @param compare A sort function for the elements. - * @param n The number of elements to return. - * @param batch The number of elements to examine before yielding to the event loop. - * @return The first n elemnts from array when sorted with compare. - */ -export function topAsync(array: T[], compare: (a: T, b: T) => number, n: number, batch: number): Promise { - // TODO@roblou cancellation - - if (n === 0) { - return Promise.resolve([]); - } - let canceled = false; - return new Promise((resolve, reject) => { - (async () => { - const o = array.length; - const result = array.slice(0, n).sort(compare); - for (let i = n, m = Math.min(n + batch, o); i < o; i = m, m = Math.min(m + batch, o)) { - if (i > n) { - await new Promise(resolve => setTimeout(resolve, 0)); // nextTick() would starve I/O. - } - if (canceled) { - throw new Error('canceled'); - } - topStep(array, compare, result, i, m); - } - return result; - })() - .then(resolve, reject); - }); -} - -function topStep(array: T[], compare: (a: T, b: T) => number, result: T[], i: number, m: number): void { - for (const n = result.length; i < m; i++) { - const element = array[i]; - if (compare(element, result[n - 1]) < 0) { - result.pop(); - const j = findFirstInSorted(result, e => compare(element, e) < 0); - result.splice(j, 0, element); - } - } -} - -/** - * Takes a sorted array and a function p. The array is sorted in such a way that all elements where p(x) is false - * are located before all elements where p(x) is true. - * @returns the least x for which p(x) is true or array.length if no element fullfills the given function. - */ -export function findFirstInSorted(array: T[], p: (x: T) => boolean): number { - let low = 0, high = array.length; - if (high === 0) { - return 0; // no children - } - while (low < high) { - let mid = Math.floor((low + high) / 2); - if (p(array[mid])) { - high = mid; - } else { - low = mid + 1; - } - } - return low; -} diff --git a/extensions/search-rg/src/common/charCode.ts b/extensions/search-rg/src/common/charCode.ts deleted file mode 100644 index dd1bc58f80b..00000000000 --- a/extensions/search-rg/src/common/charCode.ts +++ /dev/null @@ -1,422 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -// Names from https://blog.codinghorror.com/ascii-pronunciation-rules-for-programmers/ - -/** - * An inlined enum containing useful character codes (to be used with String.charCodeAt). - * Please leave the const keyword such that it gets inlined when compiled to JavaScript! - */ -export const enum CharCode { - Null = 0, - /** - * The `\t` character. - */ - Tab = 9, - /** - * The `\n` character. - */ - LineFeed = 10, - /** - * The `\r` character. - */ - CarriageReturn = 13, - Space = 32, - /** - * The `!` character. - */ - ExclamationMark = 33, - /** - * The `"` character. - */ - DoubleQuote = 34, - /** - * The `#` character. - */ - Hash = 35, - /** - * The `$` character. - */ - DollarSign = 36, - /** - * The `%` character. - */ - PercentSign = 37, - /** - * The `&` character. - */ - Ampersand = 38, - /** - * The `'` character. - */ - SingleQuote = 39, - /** - * The `(` character. - */ - OpenParen = 40, - /** - * The `)` character. - */ - CloseParen = 41, - /** - * The `*` character. - */ - Asterisk = 42, - /** - * The `+` character. - */ - Plus = 43, - /** - * The `,` character. - */ - Comma = 44, - /** - * The `-` character. - */ - Dash = 45, - /** - * The `.` character. - */ - Period = 46, - /** - * The `/` character. - */ - Slash = 47, - - Digit0 = 48, - Digit1 = 49, - Digit2 = 50, - Digit3 = 51, - Digit4 = 52, - Digit5 = 53, - Digit6 = 54, - Digit7 = 55, - Digit8 = 56, - Digit9 = 57, - - /** - * The `:` character. - */ - Colon = 58, - /** - * The `;` character. - */ - Semicolon = 59, - /** - * The `<` character. - */ - LessThan = 60, - /** - * The `=` character. - */ - Equals = 61, - /** - * The `>` character. - */ - GreaterThan = 62, - /** - * The `?` character. - */ - QuestionMark = 63, - /** - * The `@` character. - */ - AtSign = 64, - - A = 65, - B = 66, - C = 67, - D = 68, - E = 69, - F = 70, - G = 71, - H = 72, - I = 73, - J = 74, - K = 75, - L = 76, - M = 77, - N = 78, - O = 79, - P = 80, - Q = 81, - R = 82, - S = 83, - T = 84, - U = 85, - V = 86, - W = 87, - X = 88, - Y = 89, - Z = 90, - - /** - * The `[` character. - */ - OpenSquareBracket = 91, - /** - * The `\` character. - */ - Backslash = 92, - /** - * The `]` character. - */ - CloseSquareBracket = 93, - /** - * The `^` character. - */ - Caret = 94, - /** - * The `_` character. - */ - Underline = 95, - /** - * The ``(`)`` character. - */ - BackTick = 96, - - a = 97, - b = 98, - c = 99, - d = 100, - e = 101, - f = 102, - g = 103, - h = 104, - i = 105, - j = 106, - k = 107, - l = 108, - m = 109, - n = 110, - o = 111, - p = 112, - q = 113, - r = 114, - s = 115, - t = 116, - u = 117, - v = 118, - w = 119, - x = 120, - y = 121, - z = 122, - - /** - * The `{` character. - */ - OpenCurlyBrace = 123, - /** - * The `|` character. - */ - Pipe = 124, - /** - * The `}` character. - */ - CloseCurlyBrace = 125, - /** - * The `~` character. - */ - Tilde = 126, - - U_Combining_Grave_Accent = 0x0300, // U+0300 Combining Grave Accent - U_Combining_Acute_Accent = 0x0301, // U+0301 Combining Acute Accent - U_Combining_Circumflex_Accent = 0x0302, // U+0302 Combining Circumflex Accent - U_Combining_Tilde = 0x0303, // U+0303 Combining Tilde - U_Combining_Macron = 0x0304, // U+0304 Combining Macron - U_Combining_Overline = 0x0305, // U+0305 Combining Overline - U_Combining_Breve = 0x0306, // U+0306 Combining Breve - U_Combining_Dot_Above = 0x0307, // U+0307 Combining Dot Above - U_Combining_Diaeresis = 0x0308, // U+0308 Combining Diaeresis - U_Combining_Hook_Above = 0x0309, // U+0309 Combining Hook Above - U_Combining_Ring_Above = 0x030A, // U+030A Combining Ring Above - U_Combining_Double_Acute_Accent = 0x030B, // U+030B Combining Double Acute Accent - U_Combining_Caron = 0x030C, // U+030C Combining Caron - U_Combining_Vertical_Line_Above = 0x030D, // U+030D Combining Vertical Line Above - U_Combining_Double_Vertical_Line_Above = 0x030E, // U+030E Combining Double Vertical Line Above - U_Combining_Double_Grave_Accent = 0x030F, // U+030F Combining Double Grave Accent - U_Combining_Candrabindu = 0x0310, // U+0310 Combining Candrabindu - U_Combining_Inverted_Breve = 0x0311, // U+0311 Combining Inverted Breve - U_Combining_Turned_Comma_Above = 0x0312, // U+0312 Combining Turned Comma Above - U_Combining_Comma_Above = 0x0313, // U+0313 Combining Comma Above - U_Combining_Reversed_Comma_Above = 0x0314, // U+0314 Combining Reversed Comma Above - U_Combining_Comma_Above_Right = 0x0315, // U+0315 Combining Comma Above Right - U_Combining_Grave_Accent_Below = 0x0316, // U+0316 Combining Grave Accent Below - U_Combining_Acute_Accent_Below = 0x0317, // U+0317 Combining Acute Accent Below - U_Combining_Left_Tack_Below = 0x0318, // U+0318 Combining Left Tack Below - U_Combining_Right_Tack_Below = 0x0319, // U+0319 Combining Right Tack Below - U_Combining_Left_Angle_Above = 0x031A, // U+031A Combining Left Angle Above - U_Combining_Horn = 0x031B, // U+031B Combining Horn - U_Combining_Left_Half_Ring_Below = 0x031C, // U+031C Combining Left Half Ring Below - U_Combining_Up_Tack_Below = 0x031D, // U+031D Combining Up Tack Below - U_Combining_Down_Tack_Below = 0x031E, // U+031E Combining Down Tack Below - U_Combining_Plus_Sign_Below = 0x031F, // U+031F Combining Plus Sign Below - U_Combining_Minus_Sign_Below = 0x0320, // U+0320 Combining Minus Sign Below - U_Combining_Palatalized_Hook_Below = 0x0321, // U+0321 Combining Palatalized Hook Below - U_Combining_Retroflex_Hook_Below = 0x0322, // U+0322 Combining Retroflex Hook Below - U_Combining_Dot_Below = 0x0323, // U+0323 Combining Dot Below - U_Combining_Diaeresis_Below = 0x0324, // U+0324 Combining Diaeresis Below - U_Combining_Ring_Below = 0x0325, // U+0325 Combining Ring Below - U_Combining_Comma_Below = 0x0326, // U+0326 Combining Comma Below - U_Combining_Cedilla = 0x0327, // U+0327 Combining Cedilla - U_Combining_Ogonek = 0x0328, // U+0328 Combining Ogonek - U_Combining_Vertical_Line_Below = 0x0329, // U+0329 Combining Vertical Line Below - U_Combining_Bridge_Below = 0x032A, // U+032A Combining Bridge Below - U_Combining_Inverted_Double_Arch_Below = 0x032B, // U+032B Combining Inverted Double Arch Below - U_Combining_Caron_Below = 0x032C, // U+032C Combining Caron Below - U_Combining_Circumflex_Accent_Below = 0x032D, // U+032D Combining Circumflex Accent Below - U_Combining_Breve_Below = 0x032E, // U+032E Combining Breve Below - U_Combining_Inverted_Breve_Below = 0x032F, // U+032F Combining Inverted Breve Below - U_Combining_Tilde_Below = 0x0330, // U+0330 Combining Tilde Below - U_Combining_Macron_Below = 0x0331, // U+0331 Combining Macron Below - U_Combining_Low_Line = 0x0332, // U+0332 Combining Low Line - U_Combining_Double_Low_Line = 0x0333, // U+0333 Combining Double Low Line - U_Combining_Tilde_Overlay = 0x0334, // U+0334 Combining Tilde Overlay - U_Combining_Short_Stroke_Overlay = 0x0335, // U+0335 Combining Short Stroke Overlay - U_Combining_Long_Stroke_Overlay = 0x0336, // U+0336 Combining Long Stroke Overlay - U_Combining_Short_Solidus_Overlay = 0x0337, // U+0337 Combining Short Solidus Overlay - U_Combining_Long_Solidus_Overlay = 0x0338, // U+0338 Combining Long Solidus Overlay - U_Combining_Right_Half_Ring_Below = 0x0339, // U+0339 Combining Right Half Ring Below - U_Combining_Inverted_Bridge_Below = 0x033A, // U+033A Combining Inverted Bridge Below - U_Combining_Square_Below = 0x033B, // U+033B Combining Square Below - U_Combining_Seagull_Below = 0x033C, // U+033C Combining Seagull Below - U_Combining_X_Above = 0x033D, // U+033D Combining X Above - U_Combining_Vertical_Tilde = 0x033E, // U+033E Combining Vertical Tilde - U_Combining_Double_Overline = 0x033F, // U+033F Combining Double Overline - U_Combining_Grave_Tone_Mark = 0x0340, // U+0340 Combining Grave Tone Mark - U_Combining_Acute_Tone_Mark = 0x0341, // U+0341 Combining Acute Tone Mark - U_Combining_Greek_Perispomeni = 0x0342, // U+0342 Combining Greek Perispomeni - U_Combining_Greek_Koronis = 0x0343, // U+0343 Combining Greek Koronis - U_Combining_Greek_Dialytika_Tonos = 0x0344, // U+0344 Combining Greek Dialytika Tonos - U_Combining_Greek_Ypogegrammeni = 0x0345, // U+0345 Combining Greek Ypogegrammeni - U_Combining_Bridge_Above = 0x0346, // U+0346 Combining Bridge Above - U_Combining_Equals_Sign_Below = 0x0347, // U+0347 Combining Equals Sign Below - U_Combining_Double_Vertical_Line_Below = 0x0348, // U+0348 Combining Double Vertical Line Below - U_Combining_Left_Angle_Below = 0x0349, // U+0349 Combining Left Angle Below - U_Combining_Not_Tilde_Above = 0x034A, // U+034A Combining Not Tilde Above - U_Combining_Homothetic_Above = 0x034B, // U+034B Combining Homothetic Above - U_Combining_Almost_Equal_To_Above = 0x034C, // U+034C Combining Almost Equal To Above - U_Combining_Left_Right_Arrow_Below = 0x034D, // U+034D Combining Left Right Arrow Below - U_Combining_Upwards_Arrow_Below = 0x034E, // U+034E Combining Upwards Arrow Below - U_Combining_Grapheme_Joiner = 0x034F, // U+034F Combining Grapheme Joiner - U_Combining_Right_Arrowhead_Above = 0x0350, // U+0350 Combining Right Arrowhead Above - U_Combining_Left_Half_Ring_Above = 0x0351, // U+0351 Combining Left Half Ring Above - U_Combining_Fermata = 0x0352, // U+0352 Combining Fermata - U_Combining_X_Below = 0x0353, // U+0353 Combining X Below - U_Combining_Left_Arrowhead_Below = 0x0354, // U+0354 Combining Left Arrowhead Below - U_Combining_Right_Arrowhead_Below = 0x0355, // U+0355 Combining Right Arrowhead Below - U_Combining_Right_Arrowhead_And_Up_Arrowhead_Below = 0x0356, // U+0356 Combining Right Arrowhead And Up Arrowhead Below - U_Combining_Right_Half_Ring_Above = 0x0357, // U+0357 Combining Right Half Ring Above - U_Combining_Dot_Above_Right = 0x0358, // U+0358 Combining Dot Above Right - U_Combining_Asterisk_Below = 0x0359, // U+0359 Combining Asterisk Below - U_Combining_Double_Ring_Below = 0x035A, // U+035A Combining Double Ring Below - U_Combining_Zigzag_Above = 0x035B, // U+035B Combining Zigzag Above - U_Combining_Double_Breve_Below = 0x035C, // U+035C Combining Double Breve Below - U_Combining_Double_Breve = 0x035D, // U+035D Combining Double Breve - U_Combining_Double_Macron = 0x035E, // U+035E Combining Double Macron - U_Combining_Double_Macron_Below = 0x035F, // U+035F Combining Double Macron Below - U_Combining_Double_Tilde = 0x0360, // U+0360 Combining Double Tilde - U_Combining_Double_Inverted_Breve = 0x0361, // U+0361 Combining Double Inverted Breve - U_Combining_Double_Rightwards_Arrow_Below = 0x0362, // U+0362 Combining Double Rightwards Arrow Below - U_Combining_Latin_Small_Letter_A = 0x0363, // U+0363 Combining Latin Small Letter A - U_Combining_Latin_Small_Letter_E = 0x0364, // U+0364 Combining Latin Small Letter E - U_Combining_Latin_Small_Letter_I = 0x0365, // U+0365 Combining Latin Small Letter I - U_Combining_Latin_Small_Letter_O = 0x0366, // U+0366 Combining Latin Small Letter O - U_Combining_Latin_Small_Letter_U = 0x0367, // U+0367 Combining Latin Small Letter U - U_Combining_Latin_Small_Letter_C = 0x0368, // U+0368 Combining Latin Small Letter C - U_Combining_Latin_Small_Letter_D = 0x0369, // U+0369 Combining Latin Small Letter D - U_Combining_Latin_Small_Letter_H = 0x036A, // U+036A Combining Latin Small Letter H - U_Combining_Latin_Small_Letter_M = 0x036B, // U+036B Combining Latin Small Letter M - U_Combining_Latin_Small_Letter_R = 0x036C, // U+036C Combining Latin Small Letter R - U_Combining_Latin_Small_Letter_T = 0x036D, // U+036D Combining Latin Small Letter T - U_Combining_Latin_Small_Letter_V = 0x036E, // U+036E Combining Latin Small Letter V - U_Combining_Latin_Small_Letter_X = 0x036F, // U+036F Combining Latin Small Letter X - - /** - * Unicode Character 'LINE SEPARATOR' (U+2028) - * http://www.fileformat.info/info/unicode/char/2028/index.htm - */ - LINE_SEPARATOR_2028 = 8232, - - // http://www.fileformat.info/info/unicode/category/Sk/list.htm - U_CIRCUMFLEX = 0x005E, // U+005E CIRCUMFLEX - U_GRAVE_ACCENT = 0x0060, // U+0060 GRAVE ACCENT - U_DIAERESIS = 0x00A8, // U+00A8 DIAERESIS - U_MACRON = 0x00AF, // U+00AF MACRON - U_ACUTE_ACCENT = 0x00B4, // U+00B4 ACUTE ACCENT - U_CEDILLA = 0x00B8, // U+00B8 CEDILLA - U_MODIFIER_LETTER_LEFT_ARROWHEAD = 0x02C2, // U+02C2 MODIFIER LETTER LEFT ARROWHEAD - U_MODIFIER_LETTER_RIGHT_ARROWHEAD = 0x02C3, // U+02C3 MODIFIER LETTER RIGHT ARROWHEAD - U_MODIFIER_LETTER_UP_ARROWHEAD = 0x02C4, // U+02C4 MODIFIER LETTER UP ARROWHEAD - U_MODIFIER_LETTER_DOWN_ARROWHEAD = 0x02C5, // U+02C5 MODIFIER LETTER DOWN ARROWHEAD - U_MODIFIER_LETTER_CENTRED_RIGHT_HALF_RING = 0x02D2, // U+02D2 MODIFIER LETTER CENTRED RIGHT HALF RING - U_MODIFIER_LETTER_CENTRED_LEFT_HALF_RING = 0x02D3, // U+02D3 MODIFIER LETTER CENTRED LEFT HALF RING - U_MODIFIER_LETTER_UP_TACK = 0x02D4, // U+02D4 MODIFIER LETTER UP TACK - U_MODIFIER_LETTER_DOWN_TACK = 0x02D5, // U+02D5 MODIFIER LETTER DOWN TACK - U_MODIFIER_LETTER_PLUS_SIGN = 0x02D6, // U+02D6 MODIFIER LETTER PLUS SIGN - U_MODIFIER_LETTER_MINUS_SIGN = 0x02D7, // U+02D7 MODIFIER LETTER MINUS SIGN - U_BREVE = 0x02D8, // U+02D8 BREVE - U_DOT_ABOVE = 0x02D9, // U+02D9 DOT ABOVE - U_RING_ABOVE = 0x02DA, // U+02DA RING ABOVE - U_OGONEK = 0x02DB, // U+02DB OGONEK - U_SMALL_TILDE = 0x02DC, // U+02DC SMALL TILDE - U_DOUBLE_ACUTE_ACCENT = 0x02DD, // U+02DD DOUBLE ACUTE ACCENT - U_MODIFIER_LETTER_RHOTIC_HOOK = 0x02DE, // U+02DE MODIFIER LETTER RHOTIC HOOK - U_MODIFIER_LETTER_CROSS_ACCENT = 0x02DF, // U+02DF MODIFIER LETTER CROSS ACCENT - U_MODIFIER_LETTER_EXTRA_HIGH_TONE_BAR = 0x02E5, // U+02E5 MODIFIER LETTER EXTRA-HIGH TONE BAR - U_MODIFIER_LETTER_HIGH_TONE_BAR = 0x02E6, // U+02E6 MODIFIER LETTER HIGH TONE BAR - U_MODIFIER_LETTER_MID_TONE_BAR = 0x02E7, // U+02E7 MODIFIER LETTER MID TONE BAR - U_MODIFIER_LETTER_LOW_TONE_BAR = 0x02E8, // U+02E8 MODIFIER LETTER LOW TONE BAR - U_MODIFIER_LETTER_EXTRA_LOW_TONE_BAR = 0x02E9, // U+02E9 MODIFIER LETTER EXTRA-LOW TONE BAR - U_MODIFIER_LETTER_YIN_DEPARTING_TONE_MARK = 0x02EA, // U+02EA MODIFIER LETTER YIN DEPARTING TONE MARK - U_MODIFIER_LETTER_YANG_DEPARTING_TONE_MARK = 0x02EB, // U+02EB MODIFIER LETTER YANG DEPARTING TONE MARK - U_MODIFIER_LETTER_UNASPIRATED = 0x02ED, // U+02ED MODIFIER LETTER UNASPIRATED - U_MODIFIER_LETTER_LOW_DOWN_ARROWHEAD = 0x02EF, // U+02EF MODIFIER LETTER LOW DOWN ARROWHEAD - U_MODIFIER_LETTER_LOW_UP_ARROWHEAD = 0x02F0, // U+02F0 MODIFIER LETTER LOW UP ARROWHEAD - U_MODIFIER_LETTER_LOW_LEFT_ARROWHEAD = 0x02F1, // U+02F1 MODIFIER LETTER LOW LEFT ARROWHEAD - U_MODIFIER_LETTER_LOW_RIGHT_ARROWHEAD = 0x02F2, // U+02F2 MODIFIER LETTER LOW RIGHT ARROWHEAD - U_MODIFIER_LETTER_LOW_RING = 0x02F3, // U+02F3 MODIFIER LETTER LOW RING - U_MODIFIER_LETTER_MIDDLE_GRAVE_ACCENT = 0x02F4, // U+02F4 MODIFIER LETTER MIDDLE GRAVE ACCENT - U_MODIFIER_LETTER_MIDDLE_DOUBLE_GRAVE_ACCENT = 0x02F5, // U+02F5 MODIFIER LETTER MIDDLE DOUBLE GRAVE ACCENT - U_MODIFIER_LETTER_MIDDLE_DOUBLE_ACUTE_ACCENT = 0x02F6, // U+02F6 MODIFIER LETTER MIDDLE DOUBLE ACUTE ACCENT - U_MODIFIER_LETTER_LOW_TILDE = 0x02F7, // U+02F7 MODIFIER LETTER LOW TILDE - U_MODIFIER_LETTER_RAISED_COLON = 0x02F8, // U+02F8 MODIFIER LETTER RAISED COLON - U_MODIFIER_LETTER_BEGIN_HIGH_TONE = 0x02F9, // U+02F9 MODIFIER LETTER BEGIN HIGH TONE - U_MODIFIER_LETTER_END_HIGH_TONE = 0x02FA, // U+02FA MODIFIER LETTER END HIGH TONE - U_MODIFIER_LETTER_BEGIN_LOW_TONE = 0x02FB, // U+02FB MODIFIER LETTER BEGIN LOW TONE - U_MODIFIER_LETTER_END_LOW_TONE = 0x02FC, // U+02FC MODIFIER LETTER END LOW TONE - U_MODIFIER_LETTER_SHELF = 0x02FD, // U+02FD MODIFIER LETTER SHELF - U_MODIFIER_LETTER_OPEN_SHELF = 0x02FE, // U+02FE MODIFIER LETTER OPEN SHELF - U_MODIFIER_LETTER_LOW_LEFT_ARROW = 0x02FF, // U+02FF MODIFIER LETTER LOW LEFT ARROW - U_GREEK_LOWER_NUMERAL_SIGN = 0x0375, // U+0375 GREEK LOWER NUMERAL SIGN - U_GREEK_TONOS = 0x0384, // U+0384 GREEK TONOS - U_GREEK_DIALYTIKA_TONOS = 0x0385, // U+0385 GREEK DIALYTIKA TONOS - U_GREEK_KORONIS = 0x1FBD, // U+1FBD GREEK KORONIS - U_GREEK_PSILI = 0x1FBF, // U+1FBF GREEK PSILI - U_GREEK_PERISPOMENI = 0x1FC0, // U+1FC0 GREEK PERISPOMENI - U_GREEK_DIALYTIKA_AND_PERISPOMENI = 0x1FC1, // U+1FC1 GREEK DIALYTIKA AND PERISPOMENI - U_GREEK_PSILI_AND_VARIA = 0x1FCD, // U+1FCD GREEK PSILI AND VARIA - U_GREEK_PSILI_AND_OXIA = 0x1FCE, // U+1FCE GREEK PSILI AND OXIA - U_GREEK_PSILI_AND_PERISPOMENI = 0x1FCF, // U+1FCF GREEK PSILI AND PERISPOMENI - U_GREEK_DASIA_AND_VARIA = 0x1FDD, // U+1FDD GREEK DASIA AND VARIA - U_GREEK_DASIA_AND_OXIA = 0x1FDE, // U+1FDE GREEK DASIA AND OXIA - U_GREEK_DASIA_AND_PERISPOMENI = 0x1FDF, // U+1FDF GREEK DASIA AND PERISPOMENI - U_GREEK_DIALYTIKA_AND_VARIA = 0x1FED, // U+1FED GREEK DIALYTIKA AND VARIA - U_GREEK_DIALYTIKA_AND_OXIA = 0x1FEE, // U+1FEE GREEK DIALYTIKA AND OXIA - U_GREEK_VARIA = 0x1FEF, // U+1FEF GREEK VARIA - U_GREEK_OXIA = 0x1FFD, // U+1FFD GREEK OXIA - U_GREEK_DASIA = 0x1FFE, // U+1FFE GREEK DASIA - - - U_OVERLINE = 0x203E, // Unicode Character 'OVERLINE' - - /** - * UTF-8 BOM - * Unicode Character 'ZERO WIDTH NO-BREAK SPACE' (U+FEFF) - * http://www.fileformat.info/info/unicode/char/feff/index.htm - */ - UTF8_BOM = 65279 -} diff --git a/extensions/search-rg/src/common/comparers.ts b/extensions/search-rg/src/common/comparers.ts deleted file mode 100644 index fc6022526db..00000000000 --- a/extensions/search-rg/src/common/comparers.ts +++ /dev/null @@ -1,115 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import * as strings from './strings'; - -let intlFileNameCollator: Intl.Collator; -let intlFileNameCollatorIsNumeric: boolean; - -setFileNameComparer(new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' })); - -export function setFileNameComparer(collator: Intl.Collator): void { - intlFileNameCollator = collator; - intlFileNameCollatorIsNumeric = collator.resolvedOptions().numeric; -} - -export function compareFileNames(one: string, other: string, caseSensitive = false): number { - if (intlFileNameCollator) { - const a = one || ''; - const b = other || ''; - const result = intlFileNameCollator.compare(a, b); - - // Using the numeric option in the collator will - // make compare(`foo1`, `foo01`) === 0. We must disambiguate. - if (intlFileNameCollatorIsNumeric && result === 0 && a !== b) { - return a < b ? -1 : 1; - } - - return result; - } - - return noIntlCompareFileNames(one, other, caseSensitive); -} - -const FileNameMatch = /^(.*?)(\.([^.]*))?$/; - -export function noIntlCompareFileNames(one: string, other: string, caseSensitive = false): number { - if (!caseSensitive) { - one = one && one.toLowerCase(); - other = other && other.toLowerCase(); - } - - const [oneName, oneExtension] = extractNameAndExtension(one); - const [otherName, otherExtension] = extractNameAndExtension(other); - - if (oneName !== otherName) { - return oneName < otherName ? -1 : 1; - } - - if (oneExtension === otherExtension) { - return 0; - } - - return oneExtension < otherExtension ? -1 : 1; -} - -function extractNameAndExtension(str?: string): [string, string] { - const match = str ? FileNameMatch.exec(str) : [] as RegExpExecArray; - - return [(match && match[1]) || '', (match && match[3]) || '']; -} - -export function compareAnything(one: string, other: string, lookFor: string): number { - let elementAName = one.toLowerCase(); - let elementBName = other.toLowerCase(); - - // Sort prefix matches over non prefix matches - const prefixCompare = compareByPrefix(one, other, lookFor); - if (prefixCompare) { - return prefixCompare; - } - - // Sort suffix matches over non suffix matches - let elementASuffixMatch = strings.endsWith(elementAName, lookFor); - let elementBSuffixMatch = strings.endsWith(elementBName, lookFor); - if (elementASuffixMatch !== elementBSuffixMatch) { - return elementASuffixMatch ? -1 : 1; - } - - // Understand file names - let r = compareFileNames(elementAName, elementBName); - if (r !== 0) { - return r; - } - - // Compare by name - return elementAName.localeCompare(elementBName); -} - -export function compareByPrefix(one: string, other: string, lookFor: string): number { - let elementAName = one.toLowerCase(); - let elementBName = other.toLowerCase(); - - // Sort prefix matches over non prefix matches - let elementAPrefixMatch = strings.startsWith(elementAName, lookFor); - let elementBPrefixMatch = strings.startsWith(elementBName, lookFor); - if (elementAPrefixMatch !== elementBPrefixMatch) { - return elementAPrefixMatch ? -1 : 1; - } - - // Same prefix: Sort shorter matches to the top to have those on top that match more precisely - else if (elementAPrefixMatch && elementBPrefixMatch) { - if (elementAName.length < elementBName.length) { - return -1; - } - - if (elementAName.length > elementBName.length) { - return 1; - } - } - - return 0; -} diff --git a/extensions/search-rg/src/common/fileSearchScorer.ts b/extensions/search-rg/src/common/fileSearchScorer.ts deleted file mode 100644 index d73d7c77f41..00000000000 --- a/extensions/search-rg/src/common/fileSearchScorer.ts +++ /dev/null @@ -1,619 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -'use strict'; - -import { stripWildcards, equalsIgnoreCase } from './strings'; -import { matchesPrefix, matchesCamelCase, createMatches, IMatch, isUpper } from './filters'; -import { compareAnything } from './comparers'; -import { CharCode } from './charCode'; - -const isWindows = process.platform === 'win32'; -const isMacintosh = (process.platform === 'darwin'); -const isLinux = (process.platform === 'linux'); - -const nativeSep = isWindows ? '\\' : '/'; - -export type Score = [number /* score */, number[] /* match positions */]; -export type ScorerCache = { [key: string]: IItemScore }; - -const NO_MATCH = 0; -const NO_SCORE: Score = [NO_MATCH, []]; - -// const DEBUG = false; -// const DEBUG_MATRIX = false; - -export function score(target: string, query: string, queryLower: string, fuzzy: boolean): Score { - if (!target || !query) { - return NO_SCORE; // return early if target or query are undefined - } - - const targetLength = target.length; - const queryLength = query.length; - - if (targetLength < queryLength) { - return NO_SCORE; // impossible for query to be contained in target - } - - // if (DEBUG) { - // console.group(`Target: ${target}, Query: ${query}`); - // } - - const targetLower = target.toLowerCase(); - - // When not searching fuzzy, we require the query to be contained fully - // in the target string contiguously. - if (!fuzzy) { - const indexOfQueryInTarget = targetLower.indexOf(queryLower); - if (indexOfQueryInTarget === -1) { - // if (DEBUG) { - // console.log(`Characters not matching consecutively ${queryLower} within ${targetLower}`); - // } - - return NO_SCORE; - } - } - - const res = doScore(query, queryLower, queryLength, target, targetLower, targetLength); - - // if (DEBUG) { - // console.log(`%cFinal Score: ${res[0]}`, 'font-weight: bold'); - // console.groupEnd(); - // } - - return res; -} - -function doScore(query: string, queryLower: string, queryLength: number, target: string, targetLower: string, targetLength: number): [number, number[]] { - const scores = []; - const matches = []; - - // - // Build Scorer Matrix: - // - // The matrix is composed of query q and target t. For each index we score - // q[i] with t[i] and compare that with the previous score. If the score is - // equal or larger, we keep the match. In addition to the score, we also keep - // the length of the consecutive matches to use as boost for the score. - // - // t a r g e t - // q - // u - // e - // r - // y - // - for (let queryIndex = 0; queryIndex < queryLength; queryIndex++) { - for (let targetIndex = 0; targetIndex < targetLength; targetIndex++) { - const currentIndex = queryIndex * targetLength + targetIndex; - const leftIndex = currentIndex - 1; - const diagIndex = (queryIndex - 1) * targetLength + targetIndex - 1; - - const leftScore: number = targetIndex > 0 ? scores[leftIndex] : 0; - const diagScore: number = queryIndex > 0 && targetIndex > 0 ? scores[diagIndex] : 0; - - const matchesSequenceLength: number = queryIndex > 0 && targetIndex > 0 ? matches[diagIndex] : 0; - - // If we are not matching on the first query character any more, we only produce a - // score if we had a score previously for the last query index (by looking at the diagScore). - // This makes sure that the query always matches in sequence on the target. For example - // given a target of "ede" and a query of "de", we would otherwise produce a wrong high score - // for query[1] ("e") matching on target[0] ("e") because of the "beginning of word" boost. - let score: number; - if (!diagScore && queryIndex > 0) { - score = 0; - } else { - score = computeCharScore(query, queryLower, queryIndex, target, targetLower, targetIndex, matchesSequenceLength); - } - - // We have a score and its equal or larger than the left score - // Match: sequence continues growing from previous diag value - // Score: increases by diag score value - if (score && diagScore + score >= leftScore) { - matches[currentIndex] = matchesSequenceLength + 1; - scores[currentIndex] = diagScore + score; - } - - // We either have no score or the score is lower than the left score - // Match: reset to 0 - // Score: pick up from left hand side - else { - matches[currentIndex] = NO_MATCH; - scores[currentIndex] = leftScore; - } - } - } - - // Restore Positions (starting from bottom right of matrix) - const positions = []; - let queryIndex = queryLength - 1; - let targetIndex = targetLength - 1; - while (queryIndex >= 0 && targetIndex >= 0) { - const currentIndex = queryIndex * targetLength + targetIndex; - const match = matches[currentIndex]; - if (match === NO_MATCH) { - targetIndex--; // go left - } else { - positions.push(targetIndex); - - // go up and left - queryIndex--; - targetIndex--; - } - } - - // Print matrix - // if (DEBUG_MATRIX) { - // printMatrix(query, target, matches, scores); - // } - - return [scores[queryLength * targetLength - 1], positions.reverse()]; -} - -function computeCharScore(query: string, queryLower: string, queryIndex: number, target: string, targetLower: string, targetIndex: number, matchesSequenceLength: number): number { - let score = 0; - - if (queryLower[queryIndex] !== targetLower[targetIndex]) { - return score; // no match of characters - } - - // Character match bonus - score += 1; - - // if (DEBUG) { - // console.groupCollapsed(`%cCharacter match bonus: +1 (char: ${queryLower[queryIndex]} at index ${targetIndex}, total score: ${score})`, 'font-weight: normal'); - // } - - // Consecutive match bonus - if (matchesSequenceLength > 0) { - score += (matchesSequenceLength * 5); - - // if (DEBUG) { - // console.log('Consecutive match bonus: ' + (matchesSequenceLength * 5)); - // } - } - - // Same case bonus - if (query[queryIndex] === target[targetIndex]) { - score += 1; - - // if (DEBUG) { - // console.log('Same case bonus: +1'); - // } - } - - // Start of word bonus - if (targetIndex === 0) { - score += 8; - - // if (DEBUG) { - // console.log('Start of word bonus: +8'); - // } - } - - else { - - // After separator bonus - const separatorBonus = scoreSeparatorAtPos(target.charCodeAt(targetIndex - 1)); - if (separatorBonus) { - score += separatorBonus; - - // if (DEBUG) { - // console.log('After separtor bonus: +4'); - // } - } - - // Inside word upper case bonus (camel case) - else if (isUpper(target.charCodeAt(targetIndex))) { - score += 1; - - // if (DEBUG) { - // console.log('Inside word upper case bonus: +1'); - // } - } - } - - // if (DEBUG) { - // console.groupEnd(); - // } - - return score; -} - -function scoreSeparatorAtPos(charCode: number): number { - switch (charCode) { - case CharCode.Slash: - case CharCode.Backslash: - return 5; // prefer path separators... - case CharCode.Underline: - case CharCode.Dash: - case CharCode.Period: - case CharCode.Space: - case CharCode.SingleQuote: - case CharCode.DoubleQuote: - case CharCode.Colon: - return 4; // ...over other separators - default: - return 0; - } -} - -// function printMatrix(query: string, target: string, matches: number[], scores: number[]): void { -// console.log('\t' + target.split('').join('\t')); -// for (let queryIndex = 0; queryIndex < query.length; queryIndex++) { -// let line = query[queryIndex] + '\t'; -// for (let targetIndex = 0; targetIndex < target.length; targetIndex++) { -// const currentIndex = queryIndex * target.length + targetIndex; -// line = line + 'M' + matches[currentIndex] + '/' + 'S' + scores[currentIndex] + '\t'; -// } - -// console.log(line); -// } -// } - -/** - * Scoring on structural items that have a label and optional description. - */ -export interface IItemScore { - - /** - * Overall score. - */ - score: number; - - /** - * Matches within the label. - */ - labelMatch?: IMatch[]; - - /** - * Matches within the description. - */ - descriptionMatch?: IMatch[]; -} - -const NO_ITEM_SCORE: IItemScore = Object.freeze({ score: 0 }); - -export interface IItemAccessor { - - /** - * Just the label of the item to score on. - */ - getItemLabel(item: T): string; - - /** - * The optional description of the item to score on. Can be null. - */ - getItemDescription(item: T): string; - - /** - * If the item is a file, the path of the file to score on. Can be null. - */ - getItemPath(file: T): string; -} - -const PATH_IDENTITY_SCORE = 1 << 18; -const LABEL_PREFIX_SCORE = 1 << 17; -const LABEL_CAMELCASE_SCORE = 1 << 16; -const LABEL_SCORE_THRESHOLD = 1 << 15; - -export interface IPreparedQuery { - original: string; - value: string; - lowercase: string; - containsPathSeparator: boolean; -} - -/** - * Helper function to prepare a search value for scoring in quick open by removing unwanted characters. - */ -export function prepareQuery(original: string): IPreparedQuery { - let lowercase: string; - let containsPathSeparator: boolean; - let value: string; - - if (original) { - value = stripWildcards(original).replace(/\s/g, ''); // get rid of all wildcards and whitespace - if (isWindows) { - value = value.replace(/\//g, nativeSep); // Help Windows users to search for paths when using slash - } - - lowercase = value.toLowerCase(); - containsPathSeparator = value.indexOf(nativeSep) >= 0; - } - - return { original, value, lowercase, containsPathSeparator }; -} - -export function scoreItem(item: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor, cache: ScorerCache): IItemScore { - if (!item || !query.value) { - return NO_ITEM_SCORE; // we need an item and query to score on at least - } - - const label = accessor.getItemLabel(item); - if (!label) { - return NO_ITEM_SCORE; // we need a label at least - } - - const description = accessor.getItemDescription(item); - - let cacheHash: string; - if (description) { - cacheHash = `${label}${description}${query.value}${fuzzy}`; - } else { - cacheHash = `${label}${query.value}${fuzzy}`; - } - - const cached = cache[cacheHash]; - if (cached) { - return cached; - } - - const itemScore = doScoreItem(label, description, accessor.getItemPath(item), query, fuzzy); - cache[cacheHash] = itemScore; - - return itemScore; -} - -function doScoreItem(label: string, description: string, path: string, query: IPreparedQuery, fuzzy: boolean): IItemScore { - - // 1.) treat identity matches on full path highest - if (path && isLinux ? query.original === path : equalsIgnoreCase(query.original, path)) { - return { score: PATH_IDENTITY_SCORE, labelMatch: [{ start: 0, end: label.length }], descriptionMatch: description ? [{ start: 0, end: description.length }] : void 0 }; - } - - // We only consider label matches if the query is not including file path separators - const preferLabelMatches = !path || !query.containsPathSeparator; - if (preferLabelMatches) { - - // 2.) treat prefix matches on the label second highest - const prefixLabelMatch = matchesPrefix(query.value, label); - if (prefixLabelMatch) { - return { score: LABEL_PREFIX_SCORE, labelMatch: prefixLabelMatch }; - } - - // 3.) treat camelcase matches on the label third highest - const camelcaseLabelMatch = matchesCamelCase(query.value, label); - if (camelcaseLabelMatch) { - return { score: LABEL_CAMELCASE_SCORE, labelMatch: camelcaseLabelMatch }; - } - - // 4.) prefer scores on the label if any - const [labelScore, labelPositions] = score(label, query.value, query.lowercase, fuzzy); - if (labelScore) { - return { score: labelScore + LABEL_SCORE_THRESHOLD, labelMatch: createMatches(labelPositions) }; - } - } - - // 5.) finally compute description + label scores if we have a description - if (description) { - let descriptionPrefix = description; - if (!!path) { - descriptionPrefix = `${description}${nativeSep}`; // assume this is a file path - } - - const descriptionPrefixLength = descriptionPrefix.length; - const descriptionAndLabel = `${descriptionPrefix}${label}`; - - const [labelDescriptionScore, labelDescriptionPositions] = score(descriptionAndLabel, query.value, query.lowercase, fuzzy); - if (labelDescriptionScore) { - const labelDescriptionMatches = createMatches(labelDescriptionPositions); - const labelMatch: IMatch[] = []; - const descriptionMatch: IMatch[] = []; - - // We have to split the matches back onto the label and description portions - labelDescriptionMatches.forEach(h => { - - // Match overlaps label and description part, we need to split it up - if (h.start < descriptionPrefixLength && h.end > descriptionPrefixLength) { - labelMatch.push({ start: 0, end: h.end - descriptionPrefixLength }); - descriptionMatch.push({ start: h.start, end: descriptionPrefixLength }); - } - - // Match on label part - else if (h.start >= descriptionPrefixLength) { - labelMatch.push({ start: h.start - descriptionPrefixLength, end: h.end - descriptionPrefixLength }); - } - - // Match on description part - else { - descriptionMatch.push(h); - } - }); - - return { score: labelDescriptionScore, labelMatch, descriptionMatch }; - } - } - - return NO_ITEM_SCORE; -} - -export function compareItemsByScore(itemA: T, itemB: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor, cache: ScorerCache, fallbackComparer = fallbackCompare): number { - const itemScoreA = scoreItem(itemA, query, fuzzy, accessor, cache); - const itemScoreB = scoreItem(itemB, query, fuzzy, accessor, cache); - - const scoreA = itemScoreA.score; - const scoreB = itemScoreB.score; - - // 1.) prefer identity matches - if (scoreA === PATH_IDENTITY_SCORE || scoreB === PATH_IDENTITY_SCORE) { - if (scoreA !== scoreB) { - return scoreA === PATH_IDENTITY_SCORE ? -1 : 1; - } - } - - // 2.) prefer label prefix matches - if (scoreA === LABEL_PREFIX_SCORE || scoreB === LABEL_PREFIX_SCORE) { - if (scoreA !== scoreB) { - return scoreA === LABEL_PREFIX_SCORE ? -1 : 1; - } - - const labelA = accessor.getItemLabel(itemA); - const labelB = accessor.getItemLabel(itemB); - - // prefer shorter names when both match on label prefix - if (labelA.length !== labelB.length) { - return labelA.length - labelB.length; - } - } - - // 3.) prefer camelcase matches - if (scoreA === LABEL_CAMELCASE_SCORE || scoreB === LABEL_CAMELCASE_SCORE) { - if (scoreA !== scoreB) { - return scoreA === LABEL_CAMELCASE_SCORE ? -1 : 1; - } - - const labelA = accessor.getItemLabel(itemA); - const labelB = accessor.getItemLabel(itemB); - - // prefer more compact camel case matches over longer - const comparedByMatchLength = compareByMatchLength(itemScoreA.labelMatch, itemScoreB.labelMatch); - if (comparedByMatchLength !== 0) { - return comparedByMatchLength; - } - - // prefer shorter names when both match on label camelcase - if (labelA.length !== labelB.length) { - return labelA.length - labelB.length; - } - } - - // 4.) prefer label scores - if (scoreA > LABEL_SCORE_THRESHOLD || scoreB > LABEL_SCORE_THRESHOLD) { - if (scoreB < LABEL_SCORE_THRESHOLD) { - return -1; - } - - if (scoreA < LABEL_SCORE_THRESHOLD) { - return 1; - } - } - - // 5.) compare by score - if (scoreA !== scoreB) { - return scoreA > scoreB ? -1 : 1; - } - - // 6.) scores are identical, prefer more compact matches (label and description) - const itemAMatchDistance = computeLabelAndDescriptionMatchDistance(itemA, itemScoreA, accessor); - const itemBMatchDistance = computeLabelAndDescriptionMatchDistance(itemB, itemScoreB, accessor); - if (itemAMatchDistance && itemBMatchDistance && itemAMatchDistance !== itemBMatchDistance) { - return itemBMatchDistance > itemAMatchDistance ? -1 : 1; - } - - // 7.) at this point, scores are identical and match compactness as well - // for both items so we start to use the fallback compare - return fallbackComparer(itemA, itemB, query, accessor); -} - -function computeLabelAndDescriptionMatchDistance(item: T, score: IItemScore, accessor: IItemAccessor): number { - const hasLabelMatches = (score.labelMatch && score.labelMatch.length); - const hasDescriptionMatches = (score.descriptionMatch && score.descriptionMatch.length); - - let matchStart: number = -1; - let matchEnd: number = -1; - - // If we have description matches, the start is first of description match - if (hasDescriptionMatches) { - matchStart = score.descriptionMatch[0].start; - } - - // Otherwise, the start is the first label match - else if (hasLabelMatches) { - matchStart = score.labelMatch[0].start; - } - - // If we have label match, the end is the last label match - // If we had a description match, we add the length of the description - // as offset to the end to indicate this. - if (hasLabelMatches) { - matchEnd = score.labelMatch[score.labelMatch.length - 1].end; - if (hasDescriptionMatches) { - const itemDescription = accessor.getItemDescription(item); - if (itemDescription) { - matchEnd += itemDescription.length; - } - } - } - - // If we have just a description match, the end is the last description match - else if (hasDescriptionMatches) { - matchEnd = score.descriptionMatch[score.descriptionMatch.length - 1].end; - } - - return matchEnd - matchStart; -} - -function compareByMatchLength(matchesA?: IMatch[], matchesB?: IMatch[]): number { - if ((!matchesA && !matchesB) || (!matchesA.length && !matchesB.length)) { - return 0; // make sure to not cause bad comparing when matches are not provided - } - - if (!matchesB || !matchesB.length) { - return -1; - } - - if (!matchesA || !matchesA.length) { - return 1; - } - - // Compute match length of A (first to last match) - const matchStartA = matchesA[0].start; - const matchEndA = matchesA[matchesA.length - 1].end; - const matchLengthA = matchEndA - matchStartA; - - // Compute match length of B (first to last match) - const matchStartB = matchesB[0].start; - const matchEndB = matchesB[matchesB.length - 1].end; - const matchLengthB = matchEndB - matchStartB; - - // Prefer shorter match length - return matchLengthA === matchLengthB ? 0 : matchLengthB < matchLengthA ? 1 : -1; -} - -export function fallbackCompare(itemA: T, itemB: T, query: IPreparedQuery, accessor: IItemAccessor): number { - - // check for label + description length and prefer shorter - const labelA = accessor.getItemLabel(itemA); - const labelB = accessor.getItemLabel(itemB); - - const descriptionA = accessor.getItemDescription(itemA); - const descriptionB = accessor.getItemDescription(itemB); - - const labelDescriptionALength = labelA.length + (descriptionA ? descriptionA.length : 0); - const labelDescriptionBLength = labelB.length + (descriptionB ? descriptionB.length : 0); - - if (labelDescriptionALength !== labelDescriptionBLength) { - return labelDescriptionALength - labelDescriptionBLength; - } - - // check for path length and prefer shorter - const pathA = accessor.getItemPath(itemA); - const pathB = accessor.getItemPath(itemB); - - if (pathA && pathB && pathA.length !== pathB.length) { - return pathA.length - pathB.length; - } - - // 7.) finally we have equal scores and equal length, we fallback to comparer - - // compare by label - if (labelA !== labelB) { - return compareAnything(labelA, labelB, query.value); - } - - // compare by description - if (descriptionA && descriptionB && descriptionA !== descriptionB) { - return compareAnything(descriptionA, descriptionB, query.value); - } - - // compare by path - if (pathA && pathB && pathA !== pathB) { - return compareAnything(pathA, pathB, query.value); - } - - // equal - return 0; -} \ No newline at end of file diff --git a/extensions/search-rg/src/common/filters.ts b/extensions/search-rg/src/common/filters.ts deleted file mode 100644 index 63d4dcaeac3..00000000000 --- a/extensions/search-rg/src/common/filters.ts +++ /dev/null @@ -1,224 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import * as strings from './strings'; -import { CharCode } from './charCode'; - -export interface IFilter { - // Returns null if word doesn't match. - (word: string, wordToMatchAgainst: string): IMatch[]; -} - -export interface IMatch { - start: number; - end: number; -} - -// Prefix - -export const matchesPrefix: IFilter = _matchesPrefix.bind(undefined, true); - -function _matchesPrefix(ignoreCase: boolean, word: string, wordToMatchAgainst: string): IMatch[] { - if (!wordToMatchAgainst || wordToMatchAgainst.length < word.length) { - return null; - } - - let matches: boolean; - if (ignoreCase) { - matches = strings.startsWithIgnoreCase(wordToMatchAgainst, word); - } else { - matches = wordToMatchAgainst.indexOf(word) === 0; - } - - if (!matches) { - return null; - } - - return word.length > 0 ? [{ start: 0, end: word.length }] : []; -} - -// CamelCase - -function isLower(code: number): boolean { - return CharCode.a <= code && code <= CharCode.z; -} - -export function isUpper(code: number): boolean { - return CharCode.A <= code && code <= CharCode.Z; -} - -function isNumber(code: number): boolean { - return CharCode.Digit0 <= code && code <= CharCode.Digit9; -} - -function isWhitespace(code: number): boolean { - return ( - code === CharCode.Space - || code === CharCode.Tab - || code === CharCode.LineFeed - || code === CharCode.CarriageReturn - ); -} - -function isAlphanumeric(code: number): boolean { - return isLower(code) || isUpper(code) || isNumber(code); -} - -function join(head: IMatch, tail: IMatch[]): IMatch[] { - if (tail.length === 0) { - tail = [head]; - } else if (head.end === tail[0].start) { - tail[0].start = head.start; - } else { - tail.unshift(head); - } - return tail; -} - -function nextAnchor(camelCaseWord: string, start: number): number { - for (let i = start; i < camelCaseWord.length; i++) { - let c = camelCaseWord.charCodeAt(i); - if (isUpper(c) || isNumber(c) || (i > 0 && !isAlphanumeric(camelCaseWord.charCodeAt(i - 1)))) { - return i; - } - } - return camelCaseWord.length; -} - -function _matchesCamelCase(word: string, camelCaseWord: string, i: number, j: number): IMatch[] { - if (i === word.length) { - return []; - } else if (j === camelCaseWord.length) { - return null; - } else if (word[i] !== camelCaseWord[j].toLowerCase()) { - return null; - } else { - let result: IMatch[] = null; - let nextUpperIndex = j + 1; - result = _matchesCamelCase(word, camelCaseWord, i + 1, j + 1); - while (!result && (nextUpperIndex = nextAnchor(camelCaseWord, nextUpperIndex)) < camelCaseWord.length) { - result = _matchesCamelCase(word, camelCaseWord, i + 1, nextUpperIndex); - nextUpperIndex++; - } - return result === null ? null : join({ start: j, end: j + 1 }, result); - } -} - -interface ICamelCaseAnalysis { - upperPercent: number; - lowerPercent: number; - alphaPercent: number; - numericPercent: number; -} - -// Heuristic to avoid computing camel case matcher for words that don't -// look like camelCaseWords. -function analyzeCamelCaseWord(word: string): ICamelCaseAnalysis { - let upper = 0, lower = 0, alpha = 0, numeric = 0, code = 0; - - for (let i = 0; i < word.length; i++) { - code = word.charCodeAt(i); - - if (isUpper(code)) { upper++; } - if (isLower(code)) { lower++; } - if (isAlphanumeric(code)) { alpha++; } - if (isNumber(code)) { numeric++; } - } - - let upperPercent = upper / word.length; - let lowerPercent = lower / word.length; - let alphaPercent = alpha / word.length; - let numericPercent = numeric / word.length; - - return { upperPercent, lowerPercent, alphaPercent, numericPercent }; -} - -function isUpperCaseWord(analysis: ICamelCaseAnalysis): boolean { - const { upperPercent, lowerPercent } = analysis; - return lowerPercent === 0 && upperPercent > 0.6; -} - -function isCamelCaseWord(analysis: ICamelCaseAnalysis): boolean { - const { upperPercent, lowerPercent, alphaPercent, numericPercent } = analysis; - return lowerPercent > 0.2 && upperPercent < 0.8 && alphaPercent > 0.6 && numericPercent < 0.2; -} - -// Heuristic to avoid computing camel case matcher for words that don't -// look like camel case patterns. -function isCamelCasePattern(word: string): boolean { - let upper = 0, lower = 0, code = 0, whitespace = 0; - - for (let i = 0; i < word.length; i++) { - code = word.charCodeAt(i); - - if (isUpper(code)) { upper++; } - if (isLower(code)) { lower++; } - if (isWhitespace(code)) { whitespace++; } - } - - if ((upper === 0 || lower === 0) && whitespace === 0) { - return word.length <= 30; - } else { - return upper <= 5; - } -} - -export function matchesCamelCase(word: string, camelCaseWord: string): IMatch[] { - if (!camelCaseWord) { - return null; - } - - camelCaseWord = camelCaseWord.trim(); - - if (camelCaseWord.length === 0) { - return null; - } - - if (!isCamelCasePattern(word)) { - return null; - } - - if (camelCaseWord.length > 60) { - return null; - } - - const analysis = analyzeCamelCaseWord(camelCaseWord); - - if (!isCamelCaseWord(analysis)) { - if (!isUpperCaseWord(analysis)) { - return null; - } - - camelCaseWord = camelCaseWord.toLowerCase(); - } - - let result: IMatch[] = null; - let i = 0; - - word = word.toLowerCase(); - while (i < camelCaseWord.length && (result = _matchesCamelCase(word, camelCaseWord, 0, i)) === null) { - i = nextAnchor(camelCaseWord, i + 1); - } - - return result; -} - -export function createMatches(position: number[]): IMatch[] { - let ret: IMatch[] = []; - if (!position) { - return ret; - } - let last: IMatch; - for (const pos of position) { - if (last && last.end === pos) { - last.end += 1; - } else { - last = { start: pos, end: pos + 1 }; - ret.push(last); - } - } - return ret; -} diff --git a/extensions/search-rg/src/common/strings.ts b/extensions/search-rg/src/common/strings.ts deleted file mode 100644 index 2678aff1e0f..00000000000 --- a/extensions/search-rg/src/common/strings.ts +++ /dev/null @@ -1,143 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -'use strict'; - -import { CharCode } from './charCode'; - -export function stripWildcards(pattern: string): string { - return pattern.replace(/\*/g, ''); -} - -/** - * Determines if haystack starts with needle. - */ -export function startsWith(haystack: string, needle: string): boolean { - if (haystack.length < needle.length) { - return false; - } - - if (haystack === needle) { - return true; - } - - for (let i = 0; i < needle.length; i++) { - if (haystack[i] !== needle[i]) { - return false; - } - } - - return true; -} - -export function startsWithIgnoreCase(str: string, candidate: string): boolean { - const candidateLength = candidate.length; - if (candidate.length > str.length) { - return false; - } - - return doEqualsIgnoreCase(str, candidate, candidateLength); -} - -/** - * Determines if haystack ends with needle. - */ -export function endsWith(haystack: string, needle: string): boolean { - let diff = haystack.length - needle.length; - if (diff > 0) { - return haystack.indexOf(needle, diff) === diff; - } else if (diff === 0) { - return haystack === needle; - } else { - return false; - } -} - -function isLowerAsciiLetter(code: number): boolean { - return code >= CharCode.a && code <= CharCode.z; -} - -function isUpperAsciiLetter(code: number): boolean { - return code >= CharCode.A && code <= CharCode.Z; -} - -function isAsciiLetter(code: number): boolean { - return isLowerAsciiLetter(code) || isUpperAsciiLetter(code); -} - -export function equalsIgnoreCase(a: string, b: string): boolean { - const len1 = a ? a.length : 0; - const len2 = b ? b.length : 0; - - if (len1 !== len2) { - return false; - } - - return doEqualsIgnoreCase(a, b); -} - -function doEqualsIgnoreCase(a: string, b: string, stopAt = a.length): boolean { - if (typeof a !== 'string' || typeof b !== 'string') { - return false; - } - - for (let i = 0; i < stopAt; i++) { - const codeA = a.charCodeAt(i); - const codeB = b.charCodeAt(i); - - if (codeA === codeB) { - continue; - } - - // a-z A-Z - if (isAsciiLetter(codeA) && isAsciiLetter(codeB)) { - let diff = Math.abs(codeA - codeB); - if (diff !== 0 && diff !== 32) { - return false; - } - } - - // Any other charcode - else { - if (String.fromCharCode(codeA).toLowerCase() !== String.fromCharCode(codeB).toLowerCase()) { - return false; - } - } - } - - return true; -} - -/** - * Checks if the characters of the provided query string are included in the - * target string. The characters do not have to be contiguous within the string. - */ -export function fuzzyContains(target: string, query: string): boolean { - if (!target || !query) { - return false; // return early if target or query are undefined - } - - if (target.length < query.length) { - return false; // impossible for query to be contained in target - } - - const queryLen = query.length; - const targetLower = target.toLowerCase(); - - let index = 0; - let lastIndexOf = -1; - while (index < queryLen) { - let indexOf = targetLower.indexOf(query[index], lastIndexOf + 1); - if (indexOf < 0) { - return false; - } - - lastIndexOf = indexOf; - - index++; - } - - return true; -} \ No newline at end of file diff --git a/extensions/search-rg/src/extension.ts b/extensions/search-rg/src/extension.ts index f999cb0cb6c..1447f03d331 100644 --- a/extensions/search-rg/src/extension.ts +++ b/extensions/search-rg/src/extension.ts @@ -4,26 +4,26 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { RipgrepTextSearchEngine } from './ripgrepTextSearch'; import { RipgrepFileSearchEngine } from './ripgrepFileSearch'; -import { CachedSearchProvider } from './cachedSearchProvider'; +import { RipgrepTextSearchEngine } from './ripgrepTextSearch'; +import { joinPath } from './utils'; export function activate(): void { if (vscode.workspace.getConfiguration('searchRipgrep').get('enable')) { const outputChannel = vscode.window.createOutputChannel('search-rg'); + const provider = new RipgrepSearchProvider(outputChannel); - vscode.workspace.registerSearchProvider('file', provider); + vscode.workspace.registerFileIndexProvider('file', provider); + vscode.workspace.registerTextSearchProvider('file', provider); } } type SearchEngine = RipgrepFileSearchEngine | RipgrepTextSearchEngine; -class RipgrepSearchProvider implements vscode.SearchProvider { - private cachedProvider: CachedSearchProvider; +class RipgrepSearchProvider implements vscode.FileIndexProvider, vscode.TextSearchProvider { private inProgress: Set = new Set(); constructor(private outputChannel: vscode.OutputChannel) { - this.cachedProvider = new CachedSearchProvider(); process.once('exit', () => this.dispose()); } @@ -32,13 +32,16 @@ class RipgrepSearchProvider implements vscode.SearchProvider { return this.withEngine(engine, () => engine.provideTextSearchResults(query, options, progress, token)); } - provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.SearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { + provideFileIndex(options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { const engine = new RipgrepFileSearchEngine(this.outputChannel); - return this.withEngine(engine, () => this.cachedProvider.provideFileSearchResults(engine, query, options, progress, token)); - } - clearCache(cacheKey: string): void { - this.cachedProvider.clearCache(cacheKey); + const results: vscode.Uri[] = []; + const onResult = relativePathMatch => { + results.push(joinPath(options.folder, relativePathMatch)); + }; + + return this.withEngine(engine, () => engine.provideFileSearchResults(options, { report: onResult }, token)) + .then(() => results); } private withEngine(engine: SearchEngine, fn: () => Thenable): Thenable { diff --git a/extensions/search-rg/src/common/normalization.ts b/extensions/search-rg/src/normalization.ts similarity index 100% rename from extensions/search-rg/src/common/normalization.ts rename to extensions/search-rg/src/normalization.ts diff --git a/extensions/search-rg/src/ripgrepFileSearch.ts b/extensions/search-rg/src/ripgrepFileSearch.ts index fd3b6acc99a..7bf41793435 100644 --- a/extensions/search-rg/src/ripgrepFileSearch.ts +++ b/extensions/search-rg/src/ripgrepFileSearch.ts @@ -7,18 +7,17 @@ import * as cp from 'child_process'; import { Readable } from 'stream'; import { NodeStringDecoder, StringDecoder } from 'string_decoder'; import * as vscode from 'vscode'; -import { normalizeNFC, normalizeNFD } from './common/normalization'; +import { normalizeNFC, normalizeNFD } from './normalization'; import { rgPath } from './ripgrep'; -import { anchorGlob } from './utils'; import { rgErrorMsgForDisplay } from './ripgrepTextSearch'; -import { IInternalFileSearchProvider } from './cachedSearchProvider'; +import { anchorGlob } from './utils'; const isMac = process.platform === 'darwin'; // If vscode-ripgrep is in an .asar file, then the binary is unpacked. const rgDiskPath = rgPath.replace(/\bnode_modules\.asar\b/, 'node_modules.asar.unpacked'); -export class RipgrepFileSearchEngine implements IInternalFileSearchProvider { +export class RipgrepFileSearchEngine { private rgProc: cp.ChildProcess; private isDone: boolean; diff --git a/extensions/search-rg/src/utils.ts b/extensions/search-rg/src/utils.ts index fd083fe075f..d06e4f1ab8a 100644 --- a/extensions/search-rg/src/utils.ts +++ b/extensions/search-rg/src/utils.ts @@ -20,8 +20,6 @@ export function anchorGlob(glob: string): string { } export function joinPath(resource: vscode.Uri, pathFragment: string): vscode.Uri { - const joinedPath = path.join(resource.path || '/', pathFragment); - return resource.with({ - path: joinedPath - }); + const joinedPath = path.join(resource.fsPath || '/', pathFragment); + return vscode.Uri.file(joinedPath); } diff --git a/extensions/search-rg/yarn.lock b/extensions/search-rg/yarn.lock index 99b491f01ff..1be6ca92db5 100644 --- a/extensions/search-rg/yarn.lock +++ b/extensions/search-rg/yarn.lock @@ -1539,9 +1539,9 @@ vinyl@^2.0.1, vinyl@^2.0.2: remove-trailing-separator "^1.0.1" replace-ext "^1.0.0" -vscode-extension-telemetry@0.0.15: - version "0.0.15" - resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.15.tgz#685c32f3b67e8fb85ba689c1d7f88ff90ff87856" +vscode-extension-telemetry@0.0.18: + version "0.0.18" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.18.tgz#602ba20d8c71453aa34533a291e7638f6e5c0327" dependencies: applicationinsights "1.0.1" diff --git a/extensions/shellscript/test/colorize-results/test_sh.json b/extensions/shellscript/test/colorize-results/test_sh.json index 36a9ce993e4..6760e596443 100644 --- a/extensions/shellscript/test/colorize-results/test_sh.json +++ b/extensions/shellscript/test/colorize-results/test_sh.json @@ -3,9 +3,9 @@ "c": "#!", "t": "source.shell comment.line.number-sign.shebang.shell punctuation.definition.comment.shebang.shell", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -14,9 +14,9 @@ "c": "/usr/bin/env bash", "t": "source.shell comment.line.number-sign.shebang.shell", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1246,9 +1246,9 @@ "c": "#", "t": "source.shell meta.function.shell meta.scope.group.shell comment.line.number-sign.shell punctuation.definition.comment.shell", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1257,9 +1257,9 @@ "c": " Node modules", "t": "source.shell meta.function.shell meta.scope.group.shell comment.line.number-sign.shell", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1334,9 +1334,9 @@ "c": "#", "t": "source.shell meta.function.shell meta.scope.group.shell comment.line.number-sign.shell punctuation.definition.comment.shell", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1345,9 +1345,9 @@ "c": " Configuration", "t": "source.shell meta.function.shell meta.scope.group.shell comment.line.number-sign.shell", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1400,9 +1400,9 @@ "c": "#", "t": "source.shell meta.function.shell meta.scope.group.shell comment.line.number-sign.shell punctuation.definition.comment.shell", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1411,9 +1411,9 @@ "c": " Launch Code", "t": "source.shell meta.function.shell meta.scope.group.shell comment.line.number-sign.shell", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/theme-defaults/themes/dark_defaults.json b/extensions/theme-defaults/themes/dark_defaults.json index 93a316fc8f9..87b37508468 100644 --- a/extensions/theme-defaults/themes/dark_defaults.json +++ b/extensions/theme-defaults/themes/dark_defaults.json @@ -11,6 +11,7 @@ "list.dropBackground": "#383B3D", "activityBarBadge.background": "#007ACC", "sideBarTitle.foreground": "#BBBBBB", + "input.placeholderForeground": "#A6A6A6", "settings.textInputBackground": "#292929", "settings.numberInputBackground": "#292929" } diff --git a/extensions/theme-defaults/themes/dark_vs.json b/extensions/theme-defaults/themes/dark_vs.json index c2a15addb8d..2cbdca4a8f1 100644 --- a/extensions/theme-defaults/themes/dark_vs.json +++ b/extensions/theme-defaults/themes/dark_vs.json @@ -33,7 +33,7 @@ { "scope": "comment", "settings": { - "foreground": "#608b4e" + "foreground": "#6A9955" } }, { @@ -143,7 +143,7 @@ { "scope": "beginning.punctuation.definition.quote.markdown", "settings": { - "foreground": "#608b4e" + "foreground": "#6A9955" } }, { diff --git a/extensions/theme-defaults/themes/light_defaults.json b/extensions/theme-defaults/themes/light_defaults.json index 6403ded6bcb..dc53cbfb1ea 100644 --- a/extensions/theme-defaults/themes/light_defaults.json +++ b/extensions/theme-defaults/themes/light_defaults.json @@ -12,7 +12,7 @@ "activityBarBadge.background": "#007ACC", "sideBarTitle.foreground": "#6F6F6F", "list.hoverBackground": "#E8E8E8", - "input.placeholderForeground": "#ADADAD", + "input.placeholderForeground": "#767676", "settings.textInputBorder": "#CECECE", "settings.numberInputBorder": "#CECECE" } diff --git a/extensions/theme-quietlight/themes/quietlight-color-theme.json b/extensions/theme-quietlight/themes/quietlight-color-theme.json index e8915e04b78..81a8591a875 100644 --- a/extensions/theme-quietlight/themes/quietlight-color-theme.json +++ b/extensions/theme-quietlight/themes/quietlight-color-theme.json @@ -529,6 +529,7 @@ "inputValidation.errorBorder": "#f1897f", "errorForeground": "#f1897f", "badge.background": "#705697AA", - "progressBar.background": "#705697" + "progressBar.background": "#705697", + "walkThrough.embeddedEditorBackground": "#00000014" } } diff --git a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json index 37f3e385d7b..1f0cd3202e7 100644 --- a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json +++ b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json @@ -481,6 +481,9 @@ "terminal.ansiBrightBlue": "#839496", "terminal.ansiBrightMagenta": "#6c71c4", "terminal.ansiBrightCyan": "#93a1a1", - "terminal.ansiBrightWhite": "#eee8d5" + "terminal.ansiBrightWhite": "#eee8d5", + + // Interactive Playground + "walkThrough.embeddedEditorBackground": "#00000014" } } \ No newline at end of file diff --git a/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json index 4c96b7e3ae3..6f5bc2d554d 100644 --- a/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json +++ b/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/d7df3e324468b6535af67573d2956f9a852aa586", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/858d33f03943e4ec040e81cbabbc3f7892157c18", "name": "TypeScript", "scopeName": "source.ts", "patterns": [ @@ -282,55 +282,112 @@ ] }, "var-expr": { - "name": "meta.var.expr.ts", - "begin": "(?)\n )) |\n ((async\\s*)?(\n ((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.ts entity.name.function.ts" @@ -383,6 +440,40 @@ } ] }, + "var-single-const": { + "patterns": [ + { + "name": "meta.var-single-variable.expr.ts", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "beginCaptures": { + "1": { + "name": "meta.definition.variable.ts variable.other.constant.ts entity.name.function.ts" + } + }, + "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", + "patterns": [ + { + "include": "#var-single-variable-type-annotation" + } + ] + }, + { + "name": "meta.var-single-variable.expr.ts", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)", + "beginCaptures": { + "1": { + "name": "meta.definition.variable.ts variable.other.constant.ts" + } + }, + "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", + "patterns": [ + { + "include": "#var-single-variable-type-annotation" + } + ] + } + ] + }, "var-single-variable-type-annotation": { "patterns": [ { @@ -432,6 +523,42 @@ } ] }, + "destructuring-const": { + "patterns": [ + { + "name": "meta.object-binding-pattern-variable.ts", + "begin": "(?)\n )) |\n ((async\\s*)?(\n ((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.ts" @@ -810,7 +1048,7 @@ "include": "#comment" }, { - "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\?)?(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\?)?(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.ts entity.name.function.ts" @@ -1591,7 +1829,7 @@ "name": "storage.type.namespace.ts" } }, - "end": "(?<=\\})|(?=;|\\babstract\\b|\\basync\\b|\\bclass\\b|\\bconst\\b|\\bdeclare\\b|\\benum\\b|\\bexport\\b|\\bfunction\\b|\\bimport\\b|\\binterface\\b|\\blet\\b|\\bmodule\\b|\\bnamespace\\b|\\breturn\\b|\\btype\\b|\\bvar\\b)", + "end": "(?<=\\})|(?=;|^\\s*abstract\\b|^\\s*async\\b|^\\s*class\\b|^\\s*const\\b|^\\s*declare\\b|^\\s*enum\\b|^\\s*export\\b|^\\s*function\\b|^\\s*import\\b|^\\s*interface\\b|^\\s*let\\b|^\\s*module\\b|^\\s*namespace\\b|^\\s*return\\b|^\\s*type\\b|^\\s*var\\b)", "patterns": [ { "include": "#comment" @@ -1625,7 +1863,7 @@ "name": "entity.name.type.alias.ts" } }, - "end": "(?=\\}|;|\\babstract\\b|\\basync\\b|\\bclass\\b|\\bconst\\b|\\bdeclare\\b|\\benum\\b|\\bexport\\b|\\bfunction\\b|\\bimport\\b|\\binterface\\b|\\blet\\b|\\bmodule\\b|\\bnamespace\\b|\\breturn\\b|\\btype\\b|\\bvar\\b)", + "end": "(?=\\}|;|^\\s*abstract\\b|^\\s*async\\b|^\\s*class\\b|^\\s*const\\b|^\\s*declare\\b|^\\s*enum\\b|^\\s*export\\b|^\\s*function\\b|^\\s*import\\b|^\\s*interface\\b|^\\s*let\\b|^\\s*module\\b|^\\s*namespace\\b|^\\s*return\\b|^\\s*type\\b|^\\s*var\\b)", "patterns": [ { "include": "#comment" @@ -1640,7 +1878,7 @@ "name": "keyword.operator.assignment.ts" } }, - "end": "(?=\\}|;|\\babstract\\b|\\basync\\b|\\bclass\\b|\\bconst\\b|\\bdeclare\\b|\\benum\\b|\\bexport\\b|\\bfunction\\b|\\bimport\\b|\\binterface\\b|\\blet\\b|\\bmodule\\b|\\bnamespace\\b|\\breturn\\b|\\btype\\b|\\bvar\\b)", + "end": "(?=\\}|;|^\\s*abstract\\b|^\\s*async\\b|^\\s*class\\b|^\\s*const\\b|^\\s*declare\\b|^\\s*enum\\b|^\\s*export\\b|^\\s*function\\b|^\\s*import\\b|^\\s*interface\\b|^\\s*let\\b|^\\s*module\\b|^\\s*namespace\\b|^\\s*return\\b|^\\s*type\\b|^\\s*var\\b)", "patterns": [ { "include": "#type" @@ -1804,7 +2042,7 @@ "name": "keyword.control.default.ts" } }, - "end": "(?=$|;|\\babstract\\b|\\basync\\b|\\bclass\\b|\\bconst\\b|\\bdeclare\\b|\\benum\\b|\\bexport\\b|\\bfunction\\b|\\bimport\\b|\\binterface\\b|\\blet\\b|\\bmodule\\b|\\bnamespace\\b|\\breturn\\b|\\btype\\b|\\bvar\\b)", + "end": "(?=$|;|^\\s*abstract\\b|^\\s*async\\b|^\\s*class\\b|^\\s*const\\b|^\\s*declare\\b|^\\s*enum\\b|^\\s*export\\b|^\\s*function\\b|^\\s*import\\b|^\\s*interface\\b|^\\s*let\\b|^\\s*module\\b|^\\s*namespace\\b|^\\s*return\\b|^\\s*type\\b|^\\s*var\\b)", "patterns": [ { "include": "#expression" @@ -1819,7 +2057,7 @@ "name": "keyword.control.export.ts" } }, - "end": "(?=$|;|\\babstract\\b|\\basync\\b|\\bclass\\b|\\bconst\\b|\\bdeclare\\b|\\benum\\b|\\bexport\\b|\\bfunction\\b|\\bimport\\b|\\binterface\\b|\\blet\\b|\\bmodule\\b|\\bnamespace\\b|\\breturn\\b|\\btype\\b|\\bvar\\b)", + "end": "(?=$|;|^\\s*abstract\\b|^\\s*async\\b|^\\s*class\\b|^\\s*const\\b|^\\s*declare\\b|^\\s*enum\\b|^\\s*export\\b|^\\s*function\\b|^\\s*import\\b|^\\s*interface\\b|^\\s*let\\b|^\\s*module\\b|^\\s*namespace\\b|^\\s*return\\b|^\\s*type\\b|^\\s*var\\b)", "patterns": [ { "include": "#import-export-declaration" @@ -2164,7 +2402,7 @@ }, { "name": "meta.object.member.ts", - "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=:\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=:\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.ts" @@ -2409,7 +2647,7 @@ ] }, { - "begin": "(?<=[(=,]|=>)\\s*(async)?(?=\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?\\(\\s*$)", + "begin": "(?<=[(=,]|=>)\\s*(async)?(?=\\s*((((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?\\()|(<))\\s*$)", "beginCaptures": { "1": { "name": "storage.modifier.async.ts" @@ -2492,7 +2730,7 @@ "patterns": [ { "name": "cast.expr.ts", - "begin": "(?:(?*?\\&\\|\\^]|[^_$[:alnum:]](?:\\+\\+|\\-\\-)|[^\\+]\\+|[^\\-]\\-))\\s*(<)(?!*?\\&\\|\\^]|[^_$[:alnum:]](?:\\+\\+|\\-\\-)|[^\\+]\\+|[^\\-]\\-))\\s*(<)(?!)\n )) |\n ((async\\s*)?(\n ((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.ts" @@ -3508,7 +3746,7 @@ "include": "#destructuring-parameter" }, { - "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$)))", + "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?[\\(]\\s*([\\{\\[]\\s*)?$))))", "captures": { "1": { "name": "storage.modifier.ts" @@ -3880,7 +4118,7 @@ "patterns": [ { "name": "string.regexp.ts", - "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "1": { "name": "punctuation.definition.string.begin.ts" @@ -3903,7 +4141,7 @@ }, { "name": "string.regexp.ts", - "begin": "(?)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "beginCaptures": { "1": { "name": "meta.definition.variable.tsx entity.name.function.tsx" @@ -386,6 +443,40 @@ } ] }, + "var-single-const": { + "patterns": [ + { + "name": "meta.var-single-variable.expr.tsx", + "begin": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "beginCaptures": { + "1": { + "name": "meta.definition.variable.tsx variable.other.constant.tsx entity.name.function.tsx" + } + }, + "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", + "patterns": [ + { + "include": "#var-single-variable-type-annotation" + } + ] + }, + { + "name": "meta.var-single-variable.expr.tsx", + "begin": "([_$[:alpha:]][_$[:alnum:]]*)", + "beginCaptures": { + "1": { + "name": "meta.definition.variable.tsx variable.other.constant.tsx" + } + }, + "end": "(?=$|^|[;,=}]|(\\s+(of|in)\\s+))", + "patterns": [ + { + "include": "#var-single-variable-type-annotation" + } + ] + } + ] + }, "var-single-variable-type-annotation": { "patterns": [ { @@ -435,6 +526,42 @@ } ] }, + "destructuring-const": { + "patterns": [ + { + "name": "meta.object-binding-pattern-variable.tsx", + "begin": "(?)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:(?)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "storage.modifier.tsx" @@ -813,7 +1051,7 @@ "include": "#comment" }, { - "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\?)?(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)([_$[:alpha:]][_$[:alnum:]]*)(\\?)?(?=(\\?\\s*)?\\s*\n# function assignment |\n(=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)) |\n# typeannotation is fn type: < | () | (... | (param: | (param, | (param? | (param= | (param) =>\n(:\\s*(\n (<) |\n ([(]\\s*(\n ([)]) |\n (\\.\\.\\.) |\n ([_$[:alnum:]]+\\s*(\n ([:,?=])|\n ([)]\\s*=>)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))) |\n(:\\s*(=>|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(<[^<>]*>)|[^<>(),=])+=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "1": { "name": "meta.definition.property.tsx entity.name.function.tsx" @@ -1594,7 +1832,7 @@ "name": "storage.type.namespace.tsx" } }, - "end": "(?<=\\})|(?=;|\\babstract\\b|\\basync\\b|\\bclass\\b|\\bconst\\b|\\bdeclare\\b|\\benum\\b|\\bexport\\b|\\bfunction\\b|\\bimport\\b|\\binterface\\b|\\blet\\b|\\bmodule\\b|\\bnamespace\\b|\\breturn\\b|\\btype\\b|\\bvar\\b)", + "end": "(?<=\\})|(?=;|^\\s*abstract\\b|^\\s*async\\b|^\\s*class\\b|^\\s*const\\b|^\\s*declare\\b|^\\s*enum\\b|^\\s*export\\b|^\\s*function\\b|^\\s*import\\b|^\\s*interface\\b|^\\s*let\\b|^\\s*module\\b|^\\s*namespace\\b|^\\s*return\\b|^\\s*type\\b|^\\s*var\\b)", "patterns": [ { "include": "#comment" @@ -1628,7 +1866,7 @@ "name": "entity.name.type.alias.tsx" } }, - "end": "(?=\\}|;|\\babstract\\b|\\basync\\b|\\bclass\\b|\\bconst\\b|\\bdeclare\\b|\\benum\\b|\\bexport\\b|\\bfunction\\b|\\bimport\\b|\\binterface\\b|\\blet\\b|\\bmodule\\b|\\bnamespace\\b|\\breturn\\b|\\btype\\b|\\bvar\\b)", + "end": "(?=\\}|;|^\\s*abstract\\b|^\\s*async\\b|^\\s*class\\b|^\\s*const\\b|^\\s*declare\\b|^\\s*enum\\b|^\\s*export\\b|^\\s*function\\b|^\\s*import\\b|^\\s*interface\\b|^\\s*let\\b|^\\s*module\\b|^\\s*namespace\\b|^\\s*return\\b|^\\s*type\\b|^\\s*var\\b)", "patterns": [ { "include": "#comment" @@ -1643,7 +1881,7 @@ "name": "keyword.operator.assignment.tsx" } }, - "end": "(?=\\}|;|\\babstract\\b|\\basync\\b|\\bclass\\b|\\bconst\\b|\\bdeclare\\b|\\benum\\b|\\bexport\\b|\\bfunction\\b|\\bimport\\b|\\binterface\\b|\\blet\\b|\\bmodule\\b|\\bnamespace\\b|\\breturn\\b|\\btype\\b|\\bvar\\b)", + "end": "(?=\\}|;|^\\s*abstract\\b|^\\s*async\\b|^\\s*class\\b|^\\s*const\\b|^\\s*declare\\b|^\\s*enum\\b|^\\s*export\\b|^\\s*function\\b|^\\s*import\\b|^\\s*interface\\b|^\\s*let\\b|^\\s*module\\b|^\\s*namespace\\b|^\\s*return\\b|^\\s*type\\b|^\\s*var\\b)", "patterns": [ { "include": "#type" @@ -1807,7 +2045,7 @@ "name": "keyword.control.default.tsx" } }, - "end": "(?=$|;|\\babstract\\b|\\basync\\b|\\bclass\\b|\\bconst\\b|\\bdeclare\\b|\\benum\\b|\\bexport\\b|\\bfunction\\b|\\bimport\\b|\\binterface\\b|\\blet\\b|\\bmodule\\b|\\bnamespace\\b|\\breturn\\b|\\btype\\b|\\bvar\\b)", + "end": "(?=$|;|^\\s*abstract\\b|^\\s*async\\b|^\\s*class\\b|^\\s*const\\b|^\\s*declare\\b|^\\s*enum\\b|^\\s*export\\b|^\\s*function\\b|^\\s*import\\b|^\\s*interface\\b|^\\s*let\\b|^\\s*module\\b|^\\s*namespace\\b|^\\s*return\\b|^\\s*type\\b|^\\s*var\\b)", "patterns": [ { "include": "#expression" @@ -1822,7 +2060,7 @@ "name": "keyword.control.export.tsx" } }, - "end": "(?=$|;|\\babstract\\b|\\basync\\b|\\bclass\\b|\\bconst\\b|\\bdeclare\\b|\\benum\\b|\\bexport\\b|\\bfunction\\b|\\bimport\\b|\\binterface\\b|\\blet\\b|\\bmodule\\b|\\bnamespace\\b|\\breturn\\b|\\btype\\b|\\bvar\\b)", + "end": "(?=$|;|^\\s*abstract\\b|^\\s*async\\b|^\\s*class\\b|^\\s*const\\b|^\\s*declare\\b|^\\s*enum\\b|^\\s*export\\b|^\\s*function\\b|^\\s*import\\b|^\\s*interface\\b|^\\s*let\\b|^\\s*module\\b|^\\s*namespace\\b|^\\s*return\\b|^\\s*type\\b|^\\s*var\\b)", "patterns": [ { "include": "#import-export-declaration" @@ -2167,7 +2405,7 @@ }, { "name": "meta.object.member.tsx", - "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=:\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", + "match": "(?x)(?:([_$[:alpha:]][_$[:alnum:]]*)\\s*(?=:\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n)))", "captures": { "0": { "name": "meta.object-literal.key.tsx" @@ -2412,7 +2650,7 @@ ] }, { - "begin": "(?<=[(=,]|=>)\\s*(async)?(?=\\s*((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?\\(\\s*$)", + "begin": "(?<=[(=,]|=>)\\s*(async)?(?=\\s*((((<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*))?\\()|(<))\\s*$)", "beginCaptures": { "1": { "name": "storage.modifier.async.tsx" @@ -3004,7 +3242,7 @@ "include": "#object-identifiers" }, { - "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ([\\(]\\s*([\\{\\[]\\s*)?$) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", + "match": "(?x)(?:(?:(\\.)|(\\?\\.(?!\\s*[[:digit:]])))\\s*)?([_$[:alpha:]][_$[:alnum:]]*)(?=\\s*=\\s*(\n ((async\\s+)?(\n (function\\s*[(<*]) |\n (function\\s+) |\n ([_$[:alpha:]][_$[:alnum:]]*\\s*=>)\n )) |\n ((async\\s*)?(\n ((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$)) |\n # sure shot arrow functions even if => is on new line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)?\n [(]\\s*\n (\n ([)]\\s*:) | # ():\n ((\\.\\.\\.\\s*)?[_$[:alpha:]][_$[:alnum:]]*\\s*:) # [(]param: | [(]...param:\n )\n) |\n(\n [<]\\s*[_$[:alpha:]][_$[:alnum:]]*\\s+extends\\s*[^=>] # < typeparam extends\n) |\n# arrow function possible to detect only with => on same line\n(\n (<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<]|\\<\\s*([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\))|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\]))([^=<>]|=[^<])*\\>)*>\\s*)? # typeparameters\n \\(\\s*(([_$[:alpha:]]|(\\{([^\\{\\}]|(\\{[^\\{\\}]*\\}))*\\})|(\\[([^\\[\\]]|(\\[[^\\[\\]]*\\]))*\\])|(\\.\\.\\.\\s*[_$[:alpha:]]))([^()]|(\\(([^\\(\\)]|(\\([^\\(\\)]*\\)))*\\)))*)?\\) # parameters\n (\\s*:\\s*([^<>\\(\\)]|\\<[^<>]+\\>|\\([^\\(\\)]+\\))+)? # return type\n \\s*=> # arrow operator\n)\n ))\n))", "captures": { "1": { "name": "punctuation.accessor.tsx" @@ -3474,7 +3712,7 @@ "include": "#destructuring-parameter" }, { - "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*([\\(]\\s*([\\{\\[]\\s*)?$)))", + "match": "(?x)(?:(?)\n ))\n ))\n)) |\n(:\\s*((<\\s*$)|([\\(]\\s*([\\{\\[]\\s*)?$))))", "captures": { "1": { "name": "storage.modifier.tsx" @@ -3846,7 +4084,7 @@ "patterns": [ { "name": "string.regexp.tsx", - "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\])+\\/[gimsuy]*(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "1": { "name": "punctuation.definition.string.begin.tsx" @@ -3869,7 +4107,7 @@ }, { "name": "string.regexp.tsx", - "begin": "(?:*]|&&|\\|\\||\\?|^return|[^\\._$[:alnum:]]return|^default|[^\\._$[:alnum:]]default|^)\\s*(?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?))", - "end": "(?!(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?))", + "begin": "(?:*]|&&|\\|\\||\\?|^await|[^\\._$[:alnum:]]await|^return|[^\\._$[:alnum:]]return|^default|[^\\._$[:alnum:]]default|^yield|[^\\._$[:alnum:]]yield|^)\\s*(?=(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?))", + "end": "(?!(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?))", "patterns": [ { "include": "#jsx-tag-without-attributes" @@ -4618,8 +4856,8 @@ }, "jsx-tag-without-attributes": { "name": "meta.tag.without-attributes.tsx", - "begin": "(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?)", - "end": "()", + "begin": "(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?)", + "end": "()", "beginCaptures": { "1": { "name": "punctuation.definition.tag.begin.tsx" @@ -4668,8 +4906,8 @@ ] }, "jsx-tag-in-expression": { - "begin": "(?x)\n (?:*]|&&|\\|\\||\\?|^return|[^\\._$[:alnum:]]return|^default|[^\\._$[:alnum:]]default|^)\\s*\n (?!<\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s+[^=>])|,)) # look ahead is not type parameter of arrow\n (?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?))", - "end": "(?!(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?))", + "begin": "(?x)\n (?:*]|&&|\\|\\||\\?|^await|[^\\._$[:alnum:]]await|^return|[^\\._$[:alnum:]]return|^default|[^\\._$[:alnum:]]default|^yield|[^\\._$[:alnum:]]yield|^)\\s*\n (?!<\\s*[_$[:alpha:]][_$[:alnum:]]*((\\s+extends\\s+[^=>])|,)) # look ahead is not type parameter of arrow\n (?=(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?))", + "end": "(?!(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?))", "patterns": [ { "include": "#jsx-tag" @@ -4678,8 +4916,8 @@ }, "jsx-tag": { "name": "meta.tag.tsx", - "begin": "(?=(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?))", - "end": "(/>)|(?:())", + "begin": "(?=(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?))", + "end": "(/>)|(?:())", "endCaptures": { "1": { "name": "punctuation.definition.tag.end.tsx" @@ -4705,7 +4943,7 @@ }, "patterns": [ { - "begin": "(<)\\s*(?:([_$a-zA-Z][-$\\w.]*)(?)", + "begin": "(<)\\s*(?:([_$[:alpha:]][-$[:alnum:].]*)(?)", "beginCaptures": { "1": { "name": "punctuation.definition.tag.begin.tsx" @@ -4838,7 +5076,7 @@ ] }, "jsx-tag-attribute-name": { - "match": "(?x)\n \\s*\n (?:([_$a-zA-Z][-$\\w.]*)(:))?\n ([_$a-zA-Z][-$\\w]*)\n (?=\\s|=|/?>|/\\*|//)", + "match": "(?x)\n \\s*\n (?:([_$[:alpha:]][-$[:alnum:].]*)(:))?\n ([_$[:alpha:]][-$[:alnum:]]*)\n (?=\\s|=|/?>|/\\*|//)", "captures": { "1": { "name": "entity.other.attribute-name.namespace.tsx" diff --git a/extensions/typescript-basics/test/colorize-results/test-brackets_tsx.json b/extensions/typescript-basics/test/colorize-results/test-brackets_tsx.json index d0c675c716b..c7e165ae25e 100644 --- a/extensions/typescript-basics/test/colorize-results/test-brackets_tsx.json +++ b/extensions/typescript-basics/test/colorize-results/test-brackets_tsx.json @@ -146,9 +146,9 @@ "c": "//", "t": "source.tsx comment.line.double-slash.tsx punctuation.definition.comment.tsx", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -157,9 +157,9 @@ "c": " Highlight ok here", "t": "source.tsx comment.line.double-slash.tsx", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/typescript-basics/test/colorize-results/test-issue11_ts.json b/extensions/typescript-basics/test/colorize-results/test-issue11_ts.json index 4b2c5a5bbd8..379ac403ce6 100644 --- a/extensions/typescript-basics/test/colorize-results/test-issue11_ts.json +++ b/extensions/typescript-basics/test/colorize-results/test-issue11_ts.json @@ -2960,7 +2960,7 @@ }, { "c": "t", - "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts meta.definition.variable.ts variable.other.readwrite.ts", + "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts meta.definition.variable.ts variable.other.constant.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", diff --git a/extensions/typescript-basics/test/colorize-results/test-issue5431_ts.json b/extensions/typescript-basics/test/colorize-results/test-issue5431_ts.json index e2fc02cc2ad..6a27304f24a 100644 --- a/extensions/typescript-basics/test/colorize-results/test-issue5431_ts.json +++ b/extensions/typescript-basics/test/colorize-results/test-issue5431_ts.json @@ -188,7 +188,7 @@ }, { "c": "timeRange", - "t": "source.ts meta.function.ts meta.block.ts meta.var.expr.ts meta.var-single-variable.expr.ts meta.definition.variable.ts variable.other.readwrite.ts", + "t": "source.ts meta.function.ts meta.block.ts meta.var.expr.ts meta.var-single-variable.expr.ts meta.definition.variable.ts variable.other.constant.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", diff --git a/extensions/typescript-basics/test/colorize-results/test-issue5566_ts.json b/extensions/typescript-basics/test/colorize-results/test-issue5566_ts.json index 86357c7c0cd..c7c430d7632 100644 --- a/extensions/typescript-basics/test/colorize-results/test-issue5566_ts.json +++ b/extensions/typescript-basics/test/colorize-results/test-issue5566_ts.json @@ -111,7 +111,7 @@ }, { "c": "foo", - "t": "source.ts meta.function.ts meta.block.ts meta.var.expr.ts meta.var-single-variable.expr.ts meta.definition.variable.ts entity.name.function.ts", + "t": "source.ts meta.function.ts meta.block.ts meta.var.expr.ts meta.var-single-variable.expr.ts meta.definition.variable.ts variable.other.constant.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", diff --git a/extensions/typescript-basics/test/colorize-results/test_ts.json b/extensions/typescript-basics/test/colorize-results/test_ts.json index 36459b46419..3246facbff2 100644 --- a/extensions/typescript-basics/test/colorize-results/test_ts.json +++ b/extensions/typescript-basics/test/colorize-results/test_ts.json @@ -3,9 +3,9 @@ "c": "/*", "t": "source.ts comment.block.ts punctuation.definition.comment.ts", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -14,9 +14,9 @@ "c": " Game of Life", "t": "source.ts comment.block.ts", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -25,9 +25,9 @@ "c": " * Implemented in TypeScript", "t": "source.ts comment.block.ts", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -36,9 +36,9 @@ "c": " * To learn more about TypeScript, please visit http://www.typescriptlang.org/", "t": "source.ts comment.block.ts", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -47,9 +47,9 @@ "c": " ", "t": "source.ts comment.block.ts", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -58,9 +58,9 @@ "c": "*/", "t": "source.ts comment.block.ts punctuation.definition.comment.ts", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/typescript-language-features/OSSREADME.json b/extensions/typescript-language-features/OSSREADME.json index d36b840718e..692c0592a16 100644 --- a/extensions/typescript-language-features/OSSREADME.json +++ b/extensions/typescript-language-features/OSSREADME.json @@ -7,19 +7,10 @@ "description": "The files syntaxes/TypeScript.tmLanguage.json and syntaxes/TypeScriptReact.tmLanguage.json were derived from TypeScript.tmLanguage and TypeScriptReact.tmLanguage in https://github.com/Microsoft/TypeScript-TmLanguage." }, { - "name": "DefinitelyTyped", - "version": "0.0.2", + "name": "definitelytyped", "license": "MIT", "repositoryURL": "https://github.com/DefinitelyTyped/DefinitelyTyped", - "description": "Typings files that are downloaded by TypeScript. These typings power IntelliSense for JavaScript and TypeScript.", - "licenseDetail": [ - "MIT License", - "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", - ] + "description": "Typings files that are downloaded by TypeScript. These typings power IntelliSense for JavaScript and TypeScript." }, { "name": "Unicode", @@ -80,7 +71,7 @@ "Except as contained in this notice, the name of a copyright holder", "shall not be used in advertising or otherwise to promote the sale,", "use or other dealings in these Data Files or Software without prior", - "written authorization of the copyright holder.", + "written authorization of the copyright holder." ] }, { @@ -91,74 +82,25 @@ "W3C License", "This work is being provided by the copyright holders under the following license.", "By obtaining and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions.", - "Permission to copy, modify, and distribute this work, with or without modification,�for any purpose and without fee or royalty is hereby granted, provided that you include the following ", + "Permission to copy, modify, and distribute this work, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following ", "on ALL copies of the work or portions thereof, including modifications:", "* The full text of this NOTICE in a location viewable to users of the redistributed or derivative work.", "* Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, the W3C Software and Document Short Notice should be included.", "* Notice of any changes or modifications, through a copyright statement on the new code or document such as \"This software or document includes material copied from or derived ", - "from [title and URI of the W3C document]. Copyright � [YEAR] W3C� (MIT, ERCIM, Keio, Beihang).\" ", + "from Document Object Model. Copyright © 2015 W3C® (MIT, ERCIM, Keio, Beihang).\" ", "Disclaimers", "THIS WORK IS PROVIDED \"AS IS", " AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR ", "FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.", "COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT.", "The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the work without specific, written prior permission. ", - "Title to copyright in this work will at all times remain with copyright holders.", + "Title to copyright in this work will at all times remain with copyright holders." ] }, { "name": "Web Background Synchronization", - "license": "W3C Community Final Specification Agreement", - "description": "TypeScript includes files related to this specification", - "licenseDetail": [ - "W3C Community Final Specification Agreement ", - "To secure commitments from participants for the full text of a Community or Business Group Report, the group may call for voluntary commitments to the following terms; a \"summary\" is ", - "available. See also the related \"W3C Community Contributor License Agreement\".", - "1. The Purpose of this Agreement.", - "This Agreement sets forth the terms under which I make certain copyright and patent rights available to you for your implementation of the Specification. ", - "Any other capitalized terms not specifically defined herein have the same meaning as those terms have in the \"W3C Patent Policy\", and if not defined there, in the \"W3C Process Document\".", - "2. Copyrights. ", - "2.1. Copyright Grant. I grant to you a perpetual (for the duration of the applicable copyright), worldwide, non-exclusive, no-charge, royalty-free, copyright license, without any obligation for accounting to me, to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, distribute, and implement the Specification to the full extent of my copyright interest in the Specification. ", - "2.2. Attribution. As a condition of the copyright grant, you must include an attribution to the Specification in any derivative work you make based on the Specification. That attribution must include, at minimum, the Specification name and version number.", - "3. Patents. ", - "3.1. Patent Licensing Commitment. I agree to license my Essential Claims under the W3C Community RF Licensing Requirements. This requirement includes Essential Claims that I own and any that I have the right to license without obligation of payment or other consideration to an unrelated third party. W3C Community RF Licensing Requirements obligations made concerning the Specification and described in this policy are binding on me for the life of the patents in question and encumber the patents containing Essential Claims, regardless of changes in participation status or W3C Membership. I also agree to license my Essential Claims under the W3C Community RF Licensing Requirements in derivative works of the Specification so long as all normative portions of the Specification are maintained and that this licensing commitment does not extend to any portion of the derivative work that was not included in the Specification.", - "3.2. Optional, Additional Patent Grant. In addition to the provisions of Section 3.1, I may also, at my option, make certain intellectual property rights infringed by implementations of the Specification, including Essential Claims, available by providing those terms via the W3C Web site.", - "4. No Other Rights. Except as specifically set forth in this Agreement, no other express or implied patent, trademark, copyright, or other property rights are granted under this Agreement, including by implication, waiver, or estoppel.", - "5. Antitrust Compliance. I acknowledge that I may compete with other participants, that I am under no obligation to implement the Specification, that each participant is free to develop competing technologies and standards, and that each party is free to license its patent rights to third parties, including for the purpose of enabling competing technologies and standards.", - "6. Non-Circumvention. I agree that I will not intentionally take or willfully assist any third party to take any action for the purpose of circumventing my obligations under this Agreement.", - "7. Transition to W3C Recommendation Track. The Specification developed by the Project may transition to the W3C Recommendation Track. The W3C Team is responsible for notifying me that a Corresponding Working Group has been chartered. I have no obligation to join the Corresponding Working Group. If the Specification developed by the Project transitions to the W3C Recommendation Track, the following terms apply: ", - "7.1. If I join the Corresponding Working Group. If I join the Corresponding Working Group, I will be subject to all W3C rules, obligations, licensing commitments, and policies that govern that Corresponding Working Group.", - "7.2. If I Do Not Join the Corresponding Working Group. ", - "7.2.1. Licensing Obligations to Resulting Specification. If I do not join the Corresponding Working Group, I agree to offer patent licenses according to the W3C Royalty-Free licensing requirements described in Section 5 of the W3C Patent Policy for the portions of the Specification included in the resulting Recommendation. This licensing commitment does not extend to any portion of an implementation of the Recommendation that was not included in the Specification. This licensing commitment may not be revoked but may be modified through the exclusion process defined in Section 4 of the W3C Patent Policy. I am not required to join the Corresponding Working Group to exclude patents from the W3C Royalty-Free licensing commitment, but must otherwise follow the normal exclusion procedures defined by the W3C Patent Policy. The W3C Team will notify me of any Call for Exclusion in the Corresponding Working Group as set forth in Section 4.5 of the W3C Patent Policy.", - "7.2.2. No Disclosure Obligation. If I do not join the Corresponding Working Group, I have no patent disclosure obligations outside of those set forth in Section 6 of the W3C Patent Policy.", - "8. Conflict of Interest. I will disclose significant relationships when those relationships might reasonably be perceived as creating a conflict of interest with my role. I will notify W3C of any change in my affiliation using W3C-provided mechanisms.", - "9. Representations, Warranties and Disclaimers. I represent and warrant that I am legally entitled to grant the rights and promises set forth in this Agreement. IN ALL OTHER RESPECTS THE SPECIFICATION IS PROVIDED �AS IS.� The entire risk as to implementing or otherwise using the Specification is assumed by the implementer and user. Except as stated herein, I expressly disclaim any warranties (express, implied, or otherwise), including implied warranties of merchantability, non-infringement, fitness for a particular purpose, or title, related to the Specification. IN NO EVENT WILL ANY PARTY BE LIABLE TO ANY OTHER PARTY FOR LOST PROFITS OR ANY FORM OF INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER FROM ANY CAUSES OF ACTION OF ANY KIND WITH RESPECT TO THIS AGREEMENT, WHETHER BASED ON BREACH OF CONTRACT, TORT (INCLUDING NEGLIGENCE), OR OTHERWISE, AND WHETHER OR NOT THE OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. All of my obligations under Section 3 regarding the transfer, successors in interest, or assignment of Granted Claims will be satisfied if I notify the transferee or assignee of any patent that I know contains Granted Claims of the obligations under Section 3. Nothing in this Agreement requires me to undertake a patent search.", - "10. Definitions. ", - "10.1. Agreement. �Agreement� means this W3C Community Final Specification Agreement.", - "10.2. Corresponding Working Group. �Corresponding Working Group� is a W3C Working Group that is chartered to develop a Recommendation, as defined in the W3C Process Document, that takes the Specification as an input.", - "10.3. Essential Claims. �Essential Claims� shall mean all claims in any patent or patent application in any jurisdiction in the world that would necessarily be infringed by implementation of the Specification. A claim is necessarily infringed hereunder only when it is not possible to avoid infringing it because there is no non-infringing alternative for implementing the normative portions of the Specification. Existence of a non-infringing alternative shall be judged based on the state of the art at the time of the publication of the Specification. The following are expressly excluded from and shall not be deemed to constitute Essential Claims: ", - "10.3.1. any claims other than as set forth above even if contained in the same patent as Essential Claims; and", - "10.3.2. claims which would be infringed only by: ", - "portions of an implementation that are not specified in the normative portions of the Specification, or", - "enabling technologies that may be necessary to make or use any product or portion thereof that complies with the Specification and are not themselves expressly set forth in the Specification (e.g., semiconductor manufacturing technology, compiler technology, object-oriented technology, basic operating system technology, and the like); or", - "the implementation of technology developed elsewhere and merely incorporated by reference in the body of the Specification.", - "10.3.3. design patents and design registrations.", - "For purposes of this definition, the normative portions of the Specification shall be deemed to include only architectural and interoperability requirements. Optional features in the RFC 2119 sense are considered normative unless they are specifically identified as informative. Implementation examples or any other material that merely illustrate the requirements of the Specification are informative, rather than normative.", - "10.4. I, Me, or My. �I,� �me,� or �my� refers to the signatory.", - "10.5 Project. �Project� means the W3C Community Group or Business Group for which I executed this Agreement.", - "10.6. Specification. �Specification� means the Specification identified by the Project as the target of this agreement in a call for Final Specification Commitments. W3C shall provide the authoritative mechanisms for the identification of this Specification.", - "10.7. W3C Community RF Licensing Requirements. �W3C Community RF Licensing Requirements� license shall mean a non-assignable, non-sublicensable license to make, have made, use, sell, have sold, offer to sell, import, and distribute and dispose of implementations of the Specification that: ", - "10.7.1. shall be available to all, worldwide, whether or not they are W3C Members;", - "10.7.2. shall extend to all Essential Claims owned or controlled by me;", - "10.7.3. may be limited to implementations of the Specification, and to what is required by the Specification;", - "10.7.4. may be conditioned on a grant of a reciprocal RF license (as defined in this policy) to all Essential Claims owned or controlled by the licensee. A reciprocal license may be required to be available to all, and a reciprocal license may itself be conditioned on a further reciprocal license from all.", - "10.7.5. may not be conditioned on payment of royalties, fees or other consideration;", - "10.7.6. may be suspended with respect to any licensee when licensor issued by licensee for infringement of claims essential to implement the Specification or any W3C Recommendation;", - "10.7.7. may not impose any further conditions or restrictions on the use of any technology, intellectual property rights, or other restrictions on behavior of the licensee, but may include reasonable, customary terms relating to operation or maintenance of the license relationship such as the following: choice of law and dispute resolution;", - "10.7.8. shall not be considered accepted by an implementer who manifests an intent not to accept the terms of the W3C Community RF Licensing Requirements license as offered by the licensor.", - "10.7.9. The RF license conforming to the requirements in this policy shall be made available by the licensor as long as the Specification is in effect. The term of such license shall be for the life of the patents in question.", - "I am encouraged to provide a contact from which licensing information can be obtained and other relevant licensing information. Any such information will be made publicly available. ", - "10.8. You or Your. �You,� �you,� or �your� means any person or entity who exercises copyright or patent rights granted under this Agreement, and any person that person or entity controls.", - ] + "license": "Apache2", + "repositoryURL": "https://github.com/WICG/BackgroundSync", + "description": "TypeScript includes files related to this specification" } ] \ No newline at end of file diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 6ad08cfc974..cf13bdf97cd 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -18,7 +18,7 @@ "dependencies": { "jsonc-parser": "^2.0.1", "semver": "4.3.6", - "vscode-extension-telemetry": "0.0.17", + "vscode-extension-telemetry": "0.0.18", "vscode-nls": "^3.2.4" }, "devDependencies": { @@ -73,7 +73,8 @@ "type": "boolean", "default": false, "description": "%typescript.disableAutomaticTypeAcquisition%", - "scope": "window" + "scope": "window", + "tags": ["usesOnlineServices"] }, "typescript.npm": { "type": [ @@ -366,7 +367,7 @@ "javascript.implicitProjectConfig.experimentalDecorators": { "type": "boolean", "default": false, - "description": "%javascript.implicitProjectConfig.experimentalDecorators%", + "markdownDescription": "%javascript.implicitProjectConfig.experimentalDecorators%", "scope": "window" }, "javascript.nameSuggestions": { @@ -384,6 +385,12 @@ "build", "watch" ], + "enumDescriptions": [ + "%typescript.tsc.autoDetect.on%", + "%typescript.tsc.autoDetect.off%", + "%typescript.tsc.autoDetect.build%", + "%typescript.tsc.autoDetect.watch%" + ], "description": "%typescript.tsc.autoDetect%", "scope": "window" }, @@ -418,7 +425,7 @@ null ], "default": null, - "description": "%typescript.locale%", + "markdownDescription": "%typescript.locale%", "scope": "window" }, "javascript.suggestionActions.enabled": { @@ -441,7 +448,7 @@ "double" ], "default": "auto", - "description": "%typescript.preferences.quoteStyle%", + "markdownDescription": "%typescript.preferences.quoteStyle%", "scope": "resource" }, "typescript.preferences.quoteStyle": { @@ -452,7 +459,7 @@ "double" ], "default": "auto", - "description": "%typescript.preferences.quoteStyle%", + "markdownDescription": "%typescript.preferences.quoteStyle%", "scope": "resource" }, "javascript.preferences.importModuleSpecifier": { @@ -462,6 +469,11 @@ "relative", "non-relative" ], + "markdownEnumDescriptions": [ + "%typescript.preferences.importModuleSpecifier.auto%", + "%typescript.preferences.importModuleSpecifier.relative%", + "%typescript.preferences.importModuleSpecifier.nonRelative%" + ], "default": "auto", "description": "%typescript.preferences.importModuleSpecifier%", "scope": "resource" @@ -473,22 +485,15 @@ "relative", "non-relative" ], + "enumDescriptions": [ + "%typescript.preferences.importModuleSpecifier.auto%", + "%typescript.preferences.importModuleSpecifier.relative%", + "%typescript.preferences.importModuleSpecifier.nonRelative%" + ], "default": "auto", "description": "%typescript.preferences.importModuleSpecifier%", "scope": "resource" }, - "javascript.showUnused": { - "type": "boolean", - "default": true, - "description": "%typescript.showUnused%", - "scope": "resource" - }, - "typescript.showUnused": { - "type": "boolean", - "default": true, - "description": "%typescript.showUnused%", - "scope": "resource" - }, "typescript.updateImportsOnFileMove.enabled": { "type": "string", "enum": [ @@ -496,6 +501,11 @@ "always", "never" ], + "enumDescriptions": [ + "%typescript.updateImportsOnFileMove.enabled.prompt%", + "%typescript.updateImportsOnFileMove.enabled.always%", + "%typescript.updateImportsOnFileMove.enabled.never%" + ], "default": "prompt", "description": "%typescript.updateImportsOnFileMove.enabled%", "scope": "resource" diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index 6876a3706ab..15b80915353 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -15,7 +15,7 @@ "javascript.format.enable": "Enable/disable default JavaScript formatter.", "format.insertSpaceAfterCommaDelimiter": "Defines space handling after a comma delimiter.", "format.insertSpaceAfterConstructor": "Defines space handling after the constructor keyword. Requires using TypeScript 2.3.0 or newer in the workspace.", - "format.insertSpaceAfterSemicolonInForStatements": " Defines space handling after a semicolon in a for statement.", + "format.insertSpaceAfterSemicolonInForStatements": "Defines space handling after a semicolon in a for statement.", "format.insertSpaceBeforeAndAfterBinaryOperators": "Defines space handling after a binary operator.", "format.insertSpaceAfterKeywordsInControlFlowStatements": "Defines space handling after keywords in a control flow statement.", "format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": "Defines space handling after function keyword for anonymous functions.", @@ -32,29 +32,38 @@ "goToProjectConfig.title": "Go to Project Configuration", "javascript.referencesCodeLens.enabled": "Enable/disable references CodeLens in JavaScript files.", "typescript.referencesCodeLens.enabled": "Enable/disable references CodeLens in TypeScript files.", - "typescript.implementationsCodeLens.enabled": "Enable/disable implementations CodeLens.", - "typescript.openTsServerLog.title": "Open TS Server log", - "typescript.restartTsServer": "Restart TS server", - "typescript.selectTypeScriptVersion.title": "Select TypeScript Version", - "typescript.reportStyleChecksAsWarnings": "Report style checks as warnings", - "jsDocCompletion.enabled": "Enable/disable auto JSDoc comments", + "typescript.implementationsCodeLens.enabled": "Enable/disable implementations CodeLens. This CodeLens shows the implementers of an interface.", + "typescript.openTsServerLog.title": "Open TS Server log.", + "typescript.restartTsServer": "Restart TS server.", + "typescript.selectTypeScriptVersion.title": "Select TypeScript Version.", + "typescript.reportStyleChecksAsWarnings": "Report style checks as warnings.", + "jsDocCompletion.enabled": "Enable/disable auto JSDoc comments.", "javascript.implicitProjectConfig.checkJs": "Enable/disable semantic checking of JavaScript files. Existing jsconfig.json or tsconfig.json files override this setting. Requires using TypeScript 2.3.1 or newer in the workspace.", "typescript.npm": "Specifies the path to the NPM executable used for Automatic Type Acquisition. Requires using TypeScript 2.3.4 or newer in the workspace.", "typescript.check.npmIsInstalled": "Check if NPM is installed for Automatic Type Acquisition.", "javascript.nameSuggestions": "Enable/disable including unique names from the file in JavaScript suggestion lists.", - "typescript.tsc.autoDetect": "Controls auto detection of tsc tasks. 'off' disables this feature. 'build' only creates single run compile tasks. 'watch' only creates compile and watch tasks. 'on' creates both build and watch tasks. Default is 'on'.", + "typescript.tsc.autoDetect": "Controls auto detection of tsc tasks.", + "typescript.tsc.autoDetect.off": "Disable this feature.", + "typescript.tsc.autoDetect.on": "Create both build and watch tasks.", + "typescript.tsc.autoDetect.build": "Only create single run compile tasks.", + "typescript.tsc.autoDetect.watch": "Only create compile and watch tasks.", "typescript.problemMatchers.tsc.label": "TypeScript problems", "typescript.problemMatchers.tscWatch.label": "TypeScript problems (watch mode)", "typescript.quickSuggestionsForPaths": "Enable/disable quick suggestions when typing out an import path.", - "typescript.locale": "Sets the locale used to report JavaScript and TypeScript errors. Requires using TypeScript 2.6.0 or newer in the workspace. Default of 'null' uses VS Code's locale.", - "javascript.implicitProjectConfig.experimentalDecorators": "Enable/disable 'experimentalDecorators' for JavaScript files that are not part of a project. Existing jsconfig.json or tsconfig.json files override this setting. Requires using TypeScript 2.3.1 or newer in the workspace.", + "typescript.locale": "Sets the locale used to report JavaScript and TypeScript errors. Requires using TypeScript 2.6.0 or newer in the workspace. Default of `null` uses VS Code's locale.", + "javascript.implicitProjectConfig.experimentalDecorators": "Enable/disable `experimentalDecorators` for JavaScript files that are not part of a project. Existing jsconfig.json or tsconfig.json files override this setting. Requires using TypeScript 2.3.1 or newer in the workspace.", "typescript.autoImportSuggestions.enabled": "Enable/disable auto import suggestions. Requires using TypeScript 2.6.1 or newer in the workspace.", "taskDefinition.tsconfig.description": "The tsconfig file that defines the TS build.", "javascript.suggestionActions.enabled": "Enable/disable suggestion diagnostics for JavaScript files in the editor. Requires using TypeScript 2.8 or newer in the workspace.", "typescript.suggestionActions.enabled": "Enable/disable suggestion diagnostics for TypeScript files in the editor. Requires using TypeScript 2.8 or newer in the workspace.", - "typescript.preferences.quoteStyle": "Preferred quote style to use for quick fixes: 'single' quotes, 'double' quotes, or 'auto' infer quote type from existing imports. Requires using TypeScript 2.9 or newer in the workspace.", - "typescript.preferences.importModuleSpecifier": "Preferred path style for auto imports:\n- \"relative\" to the file location.\n- \"non-relative\" based on the 'baseUrl' configured in your 'jsconfig.json' / 'tsconfig.json'.\n- \"auto\" infer the shortest path type.\nRequires using TypeScript 2.9 or newer in the workspace.", - "typescript.showUnused": "Enable/disable highlighting of unused variables in code. Requires using TypeScript 2.9 or newer in the workspace.", - "typescript.updateImportsOnFileMove.enabled": "Enable/disable automatic updating of import paths when you rename or move a file in VS Code. Possible values are: 'prompt' on each rename, 'always' update paths automatically, and 'never' rename paths and don't prompt me. Requires using TypeScript 2.9 or newer in the workspace.", + "typescript.preferences.quoteStyle": "Preferred quote style to use for quick fixes: `single` quotes, `double` quotes, or `auto` infer quote type from existing imports. Requires using TypeScript 2.9 or newer in the workspace.", + "typescript.preferences.importModuleSpecifier": "Preferred path style for auto imports.", + "typescript.preferences.importModuleSpecifier.auto": "Infer the shortest path type.", + "typescript.preferences.importModuleSpecifier.relative": "Relative to the file location.", + "typescript.preferences.importModuleSpecifier.nonRelative": "Based on the `baseUrl` configured in your `jsconfig.json` / `tsconfig.json`.", + "typescript.updateImportsOnFileMove.enabled": "Enable/disable automatic updating of import paths when you rename or move a file in VS Code. Requires using TypeScript 2.9 or newer in the workspace.", + "typescript.updateImportsOnFileMove.enabled.prompt": "Prompt on each rename.", + "typescript.updateImportsOnFileMove.enabled.always": "Always update paths automatically.", + "typescript.updateImportsOnFileMove.enabled.never": "Never rename paths and don't prompt.", "typescript.autoClosingTags": "Enable/disable automatic closing of JSX tags. Requires using TypeScript 3.0 or newer in the workspace." } diff --git a/extensions/typescript-language-features/src/commands.ts b/extensions/typescript-language-features/src/commands.ts index fce4da698a2..2c40c512e18 100644 --- a/extensions/typescript-language-features/src/commands.ts +++ b/extensions/typescript-language-features/src/commands.ts @@ -9,6 +9,7 @@ import TypeScriptServiceClientHost from './typeScriptServiceClientHost'; import { Command } from './utils/commandManager'; import { Lazy } from './utils/lazy'; import { isImplicitProjectConfigFile, openOrCreateConfigFile } from './utils/tsconfig'; +import { nulToken } from './utils/cancellation'; const localize = nls.loadMessageBundle(); @@ -131,7 +132,7 @@ async function goToProjectConfig( let res: protocol.ProjectInfoResponse | undefined = undefined; try { - res = await client.execute('projectInfo', { file, needFileNameList: false } as protocol.ProjectInfoRequestArgs); + res = await client.execute('projectInfo', { file, needFileNameList: false }, nulToken); } catch { // noop } diff --git a/extensions/typescript-language-features/src/extension.ts b/extensions/typescript-language-features/src/extension.ts index 2a6dd787d06..74f0ac6f509 100644 --- a/extensions/typescript-language-features/src/extension.ts +++ b/extensions/typescript-language-features/src/extension.ts @@ -16,6 +16,7 @@ import LogDirectoryProvider from './utils/logDirectoryProvider'; import ManagedFileContextManager from './utils/managedFileContext'; import { getContributedTypeScriptServerPlugins, TypeScriptServerPlugin } from './utils/plugins'; import * as ProjectStatus from './utils/projectStatus'; +import { flatten } from './utils/arrays'; export function activate( @@ -36,7 +37,10 @@ export function activate( context.subscriptions.push(module.register()); }); - const supportedLanguage = [].concat.apply([], standardLanguageDescriptions.map(x => x.modeIds).concat(plugins.map(x => x.languages))); + const supportedLanguage = flatten([ + ...standardLanguageDescriptions.map(x => x.modeIds), + ...plugins.map(x => x.languages) + ]); function didOpenTextDocument(textDocument: vscode.TextDocument): boolean { if (isSupportedDocument(supportedLanguage, textDocument)) { openListener.dispose(); diff --git a/extensions/typescript-language-features/src/features/baseCodeLensProvider.ts b/extensions/typescript-language-features/src/features/baseCodeLensProvider.ts index 9576008eab4..6204d3521ac 100644 --- a/extensions/typescript-language-features/src/features/baseCodeLensProvider.ts +++ b/extensions/typescript-language-features/src/features/baseCodeLensProvider.ts @@ -3,18 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancellationToken, CodeLens, CodeLensProvider, Event, EventEmitter, Position, Range, TextDocument, Uri } from 'vscode'; +import * as vscode from 'vscode'; import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import { escapeRegExp } from '../utils/regexp'; import * as typeConverters from '../utils/typeConverters'; -export class ReferencesCodeLens extends CodeLens { +export class ReferencesCodeLens extends vscode.CodeLens { constructor( - public document: Uri, + public document: vscode.Uri, public file: string, - range: Range + range: vscode.Range ) { super(range); } @@ -26,7 +26,7 @@ export class CachedNavTreeResponse { private document: string = ''; public execute( - document: TextDocument, + document: vscode.TextDocument, f: () => Promise ) { if (this.matches(document)) { @@ -36,12 +36,12 @@ export class CachedNavTreeResponse { return this.update(document, f()); } - private matches(document: TextDocument): boolean { + private matches(document: vscode.TextDocument): boolean { return this.version === document.version && this.document === document.uri.toString(); } private update( - document: TextDocument, + document: vscode.TextDocument, response: Promise ): Promise { this.response = response; @@ -51,19 +51,19 @@ export class CachedNavTreeResponse { } } -export abstract class TypeScriptBaseCodeLensProvider implements CodeLensProvider { - private onDidChangeCodeLensesEmitter = new EventEmitter(); +export abstract class TypeScriptBaseCodeLensProvider implements vscode.CodeLensProvider { + private onDidChangeCodeLensesEmitter = new vscode.EventEmitter(); public constructor( protected client: ITypeScriptServiceClient, private cachedResponse: CachedNavTreeResponse ) { } - public get onDidChangeCodeLenses(): Event { + public get onDidChangeCodeLenses(): vscode.Event { return this.onDidChangeCodeLensesEmitter.event; } - async provideCodeLenses(document: TextDocument, token: CancellationToken): Promise { + async provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { const filepath = this.client.toPath(document.uri); if (!filepath) { return []; @@ -76,7 +76,7 @@ export abstract class TypeScriptBaseCodeLensProvider implements CodeLensProvider } const tree = response.body; - const referenceableSpans: Range[] = []; + const referenceableSpans: vscode.Range[] = []; if (tree && tree.childItems) { tree.childItems.forEach(item => this.walkNavTree(document, item, null, referenceableSpans)); } @@ -87,16 +87,16 @@ export abstract class TypeScriptBaseCodeLensProvider implements CodeLensProvider } protected abstract extractSymbol( - document: TextDocument, + document: vscode.TextDocument, item: Proto.NavigationTree, parent: Proto.NavigationTree | null - ): Range | null; + ): vscode.Range | null; private walkNavTree( - document: TextDocument, + document: vscode.TextDocument, item: Proto.NavigationTree, parent: Proto.NavigationTree | null, - results: Range[] + results: vscode.Range[] ): void { if (!item) { return; @@ -109,7 +109,7 @@ export abstract class TypeScriptBaseCodeLensProvider implements CodeLensProvider (item.childItems || []).forEach(child => this.walkNavTree(document, child, item, results)); } - protected getSymbolRange(document: TextDocument, item: Proto.NavigationTree): Range | null { + protected getSymbolRange(document: vscode.TextDocument, item: Proto.NavigationTree): vscode.Range | null { if (!item) { return null; } @@ -131,8 +131,8 @@ export abstract class TypeScriptBaseCodeLensProvider implements CodeLensProvider const identifierMatch = new RegExp(`^(.*?(\\b|\\W))${escapeRegExp(item.text || '')}(\\b|\\W)`, 'gm'); const match = identifierMatch.exec(text); const prefixLength = match ? match.index + match[1].length : 0; - const startOffset = document.offsetAt(new Position(range.start.line, range.start.character)) + prefixLength; - return new Range( + const startOffset = document.offsetAt(new vscode.Position(range.start.line, range.start.character)) + prefixLength; + return new vscode.Range( document.positionAt(startOffset), document.positionAt(startOffset + item.text.length)); } diff --git a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts index f6b4e8c174e..d18304c63ee 100644 --- a/extensions/typescript-language-features/src/features/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/features/bufferSyncSupport.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as fs from 'fs'; -import { CancellationTokenSource, Disposable, EventEmitter, TextDocument, TextDocumentChangeEvent, TextDocumentContentChangeEvent, Uri, workspace } from 'vscode'; +import * as vscode from 'vscode'; import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { Delayer } from '../utils/async'; -import { disposeAll } from '../utils/dispose'; +import { Disposable } from '../utils/dispose'; import * as languageModeIds from '../utils/languageModeIds'; import { ResourceMap } from '../utils/resourceMap'; import * as typeConverters from '../utils/typeConverters'; @@ -32,7 +32,7 @@ function mode2ScriptKind(mode: string): 'TS' | 'TSX' | 'JS' | 'JSX' | undefined class SyncedBuffer { constructor( - private readonly document: TextDocument, + private readonly document: vscode.TextDocument, public readonly filepath: string, private readonly client: ITypeScriptServiceClient ) { } @@ -63,10 +63,10 @@ class SyncedBuffer { } } - this.client.execute('open', args, false); + this.client.executeWithoutWaitingForResponse('open', args); } - public get resource(): Uri { + public get resource(): vscode.Uri { return this.document.uri; } @@ -91,16 +91,16 @@ class SyncedBuffer { const args: Proto.FileRequestArgs = { file: this.filepath }; - this.client.execute('close', args, false); + this.client.executeWithoutWaitingForResponse('close', args); } - public onContentChanged(events: TextDocumentContentChangeEvent[]): void { + public onContentChanged(events: vscode.TextDocumentContentChangeEvent[]): void { for (const { range, text } of events) { const args: Proto.ChangeRequestArgs = { insertString: text, ...typeConverters.Range.toFormattingRequestArgs(this.filepath, range) }; - this.client.execute('change', args, false); + this.client.executeWithoutWaitingForResponse('change', args); } } } @@ -108,7 +108,7 @@ class SyncedBuffer { class SyncedBufferMap extends ResourceMap { public getForPath(filePath: string): SyncedBuffer | undefined { - return this.get(Uri.file(filePath)); + return this.get(vscode.Uri.file(filePath)); } public get allBuffers(): Iterable { @@ -117,10 +117,16 @@ class SyncedBufferMap extends ResourceMap { } class PendingDiagnostics extends ResourceMap { - public getFileList(): Set { - return new Set(Array.from(this.entries) - .sort((a, b) => a[1] - b[1]) - .map(entry => entry[0])); + public getOrderedFileSet(): ResourceMap { + const orderedResources = Array.from(this.entries) + .sort((a, b) => a.value - b.value) + .map(entry => entry.resource); + + const map = new ResourceMap(); + for (const resource of orderedResources) { + map.set(resource, void 0); + } + return map; } } @@ -128,10 +134,10 @@ class GetErrRequest { public static executeGetErrRequest( client: ITypeScriptServiceClient, - files: string[], + files: ResourceMap, onDone: () => void ) { - const token = new CancellationTokenSource(); + const token = new vscode.CancellationTokenSource(); return new GetErrRequest(client, files, token, onDone); } @@ -139,13 +145,15 @@ class GetErrRequest { private constructor( client: ITypeScriptServiceClient, - public readonly files: string[], - private readonly _token: CancellationTokenSource, + public readonly files: ResourceMap, + private readonly _token: vscode.CancellationTokenSource, onDone: () => void ) { const args: Proto.GeterrRequestArgs = { delay: 0, - files + files: Array.from(files.entries) + .map(entry => client.normalizedPath(entry.resource)) + .filter(x => !!x) as string[] }; client.executeAsync('geterr', args, _token.token) @@ -168,14 +176,13 @@ class GetErrRequest { } } -export default class BufferSyncSupport { +export default class BufferSyncSupport extends Disposable { private readonly client: ITypeScriptServiceClient; private _validateJavaScript: boolean = true; private _validateTypeScript: boolean = true; private readonly modeIds: Set; - private readonly disposables: Disposable[] = []; private readonly syncedBuffers: SyncedBufferMap; private readonly pendingDiagnostics: PendingDiagnostics; private readonly diagnosticDelayer: Delayer; @@ -186,20 +193,21 @@ export default class BufferSyncSupport { client: ITypeScriptServiceClient, modeIds: string[] ) { + super(); this.client = client; this.modeIds = new Set(modeIds); this.diagnosticDelayer = new Delayer(300); - const pathNormalizer = (path: Uri) => this.client.normalizedPath(path); + const pathNormalizer = (path: vscode.Uri) => this.client.normalizedPath(path); this.syncedBuffers = new SyncedBufferMap(pathNormalizer); this.pendingDiagnostics = new PendingDiagnostics(pathNormalizer); this.updateConfiguration(); - workspace.onDidChangeConfiguration(this.updateConfiguration, this, this.disposables); + vscode.workspace.onDidChangeConfiguration(this.updateConfiguration, this, this._disposables); } - private readonly _onDelete = new EventEmitter(); + private readonly _onDelete = this._register(new vscode.EventEmitter()); public readonly onDelete = this._onDelete.event; public listen(): void { @@ -207,22 +215,22 @@ export default class BufferSyncSupport { return; } this.listening = true; - workspace.onDidOpenTextDocument(this.openTextDocument, this, this.disposables); - workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, this.disposables); - workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, this.disposables); - workspace.textDocuments.forEach(this.openTextDocument, this); + vscode.workspace.onDidOpenTextDocument(this.openTextDocument, this, this._disposables); + vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, this._disposables); + vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, this._disposables); + vscode.workspace.textDocuments.forEach(this.openTextDocument, this); } - public handles(resource: Uri): boolean { + public handles(resource: vscode.Uri): boolean { return this.syncedBuffers.has(resource); } - public toResource(filePath: string): Uri { + public toResource(filePath: string): vscode.Uri { const buffer = this.syncedBuffers.getForPath(filePath); if (buffer) { return buffer.resource; } - return Uri.file(filePath); + return vscode.Uri.file(filePath); } public reOpenDocuments(): void { @@ -231,12 +239,7 @@ export default class BufferSyncSupport { } } - public dispose(): void { - disposeAll(this.disposables); - this._onDelete.dispose(); - } - - public openTextDocument(document: TextDocument): void { + public openTextDocument(document: vscode.TextDocument): void { if (!this.modeIds.has(document.languageId)) { return; } @@ -256,11 +259,12 @@ export default class BufferSyncSupport { this.requestDiagnostic(syncedBuffer); } - public closeResource(resource: Uri): void { + public closeResource(resource: vscode.Uri): void { const syncedBuffer = this.syncedBuffers.get(resource); if (!syncedBuffer) { return; } + this.pendingDiagnostics.delete(resource); this.syncedBuffers.delete(resource); syncedBuffer.close(); if (!fs.existsSync(resource.fsPath)) { @@ -269,11 +273,25 @@ export default class BufferSyncSupport { } } - private onDidCloseTextDocument(document: TextDocument): void { + public interuptGetErr(f: () => R): R { + // TODO: re-enable for 1.27 insiders + return f(); + // if (!this.pendingGetErr) { + // return f(); + // } + + // this.pendingGetErr.cancel(); + // this.pendingGetErr = undefined; + // const result = f(); + // this.triggerDiagnostics(); + // return result; + } + + private onDidCloseTextDocument(document: vscode.TextDocument): void { this.closeResource(document.uri); } - private onDidChangeTextDocument(e: TextDocumentChangeEvent): void { + private onDidChangeTextDocument(e: vscode.TextDocumentChangeEvent): void { const syncedBuffer = this.syncedBuffers.get(e.document.uri); if (!syncedBuffer) { return; @@ -299,7 +317,7 @@ export default class BufferSyncSupport { this.triggerDiagnostics(); } - public getErr(resources: Uri[]): any { + public getErr(resources: vscode.Uri[]): any { const handledResources = resources.filter(resource => this.handles(resource)); if (!handledResources.length) { return; @@ -330,32 +348,28 @@ export default class BufferSyncSupport { return true; } - public hasPendingDiagnostics(resource: Uri): boolean { + public hasPendingDiagnostics(resource: vscode.Uri): boolean { return this.pendingDiagnostics.has(resource); } private sendPendingDiagnostics(): void { - const fileList = this.pendingDiagnostics.getFileList(); + const orderedFileSet = this.pendingDiagnostics.getOrderedFileSet(); // Add all open TS buffers to the geterr request. They might be visible for (const buffer of this.syncedBuffers.values) { - if (!this.pendingDiagnostics.has(buffer.resource)) { - fileList.add(buffer.filepath); - } + orderedFileSet.set(buffer.resource, void 0); } - if (this.pendingGetErr) { - for (const file of this.pendingGetErr.files) { - fileList.add(file); - } - } - - if (fileList.size) { + if (orderedFileSet.size) { if (this.pendingGetErr) { this.pendingGetErr.cancel(); + + for (const file of this.pendingGetErr.files.entries) { + orderedFileSet.set(file.resource, void 0); + } } - const getErr = this.pendingGetErr = GetErrRequest.executeGetErrRequest(this.client, Array.from(fileList), () => { + const getErr = this.pendingGetErr = GetErrRequest.executeGetErrRequest(this.client, orderedFileSet, () => { if (this.pendingGetErr === getErr) { this.pendingGetErr = undefined; } @@ -366,8 +380,8 @@ export default class BufferSyncSupport { } private updateConfiguration() { - const jsConfig = workspace.getConfiguration('javascript', null); - const tsConfig = workspace.getConfiguration('typescript', null); + const jsConfig = vscode.workspace.getConfiguration('javascript', null); + const tsConfig = vscode.workspace.getConfiguration('typescript', null); this._validateJavaScript = jsConfig.get('validate.enable', true); this._validateTypeScript = tsConfig.get('validate.enable', true); diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/features/completions.ts index c113f11bebc..063bd8cbbc6 100644 --- a/extensions/typescript-language-features/src/features/completions.ts +++ b/extensions/typescript-language-features/src/features/completions.ts @@ -16,6 +16,7 @@ import * as typeConverters from '../utils/typeConverters'; import TypingsStatus from '../utils/typingsStatus'; import FileConfigurationManager from './fileConfigurationManager'; import { memoize } from '../utils/memoize'; +import { nulToken } from '../utils/cancellation'; const localize = nls.loadMessageBundle(); @@ -205,7 +206,7 @@ class ApplyCompletionCodeActionCommand implements Command { } if (codeActions.length === 1) { - return applyCodeAction(this.client, codeActions[0]); + return applyCodeAction(this.client, codeActions[0], nulToken); } interface MyQuickPickItem extends vscode.QuickPickItem { @@ -230,7 +231,7 @@ class ApplyCompletionCodeActionCommand implements Command { if (!action) { return false; } - return applyCodeAction(this.client, action); + return applyCodeAction(this.client, action, nulToken); } } @@ -303,7 +304,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider return null; } - await this.fileConfigurationManager.ensureConfigurationForDocument(document, token); + await this.client.interuptGetErr(() => this.fileConfigurationManager.ensureConfigurationForDocument(document, token)); const args: Proto.CompletionsRequestArgs = { ...typeConverters.Position.toFileLocationRequestArgs(file, position), @@ -312,19 +313,18 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider triggerCharacter: context.triggerCharacter as Proto.CompletionsTriggerCharacter }; - let enableCommitCharacters = true; let msg: ReadonlyArray | undefined = undefined; try { if (this.client.apiVersion.gte(API.v300)) { - const { body } = await this.client.execute('completionInfo', args, token); + const { body } = await this.client.interuptGetErr(() => this.client.execute('completionInfo', args, token)); if (!body) { return null; } enableCommitCharacters = !body.isNewIdentifierLocation; msg = body.entries; } else { - const { body } = await this.client.execute('completions', args, token); + const { body } = await this.client.interuptGetErr(() => this.client.execute('completions', args, token)); if (!body) { return null; } @@ -364,14 +364,14 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider ] }; - let response: Proto.CompletionDetailsResponse; + let details: Proto.CompletionEntryDetails[] | undefined; try { - response = await this.client.execute('completionEntryDetails', args, token); + const { body } = await this.client.execute('completionEntryDetails', args, token); + details = body; } catch { return item; } - const details = response.body; if (!details || !details.length || !details[0]) { return item; } @@ -384,7 +384,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider item.additionalTextEdits = additionalTextEdits; if (detail && item.useCodeSnippet) { - const shouldCompleteFunction = await this.isValidFunctionCompletionContext(filepath, item.position); + const shouldCompleteFunction = await this.isValidFunctionCompletionContext(filepath, item.position, token); if (shouldCompleteFunction) { item.insertText = this.snippetForFunctionCall(item, detail); } @@ -524,14 +524,14 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider private async isValidFunctionCompletionContext( filepath: string, - position: vscode.Position + position: vscode.Position, + token: vscode.CancellationToken ): Promise { // Workaround for https://github.com/Microsoft/TypeScript/issues/12677 // Don't complete function calls inside of destructive assigments or imports try { - const infoResponse = await this.client.execute('quickinfo', typeConverters.Position.toFileLocationRequestArgs(filepath, position)); - const info = infoResponse.body; - switch (info && info.kind) { + const { body } = await this.client.execute('quickinfo', typeConverters.Position.toFileLocationRequestArgs(filepath, position), token); + switch (body && body.kind) { case 'var': case 'let': case 'const': diff --git a/extensions/typescript-language-features/src/features/definitionProviderBase.ts b/extensions/typescript-language-features/src/features/definitionProviderBase.ts index 8d958648799..186d3e711dd 100644 --- a/extensions/typescript-language-features/src/features/definitionProviderBase.ts +++ b/extensions/typescript-language-features/src/features/definitionProviderBase.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancellationToken, Location, Position, TextDocument } from 'vscode'; +import * as vscode from 'vscode'; import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import * as typeConverters from '../utils/typeConverters'; @@ -16,10 +16,10 @@ export default class TypeScriptDefinitionProviderBase { protected async getSymbolLocations( definitionType: 'definition' | 'implementation' | 'typeDefinition', - document: TextDocument, - position: Position, - token: CancellationToken | boolean - ): Promise { + document: vscode.TextDocument, + position: vscode.Position, + token: vscode.CancellationToken + ): Promise { const filepath = this.client.toPath(document.uri); if (!filepath) { return undefined; @@ -32,7 +32,7 @@ export default class TypeScriptDefinitionProviderBase { return locations.map(location => typeConverters.Location.fromTextSpan(this.client.toResource(location.file), location)); } catch { - return []; + return undefined; } } } \ No newline at end of file diff --git a/extensions/typescript-language-features/src/features/definitions.ts b/extensions/typescript-language-features/src/features/definitions.ts index 9ff840cdab4..6e260d5d089 100644 --- a/extensions/typescript-language-features/src/features/definitions.ts +++ b/extensions/typescript-language-features/src/features/definitions.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import * as typeConverters from '../utils/typeConverters'; @@ -17,15 +16,10 @@ export default class TypeScriptDefinitionProvider extends DefinitionProviderBase super(client); } - public async provideDefinition() { - // Implemented by provideDefinition2 - return undefined; - } - - public async provideDefinition2( + public async provideDefinition( document: vscode.TextDocument, position: vscode.Position, - token: vscode.CancellationToken | boolean + token: vscode.CancellationToken ): Promise { if (this.client.apiVersion.gte(API.v270)) { const filepath = this.client.toPath(document.uri); @@ -35,19 +29,19 @@ export default class TypeScriptDefinitionProvider extends DefinitionProviderBase const args = typeConverters.Position.toFileLocationRequestArgs(filepath, position); try { - const response = await this.client.execute('definitionAndBoundSpan', args, token); - const locations: Proto.FileSpan[] = (response && response.body && response.body.definitions) || []; - if (!locations) { + const { body } = await this.client.execute('definitionAndBoundSpan', args, token); + if (!body) { return undefined; } - const span = response.body.textSpan ? typeConverters.Range.fromTextSpan(response.body.textSpan) : undefined; - return locations + const span = body.textSpan ? typeConverters.Range.fromTextSpan(body.textSpan) : undefined; + return body.definitions .map(location => { - const loc = typeConverters.Location.fromTextSpan(this.client.toResource(location.file), location); - return { - origin: span, - ...loc, + const target = typeConverters.Location.fromTextSpan(this.client.toResource(location.file), location); + return { + originSelectionRange: span, + targetRange: target.range, + targetUri: target.uri, }; }); } catch { diff --git a/extensions/typescript-language-features/src/features/diagnostics.ts b/extensions/typescript-language-features/src/features/diagnostics.ts index bbe608e7a2c..5915247fe0c 100644 --- a/extensions/typescript-language-features/src/features/diagnostics.ts +++ b/extensions/typescript-language-features/src/features/diagnostics.ts @@ -5,25 +5,7 @@ import * as vscode from 'vscode'; import { ResourceMap } from '../utils/resourceMap'; - -export class DiagnosticSet { - private _map = new ResourceMap(); - - public set( - file: vscode.Uri, - diagnostics: vscode.Diagnostic[] - ) { - this._map.set(file, diagnostics); - } - - public get(file: vscode.Uri): vscode.Diagnostic[] { - return this._map.get(file) || []; - } - - public clear(): void { - this._map = new ResourceMap(); - } -} +import { DiagnosticLanguage, allDiagnosticLangauges } from '../utils/languageDescription'; export enum DiagnosticKind { Syntax, @@ -31,25 +13,129 @@ export enum DiagnosticKind { Suggestion } -const allDiagnosticKinds = [DiagnosticKind.Syntax, DiagnosticKind.Semantic, DiagnosticKind.Suggestion]; +class FileDiagnostics { + private readonly _diagnostics = new Map(); + + constructor( + public readonly file: vscode.Uri, + public language: DiagnosticLanguage + ) { } + + public updateDiagnostics( + language: DiagnosticLanguage, + kind: DiagnosticKind, + diagnostics: vscode.Diagnostic[] + ): boolean { + if (language !== this.language) { + this._diagnostics.clear(); + this.language = language; + } + + if (diagnostics.length === 0) { + const existing = this._diagnostics.get(kind); + if (!existing || existing && existing.length === 0) { + // No need to update + return false; + } + } + + this._diagnostics.set(kind, diagnostics); + return true; + } + + public getDiagnostics(settings: DiagnosticSettings): vscode.Diagnostic[] { + if (!settings.getValidate(this.language)) { + return []; + } + + return [ + ...this.get(DiagnosticKind.Syntax), + ...this.get(DiagnosticKind.Semantic), + ...this.getSuggestionDiagnostics(settings), + ]; + } + + private getSuggestionDiagnostics(settings: DiagnosticSettings) { + const enableSuggestions = settings.getEnableSuggestions(this.language); + return this.get(DiagnosticKind.Suggestion).filter(x => { + if (!enableSuggestions) { + // Still show unused + return x.tags && x.tags.indexOf(vscode.DiagnosticTag.Unnecessary) !== -1; + } + return true; + }); + } + + private get(kind: DiagnosticKind): vscode.Diagnostic[] { + return this._diagnostics.get(kind) || []; + } +} + +interface LangaugeDiagnosticSettings { + readonly validate: boolean; + readonly enableSuggestions: boolean; +} + +class DiagnosticSettings { + private static readonly defaultSettings: LangaugeDiagnosticSettings = { + validate: true, + enableSuggestions: true + }; + + private readonly _languageSettings = new Map(); + + constructor() { + for (const language of allDiagnosticLangauges) { + this._languageSettings.set(language, DiagnosticSettings.defaultSettings); + } + } + + public getValidate(language: DiagnosticLanguage): boolean { + return this.get(language).validate; + } + + public setValidate(language: DiagnosticLanguage, value: boolean): boolean { + return this.update(language, settings => ({ + validate: value, + enableSuggestions: settings.enableSuggestions + })); + } + + public getEnableSuggestions(language: DiagnosticLanguage): boolean { + return this.get(language).enableSuggestions; + } + + public setEnableSuggestions(language: DiagnosticLanguage, value: boolean): boolean { + return this.update(language, settings => ({ + validate: settings.validate, + enableSuggestions: value + })); + } + + private get(language: DiagnosticLanguage): LangaugeDiagnosticSettings { + return this._languageSettings.get(language) || DiagnosticSettings.defaultSettings; + } + + private update(language: DiagnosticLanguage, f: (x: LangaugeDiagnosticSettings) => LangaugeDiagnosticSettings): boolean { + const currentSettings = this.get(language); + const newSettings = f(currentSettings); + this._languageSettings.set(language, newSettings); + return currentSettings.validate === newSettings.validate + && currentSettings.enableSuggestions && currentSettings.enableSuggestions; + } +} export class DiagnosticsManager { - - private readonly _diagnostics = new Map(); + private readonly _diagnostics = new ResourceMap(); + private readonly _settings = new DiagnosticSettings(); private readonly _currentDiagnostics: vscode.DiagnosticCollection; private _pendingUpdates = new ResourceMap(); - private _validate: boolean = true; - private _enableSuggestions: boolean = true; - private readonly updateDelay = 50; + private readonly _updateDelay = 50; constructor( owner: string ) { - for (const kind of allDiagnosticKinds) { - this._diagnostics.set(kind, new DiagnosticSet()); - } - this._currentDiagnostics = vscode.languages.createDiagnosticCollection(owner); } @@ -64,63 +150,56 @@ export class DiagnosticsManager { public reInitialize(): void { this._currentDiagnostics.clear(); + this._diagnostics.clear(); + } - for (const diagnosticSet of this._diagnostics.values()) { - diagnosticSet.clear(); + public setValidate(language: DiagnosticLanguage, value: boolean) { + const didUpdate = this._settings.setValidate(language, value); + if (didUpdate) { + this.rebuild(); } } - public set validate(value: boolean) { - if (this._validate === value) { - return; - } - - this._validate = value; - if (!value) { - this._currentDiagnostics.clear(); + public setEnableSuggestions(language: DiagnosticLanguage, value: boolean) { + const didUpdate = this._settings.setEnableSuggestions(language, value); + if (didUpdate) { + this.rebuild(); } } - public set enableSuggestions(value: boolean) { - if (this._enableSuggestions === value) { - return; - } - - this._enableSuggestions = value; - if (!value) { - this._currentDiagnostics.clear(); - } - } - - public diagnosticsReceived( + public updateDiagnostics( + file: vscode.Uri, + language: DiagnosticLanguage, kind: DiagnosticKind, + diagnostics: vscode.Diagnostic[] + ): void { + + let didUpdate = false; + const entry = this._diagnostics.get(file); + if (entry) { + didUpdate = entry.updateDiagnostics(language, kind, diagnostics); + } else if (diagnostics.length) { + const fileDiagnostics = new FileDiagnostics(file, language); + fileDiagnostics.updateDiagnostics(language, kind, diagnostics); + this._diagnostics.set(file, fileDiagnostics); + didUpdate = true; + } + + if (didUpdate) { + this.scheduleDiagnosticsUpdate(file); + } + } + + public configFileDiagnosticsReceived( file: vscode.Uri, diagnostics: vscode.Diagnostic[] ): void { - const collection = this._diagnostics.get(kind); - if (!collection) { - return; - } - - if (diagnostics.length === 0) { - const existing = collection.get(file); - if (existing.length === 0) { - // No need to update - return; - } - } - - collection.set(file, diagnostics); - - this.scheduleDiagnosticsUpdate(file); - } - - public configFileDiagnosticsReceived(file: vscode.Uri, diagnostics: vscode.Diagnostic[]): void { this._currentDiagnostics.set(file, diagnostics); } public delete(resource: vscode.Uri): void { this._currentDiagnostics.delete(resource); + this._diagnostics.delete(resource); } public getDiagnostics(file: vscode.Uri): vscode.Diagnostic[] { @@ -129,35 +208,24 @@ export class DiagnosticsManager { private scheduleDiagnosticsUpdate(file: vscode.Uri) { if (!this._pendingUpdates.has(file)) { - this._pendingUpdates.set(file, setTimeout(() => this.updateCurrentDiagnostics(file), this.updateDelay)); + this._pendingUpdates.set(file, setTimeout(() => this.updateCurrentDiagnostics(file), this._updateDelay)); } } - private updateCurrentDiagnostics(file: vscode.Uri) { + private updateCurrentDiagnostics(file: vscode.Uri): void { if (this._pendingUpdates.has(file)) { clearTimeout(this._pendingUpdates.get(file)); this._pendingUpdates.delete(file); } - if (!this._validate) { - return; - } - - const allDiagnostics = [ - ...this._diagnostics.get(DiagnosticKind.Syntax)!.get(file), - ...this._diagnostics.get(DiagnosticKind.Semantic)!.get(file), - ...this.getSuggestionDiagnostics(file), - ]; - this._currentDiagnostics.set(file, allDiagnostics); + const fileDiagnostics = this._diagnostics.get(file); + this._currentDiagnostics.set(file, fileDiagnostics ? fileDiagnostics.getDiagnostics(this._settings) : []); } - private getSuggestionDiagnostics(file: vscode.Uri) { - return this._diagnostics.get(DiagnosticKind.Suggestion)!.get(file).filter(x => { - if (!this._enableSuggestions) { - // Still show unused - return x.tags && x.tags.indexOf(vscode.DiagnosticTag.Unnecessary) !== -1; - } - return true; - }); + private rebuild(): void { + this._currentDiagnostics.clear(); + for (const fileDiagnostic of Array.from(this._diagnostics.values)) { + this._currentDiagnostics.set(fileDiagnostic.file, fileDiagnostic.getDiagnostics(this._settings)); + } } } \ No newline at end of file diff --git a/extensions/typescript-language-features/src/features/directiveCommentCompletions.ts b/extensions/typescript-language-features/src/features/directiveCommentCompletions.ts index 09a6fcf8c56..8ec98eefb8f 100644 --- a/extensions/typescript-language-features/src/features/directiveCommentCompletions.ts +++ b/extensions/typescript-language-features/src/features/directiveCommentCompletions.ts @@ -12,8 +12,8 @@ import { VersionDependentRegistration } from '../utils/dependentRegistration'; const localize = nls.loadMessageBundle(); interface Directive { - value: string; - description: string; + readonly value: string; + readonly description: string; } const directives: Directive[] = [ @@ -21,17 +21,17 @@ const directives: Directive[] = [ value: '@ts-check', description: localize( 'ts-check', - 'Enables semantic checking in a JavaScript file. Must be at the top of a file.') + "Enables semantic checking in a JavaScript file. Must be at the top of a file.") }, { value: '@ts-nocheck', description: localize( 'ts-nocheck', - 'Disables semantic checking in a JavaScript file. Must be at the top of a file.') + "Disables semantic checking in a JavaScript file. Must be at the top of a file.") }, { value: '@ts-ignore', description: localize( 'ts-ignore', - 'Suppresses @ts-check errors on the next line of a file.') + "Suppresses @ts-check errors on the next line of a file.") } ]; @@ -63,13 +63,6 @@ class DirectiveCommentCompletionProvider implements vscode.CompletionItemProvide } return []; } - - public resolveCompletionItem( - item: vscode.CompletionItem, - _token: vscode.CancellationToken - ) { - return item; - } } export function register( diff --git a/extensions/typescript-language-features/src/features/documentHighlight.ts b/extensions/typescript-language-features/src/features/documentHighlight.ts index 0741aa4628b..d661e154fdd 100644 --- a/extensions/typescript-language-features/src/features/documentHighlight.ts +++ b/extensions/typescript-language-features/src/features/documentHighlight.ts @@ -26,18 +26,20 @@ class TypeScriptDocumentHighlightProvider implements vscode.DocumentHighlightPro } const args = typeConverters.Position.toFileLocationRequestArgs(file, position); + let items: Proto.OccurrencesResponseItem[]; try { - const response = await this.client.execute('occurrences', args, token); - if (response && response.body) { - return response.body - .filter(x => !x.isInString) - .map(documentHighlightFromOccurance); + const { body } = await this.client.execute('occurrences', args, token); + if (!body) { + return []; } + items = body; } catch { - // noop + return []; } - return []; + return items + .filter(x => !x.isInString) + .map(documentHighlightFromOccurance); } } diff --git a/extensions/typescript-language-features/src/features/documentSymbol.ts b/extensions/typescript-language-features/src/features/documentSymbol.ts index 84d108a4d23..8925d2fb3b1 100644 --- a/extensions/typescript-language-features/src/features/documentSymbol.ts +++ b/extensions/typescript-language-features/src/features/documentSymbol.ts @@ -7,10 +7,8 @@ import * as vscode from 'vscode'; import * as Proto from '../protocol'; import * as PConst from '../protocol.const'; import { ITypeScriptServiceClient } from '../typescriptService'; -import API from '../utils/api'; import * as typeConverters from '../utils/typeConverters'; - const getSymbolKind = (kind: string): vscode.SymbolKind => { switch (kind) { case PConst.Kind.module: return vscode.SymbolKind.Module; @@ -33,83 +31,63 @@ const getSymbolKind = (kind: string): vscode.SymbolKind => { class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider { public constructor( - private readonly client: ITypeScriptServiceClient) { } + private readonly client: ITypeScriptServiceClient + ) { } - public async provideDocumentSymbols(resource: vscode.TextDocument, token: vscode.CancellationToken): Promise { - const filepath = this.client.toPath(resource.uri); - if (!filepath) { - return []; + public async provideDocumentSymbols(resource: vscode.TextDocument, token: vscode.CancellationToken): Promise { + const file = this.client.toPath(resource.uri); + if (!file) { + return undefined; } - const args: Proto.FileRequestArgs = { - file: filepath - }; + let tree: Proto.NavigationTree; try { - if (this.client.apiVersion.gte(API.v206)) { - const response = await this.client.execute('navtree', args, token); - if (response.body) { - // The root represents the file. Ignore this when showing in the UI - const tree = response.body; - if (tree.childItems) { - const result = new Array(); - tree.childItems.forEach(item => TypeScriptDocumentSymbolProvider.convertNavTree(resource.uri, result, item)); - return result; - } - } - } else { - const response = await this.client.execute('navbar', args, token); - if (response.body) { - const result = new Array(); - const foldingMap: ObjectMap = Object.create(null); - response.body.forEach(item => TypeScriptDocumentSymbolProvider.convertNavBar(resource.uri, 0, foldingMap, result as vscode.SymbolInformation[], item)); - return result; - } + const args: Proto.FileRequestArgs = { file }; + const { body } = await this.client.execute('navtree', args, token); + if (!body) { + return undefined; } - return []; - } catch (e) { - return []; + tree = body; + } catch { + return undefined; } - } - private static convertNavBar(resource: vscode.Uri, indent: number, foldingMap: ObjectMap, bucket: vscode.SymbolInformation[], item: Proto.NavigationBarItem, containerLabel?: string): void { - const realIndent = indent + item.indent; - const key = `${realIndent}|${item.text}`; - if (realIndent !== 0 && !foldingMap[key] && TypeScriptDocumentSymbolProvider.shouldInclueEntry(item)) { - const result = new vscode.SymbolInformation(item.text, - getSymbolKind(item.kind), - containerLabel ? containerLabel : '', - typeConverters.Location.fromTextSpan(resource, item.spans[0])); - foldingMap[key] = result; - bucket.push(result); - } - if (item.childItems && item.childItems.length > 0) { - for (const child of item.childItems) { - TypeScriptDocumentSymbolProvider.convertNavBar(resource, realIndent + 1, foldingMap, bucket, child, item.text); - } + if (tree && tree.childItems) { + // The root represents the file. Ignore this when showing in the UI + const result: vscode.DocumentSymbol[] = []; + tree.childItems.forEach(item => TypeScriptDocumentSymbolProvider.convertNavTree(resource.uri, result, item)); + return result; } + + return undefined; } private static convertNavTree(resource: vscode.Uri, bucket: vscode.DocumentSymbol[], item: Proto.NavigationTree): boolean { - const symbolInfo = new vscode.DocumentSymbol( - item.text, - '', - getSymbolKind(item.kind), - typeConverters.Range.fromTextSpan(item.spans[0]), - typeConverters.Range.fromTextSpan(item.spans[0]), - ); - let shouldInclude = TypeScriptDocumentSymbolProvider.shouldInclueEntry(item); - if (item.childItems) { - for (const child of item.childItems) { - const includedChild = TypeScriptDocumentSymbolProvider.convertNavTree(resource, symbolInfo.children, child); - shouldInclude = shouldInclude || includedChild; + const children = new Set(item.childItems || []); + for (const span of item.spans) { + const range = typeConverters.Range.fromTextSpan(span); + const symbolInfo = new vscode.DocumentSymbol( + item.text, + '', + getSymbolKind(item.kind), + range, + range); + + for (const child of children) { + if (child.spans.some(span => !!range.intersection(typeConverters.Range.fromTextSpan(span)))) { + const includedChild = TypeScriptDocumentSymbolProvider.convertNavTree(resource, symbolInfo.children, child); + shouldInclude = shouldInclude || includedChild; + children.delete(child); + } + } + + if (shouldInclude) { + bucket.push(symbolInfo); } } - if (shouldInclude) { - bucket.push(symbolInfo); - } return shouldInclude; } diff --git a/extensions/typescript-language-features/src/features/fileConfigurationManager.ts b/extensions/typescript-language-features/src/features/fileConfigurationManager.ts index 1869ae9ae1b..3068e8df5e1 100644 --- a/extensions/typescript-language-features/src/features/fileConfigurationManager.ts +++ b/extensions/typescript-language-features/src/features/fileConfigurationManager.ts @@ -3,11 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancellationToken, Disposable, FormattingOptions, TextDocument, window, workspace as Workspace, workspace, WorkspaceConfiguration } from 'vscode'; +import * as vscode from 'vscode'; import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { isTypeScriptDocument } from '../utils/languageModeIds'; +import { ResourceMap } from '../utils/resourceMap'; function objsAreEqual(a: T, b: T): boolean { @@ -22,8 +23,8 @@ function objsAreEqual(a: T, b: T): boolean { } interface FileConfiguration { - formatOptions: Proto.FormatCodeSettings; - preferences: Proto.UserPreferences; + readonly formatOptions: Proto.FormatCodeSettings; + readonly preferences: Proto.UserPreferences; } function areFileConfigurationsEqual(a: FileConfiguration, b: FileConfiguration): boolean { @@ -34,19 +35,18 @@ function areFileConfigurationsEqual(a: FileConfiguration, b: FileConfiguration): } export default class FileConfigurationManager { - private onDidCloseTextDocumentSub: Disposable | undefined; - private formatOptions: { [key: string]: FileConfiguration | undefined } = Object.create(null); + private onDidCloseTextDocumentSub: vscode.Disposable | undefined; + private formatOptions = new ResourceMap(); public constructor( private readonly client: ITypeScriptServiceClient ) { - this.onDidCloseTextDocumentSub = Workspace.onDidCloseTextDocument((textDocument) => { - const key = textDocument.uri.toString(); + this.onDidCloseTextDocumentSub = vscode.workspace.onDidCloseTextDocument((textDocument) => { // When a document gets closed delete the cached formatting options. // This is necessary since the tsserver now closed a project when its // last file in it closes which drops the stored formatting options // as well. - delete this.formatOptions[key]; + this.formatOptions.delete(textDocument.uri); }); } @@ -58,38 +58,44 @@ export default class FileConfigurationManager { } public async ensureConfigurationForDocument( - document: TextDocument, - token: CancellationToken | undefined + document: vscode.TextDocument, + token: vscode.CancellationToken ): Promise { - const editor = window.visibleTextEditors.find(editor => editor.document.fileName === document.fileName); - if (editor) { - const formattingOptions = { - tabSize: editor.options.tabSize, - insertSpaces: editor.options.insertSpaces - } as FormattingOptions; + const formattingOptions = this.getFormattingOptions(document); + if (formattingOptions) { return this.ensureConfigurationOptions(document, formattingOptions, token); } } + private getFormattingOptions( + document: vscode.TextDocument + ): vscode.FormattingOptions | undefined { + const editor = vscode.window.visibleTextEditors.find(editor => editor.document.fileName === document.fileName); + return editor + ? { + tabSize: editor.options.tabSize, + insertSpaces: editor.options.insertSpaces + } as vscode.FormattingOptions + : undefined; + } + public async ensureConfigurationOptions( - document: TextDocument, - options: FormattingOptions, - token: CancellationToken | undefined + document: vscode.TextDocument, + options: vscode.FormattingOptions, + token: vscode.CancellationToken ): Promise { const file = this.client.toPath(document.uri); if (!file) { return; } - const key = document.uri.toString(); - const cachedOptions = this.formatOptions[key]; + const cachedOptions = this.formatOptions.get(document.uri); const currentOptions = this.getFileOptions(document, options); - if (cachedOptions && areFileConfigurationsEqual(cachedOptions, currentOptions)) { return; } - this.formatOptions[key] = currentOptions; + this.formatOptions.set(document.uri, currentOptions); const args: Proto.ConfigureRequestArguments = { file, ...currentOptions, @@ -97,14 +103,29 @@ export default class FileConfigurationManager { await this.client.execute('configure', args, token); } - public reset() { - this.formatOptions = Object.create(null); + public async setGlobalConfigurationFromDocument( + document: vscode.TextDocument, + token: vscode.CancellationToken, + ): Promise { + const formattingOptions = this.getFormattingOptions(document); + if (!formattingOptions) { + return; + } + + const args: Proto.ConfigureRequestArguments = { + file: undefined /*global*/, + ...this.getFileOptions(document, formattingOptions), + }; + await this.client.execute('configure', args, token); } + public reset() { + this.formatOptions.clear(); + } private getFileOptions( - document: TextDocument, - options: FormattingOptions + document: vscode.TextDocument, + options: vscode.FormattingOptions ): FileConfiguration { return { formatOptions: this.getFormatOptions(document, options), @@ -113,10 +134,10 @@ export default class FileConfigurationManager { } private getFormatOptions( - document: TextDocument, - options: FormattingOptions + document: vscode.TextDocument, + options: vscode.FormattingOptions ): Proto.FormatCodeSettings { - const config = workspace.getConfiguration( + const config = vscode.workspace.getConfiguration( isTypeScriptDocument(document) ? 'typescript.format' : 'javascript.format', document.uri); @@ -144,12 +165,12 @@ export default class FileConfigurationManager { }; } - private getPreferences(document: TextDocument): Proto.UserPreferences { + private getPreferences(document: vscode.TextDocument): Proto.UserPreferences { if (!this.client.apiVersion.gte(API.v290)) { return {}; } - const preferences = workspace.getConfiguration( + const preferences = vscode.workspace.getConfiguration( isTypeScriptDocument(document) ? 'typescript.preferences' : 'javascript.preferences', document.uri); @@ -161,7 +182,7 @@ export default class FileConfigurationManager { } } -function getQuoteStylePreference(config: WorkspaceConfiguration) { +function getQuoteStylePreference(config: vscode.WorkspaceConfiguration) { switch (config.get('quoteStyle')) { case 'single': return 'single'; case 'double': return 'double'; @@ -169,7 +190,7 @@ function getQuoteStylePreference(config: WorkspaceConfiguration) { } } -function getImportModuleSpecifierPreference(config: WorkspaceConfiguration) { +function getImportModuleSpecifierPreference(config: vscode.WorkspaceConfiguration) { switch (config.get('importModuleSpecifier')) { case 'relative': return 'relative'; case 'non-relative': return 'non-relative'; diff --git a/extensions/typescript-language-features/src/features/folding.ts b/extensions/typescript-language-features/src/features/folding.ts index 78f3193fe00..471cf26cff2 100644 --- a/extensions/typescript-language-features/src/features/folding.ts +++ b/extensions/typescript-language-features/src/features/folding.ts @@ -26,12 +26,12 @@ class TypeScriptFoldingProvider implements vscode.FoldingRangeProvider { } const args: Proto.FileRequestArgs = { file }; - const response: Proto.OutliningSpansResponse = await this.client.execute('getOutliningSpans', args, token); - if (!response || !response.body) { + const { body } = await this.client.execute('getOutliningSpans', args, token); + if (!body) { return; } - return response.body + return body .map(span => this.convertOutliningSpan(span, document)) .filter(foldingRange => !!foldingRange) as vscode.FoldingRange[]; } diff --git a/extensions/typescript-language-features/src/features/formatting.ts b/extensions/typescript-language-features/src/features/formatting.ts index 029db28f870..b3ecff09d17 100644 --- a/extensions/typescript-language-features/src/features/formatting.ts +++ b/extensions/typescript-language-features/src/features/formatting.ts @@ -10,53 +10,38 @@ import { ConfigurationDependentRegistration } from '../utils/dependentRegistrati import * as typeConverters from '../utils/typeConverters'; import FileConfigurationManager from './fileConfigurationManager'; - class TypeScriptFormattingProvider implements vscode.DocumentRangeFormattingEditProvider, vscode.OnTypeFormattingEditProvider { - private enabled: boolean = true; - public constructor( private readonly client: ITypeScriptServiceClient, private readonly formattingOptionsManager: FileConfigurationManager ) { } - public updateConfiguration(config: vscode.WorkspaceConfiguration): void { - this.enabled = config.get('format.enable', true); - } - - public isEnabled(): boolean { - return this.enabled; - } - - private async doFormat( - document: vscode.TextDocument, - options: vscode.FormattingOptions, - args: Proto.FormatRequestArgs, - token: vscode.CancellationToken - ): Promise { - await this.formattingOptionsManager.ensureConfigurationOptions(document, options, token); - try { - const response = await this.client.execute('format', args, token); - if (response.body) { - return response.body.map(typeConverters.TextEdit.fromCodeEdit); - } - } catch { - // noop - } - return []; - } - public async provideDocumentRangeFormattingEdits( document: vscode.TextDocument, range: vscode.Range, options: vscode.FormattingOptions, token: vscode.CancellationToken - ): Promise { + ): Promise { const file = this.client.toPath(document.uri); if (!file) { - return []; + return undefined; } - const args = typeConverters.Range.toFormattingRequestArgs(file, range); - return this.doFormat(document, options, args, token); + + await this.formattingOptionsManager.ensureConfigurationOptions(document, options, token); + + let edits: Proto.CodeEdit[]; + try { + const args = typeConverters.Range.toFormattingRequestArgs(file, range); + const { body } = await this.client.execute('format', args, token); + if (!body) { + return undefined; + } + edits = body; + } catch { + return undefined; + } + + return edits.map(typeConverters.TextEdit.fromCodeEdit); } public async provideOnTypeFormattingEdits( @@ -66,22 +51,20 @@ class TypeScriptFormattingProvider implements vscode.DocumentRangeFormattingEdit options: vscode.FormattingOptions, token: vscode.CancellationToken ): Promise { - const filepath = this.client.toPath(document.uri); - if (!filepath) { + const file = this.client.toPath(document.uri); + if (!file) { return []; } await this.formattingOptionsManager.ensureConfigurationOptions(document, options, token); const args: Proto.FormatOnKeyRequestArgs = { - file: filepath, - line: position.line + 1, - offset: position.character + 1, + ...typeConverters.Position.toFileLocationRequestArgs(file, position), key: ch }; try { - const response = await this.client.execute('formatonkey', args, token); - const edits = response.body; + const { body } = await this.client.execute('formatonkey', args, token); + const edits = body; const result: vscode.TextEdit[] = []; if (!edits) { return result; diff --git a/extensions/typescript-language-features/src/features/hover.ts b/extensions/typescript-language-features/src/features/hover.ts index fcc2b3401a8..19f78ee5f33 100644 --- a/extensions/typescript-language-features/src/features/hover.ts +++ b/extensions/typescript-language-features/src/features/hover.ts @@ -25,14 +25,14 @@ class TypeScriptHoverProvider implements vscode.HoverProvider { if (!filepath) { return undefined; } + const args = typeConverters.Position.toFileLocationRequestArgs(filepath, position); try { - const response = await this.client.execute('quickinfo', args, token); - if (response && response.body) { - const data = response.body; + const { body } = await this.client.interuptGetErr(() => this.client.execute('quickinfo', args, token)); + if (body) { return new vscode.Hover( - TypeScriptHoverProvider.getContents(data), - typeConverters.Range.fromTextSpan(data)); + TypeScriptHoverProvider.getContents(body), + typeConverters.Range.fromTextSpan(body)); } } catch (e) { // noop diff --git a/extensions/typescript-language-features/src/features/implementations.ts b/extensions/typescript-language-features/src/features/implementations.ts index c2627a35244..d750587ea8d 100644 --- a/extensions/typescript-language-features/src/features/implementations.ts +++ b/extensions/typescript-language-features/src/features/implementations.ts @@ -10,7 +10,7 @@ import { VersionDependentRegistration } from '../utils/dependentRegistration'; import DefinitionProviderBase from './definitionProviderBase'; class TypeScriptImplementationProvider extends DefinitionProviderBase implements vscode.ImplementationProvider { - public provideImplementation(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken | boolean): Promise { + public provideImplementation(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { return this.getSymbolLocations('implementation', document, position, token); } } diff --git a/extensions/typescript-language-features/src/features/implementationsCodeLens.ts b/extensions/typescript-language-features/src/features/implementationsCodeLens.ts index 1a205b8b7fb..5b4ef7b0eb0 100644 --- a/extensions/typescript-language-features/src/features/implementationsCodeLens.ts +++ b/extensions/typescript-language-features/src/features/implementationsCodeLens.ts @@ -23,9 +23,9 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip const codeLens = inputCodeLens as ReferencesCodeLens; const args = typeConverters.Position.toFileLocationRequestArgs(codeLens.file, codeLens.range.start); try { - const response = await this.client.execute('implementation', args, token); - if (response && response.body) { - const locations = response.body + const { body } = await this.client.execute('implementation', args, token); + if (body) { + const locations = body .map(reference => // Only take first line on implementation: https://github.com/Microsoft/vscode/issues/23924 new vscode.Location(this.client.toResource(reference.file), diff --git a/extensions/typescript-language-features/src/features/jsDocCompletions.ts b/extensions/typescript-language-features/src/features/jsDocCompletions.ts index b8683b81e77..02eb7439d95 100644 --- a/extensions/typescript-language-features/src/features/jsDocCompletions.ts +++ b/extensions/typescript-language-features/src/features/jsDocCompletions.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancellationToken, CompletionItem, CompletionItemKind, CompletionItemProvider, Disposable, DocumentSelector, languages, Position, Range, SnippetString, TextDocument, TextEditor, Uri, window } from 'vscode'; +import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; @@ -14,12 +14,12 @@ import * as typeConverters from '../utils/typeConverters'; const localize = nls.loadMessageBundle(); -class JsDocCompletionItem extends CompletionItem { +class JsDocCompletionItem extends vscode.CompletionItem { constructor( - document: TextDocument, - position: Position + document: vscode.TextDocument, + position: vscode.Position ) { - super('/** */', CompletionItemKind.Snippet); + super('/** */', vscode.CompletionItemKind.Snippet); this.detail = localize('typescript.jsDocCompletionItem.documentation', 'JSDoc comment'); this.insertText = ''; this.sortText = '\0'; @@ -28,7 +28,7 @@ class JsDocCompletionItem extends CompletionItem { const prefix = line.slice(0, position.character).match(/\/\**\s*$/); const suffix = line.slice(position.character).match(/^\s*\**\//); const start = position.translate(0, prefix ? -prefix[0].length : 0); - this.range = new Range( + this.range = new vscode.Range( start, position.translate(0, suffix ? suffix[0].length : 0)); @@ -40,7 +40,7 @@ class JsDocCompletionItem extends CompletionItem { } } -class JsDocCompletionProvider implements CompletionItemProvider { +class JsDocCompletionProvider implements vscode.CompletionItemProvider { constructor( private readonly client: ITypeScriptServiceClient, @@ -50,10 +50,10 @@ class JsDocCompletionProvider implements CompletionItemProvider { } public async provideCompletionItems( - document: TextDocument, - position: Position, - token: CancellationToken - ): Promise { + document: vscode.TextDocument, + position: vscode.Position, + token: vscode.CancellationToken + ): Promise { const file = this.client.toPath(document.uri); if (!file) { return []; @@ -72,8 +72,8 @@ class JsDocCompletionProvider implements CompletionItemProvider { private async isCommentableLocation( file: string, - position: Position, - token: CancellationToken + position: vscode.Position, + token: vscode.CancellationToken ): Promise { const args: Proto.FileRequestArgs = { file @@ -104,7 +104,7 @@ class JsDocCompletionProvider implements CompletionItemProvider { return matchesPosition(body); } - private isValidCursorPosition(document: TextDocument, position: Position): boolean { + private isValidCursorPosition(document: vscode.TextDocument, position: vscode.Position): boolean { // Only show the JSdoc completion when the everything before the cursor is whitespace // or could be the opening of a comment const line = document.lineAt(position.line).text; @@ -112,7 +112,7 @@ class JsDocCompletionProvider implements CompletionItemProvider { return prefix.match(/^\s*$|\/\*\*\s*$|^\s*\/\*\*+\s*$/) !== null; } - public resolveCompletionItem(item: CompletionItem, _token: CancellationToken) { + public resolveCompletionItem(item: vscode.CompletionItem, _token: vscode.CancellationToken) { return item; } } @@ -129,13 +129,13 @@ class TryCompleteJsDocCommand implements Command { * Try to insert a jsdoc comment, using a template provide by typescript * if possible, otherwise falling back to a default comment format. */ - public async execute(resource: Uri, start: Position): Promise { + public async execute(resource: vscode.Uri, start: vscode.Position): Promise { const file = this.client.toPath(resource); if (!file) { return false; } - const editor = window.activeTextEditor; + const editor = vscode.window.activeTextEditor; if (!editor || editor.document.uri.fsPath !== resource.fsPath) { return false; } @@ -148,7 +148,7 @@ class TryCompleteJsDocCommand implements Command { return this.tryInsertDefaultDoc(editor, start); } - private async tryInsertJsDocFromTemplate(editor: TextEditor, file: string, position: Position): Promise { + private async tryInsertJsDocFromTemplate(editor: vscode.TextEditor, file: string, position: vscode.Position): Promise { const snippet = await TryCompleteJsDocCommand.getSnippetTemplate(this.client, file, position); if (!snippet) { return false; @@ -159,11 +159,15 @@ class TryCompleteJsDocCommand implements Command { { undoStopBefore: false, undoStopAfter: true }); } - public static getSnippetTemplate(client: ITypeScriptServiceClient, file: string, position: Position): Promise { + public static getSnippetTemplate(client: ITypeScriptServiceClient, file: string, position: vscode.Position): Promise { const args = typeConverters.Position.toFileLocationRequestArgs(file, position); + const tokenSource = new vscode.CancellationTokenSource(); return Promise.race([ - client.execute('docCommentTemplate', args), - new Promise((_, reject) => setTimeout(reject, 250)) + client.execute('docCommentTemplate', args, tokenSource.token), + new Promise((_, reject) => setTimeout(() => { + tokenSource.cancel(); + reject(); + }, 250)) ]).then((res: Proto.DocCommandTemplateResponse) => { if (!res || !res.body) { return undefined; @@ -181,14 +185,14 @@ class TryCompleteJsDocCommand implements Command { /** * Insert the default JSDoc */ - private tryInsertDefaultDoc(editor: TextEditor, position: Position): Thenable { - const snippet = new SnippetString(`/**\n * $0\n */`); + private tryInsertDefaultDoc(editor: vscode.TextEditor, position: vscode.Position): Thenable { + const snippet = new vscode.SnippetString(`/**\n * $0\n */`); return editor.insertSnippet(snippet, position, { undoStopBefore: false, undoStopAfter: true }); } } -export function templateToSnippet(template: string): SnippetString { +export function templateToSnippet(template: string): vscode.SnippetString { // TODO: use append placeholder let snippetIndex = 1; template = template.replace(/\$/g, '\\$'); @@ -204,16 +208,16 @@ export function templateToSnippet(template: string): SnippetString { out += post + ` \${${snippetIndex++}}`; return out; }); - return new SnippetString(template); + return new vscode.SnippetString(template); } export function register( - selector: DocumentSelector, + selector: vscode.DocumentSelector, client: ITypeScriptServiceClient, commandManager: CommandManager -): Disposable { +): vscode.Disposable { return new ConfigurationDependentRegistration('jsDocCompletion', 'enabled', () => { - return languages.registerCompletionItemProvider(selector, + return vscode.languages.registerCompletionItemProvider(selector, new JsDocCompletionProvider(client, commandManager), '*'); }); diff --git a/extensions/typescript-language-features/src/features/languageConfiguration.ts b/extensions/typescript-language-features/src/features/languageConfiguration.ts index b3eb4ef23dd..8ee1cfecdad 100644 --- a/extensions/typescript-language-features/src/features/languageConfiguration.ts +++ b/extensions/typescript-language-features/src/features/languageConfiguration.ts @@ -9,7 +9,7 @@ * ------------------------------------------------------------------------------------------ */ import * as vscode from 'vscode'; -import { disposeAll } from '../utils/dispose'; +import { Disposable } from '../utils/dispose'; import * as languageModeIds from '../utils/languageModeIds'; const jsTsLanguageConfiguration: vscode.LanguageConfiguration = { @@ -64,10 +64,10 @@ const jsxTagsLanguageConfiguration: vscode.LanguageConfiguration = { ], }; -export class LanguageConfigurationManager { - private readonly _registrations: vscode.Disposable[] = []; +export class LanguageConfigurationManager extends Disposable { constructor() { + super(); const standardLanguages = [ languageModeIds.javascript, languageModeIds.javascriptreact, @@ -82,10 +82,6 @@ export class LanguageConfigurationManager { } private registerConfiguration(language: string, config: vscode.LanguageConfiguration) { - this._registrations.push(vscode.languages.setLanguageConfiguration(language, config)); - } - - dispose() { - disposeAll(this._registrations); + this._register(vscode.languages.setLanguageConfiguration(language, config)); } } diff --git a/extensions/typescript-language-features/src/features/organizeImports.ts b/extensions/typescript-language-features/src/features/organizeImports.ts index ecf080b313f..302da37e8fe 100644 --- a/extensions/typescript-language-features/src/features/organizeImports.ts +++ b/extensions/typescript-language-features/src/features/organizeImports.ts @@ -13,6 +13,7 @@ import { VersionDependentRegistration } from '../utils/dependentRegistration'; import * as typeconverts from '../utils/typeConverters'; import FileConfigurationManager from './fileConfigurationManager'; import TelemetryReporter from '../utils/telemetry'; +import { nulToken } from '../utils/cancellation'; const localize = nls.loadMessageBundle(); @@ -45,12 +46,8 @@ class OrganizeImportsCommand implements Command { } } }; - const response = await this.client.execute('organizeImports', args); - if (!response || !response.success) { - return false; - } - - const edits = typeconverts.WorkspaceEdit.fromFileCodeEdits(this.client, response.body); + const { body } = await this.client.execute('organizeImports', args, nulToken); + const edits = typeconverts.WorkspaceEdit.fromFileCodeEdits(this.client, body); return vscode.workspace.applyEdit(edits); } } @@ -73,7 +70,7 @@ export class OrganizeImportsCodeActionProvider implements vscode.CodeActionProvi public provideCodeActions( document: vscode.TextDocument, _range: vscode.Range, - _context: vscode.CodeActionContext, + context: vscode.CodeActionContext, token: vscode.CancellationToken ): vscode.CodeAction[] { const file = this.client.toPath(document.uri); @@ -81,6 +78,10 @@ export class OrganizeImportsCodeActionProvider implements vscode.CodeActionProvi return []; } + if (!context.only || !context.only.contains(vscode.CodeActionKind.SourceOrganizeImports)) { + return []; + } + this.fileConfigManager.ensureConfigurationForDocument(document, token); const action = new vscode.CodeAction( diff --git a/extensions/typescript-language-features/src/features/quickFix.ts b/extensions/typescript-language-features/src/features/quickFix.ts index 56232596b22..78af910606b 100644 --- a/extensions/typescript-language-features/src/features/quickFix.ts +++ b/extensions/typescript-language-features/src/features/quickFix.ts @@ -10,10 +10,12 @@ import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { applyCodeActionCommands, getEditForCodeAction } from '../utils/codeAction'; import { Command, CommandManager } from '../utils/commandManager'; +import { VersionDependentRegistration } from '../utils/dependentRegistration'; import TelemetryReporter from '../utils/telemetry'; import * as typeConverters from '../utils/typeConverters'; import { DiagnosticsManager } from './diagnostics'; import FileConfigurationManager from './fileConfigurationManager'; +import { nulToken } from '../utils/cancellation'; const localize = nls.loadMessageBundle(); @@ -29,20 +31,19 @@ class ApplyCodeActionCommand implements Command { public async execute( action: Proto.CodeFixAction ): Promise { - if (action.fixName) { - /* __GDPR__ - "quickFix.execute" : { - "fixName" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, - "${include}": [ - "${TypeScriptCommonProperties}" - ] - } - */ - this.telemetryReporter.logTelemetry('quickFix.execute', { - fixName: action.fixName - }); - } - return applyCodeActionCommands(this.client, action); + /* __GDPR__ + "quickFix.execute" : { + "fixName" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, + "${include}": [ + "${TypeScriptCommonProperties}" + ] + } + */ + this.telemetryReporter.logTelemetry('quickFix.execute', { + fixName: action.fixName + }); + + return applyCodeActionCommands(this.client, action.commands, nulToken); } } @@ -64,19 +65,17 @@ class ApplyFixAllCodeAction implements Command { return; } - if (tsAction.fixName) { - /* __GDPR__ - "quickFixAll.execute" : { - "fixName" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, - "${include}": [ - "${TypeScriptCommonProperties}" - ] - } - */ - this.telemetryReporter.logTelemetry('quickFixAll.execute', { - fixName: tsAction.fixName - }); - } + /* __GDPR__ + "quickFixAll.execute" : { + "fixName" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, + "${include}": [ + "${TypeScriptCommonProperties}" + ] + } + */ + this.telemetryReporter.logTelemetry('quickFixAll.execute', { + fixName: tsAction.fixName + }); const args: Proto.GetCombinedCodeFixRequestArgs = { scope: { @@ -87,17 +86,14 @@ class ApplyFixAllCodeAction implements Command { }; try { - const combinedCodeFixesResponse = await this.client.execute('getCombinedCodeFix', args); - if (!combinedCodeFixesResponse.body) { + const { body } = await this.client.execute('getCombinedCodeFix', args, nulToken); + if (!body) { return; } - const edit = typeConverters.WorkspaceEdit.fromFileCodeEdits(this.client, combinedCodeFixesResponse.body.changes); + const edit = typeConverters.WorkspaceEdit.fromFileCodeEdits(this.client, body.changes); await vscode.workspace.applyEdit(edit); - - if (combinedCodeFixesResponse.command) { - await vscode.commands.executeCommand(ApplyCodeActionCommand.ID, combinedCodeFixesResponse.command); - } + await applyCodeActionCommands(this.client, body.commands, nulToken); } catch { // noop } @@ -128,6 +124,34 @@ class DiagnosticsSet { public get values(): Iterable { return this._values.values(); } + + public get size() { + return this._values.size; + } +} + +class CodeActionSet { + private _actions: vscode.CodeAction[] = []; + private _fixAllActions = new Set<{}>(); + + public get values() { + return this._actions; + } + + public addAction(action: vscode.CodeAction) { + this._actions.push(action); + } + + public addFixAllAction(fixId: {}, action: vscode.CodeAction) { + if (!this.hasFixAllAction(fixId)) { + this.addAction(action); + this._fixAllActions.add(fixId); + } + } + + public hasFixAllAction(fixId: {}) { + return this._fixAllActions.has(fixId); + } } class SupportedCodeActionProvider { @@ -137,15 +161,14 @@ class SupportedCodeActionProvider { private readonly client: ITypeScriptServiceClient ) { } - public async getFixableDiagnosticsForContext(context: vscode.CodeActionContext): Promise { + public async getFixableDiagnosticsForContext(context: vscode.CodeActionContext): Promise { const supportedActions = await this.supportedCodeActions; - const fixableDiagnostics = DiagnosticsSet.from(context.diagnostics.filter(diagnostic => supportedActions.has(+(diagnostic.code!)))); - return Array.from(fixableDiagnostics.values); + return DiagnosticsSet.from(context.diagnostics.filter(diagnostic => supportedActions.has(+(diagnostic.code!)))); } private get supportedCodeActions(): Thenable> { if (!this._supportedCodeActions) { - this._supportedCodeActions = this.client.execute('getSupportedCodeFixes', null, undefined) + this._supportedCodeActions = this.client.execute('getSupportedCodeFixes', null, nulToken) .then(response => response.body || []) .then(codes => codes.map(code => +code).filter(code => !isNaN(code))) .then(codes => new Set(codes)); @@ -177,17 +200,13 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { context: vscode.CodeActionContext, token: vscode.CancellationToken ): Promise { - if (!this.client.apiVersion.gte(API.v213)) { - return []; - } - const file = this.client.toPath(document.uri); if (!file) { return []; } const fixableDiagnostics = await this.supportedCodeActionProvider.getFixableDiagnosticsForContext(context); - if (!fixableDiagnostics.length) { + if (!fixableDiagnostics.size) { return []; } @@ -198,7 +217,7 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { await this.formattingConfigurationManager.ensureConfigurationForDocument(document, token); const results: vscode.CodeAction[] = []; - for (const diagnostic of fixableDiagnostics) { + for (const diagnostic of fixableDiagnostics.values) { results.push(...await this.getFixesForDiagnostic(document, file, diagnostic, token)); } return results; @@ -214,26 +233,28 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { ...typeConverters.Range.toFileRangeRequestArgs(file, diagnostic.range), errorCodes: [+(diagnostic.code!)] }; - const codeFixesResponse = await this.client.execute('getCodeFixes', args, token); - if (codeFixesResponse.body) { - const results: vscode.CodeAction[] = []; - for (const tsCodeFix of codeFixesResponse.body) { - results.push(...await this.getAllFixesForTsCodeAction(document, file, diagnostic, tsCodeFix)); - } - return results; + const { body } = await this.client.execute('getCodeFixes', args, token); + if (!body) { + return []; } - return []; + + const results = new CodeActionSet(); + for (const tsCodeFix of body) { + this.addAllFixesForTsCodeAction(results, document, file, diagnostic, tsCodeFix); + } + return results.values; } - private async getAllFixesForTsCodeAction( + private addAllFixesForTsCodeAction( + results: CodeActionSet, document: vscode.TextDocument, file: string, diagnostic: vscode.Diagnostic, tsAction: Proto.CodeAction - ): Promise> { - const singleFix = this.getSingleFixForTsCodeAction(diagnostic, tsAction); - const fixAll = await this.getFixAllForTsCodeAction(document, file, diagnostic, tsAction as Proto.CodeFixAction); - return fixAll ? [singleFix, fixAll] : [singleFix]; + ): CodeActionSet { + results.addAction(this.getSingleFixForTsCodeAction(diagnostic, tsAction)); + this.addFixAllForTsCodeAction(results, document, file, diagnostic, tsAction as Proto.CodeFixAction); + return results; } private getSingleFixForTsCodeAction( @@ -243,42 +264,41 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { const codeAction = new vscode.CodeAction(tsAction.description, vscode.CodeActionKind.QuickFix); codeAction.edit = getEditForCodeAction(this.client, tsAction); codeAction.diagnostics = [diagnostic]; - if (tsAction.commands) { - codeAction.command = { - command: ApplyCodeActionCommand.ID, - arguments: [tsAction], - title: tsAction.description - }; - } + codeAction.command = { + command: ApplyCodeActionCommand.ID, + arguments: [tsAction], + title: '' + }; return codeAction; } - private async getFixAllForTsCodeAction( + private addFixAllForTsCodeAction( + results: CodeActionSet, document: vscode.TextDocument, file: string, diagnostic: vscode.Diagnostic, tsAction: Proto.CodeFixAction, - ): Promise { - if (!tsAction.fixId || !this.client.apiVersion.gte(API.v270)) { - return undefined; + ): CodeActionSet { + if (!tsAction.fixId || !this.client.apiVersion.gte(API.v270) || results.hasFixAllAction(results)) { + return results; } // Make sure there are multiple diagnostics of the same type in the file if (!this.diagnosticsManager.getDiagnostics(document.uri).some(x => x.code === diagnostic.code && x !== diagnostic)) { - return; + return results; } const action = new vscode.CodeAction( tsAction.fixAllDescription || localize('fixAllInFileLabel', '{0} (Fix all in file)', tsAction.description), vscode.CodeActionKind.QuickFix); action.diagnostics = [diagnostic]; - action.command = { command: ApplyFixAllCodeAction.ID, arguments: [file, tsAction], title: '' }; - return action; + results.addFixAllAction(tsAction.fixId, action); + return results; } } @@ -290,6 +310,7 @@ export function register( diagnosticsManager: DiagnosticsManager, telemetryReporter: TelemetryReporter ) { - return vscode.languages.registerCodeActionsProvider(selector, - new TypeScriptQuickFixProvider(client, fileConfigurationManager, commandManager, diagnosticsManager, telemetryReporter)); + return new VersionDependentRegistration(client, API.v213, () => + vscode.languages.registerCodeActionsProvider(selector, + new TypeScriptQuickFixProvider(client, fileConfigurationManager, commandManager, diagnosticsManager, telemetryReporter))); } diff --git a/extensions/typescript-language-features/src/features/refactor.ts b/extensions/typescript-language-features/src/features/refactor.ts index c471b10f8ff..bdc5d4eb25e 100644 --- a/extensions/typescript-language-features/src/features/refactor.ts +++ b/extensions/typescript-language-features/src/features/refactor.ts @@ -12,6 +12,7 @@ import { VersionDependentRegistration } from '../utils/dependentRegistration'; import TelemetryReporter from '../utils/telemetry'; import * as typeConverters from '../utils/typeConverters'; import FormattingOptionsManager from './fileConfigurationManager'; +import { nulToken } from '../utils/cancellation'; class ApplyRefactoringCommand implements Command { @@ -47,8 +48,7 @@ class ApplyRefactoringCommand implements Command { refactor, action }; - const response = await this.client.execute('getEditsForRefactor', args); - const body = response && response.body; + const { body } = await this.client.execute('getEditsForRefactor', args, nulToken); if (!body || !body.edits.length) { return false; } @@ -137,20 +137,21 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { return undefined; } - await this.formattingOptionsManager.ensureConfigurationForDocument(document, undefined); + await this.formattingOptionsManager.ensureConfigurationForDocument(document, token); const args: Proto.GetApplicableRefactorsRequestArgs = typeConverters.Range.toFileRangeRequestArgs(file, rangeOrSelection); - let response: Proto.GetApplicableRefactorsResponse; + let refactorings: Proto.ApplicableRefactorInfo[]; try { - response = await this.client.execute('getApplicableRefactors', args, token); - if (!response || !response.body) { + const { body } = await this.client.execute('getApplicableRefactors', args, token); + if (!body) { return undefined; } + refactorings = body; } catch { return undefined; } - return this.convertApplicableRefactors(response.body, document, file, rangeOrSelection); + return this.convertApplicableRefactors(refactorings, document, file, rangeOrSelection); } private convertApplicableRefactors( diff --git a/extensions/typescript-language-features/src/features/references.ts b/extensions/typescript-language-features/src/features/references.ts index a4b81e54f5b..50c62b3b722 100644 --- a/extensions/typescript-language-features/src/features/references.ts +++ b/extensions/typescript-language-features/src/features/references.ts @@ -26,13 +26,13 @@ class TypeScriptReferenceSupport implements vscode.ReferenceProvider { const args = typeConverters.Position.toFileLocationRequestArgs(filepath, position); try { - const msg = await this.client.execute('references', args, token); - if (!msg.body) { + const { body } = await this.client.execute('references', args, token); + if (!body) { return []; } const result: vscode.Location[] = []; const has203Features = this.client.apiVersion.gte(API.v203); - for (const ref of msg.body.refs) { + for (const ref of body.refs) { if (!options.includeDeclaration && has203Features && ref.isDefinition) { continue; } diff --git a/extensions/typescript-language-features/src/features/referencesCodeLens.ts b/extensions/typescript-language-features/src/features/referencesCodeLens.ts index 059d3e9e16e..e243e0d0e37 100644 --- a/extensions/typescript-language-features/src/features/referencesCodeLens.ts +++ b/extensions/typescript-language-features/src/features/referencesCodeLens.ts @@ -21,7 +21,7 @@ class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLensProvide const codeLens = inputCodeLens as ReferencesCodeLens; const args = typeConverters.Position.toFileLocationRequestArgs(codeLens.file, codeLens.range.start); return this.client.execute('references', args, token).then(response => { - if (!response || !response.body) { + if (!response.body) { throw codeLens; } diff --git a/extensions/typescript-language-features/src/features/rename.ts b/extensions/typescript-language-features/src/features/rename.ts index c73539409fa..1e6f0070b1a 100644 --- a/extensions/typescript-language-features/src/features/rename.ts +++ b/extensions/typescript-language-features/src/features/rename.ts @@ -32,17 +32,17 @@ class TypeScriptRenameProvider implements vscode.RenameProvider { }; try { - const response = await this.client.execute('rename', args, token); - if (!response.body) { + const { body } = await this.client.execute('rename', args, token); + if (!body) { return null; } - const renameInfo = response.body.info; + const renameInfo = body.info; if (!renameInfo.canRename) { return Promise.reject(renameInfo.localizedErrorMessage); } - return this.toWorkspaceEdit(response.body.locs, newName); + return this.toWorkspaceEdit(body.locs, newName); } catch { // noop } diff --git a/extensions/typescript-language-features/src/features/signatureHelp.ts b/extensions/typescript-language-features/src/features/signatureHelp.ts index b682e0b0367..f3a9a3e5620 100644 --- a/extensions/typescript-language-features/src/features/signatureHelp.ts +++ b/extensions/typescript-language-features/src/features/signatureHelp.ts @@ -28,13 +28,13 @@ class TypeScriptSignatureHelpProvider implements vscode.SignatureHelpProvider { } const args: Proto.SignatureHelpRequestArgs = typeConverters.Position.toFileLocationRequestArgs(filepath, position); - let info: Proto.SignatureHelpItems | undefined = undefined; + let info: Proto.SignatureHelpItems; try { - const response = await this.client.execute('signatureHelp', args, token); - info = response.body; - if (!info) { + const { body } = await this.client.execute('signatureHelp', args, token); + if (!body) { return undefined; } + info = body; } catch { return undefined; } diff --git a/extensions/typescript-language-features/src/features/tagClosing.ts b/extensions/typescript-language-features/src/features/tagClosing.ts index 66af4920ef9..ab9843ae7f2 100644 --- a/extensions/typescript-language-features/src/features/tagClosing.ts +++ b/extensions/typescript-language-features/src/features/tagClosing.ts @@ -8,19 +8,19 @@ import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { ConditionalRegistration, ConfigurationDependentRegistration, VersionDependentRegistration } from '../utils/dependentRegistration'; -import { disposeAll } from '../utils/dispose'; +import { Disposable } from '../utils/dispose'; import * as typeConverters from '../utils/typeConverters'; -class TagClosing { +class TagClosing extends Disposable { private _disposed = false; private _timeout: NodeJS.Timer | undefined = undefined; private _cancel: vscode.CancellationTokenSource | undefined = undefined; - private readonly _disposables: vscode.Disposable[] = []; constructor( private readonly client: ITypeScriptServiceClient ) { + super(); vscode.workspace.onDidChangeTextDocument( event => this.onDidChangeTextDocument(event.document, event.contentChanges), null, @@ -28,10 +28,9 @@ class TagClosing { } public dispose() { + super.dispose(); this._disposed = true; - disposeAll(this._disposables); - if (this._timeout) { clearTimeout(this._timeout); this._timeout = undefined; @@ -74,8 +73,10 @@ class TagClosing { return; } - const secondToLastCharacter = lastChange.text[lastChange.text.length - 2]; - if (secondToLastCharacter === '>') { + const priorCharacter = lastChange.range.start.character > 0 + ? document.getText(new vscode.Range(lastChange.range.start.translate({ characterDelta: -1 }), lastChange.range.start)) + : ''; + if (priorCharacter === '>') { return; } @@ -89,16 +90,16 @@ class TagClosing { } let position = new vscode.Position(rangeStart.line, rangeStart.character + lastChange.text.length); - let body: Proto.TextInsertion | undefined = undefined; + let insertion: Proto.TextInsertion; const args: Proto.JsxClosingTagRequestArgs = typeConverters.Position.toFileLocationRequestArgs(filepath, position); this._cancel = new vscode.CancellationTokenSource(); try { - const response = await this.client.execute('jsxClosingTag', args, this._cancel.token); - body = response && response.body; + const { body } = await this.client.execute('jsxClosingTag', args, this._cancel.token); if (!body) { return; } + insertion = body; } catch { return; } @@ -115,7 +116,7 @@ class TagClosing { const activeDocument = activeEditor.document; if (document === activeDocument && activeDocument.version === version) { activeEditor.insertSnippet( - this.getTagSnippet(body), + this.getTagSnippet(insertion), this.getInsertionPositions(activeEditor, position)); } }, 100); @@ -136,24 +137,19 @@ class TagClosing { } } -export class ActiveDocumentDependentRegistration { +export class ActiveDocumentDependentRegistration extends Disposable { private readonly _registration: ConditionalRegistration; - private readonly _disposables: vscode.Disposable[] = []; constructor( private readonly selector: vscode.DocumentSelector, register: () => vscode.Disposable, ) { - this._registration = new ConditionalRegistration(register); + super(); + this._registration = this._register(new ConditionalRegistration(register)); vscode.window.onDidChangeActiveTextEditor(this.update, this, this._disposables); this.update(); } - public dispose() { - disposeAll(this._disposables); - this._registration.dispose(); - } - private update() { const editor = vscode.window.activeTextEditor; const enabled = !!(editor && vscode.languages.match(this.selector, editor.document)); diff --git a/extensions/typescript-language-features/src/features/task.ts b/extensions/typescript-language-features/src/features/task.ts index 2dbacec79ec..e819a1c092b 100644 --- a/extensions/typescript-language-features/src/features/task.ts +++ b/extensions/typescript-language-features/src/features/task.ts @@ -78,7 +78,10 @@ class TscTaskProvider implements vscode.TaskProvider { private async getAllTsConfigs(token: vscode.CancellationToken): Promise { const out = new Set(); - const configs = (await this.getTsConfigForActiveFile(token)).concat(await this.getTsConfigsInWorkspace()); + const configs = [ + ...await this.getTsConfigForActiveFile(token), + ...await this.getTsConfigsInWorkspace() + ]; for (const config of configs) { if (await exists(config.path)) { out.add(config); diff --git a/extensions/typescript-language-features/src/features/tsconfig.ts b/extensions/typescript-language-features/src/features/tsconfig.ts index 0bb99e6d46d..a7e89ea1332 100644 --- a/extensions/typescript-language-features/src/features/tsconfig.ts +++ b/extensions/typescript-language-features/src/features/tsconfig.ts @@ -4,8 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as jsonc from 'jsonc-parser'; -import { dirname, join } from 'path'; +import { dirname, join, basename } from 'path'; import * as vscode from 'vscode'; +import { flatten } from '../utils/arrays'; + +function mapChildren(node: jsonc.Node | undefined, f: (x: jsonc.Node) => R): R[] { + return node && node.type === 'array' && node.children + ? node.children.map(f) + : []; +} class TsconfigLinkProvider implements vscode.DocumentLinkProvider { @@ -18,40 +25,72 @@ class TsconfigLinkProvider implements vscode.DocumentLinkProvider { return null; } - return this.getNodes(root).map(node => - new vscode.DocumentLink( - this.getRange(document, node), - this.getTarget(document, node))); + return [ + this.getExendsLink(document, root), + ...this.getFilesLinks(document, root), + ...this.getReferencesLinks(document, root) + ].filter(x => !!x) as vscode.DocumentLink[]; } - private getNodes(root: jsonc.Node): ReadonlyArray { - const nodes: jsonc.Node[] = []; + private getExendsLink(document: vscode.TextDocument, root: jsonc.Node): vscode.DocumentLink | undefined { const extendsNode = jsonc.findNodeAtLocation(root, ['extends']); - if (this.isPathValue(extendsNode)) { - nodes.push(extendsNode); + if (!this.isPathValue(extendsNode)) { + return undefined; } - const referencesNode = jsonc.findNodeAtLocation(root, ['references']); - if (referencesNode && referencesNode.type === 'array' && referencesNode.children) { - for (const child of referencesNode.children) { - const path = jsonc.findNodeAtLocation(child, ['path']); - if (this.isPathValue(path)) { - nodes.push(path); + return new vscode.DocumentLink( + this.getRange(document, extendsNode), + basename(extendsNode.value).match('.json$') + ? this.getFileTarget(document, extendsNode) + : vscode.Uri.file(join(dirname(document.uri.fsPath), extendsNode!.value + '.json'))); + } + + private getFilesLinks(document: vscode.TextDocument, root: jsonc.Node) { + return mapChildren( + jsonc.findNodeAtLocation(root, ['files']), + child => this.pathNodeToLink(document, child)); + } + + private getReferencesLinks(document: vscode.TextDocument, root: jsonc.Node) { + return mapChildren( + jsonc.findNodeAtLocation(root, ['references']), + child => { + const pathNode = jsonc.findNodeAtLocation(child, ['path']); + if (!this.isPathValue(pathNode)) { + return undefined; } - } - } - return nodes; + return new vscode.DocumentLink(this.getRange(document, pathNode), + basename(pathNode.value).match('.json$') + ? this.getFileTarget(document, pathNode) + : this.getFolderTarget(document, pathNode)); + }); + } + + private pathNodeToLink( + document: vscode.TextDocument, + node: jsonc.Node | undefined + ): vscode.DocumentLink | undefined { + return this.isPathValue(node) + ? new vscode.DocumentLink(this.getRange(document, node), this.getFileTarget(document, node)) + : undefined; } private isPathValue(extendsNode: jsonc.Node | undefined): extendsNode is jsonc.Node { - return extendsNode && extendsNode.type === 'string' && extendsNode.value; + return extendsNode + && extendsNode.type === 'string' + && extendsNode.value + && !(extendsNode.value as string).includes('*'); // don't treat globs as links. } - private getTarget(document: vscode.TextDocument, node: jsonc.Node): vscode.Uri { + private getFileTarget(document: vscode.TextDocument, node: jsonc.Node): vscode.Uri { return vscode.Uri.file(join(dirname(document.uri.fsPath), node!.value)); } + private getFolderTarget(document: vscode.TextDocument, node: jsonc.Node): vscode.Uri { + return vscode.Uri.file(join(dirname(document.uri.fsPath), node!.value, 'tsconfig.json')); + } + private getRange(document: vscode.TextDocument, node: jsonc.Node) { const offset = node!.offset; const start = document.positionAt(offset + 1); @@ -68,7 +107,9 @@ export function register() { const languages = ['json', 'jsonc']; - const selector: vscode.DocumentSelector = ([] as any[]).concat( - ...languages.map(language => patterns.map((pattern): vscode.DocumentFilter => ({ language, pattern })))); + const selector: vscode.DocumentSelector = flatten( + languages.map(language => + patterns.map((pattern): vscode.DocumentFilter => ({ language, pattern })))); + return vscode.languages.registerDocumentLinkProvider(selector, new TsconfigLinkProvider()); } diff --git a/extensions/typescript-language-features/src/features/typeDefinitions.ts b/extensions/typescript-language-features/src/features/typeDefinitions.ts index 23f8b18af39..45d89aeaedd 100644 --- a/extensions/typescript-language-features/src/features/typeDefinitions.ts +++ b/extensions/typescript-language-features/src/features/typeDefinitions.ts @@ -10,7 +10,7 @@ import { VersionDependentRegistration } from '../utils/dependentRegistration'; import DefinitionProviderBase from './definitionProviderBase'; export default class TypeScriptTypeDefinitionProvider extends DefinitionProviderBase implements vscode.TypeDefinitionProvider { - public provideTypeDefinition(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken | boolean): Promise { + public provideTypeDefinition(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { return this.getSymbolLocations('typeDefinition', document, position, token); } } diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index 9b2f3c3ce5f..44c05cd7b4a 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -15,6 +15,8 @@ import { isTypeScriptDocument } from '../utils/languageModeIds'; import { escapeRegExp } from '../utils/regexp'; import * as typeConverters from '../utils/typeConverters'; import FileConfigurationManager from './fileConfigurationManager'; +import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import { nulToken } from '../utils/cancellation'; const localize = nls.loadMessageBundle(); @@ -26,7 +28,7 @@ enum UpdateImportsOnFileMoveSetting { Never = 'never', } -export class UpdateImportsOnFileRenameHandler { +class UpdateImportsOnFileRenameHandler { private readonly _onDidRenameSub: vscode.Disposable; public constructor( @@ -47,10 +49,6 @@ export class UpdateImportsOnFileRenameHandler { oldResource: vscode.Uri, newResource: vscode.Uri, ): Promise { - if (!this.client.apiVersion.gte(API.v290)) { - return; - } - const targetResource = await this.getTargetResource(newResource); if (!targetResource) { return; @@ -87,11 +85,11 @@ export class UpdateImportsOnFileRenameHandler { // Workaround for https://github.com/Microsoft/vscode/issues/52967 // Never attempt to update import paths if the file does not contain something the looks like an export try { - const tree = await this.client.execute('navtree', { file: newFile }); + const { body } = await this.client.execute('navtree', { file: newFile }, nulToken); const hasExport = (node: Proto.NavigationTree): boolean => { return !!node.kindModifiers.match(/\bexports?\b/g) || !!(node.childItems && node.childItems.some(hasExport)); }; - if (!tree.body || !tree.body || !hasExport(tree.body)) { + if (!body || !hasExport(body)) { return; } } catch { @@ -232,14 +230,14 @@ export class UpdateImportsOnFileRenameHandler { newFile: string, ) { const isDirectoryRename = fs.lstatSync(newFile).isDirectory(); - await this.fileConfigurationManager.ensureConfigurationForDocument(document, undefined); + await this.fileConfigurationManager.setGlobalConfigurationFromDocument(document, nulToken); const args: Proto.GetEditsForFileRenameRequestArgs & { file: string } = { file: targetResource, oldFilePath: oldFile, newFilePath: newFile, }; - const response = await this.client.execute('getEditsForFileRename', args); + const response = await this.client.execute('getEditsForFileRename', args, nulToken); if (!response || !response.body) { return; } @@ -304,3 +302,11 @@ export class UpdateImportsOnFileRenameHandler { } } +export function register( + client: ITypeScriptServiceClient, + fileConfigurationManager: FileConfigurationManager, + handles: (uri: vscode.Uri) => Promise, +) { + return new VersionDependentRegistration(client, API.v290, () => + new UpdateImportsOnFileRenameHandler(client, fileConfigurationManager, handles)); +} \ No newline at end of file diff --git a/extensions/typescript-language-features/src/features/workspaceSymbols.ts b/extensions/typescript-language-features/src/features/workspaceSymbols.ts index 5d987b85133..4d06850baa0 100644 --- a/extensions/typescript-language-features/src/features/workspaceSymbols.ts +++ b/extensions/typescript-language-features/src/features/workspaceSymbols.ts @@ -44,13 +44,13 @@ class TypeScriptWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvide file: filepath, searchValue: search }; - const response = await this.client.execute('navto', args, token); - if (!response.body) { + const { body } = await this.client.execute('navto', args, token); + if (!body) { return []; } const result: vscode.SymbolInformation[] = []; - for (const item of response.body) { + for (const item of body) { if (!item.containerName && item.kind === 'alias') { continue; } diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index 3647f8dbcf5..aad5f1a3a44 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -6,11 +6,11 @@ import { basename } from 'path'; import * as vscode from 'vscode'; import { CachedNavTreeResponse } from './features/baseCodeLensProvider'; -import { DiagnosticKind, DiagnosticsManager } from './features/diagnostics'; +import { DiagnosticKind } from './features/diagnostics'; import FileConfigurationManager from './features/fileConfigurationManager'; import TypeScriptServiceClient from './typescriptServiceClient'; import { CommandManager } from './utils/commandManager'; -import { disposeAll } from './utils/dispose'; +import { Disposable } from './utils/dispose'; import * as fileSchemes from './utils/fileSchemes'; import { LanguageDescription } from './utils/languageDescription'; import { memoize } from './utils/memoize'; @@ -21,13 +21,7 @@ import TypingsStatus from './utils/typingsStatus'; const validateSetting = 'validate.enable'; const suggestionSetting = 'suggestionActions.enabled'; -export default class LanguageProvider { - private readonly diagnosticsManager: DiagnosticsManager; - - private _validate: boolean = true; - private _enableSuggestionDiagnostics: boolean = true; - - private readonly disposables: vscode.Disposable[] = []; +export default class LanguageProvider extends Disposable { constructor( private readonly client: TypeScriptServiceClient, @@ -37,13 +31,8 @@ export default class LanguageProvider { private readonly typingsStatus: TypingsStatus, private readonly fileConfigurationManager: FileConfigurationManager ) { - this.client.bufferSyncSupport.onDelete(resource => { - this.diagnosticsManager.delete(resource); - }, null, this.disposables); - - this.diagnosticsManager = new DiagnosticsManager(description.diagnosticOwner); - - vscode.workspace.onDidChangeConfiguration(this.configurationChanged, this, this.disposables); + super(); + vscode.workspace.onDidChangeConfiguration(this.configurationChanged, this, this._disposables); this.configurationChanged(); client.onReady(async () => { @@ -51,11 +40,6 @@ export default class LanguageProvider { }); } - public dispose(): void { - disposeAll(this.disposables); - - this.diagnosticsManager.dispose(); - } @memoize private get documentSelector(): vscode.DocumentFilter[] { @@ -73,27 +57,27 @@ export default class LanguageProvider { const cachedResponse = new CachedNavTreeResponse(); - this.disposables.push((await import('./features/completions')).register(selector, this.client, this.typingsStatus, this.fileConfigurationManager, this.commandManager)); - this.disposables.push((await import('./features/definitions')).register(selector, this.client)); - this.disposables.push((await import('./features/directiveCommentCompletions')).register(selector, this.client)); - this.disposables.push((await import('./features/documentHighlight')).register(selector, this.client)); - this.disposables.push((await import('./features/documentSymbol')).register(selector, this.client)); - this.disposables.push((await import('./features/folding')).register(selector, this.client)); - this.disposables.push((await import('./features/formatting')).register(selector, this.description.id, this.client, this.fileConfigurationManager)); - this.disposables.push((await import('./features/hover')).register(selector, this.client)); - this.disposables.push((await import('./features/implementations')).register(selector, this.client)); - this.disposables.push((await import('./features/implementationsCodeLens')).register(selector, this.description.id, this.client, cachedResponse)); - this.disposables.push((await import('./features/jsDocCompletions')).register(selector, this.client, this.commandManager)); - this.disposables.push((await import('./features/organizeImports')).register(selector, this.client, this.commandManager, this.fileConfigurationManager, this.telemetryReporter)); - this.disposables.push((await import('./features/quickFix')).register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.diagnosticsManager, this.telemetryReporter)); - this.disposables.push((await import('./features/refactor')).register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.telemetryReporter)); - this.disposables.push((await import('./features/references')).register(selector, this.client)); - this.disposables.push((await import('./features/referencesCodeLens')).register(selector, this.description.id, this.client, cachedResponse)); - this.disposables.push((await import('./features/rename')).register(selector, this.client)); - this.disposables.push((await import('./features/signatureHelp')).register(selector, this.client)); - this.disposables.push((await import('./features/tagClosing')).register(selector, this.description.id, this.client)); - this.disposables.push((await import('./features/typeDefinitions')).register(selector, this.client)); - this.disposables.push((await import('./features/workspaceSymbols')).register(this.client, this.description.modeIds)); + this._register((await import('./features/completions')).register(selector, this.client, this.typingsStatus, this.fileConfigurationManager, this.commandManager)); + this._register((await import('./features/definitions')).register(selector, this.client)); + this._register((await import('./features/directiveCommentCompletions')).register(selector, this.client)); + this._register((await import('./features/documentHighlight')).register(selector, this.client)); + this._register((await import('./features/documentSymbol')).register(selector, this.client)); + this._register((await import('./features/folding')).register(selector, this.client)); + this._register((await import('./features/formatting')).register(selector, this.description.id, this.client, this.fileConfigurationManager)); + this._register((await import('./features/hover')).register(selector, this.client)); + this._register((await import('./features/implementations')).register(selector, this.client)); + this._register((await import('./features/implementationsCodeLens')).register(selector, this.description.id, this.client, cachedResponse)); + this._register((await import('./features/jsDocCompletions')).register(selector, this.client, this.commandManager)); + this._register((await import('./features/organizeImports')).register(selector, this.client, this.commandManager, this.fileConfigurationManager, this.telemetryReporter)); + this._register((await import('./features/quickFix')).register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.client.diagnosticsManager, this.telemetryReporter)); + this._register((await import('./features/refactor')).register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.telemetryReporter)); + this._register((await import('./features/references')).register(selector, this.client)); + this._register((await import('./features/referencesCodeLens')).register(selector, this.description.id, this.client, cachedResponse)); + this._register((await import('./features/rename')).register(selector, this.client)); + this._register((await import('./features/signatureHelp')).register(selector, this.client)); + this._register((await import('./features/tagClosing')).register(selector, this.description.id, this.client)); + this._register((await import('./features/typeDefinitions')).register(selector, this.client)); + this._register((await import('./features/workspaceSymbols')).register(this.client, this.description.modeIds)); } private configurationChanged(): void { @@ -120,30 +104,15 @@ export default class LanguageProvider { } private updateValidate(value: boolean) { - if (this._validate === value) { - return; - } - this._validate = value; - this.diagnosticsManager.validate = value; - if (value) { - this.triggerAllDiagnostics(); - } + this.client.diagnosticsManager.setValidate(this._diagnosticLanguage, value); } private updateSuggestionDiagnostics(value: boolean) { - if (this._enableSuggestionDiagnostics === value) { - return; - } - - this._enableSuggestionDiagnostics = value; - this.diagnosticsManager.enableSuggestions = value; - if (value) { - this.triggerAllDiagnostics(); - } + this.client.diagnosticsManager.setEnableSuggestions(this._diagnosticLanguage, value); } public reInitialize(): void { - this.diagnosticsManager.reInitialize(); + this.client.diagnosticsManager.reInitialize(); } public triggerAllDiagnostics(): void { @@ -153,7 +122,7 @@ export default class LanguageProvider { public diagnosticsReceived(diagnosticsKind: DiagnosticKind, file: vscode.Uri, diagnostics: (vscode.Diagnostic & { reportUnnecessary: any })[]): void { const config = vscode.workspace.getConfiguration(this.id, file); const reportUnnecessary = config.get('showUnused', true); - this.diagnosticsManager.diagnosticsReceived(diagnosticsKind, file, diagnostics.filter(diag => { + this.client.diagnosticsManager.updateDiagnostics(file, this._diagnosticLanguage, diagnosticsKind, diagnostics.filter(diag => { if (!reportUnnecessary) { diag.tags = undefined; if (diag.reportUnnecessary && diag.severity === vscode.DiagnosticSeverity.Hint) { @@ -165,6 +134,10 @@ export default class LanguageProvider { } public configFileDiagnosticsReceived(file: vscode.Uri, diagnostics: vscode.Diagnostic[]): void { - this.diagnosticsManager.configFileDiagnosticsReceived(file, diagnostics); + this.client.diagnosticsManager.configFileDiagnosticsReceived(file, diagnostics); + } + + private get _diagnosticLanguage() { + return this.description.diagnosticLanguage; } } \ No newline at end of file diff --git a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts index 205aa96423d..afff7001672 100644 --- a/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts +++ b/extensions/typescript-language-features/src/typeScriptServiceClientHost.ts @@ -8,18 +8,18 @@ * https://github.com/Microsoft/TypeScript-Sublime-Plugin/blob/master/TypeScript%20Indent.tmPreferences * ------------------------------------------------------------------------------------------ */ -import { Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Disposable, Memento, Range, Uri, workspace } from 'vscode'; +import * as vscode from 'vscode'; import { DiagnosticKind } from './features/diagnostics'; import FileConfigurationManager from './features/fileConfigurationManager'; -import { UpdateImportsOnFileRenameHandler } from './features/updatePathsOnRename'; +import { register as registerUpdatePathsOnRename } from './features/updatePathsOnRename'; import LanguageProvider from './languageProvider'; import * as Proto from './protocol'; import * as PConst from './protocol.const'; import TypeScriptServiceClient from './typescriptServiceClient'; import API from './utils/api'; import { CommandManager } from './utils/commandManager'; -import { disposeAll } from './utils/dispose'; -import { LanguageDescription } from './utils/languageDescription'; +import { Disposable } from './utils/dispose'; +import { LanguageDescription, DiagnosticLanguage } from './utils/languageDescription'; import LogDirectoryProvider from './utils/logDirectoryProvider'; import { TypeScriptServerPlugin } from './utils/plugins'; import * as typeConverters from './utils/typeConverters'; @@ -36,28 +36,26 @@ const styleCheckDiagnostics = [ 7030 // not all code paths return a value ]; -export default class TypeScriptServiceClientHost { - private readonly ataProgressReporter: AtaProgressReporter; +export default class TypeScriptServiceClientHost extends Disposable { private readonly typingsStatus: TypingsStatus; private readonly client: TypeScriptServiceClient; private readonly languages: LanguageProvider[] = []; private readonly languagePerId = new Map(); - private readonly disposables: Disposable[] = []; private readonly versionStatus: VersionStatus; private readonly fileConfigurationManager: FileConfigurationManager; - private readonly updateImportsOnFileRenameHandler: UpdateImportsOnFileRenameHandler; private reportStyleCheckAsWarnings: boolean = true; constructor( descriptions: LanguageDescription[], - workspaceState: Memento, + workspaceState: vscode.Memento, plugins: TypeScriptServerPlugin[], private readonly commandManager: CommandManager, logDirectoryProvider: LogDirectoryProvider ) { + super(); const handleProjectCreateOrDelete = () => { - this.client.execute('reloadProjects', null, false); + this.client.executeWithoutWaitingForResponse('reloadProjects', null); this.triggerAllDiagnostics(); }; const handleProjectChange = () => { @@ -65,11 +63,11 @@ export default class TypeScriptServiceClientHost { this.triggerAllDiagnostics(); }, 1500); }; - const configFileWatcher = workspace.createFileSystemWatcher('**/[tj]sconfig.json'); - this.disposables.push(configFileWatcher); - configFileWatcher.onDidCreate(handleProjectCreateOrDelete, this, this.disposables); - configFileWatcher.onDidDelete(handleProjectCreateOrDelete, this, this.disposables); - configFileWatcher.onDidChange(handleProjectChange, this, this.disposables); + const configFileWatcher = vscode.workspace.createFileSystemWatcher('**/[tj]sconfig.json'); + this._register(configFileWatcher); + configFileWatcher.onDidCreate(handleProjectCreateOrDelete, this, this._disposables); + configFileWatcher.onDidDelete(handleProjectCreateOrDelete, this, this._disposables); + configFileWatcher.onDidChange(handleProjectChange, this, this._disposables); const allModeIds = this.getAllModeIds(descriptions); this.client = new TypeScriptServiceClient( @@ -78,30 +76,30 @@ export default class TypeScriptServiceClientHost { plugins, logDirectoryProvider, allModeIds); - this.disposables.push(this.client); + this._register(this.client); this.client.onDiagnosticsReceived(({ kind, resource, diagnostics }) => { this.diagnosticsReceived(kind, resource, diagnostics); - }, null, this.disposables); + }, null, this._disposables); - this.client.onConfigDiagnosticsReceived(diag => this.configFileDiagnosticsReceived(diag), null, this.disposables); - this.client.onResendModelsRequested(() => this.populateService(), null, this.disposables); + this.client.onConfigDiagnosticsReceived(diag => this.configFileDiagnosticsReceived(diag), null, this._disposables); + this.client.onResendModelsRequested(() => this.populateService(), null, this._disposables); this.versionStatus = new VersionStatus(resource => this.client.toPath(resource)); - this.disposables.push(this.versionStatus); + this._register(this.versionStatus); - this.typingsStatus = new TypingsStatus(this.client); - this.ataProgressReporter = new AtaProgressReporter(this.client); - this.fileConfigurationManager = new FileConfigurationManager(this.client); + this._register(new AtaProgressReporter(this.client)); + this.typingsStatus = this._register(new TypingsStatus(this.client)); + this.fileConfigurationManager = this._register(new FileConfigurationManager(this.client)); for (const description of descriptions) { const manager = new LanguageProvider(this.client, description, this.commandManager, this.client.telemetryReporter, this.typingsStatus, this.fileConfigurationManager); this.languages.push(manager); - this.disposables.push(manager); + this._register(manager); this.languagePerId.set(description.id, manager); } - this.updateImportsOnFileRenameHandler = new UpdateImportsOnFileRenameHandler(this.client, this.fileConfigurationManager, uri => this.handles(uri)); + this._register(registerUpdatePathsOnRename(this.client, this.fileConfigurationManager, uri => this.handles(uri))); this.client.ensureServiceStarted(); this.client.onReady(() => { @@ -119,13 +117,14 @@ export default class TypeScriptServiceClientHost { const description: LanguageDescription = { id: 'typescript-plugins', modeIds: Array.from(languages.values()), - diagnosticSource: 'ts-plugins', + diagnosticSource: 'ts-plugin', + diagnosticLanguage: DiagnosticLanguage.TypeScript, diagnosticOwner: 'typescript', isExternal: true }; const manager = new LanguageProvider(this.client, description, this.commandManager, this.client.telemetryReporter, this.typingsStatus, this.fileConfigurationManager); this.languages.push(manager); - this.disposables.push(manager); + this._register(manager); this.languagePerId.set(description.id, manager); } }); @@ -134,7 +133,7 @@ export default class TypeScriptServiceClientHost { this.triggerAllDiagnostics(); }); - workspace.onDidChangeConfiguration(this.configurationChanged, this, this.disposables); + vscode.workspace.onDidChangeConfiguration(this.configurationChanged, this, this._disposables); this.configurationChanged(); } @@ -146,24 +145,16 @@ export default class TypeScriptServiceClientHost { return allModeIds; } - public dispose(): void { - disposeAll(this.disposables); - this.typingsStatus.dispose(); - this.ataProgressReporter.dispose(); - this.fileConfigurationManager.dispose(); - this.updateImportsOnFileRenameHandler.dispose(); - } - public get serviceClient(): TypeScriptServiceClient { return this.client; } public reloadProjects(): void { - this.client.execute('reloadProjects', null, false); + this.client.executeWithoutWaitingForResponse('reloadProjects', null); this.triggerAllDiagnostics(); } - public async handles(resource: Uri): Promise { + public async handles(resource: vscode.Uri): Promise { const provider = await this.findLanguage(resource); if (provider) { return true; @@ -172,14 +163,14 @@ export default class TypeScriptServiceClientHost { } private configurationChanged(): void { - const typescriptConfig = workspace.getConfiguration('typescript'); + const typescriptConfig = vscode.workspace.getConfiguration('typescript'); this.reportStyleCheckAsWarnings = typescriptConfig.get('reportStyleChecksAsWarnings', true); } - private async findLanguage(resource: Uri): Promise { + private async findLanguage(resource: vscode.Uri): Promise { try { - const doc = await workspace.openTextDocument(resource); + const doc = await vscode.workspace.openTextDocument(resource); return this.languages.find(language => language.handles(resource, doc)); } catch { return undefined; @@ -198,7 +189,7 @@ export default class TypeScriptServiceClientHost { this.client.bufferSyncSupport.requestAllDiagnostics(); // See https://github.com/Microsoft/TypeScript/issues/5530 - workspace.saveAll(false).then(() => { + vscode.workspace.saveAll(false).then(() => { for (const language of this.languagePerId.values()) { language.reInitialize(); } @@ -207,7 +198,7 @@ export default class TypeScriptServiceClientHost { private async diagnosticsReceived( kind: DiagnosticKind, - resource: Uri, + resource: vscode.Uri, diagnostics: Proto.Diagnostic[] ): Promise { const language = await this.findLanguage(resource); @@ -233,10 +224,10 @@ export default class TypeScriptServiceClientHost { if (body.diagnostics.length === 0) { language.configFileDiagnosticsReceived(this.client.toResource(body.configFile), []); } else if (body.diagnostics.length >= 1) { - workspace.openTextDocument(Uri.file(body.configFile)).then((document) => { + vscode.workspace.openTextDocument(vscode.Uri.file(body.configFile)).then((document) => { let curly: [number, number, number] | undefined = undefined; let nonCurly: [number, number, number] | undefined = undefined; - let diagnostic: Diagnostic; + let diagnostic: vscode.Diagnostic; for (let index = 0; index < document.lineCount; index++) { const line = document.lineAt(index); const text = line.text; @@ -255,16 +246,16 @@ export default class TypeScriptServiceClientHost { } const match = curly || nonCurly; if (match) { - diagnostic = new Diagnostic(new Range(match[0], match[1], match[0], match[2]), body.diagnostics[0].text); + diagnostic = new vscode.Diagnostic(new vscode.Range(match[0], match[1], match[0], match[2]), body.diagnostics[0].text); } else { - diagnostic = new Diagnostic(new Range(0, 0, 0, 0), body.diagnostics[0].text); + diagnostic = new vscode.Diagnostic(new vscode.Range(0, 0, 0, 0), body.diagnostics[0].text); } if (diagnostic) { diagnostic.source = language.diagnosticSource; language.configFileDiagnosticsReceived(this.client.toResource(body.configFile), [diagnostic]); } }, _error => { - language.configFileDiagnosticsReceived(this.client.toResource(body.configFile), [new Diagnostic(new Range(0, 0, 0, 0), body.diagnostics[0].text)]); + language.configFileDiagnosticsReceived(this.client.toResource(body.configFile), [new vscode.Diagnostic(new vscode.Range(0, 0, 0, 0), body.diagnostics[0].text)]); }); } }); @@ -273,14 +264,14 @@ export default class TypeScriptServiceClientHost { private createMarkerDatas( diagnostics: Proto.Diagnostic[], source: string - ): (Diagnostic & { reportUnnecessary: any })[] { + ): (vscode.Diagnostic & { reportUnnecessary: any })[] { return diagnostics.map(tsDiag => this.tsDiagnosticToVsDiagnostic(tsDiag, source)); } - private tsDiagnosticToVsDiagnostic(diagnostic: Proto.Diagnostic, source: string): Diagnostic & { reportUnnecessary: any } { + private tsDiagnosticToVsDiagnostic(diagnostic: Proto.Diagnostic, source: string): vscode.Diagnostic & { reportUnnecessary: any } { const { start, end, text } = diagnostic; - const range = new Range(typeConverters.Position.fromLocation(start), typeConverters.Position.fromLocation(end)); - const converted = new Diagnostic(range, text); + const range = new vscode.Range(typeConverters.Position.fromLocation(start), typeConverters.Position.fromLocation(end)); + const converted = new vscode.Diagnostic(range, text); converted.severity = this.getDiagnosticSeverity(diagnostic); converted.source = diagnostic.source || source; if (diagnostic.code) { @@ -293,36 +284,36 @@ export default class TypeScriptServiceClientHost { if (!span) { return undefined; } - return new DiagnosticRelatedInformation(typeConverters.Location.fromTextSpan(this.client.toResource(span.file), span), info.message); - }).filter((x: any) => !!x) as DiagnosticRelatedInformation[]; + return new vscode.DiagnosticRelatedInformation(typeConverters.Location.fromTextSpan(this.client.toResource(span.file), span), info.message); + }).filter((x: any) => !!x) as vscode.DiagnosticRelatedInformation[]; } if (diagnostic.reportsUnnecessary) { - converted.tags = [DiagnosticTag.Unnecessary]; + converted.tags = [vscode.DiagnosticTag.Unnecessary]; } - (converted as Diagnostic & { reportUnnecessary: any }).reportUnnecessary = diagnostic.reportsUnnecessary; - return converted as Diagnostic & { reportUnnecessary: any }; + (converted as vscode.Diagnostic & { reportUnnecessary: any }).reportUnnecessary = diagnostic.reportsUnnecessary; + return converted as vscode.Diagnostic & { reportUnnecessary: any }; } - private getDiagnosticSeverity(diagnostic: Proto.Diagnostic): DiagnosticSeverity { + private getDiagnosticSeverity(diagnostic: Proto.Diagnostic): vscode.DiagnosticSeverity { if (this.reportStyleCheckAsWarnings && this.isStyleCheckDiagnostic(diagnostic.code) && diagnostic.category === PConst.DiagnosticCategory.error ) { - return DiagnosticSeverity.Warning; + return vscode.DiagnosticSeverity.Warning; } switch (diagnostic.category) { case PConst.DiagnosticCategory.error: - return DiagnosticSeverity.Error; + return vscode.DiagnosticSeverity.Error; case PConst.DiagnosticCategory.warning: - return DiagnosticSeverity.Warning; + return vscode.DiagnosticSeverity.Warning; case PConst.DiagnosticCategory.suggestion: - return DiagnosticSeverity.Hint; + return vscode.DiagnosticSeverity.Hint; default: - return DiagnosticSeverity.Error; + return vscode.DiagnosticSeverity.Error; } } diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index 71c6c005d65..aff38d6a44c 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancellationToken, Event, Uri } from 'vscode'; +import * as vscode from 'vscode'; import BufferSyncSupport from './features/bufferSyncSupport'; import * as Proto from './protocol'; import API from './utils/api'; @@ -11,33 +11,66 @@ import { TypeScriptServiceConfiguration } from './utils/configuration'; import Logger from './utils/logger'; import { TypeScriptServerPlugin } from './utils/plugins'; +interface TypeScriptRequestTypes { + 'applyCodeActionCommand': [Proto.ApplyCodeActionCommandRequestArgs, Proto.ApplyCodeActionCommandResponse]; + 'completionEntryDetails': [Proto.CompletionDetailsRequestArgs, Proto.CompletionDetailsResponse]; + 'completionInfo': [Proto.CompletionsRequestArgs, Proto.CompletionInfoResponse]; + 'completions': [Proto.CompletionsRequestArgs, Proto.CompletionsResponse]; + 'configure': [Proto.ConfigureRequestArguments, Proto.ConfigureResponse]; + 'definition': [Proto.FileLocationRequestArgs, Proto.DefinitionResponse]; + 'definitionAndBoundSpan': [Proto.FileLocationRequestArgs, Proto.DefinitionInfoAndBoundSpanReponse]; + 'docCommentTemplate': [Proto.FileLocationRequestArgs, Proto.DocCommandTemplateResponse]; + 'format': [Proto.FormatRequestArgs, Proto.FormatResponse]; + 'formatonkey': [Proto.FormatOnKeyRequestArgs, Proto.FormatResponse]; + 'getApplicableRefactors': [Proto.GetApplicableRefactorsRequestArgs, Proto.GetApplicableRefactorsResponse]; + 'getCodeFixes': [Proto.CodeFixRequestArgs, Proto.GetCodeFixesResponse]; + 'getCombinedCodeFix': [Proto.GetCombinedCodeFixRequestArgs, Proto.GetCombinedCodeFixResponse]; + 'getEditsForFileRename': [Proto.GetEditsForFileRenameRequestArgs, Proto.GetEditsForFileRenameResponse]; + 'getEditsForRefactor': [Proto.GetEditsForRefactorRequestArgs, Proto.GetEditsForRefactorResponse]; + 'getOutliningSpans': [Proto.FileRequestArgs, Proto.OutliningSpansResponse]; + 'getSupportedCodeFixes': [null, Proto.GetSupportedCodeFixesResponse]; + 'implementation': [Proto.FileLocationRequestArgs, Proto.ImplementationResponse]; + 'jsxClosingTag': [Proto.JsxClosingTagRequestArgs, Proto.JsxClosingTagResponse]; + 'navto': [Proto.NavtoRequestArgs, Proto.NavtoResponse]; + 'navtree': [Proto.FileRequestArgs, Proto.NavTreeResponse]; + 'occurrences': [Proto.FileLocationRequestArgs, Proto.OccurrencesResponse]; + 'organizeImports': [Proto.OrganizeImportsRequestArgs, Proto.OrganizeImportsResponse]; + 'projectInfo': [Proto.ProjectInfoRequestArgs, Proto.ProjectInfoResponse]; + 'quickinfo': [Proto.FileLocationRequestArgs, Proto.QuickInfoResponse]; + 'references': [Proto.FileLocationRequestArgs, Proto.ReferencesResponse]; + 'rename': [Proto.RenameRequestArgs, Proto.RenameResponse]; + 'signatureHelp': [Proto.SignatureHelpRequestArgs, Proto.SignatureHelpResponse]; + 'typeDefinition': [Proto.FileLocationRequestArgs, Proto.TypeDefinitionResponse]; +} + + export interface ITypeScriptServiceClient { /** * Convert a resource (VS Code) to a normalized path (TypeScript). * * Does not try handling case insensitivity. */ - normalizedPath(resource: Uri): string | null; + normalizedPath(resource: vscode.Uri): string | null; /** * Map a resource to a normalized path * * This will attempt to handle case insensitivity. */ - toPath(resource: Uri): string | null; + toPath(resource: vscode.Uri): string | null; /** * Convert a path to a resource. */ - toResource(filepath: string): Uri; + toResource(filepath: string): vscode.Uri; - getWorkspaceRootForResource(resource: Uri): string | undefined; + getWorkspaceRootForResource(resource: vscode.Uri): string | undefined; - readonly onTsServerStarted: Event; - readonly onProjectLanguageServiceStateChanged: Event; - readonly onDidBeginInstallTypings: Event; - readonly onDidEndInstallTypings: Event; - readonly onTypesInstallerInitializationFailed: Event; + readonly onTsServerStarted: vscode.Event; + readonly onProjectLanguageServiceStateChanged: vscode.Event; + readonly onDidBeginInstallTypings: vscode.Event; + readonly onDidEndInstallTypings: vscode.Event; + readonly onTypesInstallerInitializationFailed: vscode.Event; readonly apiVersion: API; readonly plugins: TypeScriptServerPlugin[]; @@ -45,43 +78,22 @@ export interface ITypeScriptServiceClient { readonly logger: Logger; readonly bufferSyncSupport: BufferSyncSupport; - execute(command: 'configure', args: Proto.ConfigureRequestArguments, token?: CancellationToken): Promise; - execute(command: 'open', args: Proto.OpenRequestArgs, expectedResult: boolean, token?: CancellationToken): Promise; - execute(command: 'close', args: Proto.FileRequestArgs, expectedResult: boolean, token?: CancellationToken): Promise; - execute(command: 'change', args: Proto.ChangeRequestArgs, expectedResult: boolean, token?: CancellationToken): Promise; - execute(command: 'quickinfo', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise; - execute(command: 'completions', args: Proto.CompletionsRequestArgs, token?: CancellationToken): Promise; - execute(command: 'completionInfo', args: Proto.CompletionsRequestArgs, token?: CancellationToken): Promise; - execute(command: 'completionEntryDetails', args: Proto.CompletionDetailsRequestArgs, token?: CancellationToken): Promise; - execute(command: 'signatureHelp', args: Proto.SignatureHelpRequestArgs, token?: CancellationToken): Promise; - execute(command: 'definition', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise; - execute(command: 'definitionAndBoundSpan', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise; - execute(command: 'implementation', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise; - execute(command: 'typeDefinition', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise; - execute(command: 'references', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise; - execute(command: 'navto', args: Proto.NavtoRequestArgs, token?: CancellationToken): Promise; - execute(command: 'navbar', args: Proto.FileRequestArgs, token?: CancellationToken): Promise; - execute(command: 'format', args: Proto.FormatRequestArgs, token?: CancellationToken): Promise; - execute(command: 'formatonkey', args: Proto.FormatOnKeyRequestArgs, token?: CancellationToken): Promise; - execute(command: 'rename', args: Proto.RenameRequestArgs, token?: CancellationToken): Promise; - execute(command: 'occurrences', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise; - execute(command: 'projectInfo', args: Proto.ProjectInfoRequestArgs, token?: CancellationToken): Promise; - execute(command: 'reloadProjects', args: any, expectedResult: boolean, token?: CancellationToken): Promise; - execute(command: 'reload', args: Proto.ReloadRequestArgs, expectedResult: boolean, token?: CancellationToken): Promise; - execute(command: 'compilerOptionsForInferredProjects', args: Proto.SetCompilerOptionsForInferredProjectsArgs, token?: CancellationToken): Promise; - execute(command: 'navtree', args: Proto.FileRequestArgs, token?: CancellationToken): Promise; - execute(command: 'getCodeFixes', args: Proto.CodeFixRequestArgs, token?: CancellationToken): Promise; - execute(command: 'getSupportedCodeFixes', args: null, token?: CancellationToken): Promise; - execute(command: 'getCombinedCodeFix', args: Proto.GetCombinedCodeFixRequestArgs, token?: CancellationToken): Promise; - execute(command: 'docCommentTemplate', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise; - execute(command: 'getApplicableRefactors', args: Proto.GetApplicableRefactorsRequestArgs, token?: CancellationToken): Promise; - execute(command: 'getEditsForRefactor', args: Proto.GetEditsForRefactorRequestArgs, token?: CancellationToken): Promise; - execute(command: 'applyCodeActionCommand', args: Proto.ApplyCodeActionCommandRequestArgs, token?: CancellationToken): Promise; - execute(command: 'organizeImports', args: Proto.OrganizeImportsRequestArgs, token?: CancellationToken): Promise; - execute(command: 'getOutliningSpans', args: Proto.FileRequestArgs, token: CancellationToken): Promise; - execute(command: 'getEditsForFileRename', args: Proto.GetEditsForFileRenameRequestArgs): Promise; - execute(command: 'jsxClosingTag', args: Proto.JsxClosingTagRequestArgs, token: CancellationToken): Promise; - execute(command: string, args: any, expectedResult: boolean | CancellationToken, token?: CancellationToken): Promise; + execute( + command: K, + args: TypeScriptRequestTypes[K][0], + token: vscode.CancellationToken + ): Promise; - executeAsync(command: 'geterr', args: Proto.GeterrRequestArgs, token: CancellationToken): Promise; + executeWithoutWaitingForResponse(command: 'open', args: Proto.OpenRequestArgs): void; + executeWithoutWaitingForResponse(command: 'close', args: Proto.FileRequestArgs): void; + executeWithoutWaitingForResponse(command: 'change', args: Proto.ChangeRequestArgs): void; + executeWithoutWaitingForResponse(command: 'compilerOptionsForInferredProjects', args: Proto.SetCompilerOptionsForInferredProjectsArgs): void; + executeWithoutWaitingForResponse(command: 'reloadProjects', args: null): void; + + executeAsync(command: 'geterr', args: Proto.GeterrRequestArgs, token: vscode.CancellationToken): Promise; + + /** + * Cancel on going geterr requests and re-queue them after `f` has been evaluated. + */ + interuptGetErr(f: () => R): R; } \ No newline at end of file diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index c5666b895dc..016f533b6e2 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -6,15 +6,15 @@ import * as cp from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; -import { CancellationToken, commands, Disposable, env, EventEmitter, Memento, MessageItem, Uri, window, workspace } from 'vscode'; +import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import BufferSyncSupport from './features/bufferSyncSupport'; -import { DiagnosticKind } from './features/diagnostics'; +import { DiagnosticKind, DiagnosticsManager } from './features/diagnostics'; import * as Proto from './protocol'; import { ITypeScriptServiceClient } from './typescriptService'; import API from './utils/api'; import { TsServerLogLevel, TypeScriptServiceConfiguration } from './utils/configuration'; -import { disposeAll } from './utils/dispose'; +import { Disposable } from './utils/dispose'; import * as electron from './utils/electron'; import * as fileSchemes from './utils/fileSchemes'; import * as is from './utils/is'; @@ -30,9 +30,6 @@ import { TypeScriptVersion, TypeScriptVersionProvider } from './utils/versionPro import { ICallback, Reader } from './utils/wireProtocol'; - - - const localize = nls.loadMessageBundle(); interface CallbackItem { @@ -155,11 +152,11 @@ class ForkedTsServerProcess { export interface TsDiagnostics { readonly kind: DiagnosticKind; - readonly resource: Uri; + readonly resource: vscode.Uri; readonly diagnostics: Proto.Diagnostic[]; } -export default class TypeScriptServiceClient implements ITypeScriptServiceClient { +export default class TypeScriptServiceClient extends Disposable implements ITypeScriptServiceClient { private static readonly WALK_THROUGH_SNIPPET_SCHEME_COLON = `${fileSchemes.walkThroughSnippet}:`; private pathSeparator: string; @@ -194,17 +191,17 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient */ private _tsserverVersion: string | undefined; - private readonly disposables: Disposable[] = []; - public readonly bufferSyncSupport: BufferSyncSupport; + public readonly diagnosticsManager: DiagnosticsManager; constructor( - private readonly workspaceState: Memento, + private readonly workspaceState: vscode.Memento, private readonly onDidChangeTypeScriptVersion: (version: TypeScriptVersion) => void, public readonly plugins: TypeScriptServerPlugin[], private readonly logDirectoryProvider: LogDirectoryProvider, allModeIds: string[] ) { + super(); this.pathSeparator = path.sep; this.lastStart = Date.now(); @@ -231,7 +228,12 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient this.bufferSyncSupport = new BufferSyncSupport(this, allModeIds); this.onReady(() => { this.bufferSyncSupport.listen(); }); - workspace.onDidChangeConfiguration(() => { + this.diagnosticsManager = new DiagnosticsManager('typescript'); + this.bufferSyncSupport.onDelete(resource => { + this.diagnosticsManager.delete(resource); + }, null, this._disposables); + + vscode.workspace.onDidChangeConfiguration(() => { const oldConfiguration = this._configuration; this._configuration = TypeScriptServiceConfiguration.loadFromWorkspace(); @@ -250,9 +252,9 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient this.restartTsServer(); } } - }, this, this.disposables); + }, this, this._disposables); this.telemetryReporter = new TelemetryReporter(() => this._tsserverVersion || this._apiVersion.versionString); - this.disposables.push(this.telemetryReporter); + this._register(this.telemetryReporter); } public get configuration() { @@ -260,22 +262,15 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient } public dispose() { + super.dispose(); + this.bufferSyncSupport.dispose(); - this._onTsServerStarted.dispose(); - this._onDidBeginInstallTypings.dispose(); - this._onDidEndInstallTypings.dispose(); - this._onTypesInstallerInitializationFailed.dispose(); if (this.servicePromise) { this.servicePromise.then(childProcess => { childProcess.kill(); }).then(undefined, () => void 0); } - - disposeAll(this.disposables); - this._onDiagnosticsReceived.dispose(); - this._onConfigDiagnosticsReceived.dispose(); - this._onResendModelsRequested.dispose(); } public restartTsServer(): void { @@ -296,28 +291,28 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient } } - private readonly _onTsServerStarted = new EventEmitter(); + private readonly _onTsServerStarted = this._register(new vscode.EventEmitter()); public readonly onTsServerStarted = this._onTsServerStarted.event; - private readonly _onDiagnosticsReceived = new EventEmitter(); + private readonly _onDiagnosticsReceived = this._register(new vscode.EventEmitter()); public readonly onDiagnosticsReceived = this._onDiagnosticsReceived.event; - private readonly _onConfigDiagnosticsReceived = new EventEmitter(); + private readonly _onConfigDiagnosticsReceived = this._register(new vscode.EventEmitter()); public readonly onConfigDiagnosticsReceived = this._onConfigDiagnosticsReceived.event; - private readonly _onResendModelsRequested = new EventEmitter(); + private readonly _onResendModelsRequested = this._register(new vscode.EventEmitter()); public readonly onResendModelsRequested = this._onResendModelsRequested.event; - private readonly _onProjectLanguageServiceStateChanged = new EventEmitter(); + private readonly _onProjectLanguageServiceStateChanged = this._register(new vscode.EventEmitter()); public readonly onProjectLanguageServiceStateChanged = this._onProjectLanguageServiceStateChanged.event; - private readonly _onDidBeginInstallTypings = new EventEmitter(); + private readonly _onDidBeginInstallTypings = this._register(new vscode.EventEmitter()); public readonly onDidBeginInstallTypings = this._onDidBeginInstallTypings.event; - private readonly _onDidEndInstallTypings = new EventEmitter(); + private readonly _onDidEndInstallTypings = this._register(new vscode.EventEmitter()); public readonly onDidEndInstallTypings = this._onDidEndInstallTypings.event; - private readonly _onTypesInstallerInitializationFailed = new EventEmitter(); + private readonly _onTypesInstallerInitializationFailed = this._register(new vscode.EventEmitter()); public readonly onTypesInstallerInitializationFailed = this._onTypesInstallerInitializationFailed.event; public get apiVersion(): API { @@ -365,7 +360,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient this.info(`Using tsserver from: ${currentVersion.path}`); if (!fs.existsSync(currentVersion.tsServerPath)) { - window.showWarningMessage(localize('noServerFound', 'The path {0} doesn\'t point to a valid tsserver install. Falling back to bundled TypeScript version.', currentVersion.path)); + vscode.window.showWarningMessage(localize('noServerFound', 'The path {0} doesn\'t point to a valid tsserver install. Falling back to bundled TypeScript version.', currentVersion.path)); this.versionPicker.useBundledVersion(); currentVersion = this.versionPicker.currentVersion; @@ -389,7 +384,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient if (err || !childProcess) { this.lastError = err; this.error('Starting TSServer failed with error.', err); - window.showErrorMessage(localize('serverCouldNotBeStarted', 'TypeScript language server couldn\'t be started. Error message is: {0}', err.message || err)); + vscode.window.showErrorMessage(localize('serverCouldNotBeStarted', 'TypeScript language server couldn\'t be started. Error message is: {0}', err.message || err)); /* __GDPR__ "error" : { "${include}": [ @@ -476,7 +471,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient public async openTsServerLogFile(): Promise { if (!this.apiVersion.gte(API.v222)) { - window.showErrorMessage( + vscode.window.showErrorMessage( localize( 'typescript.openTsServerLog.notSupported', 'TS Server logging requires TS 2.2.2+')); @@ -484,7 +479,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient } if (this._configuration.tsServerLogLevel === TsServerLogLevel.Off) { - window.showErrorMessage( + vscode.window.showErrorMessage( localize( 'typescript.openTsServerLog.loggingNotEnabled', 'TS Server logging is off. Please set `typescript.tsserver.log` and restart the TS server to enable logging'), @@ -495,7 +490,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient }) .then(selection => { if (selection) { - return workspace.getConfiguration().update('typescript.tsserver.log', 'verbose', true).then(() => { + return vscode.workspace.getConfiguration().update('typescript.tsserver.log', 'verbose', true).then(() => { this.restartTsServer(); }); } @@ -505,17 +500,17 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient } if (!this.tsServerLogFile) { - window.showWarningMessage(localize( + vscode.window.showWarningMessage(localize( 'typescript.openTsServerLog.noLogFile', 'TS Server has not started logging.')); return false; } try { - await commands.executeCommand('revealFileInOS', Uri.parse(this.tsServerLogFile)); + await vscode.commands.executeCommand('revealFileInOS', vscode.Uri.parse(this.tsServerLogFile)); return true; } catch { - window.showWarningMessage(localize( + vscode.window.showWarningMessage(localize( 'openTsServerLog.openFileFailedFailed', 'Could not open TS Server log file')); return false; @@ -526,7 +521,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient const configureOptions: Proto.ConfigureRequestArguments = { hostInfo: 'vscode' }; - this.execute('configure', configureOptions); + this.executeWithoutWaitingForResponse('configure', configureOptions); this.setCompilerOptionsForInferredProjects(this._configuration); if (resendModels) { this._onResendModelsRequested.fire(); @@ -541,7 +536,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient const args: Proto.SetCompilerOptionsForInferredProjectsArgs = { options: this.getCompilerOptionsForInferredProjects(configuration) }; - this.execute('compilerOptionsForInferredProjects', args, true); + this.executeWithoutWaitingForResponse('compilerOptionsForInferredProjects', args); } private getCompilerOptionsForInferredProjects(configuration: TypeScriptServiceConfiguration): Proto.ExternalProjectCompilerOptions { @@ -558,7 +553,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient reportIssue } - interface MyMessageItem extends MessageItem { + interface MyMessageItem extends vscode.MessageItem { id: MessageAction; } @@ -579,7 +574,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient if (diff < 10 * 1000 /* 10 seconds */) { this.lastStart = Date.now(); startService = false; - prompt = window.showErrorMessage( + prompt = vscode.window.showErrorMessage( localize('serverDiedAfterStart', 'The TypeScript language service died 5 times right after it got started. The service will not be restarted.'), { title: localize('serverDiedReportIssue', 'Report Issue'), @@ -596,7 +591,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient this.resetClientVersion(); } else if (diff < 60 * 1000 /* 1 Minutes */) { this.lastStart = Date.now(); - prompt = window.showWarningMessage( + prompt = vscode.window.showWarningMessage( localize('serverDied', 'The TypeScript language service died unexpectedly 5 times in the last 5 Minutes.'), { title: localize('serverDiedReportIssue', 'Report Issue'), @@ -606,7 +601,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient if (prompt) { prompt.then(item => { if (item && item.id === MessageAction.reportIssue) { - return commands.executeCommand('workbench.action.reportIssues'); + return vscode.commands.executeCommand('workbench.action.reportIssues'); } return undefined; }); @@ -618,7 +613,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient } } - public normalizedPath(resource: Uri): string | null { + public normalizedPath(resource: vscode.Uri): string | null { if (this._apiVersion.gte(API.v213)) { if (resource.scheme === fileSchemes.walkThroughSnippet || resource.scheme === fileSchemes.untitled) { const dirName = path.dirname(resource.path); @@ -640,7 +635,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient return result.replace(new RegExp('\\' + this.pathSeparator, 'g'), '/'); } - public toPath(resource: Uri): string | null { + public toPath(resource: vscode.Uri): string | null { return this.normalizedPath(resource); } @@ -648,11 +643,11 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient return this._apiVersion.gte(API.v270) ? '^' : ''; } - public toResource(filepath: string): Uri { + public toResource(filepath: string): vscode.Uri { if (this._apiVersion.gte(API.v213)) { if (filepath.startsWith(TypeScriptServiceClient.WALK_THROUGH_SNIPPET_SCHEME_COLON) || (filepath.startsWith(fileSchemes.untitled + ':')) ) { - let resource = Uri.parse(filepath); + let resource = vscode.Uri.parse(filepath); if (this.inMemoryResourcePrefix) { const dirName = path.dirname(resource.path); const fileName = path.basename(resource.path); @@ -666,8 +661,8 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient return this.bufferSyncSupport.toResource(filepath); } - public getWorkspaceRootForResource(resource: Uri): string | undefined { - const roots = workspace.workspaceFolders; + public getWorkspaceRootForResource(resource: vscode.Uri): string | undefined { + const roots = vscode.workspace.workspaceFolders; if (!roots || !roots.length) { return undefined; } @@ -684,22 +679,31 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient return undefined; } - public executeAsync(command: string, args: Proto.GeterrRequestArgs, token: CancellationToken): Promise { - return this.executeImpl(command, args, { isAsync: true, token, expectsResult: true }); + public execute(command: string, args: any, token: vscode.CancellationToken): Promise { + return this.executeImpl(command, args, { + isAsync: false, + token, + expectsResult: true + }); } - public execute(command: string, args: any, expectsResultOrToken?: boolean | CancellationToken): Promise { - let token: CancellationToken | undefined = undefined; - let expectsResult = true; - if (typeof expectsResultOrToken === 'boolean') { - expectsResult = expectsResultOrToken; - } else { - token = expectsResultOrToken; - } - return this.executeImpl(command, args, { isAsync: false, token, expectsResult }); + public executeWithoutWaitingForResponse(command: string, args: any): void { + this.executeImpl(command, args, { + isAsync: false, + token: undefined, + expectsResult: false + }); } - private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: CancellationToken, expectsResult: boolean }): Promise { + public executeAsync(command: string, args: Proto.GeterrRequestArgs, token: vscode.CancellationToken): Promise { + return this.executeImpl(command, args, { + isAsync: true, + token, + expectsResult: true + }); + } + + private executeImpl(command: string, args: any, executeInfo: { isAsync: boolean, token?: vscode.CancellationToken, expectsResult: boolean }): Promise { const request = this.requestQueue.createRequest(command, args); const requestInfo: RequestItem = { request: request, @@ -734,6 +738,10 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient return result; } + public interuptGetErr(f: () => R): R { + return this.bufferSyncSupport.interuptGetErr(f); + } + /** * Given a `errorText` from a tsserver request indicating failure in handling a request, * prepares a payload for telemetry-logging. @@ -879,7 +887,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient case 'projectsUpdatedInBackground': if (event.body) { const body = (event as Proto.ProjectsUpdatedInBackgroundEvent).body; - const resources = body.openFiles.map(Uri.file); + const resources = body.openFiles.map(vscode.Uri.file); this.bufferSyncSupport.getErr(resources); } break; @@ -974,7 +982,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient } if (this.apiVersion.gte(API.v222)) { - this.cancellationPipeName = electron.getTempSock('tscancellation'); + this.cancellationPipeName = electron.getTempFile('tscancellation'); args.push('--cancellationPipeName', this.cancellationPipeName + '*'); } @@ -1053,7 +1061,7 @@ export default class TypeScriptServiceClient implements ITypeScriptServiceClient const getTsLocale = (configuration: TypeScriptServiceConfiguration): string | undefined => (configuration.locale ? configuration.locale - : env.language); + : vscode.env.language); function getDignosticsKind(event: Proto.Event) { switch (event.event) { diff --git a/extensions/typescript-language-features/src/utils/arrays.ts b/extensions/typescript-language-features/src/utils/arrays.ts index 57dbd54a29c..3a15e38981a 100644 --- a/extensions/typescript-language-features/src/utils/arrays.ts +++ b/extensions/typescript-language-features/src/utils/arrays.ts @@ -14,4 +14,8 @@ export function equals(one: T[], other: T[], itemEquals: (a: T, b: T) => bool } return true; +} + +export function flatten(arr: T[][]): T[] { + return [].concat.apply([], arr); } \ No newline at end of file diff --git a/extensions/typescript-language-features/src/utils/cancellation.ts b/extensions/typescript-language-features/src/utils/cancellation.ts new file mode 100644 index 00000000000..10933baa939 --- /dev/null +++ b/extensions/typescript-language-features/src/utils/cancellation.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +const nulTokenSource = new vscode.CancellationTokenSource(); + +export const nulToken = nulTokenSource.token; \ No newline at end of file diff --git a/extensions/typescript-language-features/src/utils/codeAction.ts b/extensions/typescript-language-features/src/utils/codeAction.ts index f9dec3a7d4d..c45379e611a 100644 --- a/extensions/typescript-language-features/src/utils/codeAction.ts +++ b/extensions/typescript-language-features/src/utils/codeAction.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { workspace, WorkspaceEdit } from 'vscode'; +import * as vscode from 'vscode'; import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import * as typeConverters from './typeConverters'; @@ -11,7 +11,7 @@ import * as typeConverters from './typeConverters'; export function getEditForCodeAction( client: ITypeScriptServiceClient, action: Proto.CodeAction -): WorkspaceEdit | undefined { +): vscode.WorkspaceEdit | undefined { return action.changes && action.changes.length ? typeConverters.WorkspaceEdit.fromFileCodeEdits(client, action.changes) : undefined; @@ -19,27 +19,26 @@ export function getEditForCodeAction( export async function applyCodeAction( client: ITypeScriptServiceClient, - action: Proto.CodeAction + action: Proto.CodeAction, + token: vscode.CancellationToken ): Promise { const workspaceEdit = getEditForCodeAction(client, action); if (workspaceEdit) { - if (!(await workspace.applyEdit(workspaceEdit))) { + if (!(await vscode.workspace.applyEdit(workspaceEdit))) { return false; } } - return applyCodeActionCommands(client, action); + return applyCodeActionCommands(client, action.commands, token); } export async function applyCodeActionCommands( client: ITypeScriptServiceClient, - action: Proto.CodeAction + commands: ReadonlyArray<{}> | undefined, + token: vscode.CancellationToken, ): Promise { - if (action.commands && action.commands.length) { - for (const command of action.commands) { - const response = await client.execute('applyCodeActionCommand', { command }); - if (!response || !response.body) { - return false; - } + if (commands && commands.length) { + for (const command of commands) { + await client.execute('applyCodeActionCommand', { command }, token); } } return true; diff --git a/extensions/typescript-language-features/src/utils/configuration.ts b/extensions/typescript-language-features/src/utils/configuration.ts index d5c0d00bdf3..adeb071062e 100644 --- a/extensions/typescript-language-features/src/utils/configuration.ts +++ b/extensions/typescript-language-features/src/utils/configuration.ts @@ -2,7 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { workspace, WorkspaceConfiguration } from 'vscode'; +import * as vscode from 'vscode'; import * as arrays from './arrays'; export enum TsServerLogLevel { @@ -58,7 +58,7 @@ export class TypeScriptServiceConfiguration { } private constructor() { - const configuration = workspace.getConfiguration(); + const configuration = vscode.workspace.getConfiguration(); this.locale = TypeScriptServiceConfiguration.extractLocale(configuration); this.globalTsdk = TypeScriptServiceConfiguration.extractGlobalTsdk(configuration); @@ -83,7 +83,7 @@ export class TypeScriptServiceConfiguration { && arrays.equals(this.tsServerPluginPaths, other.tsServerPluginPaths); } - private static extractGlobalTsdk(configuration: WorkspaceConfiguration): string | null { + private static extractGlobalTsdk(configuration: vscode.WorkspaceConfiguration): string | null { const inspect = configuration.inspect('typescript.tsdk'); if (inspect && inspect.globalValue && 'string' === typeof inspect.globalValue) { return inspect.globalValue; @@ -91,7 +91,7 @@ export class TypeScriptServiceConfiguration { return null; } - private static extractLocalTsdk(configuration: WorkspaceConfiguration): string | null { + private static extractLocalTsdk(configuration: vscode.WorkspaceConfiguration): string | null { const inspect = configuration.inspect('typescript.tsdk'); if (inspect && inspect.workspaceValue && 'string' === typeof inspect.workspaceValue) { return inspect.workspaceValue; @@ -99,32 +99,32 @@ export class TypeScriptServiceConfiguration { return null; } - private static readTsServerLogLevel(configuration: WorkspaceConfiguration): TsServerLogLevel { + private static readTsServerLogLevel(configuration: vscode.WorkspaceConfiguration): TsServerLogLevel { const setting = configuration.get('typescript.tsserver.log', 'off'); return TsServerLogLevel.fromString(setting); } - private static readTsServerPluginPaths(configuration: WorkspaceConfiguration): string[] { + private static readTsServerPluginPaths(configuration: vscode.WorkspaceConfiguration): string[] { return configuration.get('typescript.tsserver.pluginPaths', []); } - private static readCheckJs(configuration: WorkspaceConfiguration): boolean { + private static readCheckJs(configuration: vscode.WorkspaceConfiguration): boolean { return configuration.get('javascript.implicitProjectConfig.checkJs', false); } - private static readExperimentalDecorators(configuration: WorkspaceConfiguration): boolean { + private static readExperimentalDecorators(configuration: vscode.WorkspaceConfiguration): boolean { return configuration.get('javascript.implicitProjectConfig.experimentalDecorators', false); } - private static readNpmLocation(configuration: WorkspaceConfiguration): string | null { + private static readNpmLocation(configuration: vscode.WorkspaceConfiguration): string | null { return configuration.get('typescript.npm', null); } - private static readDisableAutomaticTypeAcquisition(configuration: WorkspaceConfiguration): boolean { + private static readDisableAutomaticTypeAcquisition(configuration: vscode.WorkspaceConfiguration): boolean { return configuration.get('typescript.disableAutomaticTypeAcquisition', false); } - private static extractLocale(configuration: WorkspaceConfiguration): string | null { + private static extractLocale(configuration: vscode.WorkspaceConfiguration): string | null { return configuration.get('typescript.locale', null); } } diff --git a/extensions/typescript-language-features/src/utils/dependentRegistration.ts b/extensions/typescript-language-features/src/utils/dependentRegistration.ts index 232d6b8e2cf..434379f57e1 100644 --- a/extensions/typescript-language-features/src/utils/dependentRegistration.ts +++ b/extensions/typescript-language-features/src/utils/dependentRegistration.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from './api'; -import { disposeAll } from './dispose'; +import { Disposable } from './dispose'; export class ConditionalRegistration { private registration: vscode.Disposable | undefined = undefined; @@ -36,15 +36,15 @@ export class ConditionalRegistration { } } -export class VersionDependentRegistration { +export class VersionDependentRegistration extends Disposable { private readonly _registration: ConditionalRegistration; - private readonly _disposables: vscode.Disposable[] = []; constructor( private readonly client: ITypeScriptServiceClient, private readonly minVersion: API, register: () => vscode.Disposable, ) { + super(); this._registration = new ConditionalRegistration(register); this.update(client.apiVersion); @@ -55,7 +55,7 @@ export class VersionDependentRegistration { } public dispose() { - disposeAll(this._disposables); + super.dispose(); this._registration.dispose(); } @@ -65,22 +65,22 @@ export class VersionDependentRegistration { } -export class ConfigurationDependentRegistration { +export class ConfigurationDependentRegistration extends Disposable { private readonly _registration: ConditionalRegistration; - private readonly _disposables: vscode.Disposable[] = []; constructor( private readonly language: string, private readonly configValue: string, register: () => vscode.Disposable, ) { + super(); this._registration = new ConditionalRegistration(register); this.update(); vscode.workspace.onDidChangeConfiguration(this.update, this, this._disposables); } public dispose() { - disposeAll(this._disposables); + super.dispose(); this._registration.dispose(); } diff --git a/extensions/typescript-language-features/src/utils/dispose.ts b/extensions/typescript-language-features/src/utils/dispose.ts index b7c72f8b8b4..a0b91f441b2 100644 --- a/extensions/typescript-language-features/src/utils/dispose.ts +++ b/extensions/typescript-language-features/src/utils/dispose.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; -export function disposeAll(disposables: vscode.Disposable[]) { +function disposeAll(disposables: vscode.Disposable[]) { while (disposables.length) { const item = disposables.pop(); if (item) { @@ -13,3 +13,26 @@ export function disposeAll(disposables: vscode.Disposable[]) { } } } + +export abstract class Disposable { + private _isDisposed = false; + + protected _disposables: vscode.Disposable[] = []; + + public dispose(): any { + if (this._isDisposed) { + return; + } + this._isDisposed = true; + disposeAll(this._disposables); + } + + protected _register(value: T): T { + if (this._isDisposed) { + value.dispose(); + } else { + this._disposables.push(value); + } + return value; + } +} \ No newline at end of file diff --git a/extensions/typescript-language-features/src/utils/electron.ts b/extensions/typescript-language-features/src/utils/electron.ts index 34a6534f8ec..46129f60f0e 100644 --- a/extensions/typescript-language-features/src/utils/electron.ts +++ b/extensions/typescript-language-features/src/utils/electron.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import Logger from './logger'; -import { getTempFile, makeRandomHexString } from './temp'; +import * as temp from './temp'; import path = require('path'); -import os = require('os'); +import fs = require('fs'); import net = require('net'); import cp = require('child_process'); @@ -15,13 +15,25 @@ export interface IForkOptions { execArgv?: string[]; } -export function getTempSock(prefix: string): string { - const fullName = `vscode-${prefix}-${makeRandomHexString(20)}`; - return getTempFile(fullName + '.sock'); +const getRootTempDir = (() => { + let dir: string | undefined; + return () => { + if (!dir) { + dir = temp.getTempFile(`vscode-typescript`); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir); + } + } + return dir; + }; +})(); + +export function getTempFile(prefix: string): string { + return path.join(getRootTempDir(), `${prefix}-${temp.makeRandomHexString(20)}.tmp`); } function generatePipeName(): string { - return getPipeName(makeRandomHexString(40)); + return getPipeName(temp.makeRandomHexString(40)); } function getPipeName(name: string): string { @@ -31,7 +43,7 @@ function getPipeName(name: string): string { } // Mac/Unix: use socket file - return path.join(os.tmpdir(), fullName + '.sock'); + return path.join(getRootTempDir(), fullName + '.sock'); } function generatePatchedEnv( diff --git a/extensions/typescript-language-features/src/utils/languageDescription.ts b/extensions/typescript-language-features/src/utils/languageDescription.ts index cd84e9b5f47..f6f4806ae03 100644 --- a/extensions/typescript-language-features/src/utils/languageDescription.ts +++ b/extensions/typescript-language-features/src/utils/languageDescription.ts @@ -4,26 +4,36 @@ *--------------------------------------------------------------------------------------------*/ import * as languageModeIds from './languageModeIds'; +export enum DiagnosticLanguage { + JavaScript, + TypeScript +} + +export const allDiagnosticLangauges = [DiagnosticLanguage.JavaScript, DiagnosticLanguage.TypeScript]; + export interface LanguageDescription { readonly id: string; + readonly diagnosticOwner: string; readonly diagnosticSource: string; + readonly diagnosticLanguage: DiagnosticLanguage; readonly modeIds: string[]; readonly configFile?: string; readonly isExternal?: boolean; - readonly diagnosticOwner: string; } export const standardLanguageDescriptions: LanguageDescription[] = [ { id: 'typescript', - diagnosticSource: 'ts', diagnosticOwner: 'typescript', + diagnosticSource: 'ts', + diagnosticLanguage: DiagnosticLanguage.TypeScript, modeIds: [languageModeIds.typescript, languageModeIds.typescriptreact], configFile: 'tsconfig.json' }, { id: 'javascript', - diagnosticSource: 'ts', diagnosticOwner: 'typescript', + diagnosticSource: 'ts', + diagnosticLanguage: DiagnosticLanguage.JavaScript, modeIds: [languageModeIds.javascript, languageModeIds.javascriptreact], configFile: 'jsconfig.json' } diff --git a/extensions/typescript-language-features/src/utils/logger.ts b/extensions/typescript-language-features/src/utils/logger.ts index 782fe871fe3..7ba79adad12 100644 --- a/extensions/typescript-language-features/src/utils/logger.ts +++ b/extensions/typescript-language-features/src/utils/logger.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { OutputChannel, window } from 'vscode'; +import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import * as is from './is'; import { memoize } from './memoize'; @@ -13,8 +13,8 @@ const localize = nls.loadMessageBundle(); export default class Logger { @memoize - private get output(): OutputChannel { - return window.createOutputChannel(localize('channelName', 'TypeScript')); + private get output(): vscode.OutputChannel { + return vscode.window.createOutputChannel(localize('channelName', 'TypeScript')); } private data2String(data: any): string { diff --git a/extensions/typescript-language-features/src/utils/pluginPathsProvider.ts b/extensions/typescript-language-features/src/utils/pluginPathsProvider.ts index d001512234b..547660f0649 100644 --- a/extensions/typescript-language-features/src/utils/pluginPathsProvider.ts +++ b/extensions/typescript-language-features/src/utils/pluginPathsProvider.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as path from 'path'; -import { workspace } from 'vscode'; +import * as vscode from 'vscode'; import { TypeScriptServiceConfiguration } from './configuration'; import { RelativeWorkspacePathResolver } from './relativePathResolver'; @@ -37,7 +37,7 @@ export class TypeScriptPluginPathsProvider { return [workspacePath]; } - return (workspace.workspaceFolders || []) + return (vscode.workspace.workspaceFolders || []) .map(workspaceFolder => path.join(workspaceFolder.uri.fsPath, pluginPath)); } } \ No newline at end of file diff --git a/extensions/typescript-language-features/src/utils/plugins.ts b/extensions/typescript-language-features/src/utils/plugins.ts index 13966dd0b06..e67df651b40 100644 --- a/extensions/typescript-language-features/src/utils/plugins.ts +++ b/extensions/typescript-language-features/src/utils/plugins.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { extensions } from 'vscode'; +import * as vscode from 'vscode'; export interface TypeScriptServerPlugin { readonly path: string; @@ -13,7 +13,7 @@ export interface TypeScriptServerPlugin { export function getContributedTypeScriptServerPlugins(): TypeScriptServerPlugin[] { const plugins: TypeScriptServerPlugin[] = []; - for (const extension of extensions.all) { + for (const extension of vscode.extensions.all) { const pack = extension.packageJSON; if (pack.contributes && pack.contributes.typescriptServerPlugins && Array.isArray(pack.contributes.typescriptServerPlugins)) { for (const plugin of pack.contributes.typescriptServerPlugins) { diff --git a/extensions/typescript-language-features/src/utils/previewer.ts b/extensions/typescript-language-features/src/utils/previewer.ts index df0105ac287..aa698b2c163 100644 --- a/extensions/typescript-language-features/src/utils/previewer.ts +++ b/extensions/typescript-language-features/src/utils/previewer.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { MarkdownString } from 'vscode'; +import * as vscode from 'vscode'; import * as Proto from '../protocol'; function getTagBodyText(tag: Proto.JSDocTagInfo): string | undefined { @@ -64,17 +64,17 @@ export function tagsMarkdownPreview(tags: Proto.JSDocTagInfo[]): string { export function markdownDocumentation( documentation: Proto.SymbolDisplayPart[], tags: Proto.JSDocTagInfo[] -): MarkdownString { - const out = new MarkdownString(); +): vscode.MarkdownString { + const out = new vscode.MarkdownString(); addMarkdownDocumentation(out, documentation, tags); return out; } export function addMarkdownDocumentation( - out: MarkdownString, + out: vscode.MarkdownString, documentation: Proto.SymbolDisplayPart[] | undefined, tags: Proto.JSDocTagInfo[] | undefined -): MarkdownString { +): vscode.MarkdownString { if (documentation) { out.appendMarkdown(plain(documentation)); } diff --git a/extensions/typescript-language-features/src/utils/relativePathResolver.ts b/extensions/typescript-language-features/src/utils/relativePathResolver.ts index e424fca126a..85b72a55d6b 100644 --- a/extensions/typescript-language-features/src/utils/relativePathResolver.ts +++ b/extensions/typescript-language-features/src/utils/relativePathResolver.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as path from 'path'; -import { workspace } from 'vscode'; +import * as vscode from 'vscode'; export class RelativeWorkspacePathResolver { public asAbsoluteWorkspacePath(relativePath: string): string | undefined { - for (const root of workspace.workspaceFolders || []) { + for (const root of vscode.workspace.workspaceFolders || []) { const rootPrefixes = [`./${root.name}/`, `${root.name}/`, `.\\${root.name}\\`, `${root.name}\\`]; for (const rootPrefix of rootPrefixes) { if (relativePath.startsWith(rootPrefix)) { diff --git a/extensions/typescript-language-features/src/utils/resourceMap.ts b/extensions/typescript-language-features/src/utils/resourceMap.ts index f59ab661f5a..33d6f00b4be 100644 --- a/extensions/typescript-language-features/src/utils/resourceMap.ts +++ b/extensions/typescript-language-features/src/utils/resourceMap.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as fs from 'fs'; -import { Uri } from 'vscode'; +import * as vscode from 'vscode'; import { memoize } from './memoize'; import { getTempFile } from './temp'; @@ -15,50 +15,64 @@ import { getTempFile } from './temp'; * file systems. */ export class ResourceMap { - private readonly _map = new Map(); + private readonly _map = new Map(); constructor( - private readonly _normalizePath?: (resource: Uri) => string | null + private readonly _normalizePath: (resource: vscode.Uri) => string | null = (resource) => resource.fsPath ) { } - public has(resource: Uri): boolean { + public get size() { + return this._map.size; + } + + public has(resource: vscode.Uri): boolean { const file = this.toKey(resource); return !!file && this._map.has(file); } - public get(resource: Uri): T | undefined { + public get(resource: vscode.Uri): T | undefined { const file = this.toKey(resource); - return file ? this._map.get(file) : undefined; + if (!file) { + return undefined; + } + const entry = this._map.get(file); + return entry ? entry.value : undefined; } - public set(resource: Uri, value: T) { + public set(resource: vscode.Uri, value: T) { const file = this.toKey(resource); - if (file) { - this._map.set(file, value); + if (!file) { + return; + } + const entry = this._map.get(file); + if (entry) { + entry.value = value; + } else { + this._map.set(file, { resource, value }); } } - public delete(resource: Uri): void { + public delete(resource: vscode.Uri): void { const file = this.toKey(resource); if (file) { this._map.delete(file); } } - public clear() { + public clear(): void { this._map.clear(); } public get values(): Iterable { + return Array.from(this._map.values()).map(x => x.value); + } + + public get entries(): Iterable<{ resource: vscode.Uri, value: T }> { return this._map.values(); } - public get entries() { - return this._map.entries(); - } - - private toKey(resource: Uri): string | null { - const key = this._normalizePath ? this._normalizePath(resource) : resource.fsPath; + private toKey(resource: vscode.Uri): string | null { + const key = this._normalizePath(resource); if (!key) { return key; } diff --git a/extensions/typescript-language-features/src/utils/tracer.ts b/extensions/typescript-language-features/src/utils/tracer.ts index 2c12e2bf15a..2f2394dcaa1 100644 --- a/extensions/typescript-language-features/src/utils/tracer.ts +++ b/extensions/typescript-language-features/src/utils/tracer.ts @@ -3,12 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { workspace } from 'vscode'; +import * as vscode from 'vscode'; import * as Proto from '../protocol'; import Logger from './logger'; - - enum Trace { Off, Messages, @@ -45,7 +43,7 @@ export default class Tracer { } private static readTrace(): Trace { - let result: Trace = Trace.fromString(workspace.getConfiguration().get('typescript.tsserver.trace', 'off')); + let result: Trace = Trace.fromString(vscode.workspace.getConfiguration().get('typescript.tsserver.trace', 'off')); if (result === Trace.Off && !!process.env.TSS_TRACE) { result = Trace.Messages; } diff --git a/extensions/typescript-language-features/src/utils/typingsStatus.ts b/extensions/typescript-language-features/src/utils/typingsStatus.ts index 0113435deda..78f303f0f39 100644 --- a/extensions/typescript-language-features/src/utils/typingsStatus.ts +++ b/extensions/typescript-language-features/src/utils/typingsStatus.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, MessageItem, ProgressLocation, window, workspace } from 'vscode'; +import * as vscode from 'vscode'; import { loadMessageBundle } from 'vscode-nls'; import { ITypeScriptServiceClient } from '../typescriptService'; @@ -11,10 +11,10 @@ const localize = loadMessageBundle(); const typingsInstallTimeout = 30 * 1000; -export default class TypingsStatus extends Disposable { +export default class TypingsStatus extends vscode.Disposable { private _acquiringTypings: { [eventId: string]: NodeJS.Timer } = Object.create({}); private _client: ITypeScriptServiceClient; - private _subscriptions: Disposable[] = []; + private _subscriptions: vscode.Disposable[] = []; constructor(client: ITypeScriptServiceClient) { super(() => this.dispose()); @@ -60,10 +60,10 @@ export default class TypingsStatus extends Disposable { export class AtaProgressReporter { private _promises = new Map(); - private _disposable: Disposable; + private _disposable: vscode.Disposable; constructor(client: ITypeScriptServiceClient) { - this._disposable = Disposable.from( + this._disposable = vscode.Disposable.from( client.onDidBeginInstallTypings(e => this._onBegin(e.eventId)), client.onDidEndInstallTypings(e => this._onEndOrTimeout(e.eventId)), client.onTypesInstallerInitializationFailed(_ => this.onTypesInstallerInitializationFailed())); @@ -83,8 +83,8 @@ export class AtaProgressReporter { }); }); - window.withProgress({ - location: ProgressLocation.Window, + vscode.window.withProgress({ + location: vscode.ProgressLocation.Window, title: localize('installingPackages', "Fetching data for better TypeScript IntelliSense") }, () => promise); } @@ -98,12 +98,12 @@ export class AtaProgressReporter { } private onTypesInstallerInitializationFailed() { - interface MyMessageItem extends MessageItem { + interface MyMessageItem extends vscode.MessageItem { id: number; } - if (workspace.getConfiguration('typescript').get('check.npmIsInstalled', true)) { - window.showWarningMessage( + if (vscode.workspace.getConfiguration('typescript').get('check.npmIsInstalled', true)) { + vscode.window.showWarningMessage( localize( 'typesInstallerInitializationFailed.title', "Could not install typings files for JavaScript language features. Please ensure that NPM is installed or configure 'typescript.npm' in your user settings. Click [here]({0}) to learn more.", @@ -118,7 +118,7 @@ export class AtaProgressReporter { } switch (selected.id) { case 1: - const tsConfig = workspace.getConfiguration('typescript'); + const tsConfig = vscode.workspace.getConfiguration('typescript'); tsConfig.update('check.npmIsInstalled', false, true); break; } diff --git a/extensions/typescript-language-features/src/utils/versionPicker.ts b/extensions/typescript-language-features/src/utils/versionPicker.ts index 0702a60e533..bf81cb5c466 100644 --- a/extensions/typescript-language-features/src/utils/versionPicker.ts +++ b/extensions/typescript-language-features/src/utils/versionPicker.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { commands, Memento, QuickPickItem, Uri, window, workspace } from 'vscode'; +import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import { TypeScriptVersion, TypeScriptVersionProvider } from './versionProvider'; @@ -11,7 +11,7 @@ const localize = nls.loadMessageBundle(); const useWorkspaceTsdkStorageKey = 'typescript.useWorkspaceTsdk'; -interface MyQuickPickItem extends QuickPickItem { +interface MyQuickPickItem extends vscode.QuickPickItem { id: MessageAction; version?: TypeScriptVersion; } @@ -27,7 +27,7 @@ export class TypeScriptVersionPicker { public constructor( private readonly versionProvider: TypeScriptVersionProvider, - private readonly workspaceState: Memento + private readonly workspaceState: vscode.Memento ) { this._currentVersion = this.versionProvider.defaultVersion; @@ -82,7 +82,7 @@ export class TypeScriptVersionPicker { id: MessageAction.learnMore }); - const selected = await window.showQuickPick(pickOptions, { + const selected = await vscode.window.showQuickPick(pickOptions, { placeHolder: localize( 'selectTsVersion', 'Select the TypeScript version used for JavaScript and TypeScript language features'), @@ -97,7 +97,7 @@ export class TypeScriptVersionPicker { case MessageAction.useLocal: await this.workspaceState.update(useWorkspaceTsdkStorageKey, true); if (selected.version) { - const tsConfig = workspace.getConfiguration('typescript'); + const tsConfig = vscode.workspace.getConfiguration('typescript'); await tsConfig.update('tsdk', selected.version.pathLabel, false); const previousVersion = this.currentVersion; @@ -114,7 +114,7 @@ export class TypeScriptVersionPicker { case MessageAction.learnMore: - commands.executeCommand('vscode.open', Uri.parse('https://go.microsoft.com/fwlink/?linkid=839919')); + vscode.commands.executeCommand('vscode.open', vscode.Uri.parse('https://go.microsoft.com/fwlink/?linkid=839919')); return { oldVersion: this.currentVersion }; default: diff --git a/extensions/typescript-language-features/src/utils/versionProvider.ts b/extensions/typescript-language-features/src/utils/versionProvider.ts index 27b2d9fdebe..3061f1576b9 100644 --- a/extensions/typescript-language-features/src/utils/versionProvider.ts +++ b/extensions/typescript-language-features/src/utils/versionProvider.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as fs from 'fs'; import * as path from 'path'; -import { window, workspace } from 'vscode'; +import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import API from './api'; import { TypeScriptServiceConfiguration } from './configuration'; @@ -12,9 +12,6 @@ import { RelativeWorkspacePathResolver } from './relativePathResolver'; const localize = nls.loadMessageBundle(); - - - export class TypeScriptVersion { constructor( public readonly path: string, @@ -40,7 +37,7 @@ export class TypeScriptVersion { } // Allow TS developers to provide custom version - const tsdkVersion = workspace.getConfiguration().get('typescript.tsdk_version', undefined); + const tsdkVersion = vscode.workspace.getConfiguration().get('typescript.tsdk_version', undefined); if (tsdkVersion) { return API.fromVersionString(tsdkVersion); } @@ -152,7 +149,7 @@ export class TypeScriptVersionProvider { } catch (e) { // noop } - window.showErrorMessage(localize( + vscode.window.showErrorMessage(localize( 'noBundledServerFound', 'VS Code\'s tsserver was deleted by another application such as a misbehaving virus detection tool. Please reinstall VS Code.')); throw new Error('Could not find bundled tsserver.js'); @@ -182,14 +179,14 @@ export class TypeScriptVersionProvider { } private loadTypeScriptVersionsFromPath(relativePath: string): TypeScriptVersion[] { - if (!workspace.workspaceFolders) { + if (!vscode.workspace.workspaceFolders) { return []; } const versions: TypeScriptVersion[] = []; - for (const root of workspace.workspaceFolders) { + for (const root of vscode.workspace.workspaceFolders) { let label: string = relativePath; - if (workspace.workspaceFolders && workspace.workspaceFolders.length > 1) { + if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 1) { label = path.join(root.name, relativePath); } diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index 3f1d2b4ed86..b0a99345853 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -1546,9 +1546,9 @@ vinyl@^2.0.1, vinyl@^2.0.2: remove-trailing-separator "^1.0.1" replace-ext "^1.0.0" -vscode-extension-telemetry@0.0.17: - version "0.0.17" - resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.17.tgz#15123e7edb34e7b9724b6056f54a869bbb922cb7" +vscode-extension-telemetry@0.0.18: + version "0.0.18" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.18.tgz#602ba20d8c71453aa34533a291e7638f6e5c0327" dependencies: applicationinsights "1.0.1" diff --git a/extensions/vb/test/colorize-results/test_vb.json b/extensions/vb/test/colorize-results/test_vb.json index b4567db9c74..ee27f71ae6c 100644 --- a/extensions/vb/test/colorize-results/test_vb.json +++ b/extensions/vb/test/colorize-results/test_vb.json @@ -3,9 +3,9 @@ "c": "'", "t": "source.asp.vb.net comment.line.apostrophe.asp punctuation.definition.comment.asp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -14,9 +14,9 @@ "c": " Copyright (c) Microsoft Corporation. All rights reserved.", "t": "source.asp.vb.net comment.line.apostrophe.asp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -630,9 +630,9 @@ "c": "'", "t": "source.asp.vb.net comment.line.apostrophe.asp punctuation.definition.comment.asp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -641,9 +641,9 @@ "c": " The Timer property of the DateAndTime object returns the seconds", "t": "source.asp.vb.net comment.line.apostrophe.asp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -674,9 +674,9 @@ "c": "'", "t": "source.asp.vb.net comment.line.apostrophe.asp punctuation.definition.comment.asp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -685,9 +685,9 @@ "c": " and milliseconds that have passed since midnight.", "t": "source.asp.vb.net comment.line.apostrophe.asp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1114,9 +1114,9 @@ "c": "'", "t": "source.asp.vb.net comment.line.apostrophe.asp punctuation.definition.comment.asp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1125,9 +1125,9 @@ "c": " In a real application, some unit of work would", "t": "source.asp.vb.net comment.line.apostrophe.asp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1180,9 +1180,9 @@ "c": "'", "t": "source.asp.vb.net comment.line.apostrophe.asp punctuation.definition.comment.asp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1191,9 +1191,9 @@ "c": " be done here each time through the loop.", "t": "source.asp.vb.net comment.line.apostrophe.asp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1763,9 +1763,9 @@ "c": "'", "t": "source.asp.vb.net comment.line.apostrophe.asp punctuation.definition.comment.asp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -1774,9 +1774,9 @@ "c": " Check to see if the operation was canceled.", "t": "source.asp.vb.net comment.line.apostrophe.asp", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index a350f5334e2..41305d076b2 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -520,6 +520,16 @@ suite('workspace-namespace', () => { assert.equal(vscode.workspace.asRelativePath(results[0].uri), '10linefile.ts'); }); + test('findTextInFiles, cancellation', async () => { + const results: vscode.TextSearchResult[] = []; + const cancellation = new vscode.CancellationTokenSource(); + cancellation.cancel(); + + await vscode.workspace.findTextInFiles({ pattern: 'foo' }, result => { + results.push(result); + }, cancellation.token); + }); + test('applyEdit', () => { return vscode.workspace.openTextDocument(vscode.Uri.parse('untitled:' + join(vscode.workspace.rootPath || '', './new2.txt'))).then(doc => { diff --git a/extensions/xml/syntaxes/xml.tmLanguage.json b/extensions/xml/syntaxes/xml.tmLanguage.json index 532e3908f25..acebb5275ed 100644 --- a/extensions/xml/syntaxes/xml.tmLanguage.json +++ b/extensions/xml/syntaxes/xml.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-xml/commit/27352842917b911383122bdcf98ed0d69d55c179", + "version": "https://github.com/atom/language-xml/commit/bd810deb404a12bea8ec5799fda2909349ba2654", "name": "XML", "scopeName": "text.xml", "patterns": [ @@ -350,14 +350,38 @@ ] }, "comments": { - "begin": "<[!%]--", - "captures": { - "0": { - "name": "punctuation.definition.comment.xml" + "patterns": [ + { + "begin": "<%--", + "captures": { + "0": { + "name": "punctuation.definition.comment.xml" + }, + "end": "--%>", + "name": "comment.block.xml" + } + }, + { + "begin": "", + "name": "comment.block.xml", + "patterns": [ + { + "begin": "--(?!>)", + "captures": { + "0": { + "name": "invalid.illegal.bad-comments-or-CDATA.xml" + } + } + } + ] } - }, - "end": "--%?>", - "name": "comment.block.xml" + ] } } } \ No newline at end of file diff --git a/extensions/xml/test/colorize-results/test_xml.json b/extensions/xml/test/colorize-results/test_xml.json index 80525b29179..b16beb8b122 100644 --- a/extensions/xml/test/colorize-results/test_xml.json +++ b/extensions/xml/test/colorize-results/test_xml.json @@ -795,9 +795,9 @@ "c": "", "t": "text.xml comment.block.xml punctuation.definition.comment.xml", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/yaml/test/colorize-results/test_yaml.json b/extensions/yaml/test/colorize-results/test_yaml.json index 6c0e70abaaf..6c871b32208 100644 --- a/extensions/yaml/test/colorize-results/test_yaml.json +++ b/extensions/yaml/test/colorize-results/test_yaml.json @@ -3,9 +3,9 @@ "c": "#", "t": "source.yaml comment.line.number-sign.yaml punctuation.definition.comment.yaml", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -14,9 +14,9 @@ "c": " sequencer protocols for Laser eye surgery", "t": "source.yaml comment.line.number-sign.yaml", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -124,9 +124,9 @@ "c": "#", "t": "source.yaml comment.line.number-sign.yaml punctuation.definition.comment.yaml", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -135,9 +135,9 @@ "c": " defines anchor label &id001", "t": "source.yaml comment.line.number-sign.yaml", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -399,9 +399,9 @@ "c": "#", "t": "source.yaml comment.line.number-sign.yaml punctuation.definition.comment.yaml", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } @@ -410,9 +410,9 @@ "c": " refers to the first step (with anchor &id001)", "t": "source.yaml comment.line.number-sign.yaml", "r": { - "dark_plus": "comment: #608B4E", + "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", - "dark_vs": "comment: #608B4E", + "dark_vs": "comment: #6A9955", "light_vs": "comment: #008000", "hc_black": "comment: #7CA668" } diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 6ed3a9d31c4..67d803d4e5e 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,6 +2,6 @@ # yarn lockfile v1 -typescript@3.0.1-insiders.20180713: - version "3.0.1-insiders.20180713" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.0.1-insiders.20180713.tgz#02775b5197ab02b79ed5b84e7483c18ed164e55e" +typescript@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.0.1.tgz#43738f29585d3a87575520a4b93ab6026ef11fdb" diff --git a/gulpfile.js b/gulpfile.js index db6d924ae73..ebdca25bcb4 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -15,12 +15,12 @@ const compilation = require('./build/lib/compilation'); // Fast compile for development time gulp.task('clean-client', util.rimraf('out')); -gulp.task('compile-client', ['clean-client'], compilation.compileTask('out', false)); +gulp.task('compile-client', ['clean-client'], compilation.compileTask('src', 'out', false)); gulp.task('watch-client', ['clean-client'], compilation.watchTask('out', false)); // Full compile, including nls and inline sources in sourcemaps, for build gulp.task('clean-client-build', util.rimraf('out-build')); -gulp.task('compile-client-build', ['clean-client-build'], compilation.compileTask('out-build', true)); +gulp.task('compile-client-build', ['clean-client-build'], compilation.compileTask('src', 'out-build', true)); gulp.task('watch-client-build', ['clean-client-build'], compilation.watchTask('out-build', true)); // Default diff --git a/package.json b/package.json index aba3d5488a6..726eb698918 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", - "version": "1.26.0", - "distro": "1e7c2f0e193ccea1d45e244eef9caef24bb57f1f", + "version": "1.27.0", + "distro": "73cb7173636931afba8d741be343ceafb11a177e", "author": { "name": "Microsoft Corporation" }, @@ -46,11 +46,12 @@ "sudo-prompt": "8.2.0", "v8-inspect-profiler": "^0.0.8", "vscode-chokidar": "^1.6.2", - "vscode-debugprotocol": "1.30.0", + "vscode-debugprotocol": "1.31.0", "vscode-nsfw": "bpasero/nsfw#electron-3.0.x", "vscode-ripgrep": "^1.0.1", "vscode-textmate": "Microsoft/vscode-textmate#electron-3.0.x", - "vscode-xterm": "3.6.0-beta3", + "vscode-xterm": "3.7.0-beta3", + "winreg": "^1.2.4", "yauzl": "^2.9.1" }, "devDependencies": { @@ -60,7 +61,6 @@ "@types/mocha": "2.2.39", "@types/sinon": "1.16.34", "asar": "^0.14.0", - "azure-storage": "^0.3.1", "chromium-pickle-js": "^0.2.0", "clean-css": "3.4.6", "coveralls": "^2.11.11", @@ -102,7 +102,7 @@ "istanbul": "^0.3.17", "jsdom-no-contextify": "^3.1.0", "lazy.js": "^0.4.2", - "mime": "1.2.11", + "mime": "^1.4.1", "minimatch": "^2.0.10", "mkdirp": "^0.5.0", "mocha": "^2.2.5", diff --git a/product.json b/product.json index d16dca66ecd..ed3ced1c1be 100644 --- a/product.json +++ b/product.json @@ -4,7 +4,6 @@ "applicationName": "code-oss", "dataFolderName": ".vscode-oss", "win32MutexName": "vscodeoss", - "enableTelemetry": true, "licenseName": "MIT", "licenseUrl": "https://github.com/Microsoft/vscode/blob/master/LICENSE.txt", "win32DirName": "Microsoft Code OSS", diff --git a/resources/linux/debian/control.template b/resources/linux/debian/control.template index eeaf96e0751..92dbac93f45 100644 --- a/resources/linux/debian/control.template +++ b/resources/linux/debian/control.template @@ -1,7 +1,7 @@ Package: @@NAME@@ Version: @@VERSION@@ Section: devel -Depends: libnotify4, libnss3, gnupg, apt, libxkbfile1, libgconf-2-4, libsecret-1-0, libgtk-3-0 (>= 3.10.0) +Depends: libnotify4, libnss3 (>= 2:3.26), gnupg, apt, libxkbfile1, libgconf-2-4, libsecret-1-0, libgtk-3-0 (>= 3.10.0) Priority: optional Architecture: @@ARCHITECTURE@@ Maintainer: Microsoft Corporation @@ -12,4 +12,3 @@ Conflicts: visual-studio-@@NAME@@ Replaces: visual-studio-@@NAME@@ Description: Code editing. Redefined. Visual Studio Code is a new choice of tool that combines the simplicity of a code editor with what developers need for the core edit-build-debug cycle. See https://code.visualstudio.com/docs/setup/linux for installation instructions and FAQ. - \ No newline at end of file diff --git a/src/bootstrap-amd.js b/src/bootstrap-amd.js index 98bc02c22cd..752d996f6b9 100644 --- a/src/bootstrap-amd.js +++ b/src/bootstrap-amd.js @@ -69,6 +69,11 @@ loader.config({ nodeCachedDataDir: process.env['VSCODE_NODE_CACHED_DATA_DIR_' + process.pid] }); +if (process.env['ELECTRON_RUN_AS_NODE'] || process.versions.electron) { + // running in Electron + loader.define('fs', ['original-fs'], function (originalFS) { return originalFS; }); // replace the patched electron fs with the original node fs for all AMD code +} + if (nlsConfig.pseudo) { loader(['vs/nls'], function (nlsPlugin) { nlsPlugin.setPseudoTranslation(nlsConfig.pseudo); diff --git a/src/typings/vscode-xterm.d.ts b/src/typings/vscode-xterm.d.ts index eb8c87e277a..5f1e27de082 100644 --- a/src/typings/vscode-xterm.d.ts +++ b/src/typings/vscode-xterm.d.ts @@ -680,6 +680,8 @@ declare module 'vscode-xterm' { // Modifications to official .d.ts below declare module 'vscode-xterm' { interface TerminalCore { + debug: boolean; + buffer: { y: number; ybase: number; @@ -690,7 +692,7 @@ declare module 'vscode-xterm' { translateBufferLineToString(lineIndex: number, trimRight: boolean): string; }; - send(text: string): void; + handler(text: string): void; /** * Emit an event on the terminal. diff --git a/src/typings/winreg.d.ts b/src/typings/winreg.d.ts new file mode 100644 index 00000000000..70047d8b50f --- /dev/null +++ b/src/typings/winreg.d.ts @@ -0,0 +1,338 @@ +// Type definitions for Winreg v1.2.0 +// Project: http://fresc81.github.io/node-winreg/ +// Definitions by: RX14 , BobBuehler +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +declare var Winreg: WinregStatic; + +interface WinregStatic { + /** + * Creates a registry object, which provides access to a single registry key. + * Note: This class is returned by a call to ```require('winreg')```. + * + * @public + * @class + * + * @param {@link Options} options - the options + * + * @example + * var Registry = require('winreg') + * , autoStartCurrentUser = new Registry({ + * hive: Registry.HKCU, + * key: '\\Software\\Microsoft\\Windows\\CurrentVersion\\Run' + * }); + */ + new (options: Winreg.Options): Winreg.Registry; + + /** + * Registry hive key HKEY_LOCAL_MACHINE. + * Note: For writing to this hive your program has to run with admin privileges. + */ + HKLM: string; + + /** + * Registry hive key HKEY_CURRENT_USER. + */ + HKCU: string; + + /** + * Registry hive key HKEY_CLASSES_ROOT. + * Note: For writing to this hive your program has to run with admin privileges. + */ + HKCR: string; + + /** + * Registry hive key HKEY_USERS. + * Note: For writing to this hive your program has to run with admin privileges. + */ + HKU: string; + + /** + * Registry hive key HKEY_CURRENT_CONFIG. + * Note: For writing to this hive your program has to run with admin privileges. + */ + HKCC: string; + + /** + * Collection of available registry hive keys. + */ + HIVES: Array; + + /** + * Registry value type STRING. + * + * Values of this type contain a string. + */ + REG_SZ: string; + + /** + * Registry value type MULTILINE_STRING. + * + * Values of this type contain a multiline string. + */ + REG_MULTI_SZ: string; + + /** + * Registry value type EXPANDABLE_STRING. + * + * Values of this type contain an expandable string. + */ + REG_EXPAND_SZ: string; + + /** + * Registry value type DOUBLE_WORD. + * + * Values of this type contain a double word (32 bit integer). + */ + REG_DWORD: string; + + /** + * Registry value type QUAD_WORD. + * + * Values of this type contain a quad word (64 bit integer). + */ + REG_QWORD: string; + + /** + * Registry value type BINARY. + * + * Values of this type contain a binary value. + */ + REG_BINARY: string; + + /** + * Registry value type UNKNOWN. + * + * Values of this type contain a value of an unknown type. + */ + REG_NONE: string; + + /** + * Collection of available registry value types. + */ + REG_TYPES: Array; + + /** + * The name of the default value. May be used instead of the empty string literal for better readability. + */ + DEFAULT_VALUE: string; +} + +declare namespace Winreg { + export interface Options { + /** + * Optional hostname, must start with '\\' sequence. + */ + host?: string; + + /** + * Optional hive ID, default is HKLM. + */ + hive?: string; + + /** + * Optional key, default is the root key. + */ + key?: string; + + /** + * Optional registry hive architecture ('x86' or 'x64'; only valid on Windows 64 Bit Operating Systems). + */ + arch?: string; + } + + /** + * A registry object, which provides access to a single registry key. + */ + export interface Registry { + /** + * The hostname. + * @readonly + */ + host: string; + + /** + * The hive id. + * @readonly + */ + hive: string; + + /** + * The registry key name. + * @readonly + */ + key: string; + + /** + * The full path to the registry key. + * @readonly + */ + path: string; + + /** + * The registry hive architecture ('x86' or 'x64'). + * @readonly + */ + arch: string; + + /** + * Creates a new {@link Registry} instance that points to the parent registry key. + * @readonly + */ + parent: Registry; + + /** + * Retrieve all values from this registry key. + * @param {valuesCallback} cb - callback function + * @param {error=} cb.err - error object or null if successful + * @param {array=} cb.items - an array of {@link RegistryItem} objects + * @returns {Registry} this registry key object + */ + values(cb: (err: Error, result: Array) => void): Registry; + + /** + * Retrieve all subkeys from this registry key. + * @param {function (err, items)} cb - callback function + * @param {error=} cb.err - error object or null if successful + * @param {array=} cb.items - an array of {@link Registry} objects + * @returns {Registry} this registry key object + */ + keys(cb: (err: Error, result: Array) => void): Registry; + + /** + * Gets a named value from this registry key. + * @param {string} name - the value name, use {@link Registry.DEFAULT_VALUE} or an empty string for the default value + * @param {function (err, item)} cb - callback function + * @param {error=} cb.err - error object or null if successful + * @param {RegistryItem=} cb.item - the retrieved registry item + * @returns {Registry} this registry key object + */ + get(name: string, cb: (err: Error, result: Winreg.RegistryItem) => void): Registry; + + /** + * Sets a named value in this registry key, overwriting an already existing value. + * @param {string} name - the value name, use {@link Registry.DEFAULT_VALUE} or an empty string for the default value + * @param {string} type - the value type + * @param {string} value - the value + * @param {function (err)} cb - callback function + * @param {error=} cb.err - error object or null if successful + * @returns {Registry} this registry key object + */ + set(name: string, type: string, value: string, cb: (err: Error) => void): Registry; + + /** + * Remove a named value from this registry key. If name is empty, sets the default value of this key. + * Note: This key must be already existing. + * @param {string} name - the value name, use {@link Registry.DEFAULT_VALUE} or an empty string for the default value + * @param {function (err)} cb - callback function + * @param {error=} cb.err - error object or null if successful + * @returns {Registry} this registry key object + */ + remove(name: string, cb: (err: Error) => void): Registry; + + /** + * Remove all subkeys and values (including the default value) from this registry key. + * @param {function (err)} cb - callback function + * @param {error=} cb.err - error object or null if successful + * @returns {Registry} this registry key object + */ + clear(cb: (err: Error) => void): Registry; + + /** + * Alias for the clear method to keep it backward compatible. + * @method + * @deprecated Use {@link Registry#clear} or {@link Registry#destroy} in favour of this method. + * @param {function (err)} cb - callback function + * @param {error=} cb.err - error object or null if successful + * @returns {Registry} this registry key object + */ + erase(cb: (err: Error) => void): Registry; + + /** + * Delete this key and all subkeys from the registry. + * @param {function (err)} cb - callback function + * @param {error=} cb.err - error object or null if successful + * @returns {Registry} this registry key object + */ + destroy(cb: (err: Error) => void): Registry; + + /** + * Create this registry key. Note that this is a no-op if the key already exists. + * @param {function (err)} cb - callback function + * @param {error=} cb.err - error object or null if successful + * @returns {Registry} this registry key object + */ + create(cb: (err: Error) => void): Registry; + + /** + * Checks if this key already exists. + * @param {function (err, exists)} cb - callback function + * @param {error=} cb.err - error object or null if successful + * @param {boolean=} cb.exists - true if a registry key with this name already exists + * @returns {Registry} this registry key object + */ + keyExists(cb: (err: Error, exists: boolean) => void): Registry; + + /** + * Checks if a value with the given name already exists within this key. + * @param {string} name - the value name, use {@link Registry.DEFAULT_VALUE} or an empty string for the default value + * @param {function (err, exists)} cb - callback function + * @param {error=} cb.err - error object or null if successful + * @param {boolean=} cb.exists - true if a value with the given name was found in this key + * @returns {Registry} this registry key object + */ + valueExists(name: string, cb: (err: Error, exists: boolean) => void): Registry; + } + + /** + * A single registry value record. + * Objects of this type are created internally and returned by methods of {@link Registry} objects. + */ + export interface RegistryItem { + /** + * The hostname. + * @readonly + */ + host: string; + + /** + * The hive id. + * @readonly + */ + hive: string; + + /** + * The registry key. + * @readonly + */ + key: string; + + /** + * The value name. + * @readonly + */ + name: string; + + /** + * The value type. + * @readonly + */ + type: string; + + /** + * The value. + * @readonly + */ + value: string; + + /** + * The hive architecture. + * @readonly + */ + arch: string; + } +} + +declare module "winreg" { + export = Winreg; +} \ No newline at end of file diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index 7d350ed2262..dd85d1c0645 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -496,6 +496,39 @@ export class ActionBar implements IActionRunner { this.actionsList.setAttribute('aria-label', this.options.ariaLabel); } + if (this.options.isMenu) { + this.domNode.tabIndex = 0; + + $(this.domNode).on(DOM.EventType.MOUSE_OUT, (e) => { + let relatedTarget = (e as MouseEvent).relatedTarget as HTMLElement; + if (!DOM.isAncestor(relatedTarget, this.domNode)) { + this.focusedItem = undefined; + this.updateFocus(); + e.stopPropagation(); + } + }); + + $(this.actionsList).on(DOM.EventType.MOUSE_OVER, (e) => { + let target = e.target as HTMLElement; + if (!target || !DOM.isAncestor(target, this.actionsList) || target === this.actionsList) { + return; + } + + while (target.parentElement !== this.actionsList) { + target = target.parentElement; + } + + if (DOM.hasClass(target, 'action-item')) { + const lastFocusedItem = this.focusedItem; + this.setFocusedItem(target); + + if (lastFocusedItem !== this.focusedItem) { + this.updateFocus(); + } + } + }); + } + this.domNode.appendChild(this.actionsList); container.appendChild(this.domNode); @@ -525,6 +558,16 @@ export class ActionBar implements IActionRunner { } } + private setFocusedItem(element: HTMLElement): void { + for (let i = 0; i < this.actionsList.children.length; i++) { + let elem = this.actionsList.children[i]; + if (element === elem) { + this.focusedItem = i; + break; + } + } + } + private updateFocusedItem(): void { for (let i = 0; i < this.actionsList.children.length; i++) { let elem = this.actionsList.children[i]; @@ -696,7 +739,6 @@ export class ActionBar implements IActionRunner { private updateFocus(fromRight?: boolean): void { if (typeof this.focusedItem === 'undefined') { this.domNode.focus(); - return; } for (let i = 0; i < this.items.length; i++) { @@ -705,8 +747,12 @@ export class ActionBar implements IActionRunner { let actionItem = item; if (i === this.focusedItem) { - if (types.isFunction(actionItem.focus)) { - actionItem.focus(fromRight); + if (types.isFunction(actionItem.isEnabled)) { + if (actionItem.isEnabled() && types.isFunction(actionItem.focus)) { + actionItem.focus(fromRight); + } else { + this.domNode.focus(); + } } } else { if (types.isFunction(actionItem.blur)) { diff --git a/src/vs/base/browser/ui/aria/aria.ts b/src/vs/base/browser/ui/aria/aria.ts index 9d59cecfdce..61c7dfd348d 100644 --- a/src/vs/base/browser/ui/aria/aria.ts +++ b/src/vs/base/browser/ui/aria/aria.ts @@ -50,13 +50,27 @@ export function status(msg: string): void { } } +let repeatedTimes = 0; +let prevText: string | undefined = undefined; function insertMessage(target: HTMLElement, msg: string): void { if (!ariaContainer) { // console.warn('ARIA support needs a container. Call setARIAContainer() first.'); return; } - if (target.textContent === msg) { - msg = nls.localize('repeated', "{0} (occurred again)", msg); + + if (prevText === msg) { + repeatedTimes++; + } + else { + prevText = msg; + repeatedTimes = 0; + } + + + switch (repeatedTimes) { + case 0: break; + case 1: msg = nls.localize('repeated', "{0} (occurred again)", msg); break; + default: msg = nls.localize('repeatedNtimes', "{0} (occurred {1} times)", msg, repeatedTimes); break; } dom.clearNode(target); diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css index d268c209a15..da510856593 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.css @@ -20,6 +20,7 @@ cursor: pointer; align-self: center; height: 100%; + outline: none; } .monaco-breadcrumbs .monaco-breadcrumb-item:not(:first-child)::before { diff --git a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts index 222325edf6f..faa5908492e 100644 --- a/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts +++ b/src/vs/base/browser/ui/breadcrumbs/breadcrumbsWidget.ts @@ -13,7 +13,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { Event, Emitter } from 'vs/base/common/event'; import { Color } from 'vs/base/common/color'; -import { commonPrefixLength, tail } from 'vs/base/common/arrays'; +import { commonPrefixLength } from 'vs/base/common/arrays'; export abstract class BreadcrumbsItem { dispose(): void { } @@ -86,7 +86,8 @@ export class BreadcrumbsWidget { ) { this._domNode = document.createElement('div'); this._domNode.className = 'monaco-breadcrumbs'; - this._domNode.tabIndex = -1; + this._domNode.tabIndex = 0; + this._domNode.setAttribute('role', 'list'); this._scrollable = new DomScrollableElement(this._domNode, { vertical: ScrollbarVisibility.Hidden, horizontal: ScrollbarVisibility.Auto, @@ -114,13 +115,13 @@ export class BreadcrumbsWidget { } layout(dim: dom.Dimension): void { - if (!dim) { - this._scrollable.scanDomNode(); - } else { + if (dim) { this._domNode.style.width = `${dim.width}px`; this._domNode.style.height = `${dim.height}px`; - this._scrollable.scanDomNode(); } + this._scrollable.setRevealOnScroll(false); + this._scrollable.scanDomNode(); + this._scrollable.setRevealOnScroll(true); } style(style: IBreadcrumbsWidgetStyles): void { @@ -152,13 +153,23 @@ export class BreadcrumbsWidget { } domFocus(): void { - const focused = this.getFocused() || tail(this._items); - this.setFocused(focused); - this._domNode.focus(); + let idx = this._focusedItemIdx >= 0 ? this._focusedItemIdx : this._items.length - 1; + if (idx >= 0 && idx < this._items.length) { + this._focus(idx, undefined); + } else { + this._domNode.focus(); + } } isDOMFocused(): boolean { - return this._domNode === document.activeElement; + let candidate = document.activeElement; + while (candidate) { + if (this._domNode === candidate) { + return true; + } + candidate = candidate.parentElement; + } + return false; } getFocused(): BreadcrumbsItem { @@ -190,6 +201,7 @@ export class BreadcrumbsWidget { } else { this._focusedItemIdx = i; dom.addClass(node, 'focused'); + node.focus(); } } this._reveal(this._focusedItemIdx); @@ -206,7 +218,9 @@ export class BreadcrumbsWidget { private _reveal(nth: number): void { const node = this._nodes[nth]; if (node) { + this._scrollable.setRevealOnScroll(false); this._scrollable.setScrollPosition({ scrollLeft: node.offsetLeft }); + this._scrollable.setRevealOnScroll(true); } } @@ -232,6 +246,10 @@ export class BreadcrumbsWidget { this._onDidSelectItem.fire({ type: 'select', item: this._items[this._selectedItemIdx], node: this._nodes[this._selectedItemIdx], payload }); } + getItems(): ReadonlyArray { + return this._items; + } + setItems(items: BreadcrumbsItem[]): void { let prefix = commonPrefixLength(this._items, items, (a, b) => a.equals(b)); let removed = this._items.splice(prefix, this._items.length - prefix, ...items.slice(prefix)); @@ -268,7 +286,8 @@ export class BreadcrumbsWidget { dom.clearNode(container); container.className = ''; item.render(container); - dom.append(container); + container.tabIndex = -1; + container.setAttribute('role', 'listitem'); dom.addClass(container, 'monaco-breadcrumb-item'); } diff --git a/src/vs/base/browser/ui/contextview/contextview.ts b/src/vs/base/browser/ui/contextview/contextview.ts index b5b1c296425..8da408322a4 100644 --- a/src/vs/base/browser/ui/contextview/contextview.ts +++ b/src/vs/base/browser/ui/contextview/contextview.ts @@ -8,7 +8,7 @@ import 'vs/css!./contextview'; import { Builder, $ } from 'vs/base/browser/builder'; import * as DOM from 'vs/base/browser/dom'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; export interface IAnchor { x: number; @@ -54,51 +54,46 @@ export interface ISize { export interface IView extends IPosition, ISize { } -function layout(view: ISize, around: IView, viewport: IView, anchorPosition: AnchorPosition, anchorAlignment: AnchorAlignment): IPosition { +export enum LayoutAnchorPosition { + Before, + After +} - let chooseBiased = (a: number, aIsGood: boolean, b: number, bIsGood: boolean) => { - if (aIsGood) { - return a; +export interface ILayoutAnchor { + offset: number; + size: number; + position: LayoutAnchorPosition; +} + +/** + * Lays out a one dimensional view next to an anchor in a viewport. + * + * @returns The view offset within the viewport. + */ +export function layout(viewportSize: number, viewSize: number, anchor: ILayoutAnchor): number { + const anchorEnd = anchor.offset + anchor.size; + + if (anchor.position === LayoutAnchorPosition.Before) { + if (viewSize <= viewportSize - anchorEnd) { + return anchorEnd; // happy case, lay it out after the anchor } - if (bIsGood) { - return b; + + if (viewSize <= anchor.offset) { + return anchor.offset - viewSize; // ok case, lay it out before the anchor } - return a; - }; - let chooseOne = (a: number, aIsGood: boolean, b: number, bIsGood: boolean, aIsPreferred: boolean) => { - if (aIsPreferred) { - return chooseBiased(a, aIsGood, b, bIsGood); - } else { - return chooseBiased(b, bIsGood, a, aIsGood); + return Math.max(viewportSize - viewSize, 0); // sad case, lay it over the anchor + } else { + if (viewSize <= anchor.offset) { + return anchor.offset - viewSize; // happy case, lay it out before the anchor } - }; - let top = (() => { - // Compute both options (putting the segment above and below) - let posAbove = around.top - view.height; - let posBelow = around.top + around.height; + if (viewSize <= viewportSize - anchorEnd) { + return anchorEnd; // ok case, lay it out after the anchor + } - // Check for both options if they are good - let aboveIsGood = (posAbove >= viewport.top && posAbove + view.height <= viewport.top + viewport.height); - let belowIsGood = (posBelow >= viewport.top && posBelow + view.height <= viewport.top + viewport.height); - - return chooseOne(posAbove, aboveIsGood, posBelow, belowIsGood, anchorPosition === AnchorPosition.ABOVE); - })(); - - let left = (() => { - // Compute both options (aligning left and right) - let posLeft = around.left; - let posRight = around.left + around.width - view.width; - - // Check for both options if they are good - let leftIsGood = (posLeft >= viewport.left && posLeft + view.width <= viewport.left + viewport.width); - let rightIsGood = (posRight >= viewport.left && posRight + view.width <= viewport.left + viewport.width); - - return chooseOne(posLeft, leftIsGood, posRight, rightIsGood, anchorAlignment === AnchorAlignment.LEFT); - })(); - - return { top: top, left: left }; + return 0; // sad case, lay it over the anchor + } } export class ContextView { @@ -116,11 +111,9 @@ export class ContextView { this.$view = $('.context-view').hide(); this.setContainer(container); - this.toDispose = [{ - dispose: () => { - this.setContainer(null); - } - }]; + this.toDispose = [toDisposable(() => { + this.setContainer(null); + })]; this.toDisposeOnClean = null; } @@ -207,30 +200,28 @@ export class ContextView { }; } - let viewport = { - top: DOM.StandardWindow.scrollY, - left: DOM.StandardWindow.scrollX, - height: window.innerHeight, - width: window.innerWidth - }; + const viewSize = this.$view.getTotalSize(); + const anchorPosition = this.delegate.anchorPosition || AnchorPosition.BELOW; + const anchorAlignment = this.delegate.anchorAlignment || AnchorAlignment.LEFT; - // Get the view's size - let viewSize = this.$view.getTotalSize(); - let view = { width: viewSize.width, height: viewSize.height }; + const verticalAnchor: ILayoutAnchor = { offset: around.top, size: around.height, position: anchorPosition === AnchorPosition.BELOW ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After }; - let anchorPosition = this.delegate.anchorPosition || AnchorPosition.BELOW; - let anchorAlignment = this.delegate.anchorAlignment || AnchorAlignment.LEFT; + let horizontalAnchor: ILayoutAnchor; - let result = layout(view, around, viewport, anchorPosition, anchorAlignment); + if (anchorAlignment === AnchorAlignment.LEFT) { + horizontalAnchor = { offset: around.left, size: 0, position: LayoutAnchorPosition.Before }; + } else { + horizontalAnchor = { offset: around.left + around.width, size: 0, position: LayoutAnchorPosition.After }; + } - let containerPosition = DOM.getDomNodePagePosition(this.$container.getHTMLElement()); - result.top -= containerPosition.top; - result.left -= containerPosition.left; + const containerPosition = DOM.getDomNodePagePosition(this.$container.getHTMLElement()); + const top = layout(window.innerHeight, viewSize.height, verticalAnchor) - containerPosition.top; + const left = layout(window.innerWidth, viewSize.width, horizontalAnchor) - containerPosition.left; this.$view.removeClass('top', 'bottom', 'left', 'right'); this.$view.addClass(anchorPosition === AnchorPosition.BELOW ? 'bottom' : 'top'); this.$view.addClass(anchorAlignment === AnchorAlignment.LEFT ? 'left' : 'right'); - this.$view.style({ top: result.top + 'px', left: result.left + 'px', width: 'initial' }); + this.$view.style({ top: `${top}px`, left: `${left}px`, width: 'initial' }); } public hide(data?: any): void { diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index 8d62877e57d..74be56db003 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -9,9 +9,6 @@ import 'vs/css!./iconlabel'; import * as dom from 'vs/base/browser/dom'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { IMatch } from 'vs/base/common/filters'; -import uri from 'vs/base/common/uri'; -import * as paths from 'vs/base/common/paths'; -import { IWorkspaceFolderProvider, getPathLabel, IUserHomeProvider, getBaseLabel } from 'vs/base/common/labels'; import { IDisposable, combinedDisposable, Disposable } from 'vs/base/common/lifecycle'; export interface IIconLabelCreationOptions { @@ -22,6 +19,7 @@ export interface IIconLabelCreationOptions { export interface IIconLabelValueOptions { title?: string; descriptionTitle?: string; + hideIcon?: boolean; extraClasses?: string[]; italic?: boolean; matches?: IMatch[]; @@ -166,17 +164,3 @@ export class IconLabel extends Disposable { } } -export class FileLabel extends IconLabel { - - constructor(container: HTMLElement, file: uri, provider: IWorkspaceFolderProvider, userHome?: IUserHomeProvider) { - super(container); - - this.setFile(file, provider, userHome); - } - - setFile(file: uri, provider: IWorkspaceFolderProvider, userHome: IUserHomeProvider): void { - const parent = paths.dirname(file.fsPath); - - this.setValue(getBaseLabel(file), parent && parent !== '.' ? getPathLabel(parent, userHome, provider) : '', { title: file.fsPath }); - } -} diff --git a/src/vs/base/browser/ui/iconLabel/iconlabel.css b/src/vs/base/browser/ui/iconLabel/iconlabel.css index 66125a6d41a..3516227e43d 100644 --- a/src/vs/base/browser/ui/iconLabel/iconlabel.css +++ b/src/vs/base/browser/ui/iconLabel/iconlabel.css @@ -40,7 +40,6 @@ } .monaco-icon-label > .monaco-icon-label-description-container > .label-description { - opacity: 0.7; margin-left: 0.5em; font-size: 0.9em; white-space: pre; /* enable to show labels that include multiple whitespaces */ diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index 17b36ec3dea..1691821da48 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -535,6 +535,7 @@ export class HistoryInputBox extends InputBox implements IHistoryNavigationWidge if (next) { this.value = next; + aria.status(this.value); } } @@ -550,6 +551,7 @@ export class HistoryInputBox extends InputBox implements IHistoryNavigationWidge if (previous) { this.value = previous; + aria.status(this.value); } } diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index f8ffd1a0feb..efaf1d52986 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./list'; +import { localize } from 'vs/nls'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { isNumber } from 'vs/base/common/types'; import { range, firstIndex } from 'vs/base/common/arrays'; @@ -355,6 +356,11 @@ class DOMFocusController implements IDisposable { return; } + const style = window.getComputedStyle(tabIndexElement); + if (style.visibility === 'hidden' || style.display === 'none') { + return; + } + e.preventDefault(); e.stopPropagation(); tabIndexElement.focus(); @@ -934,7 +940,7 @@ export class List implements ISpliceable, IDisposable { this.onSelectionChange(this._onSelectionChange, this, this.disposables); if (options.ariaLabel) { - this.view.domNode.setAttribute('aria-label', options.ariaLabel); + this.view.domNode.setAttribute('aria-label', localize('aria list', "{0}. Use the navigation keys to navigate.", options.ariaLabel)); } this.style(options); diff --git a/src/vs/base/browser/ui/menu/menu.css b/src/vs/base/browser/ui/menu/menu.css index ef4b8cedfe4..221640196a1 100644 --- a/src/vs/base/browser/ui/menu/menu.css +++ b/src/vs/base/browser/ui/menu/menu.css @@ -35,24 +35,23 @@ background-color: #E4E4E4; } -.monaco-menu .monaco-action-bar.vertical .action-item:hover:not(.disabled) { - background-color: #EEE; -} - .monaco-menu .monaco-action-bar.vertical .action-menu-item { -ms-flex: 1 1 auto; flex: 1 1 auto; display: -ms-flexbox; display: flex; + height: 2.6em; + align-items: center; } .monaco-menu .monaco-action-bar.vertical .action-label { -ms-flex: 1 1 auto; flex: 1 1 auto; text-decoration: none; - padding: 0.8em 1em; - line-height: 1.1em; + padding: 0 1em; background: none; + font-size: inherit; + line-height: 1; } .monaco-menu .monaco-action-bar.vertical .keybinding, @@ -60,15 +59,12 @@ display: inline-block; -ms-flex: 2 1 auto; flex: 2 1 auto; - padding: 0.8em 1em; - line-height: 1.1em; - font-size: 12px; + padding: 0 1em; text-align: right; + font-size: inherit; + line-height: 1; } -.monaco-menu .monaco-action-bar.vertical .submenu-indicator { - padding: 0.8em .5em; -} .monaco-menu .monaco-action-bar.vertical .action-item.disabled .keybinding, .monaco-menu .monaco-action-bar.vertical .action-item.disabled .submenu-indicator { @@ -121,19 +117,21 @@ animation: fadeIn 0.083s linear; } -.context-view.monaco-menu-container :focus { +.context-view.monaco-menu-container :focus, +.context-view.monaco-menu-container .monaco-action-bar.vertical:focus, +.context-view.monaco-menu-container .monaco-action-bar.vertical :focus { outline: 0; } +.monaco-menu .monaco-action-bar.vertical .action-item { + border: 1px solid transparent; /* prevents jumping behaviour on hover or focus */ +} + /* Dark theme */ .vs-dark .monaco-menu .monaco-action-bar.vertical .action-item.focused { background-color: #4B4C4D; } -.vs-dark .monaco-menu .monaco-action-bar.vertical .action-item:hover:not(.disabled) { - background-color: #3A3A3A; -} - .vs-dark .context-view.monaco-menu-container { box-shadow: 0 2px 8px #000; color: #BBB; @@ -148,16 +146,7 @@ box-shadow: none; } -.hc-black .monaco-menu .monaco-action-bar.vertical .action-item { - border: 1px solid transparent; /* prevents jumpig behaviour on hover or focus */ -} - .hc-black .monaco-menu .monaco-action-bar.vertical .action-item.focused { background: none; border: 1px dotted #f38518; -} - -.hc-black .monaco-menu .monaco-action-bar.vertical .action-item:hover:not(.disabled) { - background: none; - border: 1px dashed #f38518; } \ No newline at end of file diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index 3851600c6df..3a79019d4f1 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -12,7 +12,7 @@ import { IActionRunner, IAction, Action } from 'vs/base/common/actions'; import { ActionBar, IActionItemProvider, ActionsOrientation, Separator, ActionItem, IActionItemOptions, BaseActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes'; import { Event } from 'vs/base/common/event'; -import { addClass, EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus } from 'vs/base/browser/dom'; +import { addClass, EventType, EventHelper, EventLike, removeTabIndexAndUpdateFocus, isAncestor } from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { $, Builder } from 'vs/base/browser/builder'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -93,9 +93,9 @@ export class Menu { return this.actionBar.onDidBlur; } - public focus() { + public focus(selectFirst = true) { if (this.actionBar) { - this.actionBar.focus(true); + this.actionBar.focus(selectFirst); } } @@ -256,13 +256,13 @@ class SubmenuActionItem extends MenuActionItem { this.showScheduler = new RunOnceScheduler(() => { if (this.mouseOver) { this.cleanupExistingSubmenu(false); - this.createSubmenu(); + this.createSubmenu(false); } }, 250); this.hideScheduler = new RunOnceScheduler(() => { - if (!this.mouseOver && this.parentData.submenu === this.mysubmenu) { - this.parentData.parent.focus(); + if ((!isAncestor(document.activeElement, this.builder.getHTMLElement()) && this.parentData.submenu === this.mysubmenu)) { + this.parentData.parent.focus(false); this.cleanupExistingSubmenu(true); } }, 750); @@ -280,7 +280,7 @@ class SubmenuActionItem extends MenuActionItem { if (event.equals(KeyCode.RightArrow)) { EventHelper.stop(e, true); - this.createSubmenu(); + this.createSubmenu(true); } }); @@ -301,8 +301,12 @@ class SubmenuActionItem extends MenuActionItem { $(this.builder).on(EventType.MOUSE_LEAVE, (e) => { this.mouseOver = false; + }); - this.hideScheduler.schedule(); + $(this.builder).on(EventType.FOCUS_OUT, (e) => { + if (!isAncestor(document.activeElement, this.builder.getHTMLElement())) { + this.hideScheduler.schedule(); + } }); } @@ -310,7 +314,7 @@ class SubmenuActionItem extends MenuActionItem { // stop clicking from trying to run an action EventHelper.stop(e, true); - this.createSubmenu(); + this.createSubmenu(false); } private cleanupExistingSubmenu(force: boolean) { @@ -325,7 +329,7 @@ class SubmenuActionItem extends MenuActionItem { } } - private createSubmenu() { + private createSubmenu(selectFirstItem = true) { if (!this.parentData.submenu) { this.submenuContainer = $(this.builder).div({ class: 'monaco-submenu menubar-menu-items-holder context-view' }); @@ -356,9 +360,11 @@ class SubmenuActionItem extends MenuActionItem { this.parentData.submenu = new Menu(this.submenuContainer.getHTMLElement(), this.submenuActions, this.submenuOptions); - this.parentData.submenu.focus(); + this.parentData.submenu.focus(selectFirstItem); this.mysubmenu = this.parentData.submenu; + } else { + this.parentData.submenu.focus(false); } } @@ -366,7 +372,6 @@ class SubmenuActionItem extends MenuActionItem { super.dispose(); this.hideScheduler.dispose(); - this.showScheduler.dispose(); if (this.mysubmenu) { this.mysubmenu.dispose(); diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index 5aafe0bd8a0..3d2f82a5cc7 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -163,6 +163,8 @@ export abstract class AbstractScrollableElement extends Widget { private readonly _hideTimeout: TimeoutTimer; private _shouldRender: boolean; + private _revealOnScroll: boolean; + private readonly _onScroll = this._register(new Emitter()); public readonly onScroll: Event = this._onScroll.event; @@ -221,6 +223,8 @@ export abstract class AbstractScrollableElement extends Widget { this._mouseIsOver = false; this._shouldRender = true; + + this._revealOnScroll = true; } public dispose(): void { @@ -286,6 +290,10 @@ export abstract class AbstractScrollableElement extends Widget { } } + public setRevealOnScroll(value: boolean) { + this._revealOnScroll = value; + } + // -------------------- mouse wheel scrolling -------------------- private _setListeningToMouseWheel(shouldListen: boolean): void { @@ -382,7 +390,9 @@ export abstract class AbstractScrollableElement extends Widget { this._shouldRender = true; } - this._reveal(); + if (this._revealOnScroll) { + this._reveal(); + } if (!this._options.lazyRender) { this._render(); diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 2efed5c5dcf..d78f3f89e15 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -58,6 +58,9 @@ class SelectListRenderer implements IRendererdata.root), 'option-disabled'); diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 50e06012bba..02fcb82fa11 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -171,7 +171,7 @@ class ToggleMenuAction extends Action { private toggleDropdownMenu: () => void; constructor(toggleDropdownMenu: () => void) { - super(ToggleMenuAction.ID, nls.localize('more', "More"), null, true); + super(ToggleMenuAction.ID, nls.localize('moreActions', "More Actions..."), null, true); this.toggleDropdownMenu = toggleDropdownMenu; } diff --git a/src/vs/base/common/amd.ts b/src/vs/base/common/amd.ts new file mode 100644 index 00000000000..45e45a7bbe2 --- /dev/null +++ b/src/vs/base/common/amd.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. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import URI from 'vs/base/common/uri'; + +export function getPathFromAmdModule(requirefn: typeof require, relativePath: string): string { + return URI.parse(requirefn.toUrl(relativePath)).fsPath; +} diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 7b2d7e3b563..4719c7c834d 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -12,7 +12,7 @@ import { ISplice } from 'vs/base/common/sequence'; * @param array The array. * @param n Which element from the end (default is zero). */ -export function tail(array: T[], n: number = 0): T { +export function tail(array: ArrayLike, n: number = 0): T { return array[array.length - (1 + n)]; } diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 4c06af4e2f5..9b4a65a1bcb 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -6,7 +6,7 @@ 'use strict'; import * as errors from 'vs/base/common/errors'; -import { TPromise, ValueCallback, ErrorCallback, ProgressCallback } from 'vs/base/common/winjs.base'; +import { TPromise, ValueCallback, ErrorCallback } from 'vs/base/common/winjs.base'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; @@ -68,7 +68,7 @@ export function createCancelablePromise(callback: (token: CancellationToken) export function asWinJsPromise(callback: (token: CancellationToken) => T | TPromise | Thenable): TPromise { let source = new CancellationTokenSource(); - return new TPromise((resolve, reject, progress) => { + return new TPromise((resolve, reject) => { let item = callback(source.token); if (item instanceof TPromise) { item.then(result => { @@ -77,7 +77,7 @@ export function asWinJsPromise(callback: (token: CancellationToken) => T | TP }, err => { source.dispose(); reject(err); - }, progress); + }); } else if (isThenable(item)) { item.then(result => { source.dispose(); @@ -194,15 +194,15 @@ export class Throttler { return result; }; - this.queuedPromise = new TPromise((c, e, p) => { - this.activePromise.then(onComplete, onComplete, p).done(c); + this.queuedPromise = new TPromise(c => { + this.activePromise.then(onComplete, onComplete).done(c); }, () => { this.activePromise.cancel(); }); } - return new TPromise((c, e, p) => { - this.queuedPromise.then(c, e, p); + return new TPromise((c, e) => { + this.queuedPromise.then(c, e); }, () => { // no-op }); @@ -210,14 +210,14 @@ export class Throttler { this.activePromise = promiseFactory(); - return new TPromise((c, e, p) => { + return new TPromise((c, e) => { this.activePromise.done((result: any) => { this.activePromise = null; c(result); }, (err: any) => { this.activePromise = null; e(err); - }, p); + }); }, () => { this.activePromise.cancel(); }); @@ -378,20 +378,18 @@ export class ShallowCancelThenPromise extends TPromise { constructor(outer: TPromise) { let completeCallback: ValueCallback, - errorCallback: ErrorCallback, - progressCallback: ProgressCallback; + errorCallback: ErrorCallback; - super((c, e, p) => { + super((c, e) => { completeCallback = c; errorCallback = e; - progressCallback = p; }, () => { // cancel this promise but not the // outer promise errorCallback(errors.canceled()); }); - outer.then(completeCallback, errorCallback, progressCallback); + outer.then(completeCallback, errorCallback); } } @@ -425,7 +423,7 @@ export function always(thenable: TPromise, f: Function): TPromise; export function always(promise: Thenable, f: Function): Thenable; export function always(winjsPromiseOrThenable: Thenable | TPromise, f: Function): TPromise | Thenable { if (isWinJSPromise(winjsPromiseOrThenable)) { - return new TPromise((c, e, p) => { + return new TPromise((c, e) => { winjsPromiseOrThenable.done((result) => { try { f(result); @@ -440,8 +438,6 @@ export function always(winjsPromiseOrThenable: Thenable | TPromise, f: errors.onUnexpectedError(e1); } e(err); - }, (progress) => { - p(progress); }); }, () => { winjsPromiseOrThenable.cancel(); @@ -534,7 +530,6 @@ interface ILimitedTaskFactory { factory: ITask; c: ValueCallback; e: ErrorCallback; - p: ProgressCallback; } /** @@ -563,14 +558,9 @@ export class Limiter { } queue(promiseFactory: ITask): TPromise; - queue(promiseFactory: ITask>): TPromise { - return new TPromise((c, e, p) => { - this.outstandingPromises.push({ - factory: promiseFactory, - c: c, - e: e, - p: p - }); + queue(factory: ITask>): TPromise { + return new TPromise((c, e) => { + this.outstandingPromises.push({ factory, c, e }); this.consume(); }); @@ -582,7 +572,7 @@ export class Limiter { this.runningPromises++; const promise = iLimitedTask.factory(); - promise.done(iLimitedTask.c, iLimitedTask.e, iLimitedTask.p); + promise.done(iLimitedTask.c, iLimitedTask.e); promise.done(() => this.consumed(), () => this.consumed()); } } diff --git a/src/vs/base/common/diagnostics.ts b/src/vs/base/common/diagnostics.ts deleted file mode 100644 index 7ad7daa18d4..00000000000 --- a/src/vs/base/common/diagnostics.ts +++ /dev/null @@ -1,88 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import * as Platform from 'vs/base/common/platform'; - -/** - * To enable diagnostics, open a browser console and type: window.Monaco.Diagnostics. = true. - * Then trigger an action that will write to diagnostics to see all cached output from the past. - */ - -const globals = Platform.globals; -if (!globals.Monaco) { - globals.Monaco = {}; -} -globals.Monaco.Diagnostics = {}; - -const switches = globals.Monaco.Diagnostics; -const map = new Map(); -const data: any[] = []; - -function fifo(array: any[], size: number) { - while (array.length > size) { - array.shift(); - } -} - -export function register(what: string, fn: Function): (...args: any[]) => void { - - let disable = true; // Otherwise we have unreachable code. - if (disable) { - return () => { - // Intentional empty, disable for now because it is leaking memory - }; - } - - // register switch - const flag = switches[what] || false; - switches[what] = flag; - - // register function - const tracers = map.get(what) || []; - tracers.push(fn); - map.set(what, tracers); - - const result = function (...args: any[]) { - - let idx: number; - - if (switches[what] === true) { - // replay back-in-time functions - const allArgs = [arguments]; - idx = data.indexOf(fn); - if (idx !== -1) { - allArgs.unshift.apply(allArgs, data[idx + 1] || []); - data[idx + 1] = []; - } - - const doIt: () => void = function () { - const thisArguments = allArgs.shift(); - fn.apply(fn, thisArguments); - if (allArgs.length > 0) { - setTimeout(doIt, 500); - } - }; - doIt(); - - } else { - // know where to store - idx = data.indexOf(fn); - idx = idx !== -1 ? idx : data.length; - const dataIdx = idx + 1; - - // store arguments - const allargs = data[dataIdx] || []; - allargs.push(arguments); - fifo(allargs, 50); - - // store data - data[idx] = fn; - data[dataIdx] = allargs; - } - }; - - return result; -} diff --git a/src/vs/base/common/keybindingLabels.ts b/src/vs/base/common/keybindingLabels.ts index 29916e201d4..6cb492d858c 100644 --- a/src/vs/base/common/keybindingLabels.ts +++ b/src/vs/base/common/keybindingLabels.ts @@ -59,6 +59,13 @@ export const UILabelProvider = new ModifierLabelProvider( altKey: nls.localize({ key: 'altKey', comment: ['This is the short form for the Alt key on the keyboard'] }, "Alt"), metaKey: nls.localize({ key: 'windowsKey', comment: ['This is the short form for the Windows key on the keyboard'] }, "Windows"), separator: '+', + }, + { + ctrlKey: nls.localize({ key: 'ctrlKey', comment: ['This is the short form for the Control key on the keyboard'] }, "Ctrl"), + shiftKey: nls.localize({ key: 'shiftKey', comment: ['This is the short form for the Shift key on the keyboard'] }, "Shift"), + altKey: nls.localize({ key: 'altKey', comment: ['This is the short form for the Alt key on the keyboard'] }, "Alt"), + metaKey: nls.localize({ key: 'superKey', comment: ['This is the short form for the Super key on the keyboard'] }, "Super"), + separator: '+', } ); @@ -79,6 +86,13 @@ export const AriaLabelProvider = new ModifierLabelProvider( altKey: nls.localize({ key: 'altKey.long', comment: ['This is the long form for the Alt key on the keyboard'] }, "Alt"), metaKey: nls.localize({ key: 'windowsKey.long', comment: ['This is the long form for the Windows key on the keyboard'] }, "Windows"), separator: '+', + }, + { + ctrlKey: nls.localize({ key: 'ctrlKey.long', comment: ['This is the long form for the Control key on the keyboard'] }, "Control"), + shiftKey: nls.localize({ key: 'shiftKey.long', comment: ['This is the long form for the Shift key on the keyboard'] }, "Shift"), + altKey: nls.localize({ key: 'altKey.long', comment: ['This is the long form for the Alt key on the keyboard'] }, "Alt"), + metaKey: nls.localize({ key: 'superKey.long', comment: ['This is the long form for the Super key on the keyboard'] }, "Super"), + separator: '+', } ); diff --git a/src/vs/base/common/labels.ts b/src/vs/base/common/labels.ts index 8a4a058d898..44d9373ab80 100644 --- a/src/vs/base/common/labels.ts +++ b/src/vs/base/common/labels.ts @@ -23,9 +23,7 @@ export interface IUserHomeProvider { } /** - * @param resource for which to compute the path label - * @param userHomeProvider if a resource has a file schema userHomeProvider is used for tildifiying the label - * @param rootProvider only passed in if the label should be relative to the workspace root + * @deprecated use UriLabelService instead */ export function getPathLabel(resource: URI | string, userHomeProvider: IUserHomeProvider, rootProvider?: IWorkspaceFolderProvider): string { if (!resource) { @@ -84,7 +82,7 @@ export function getBaseLabel(resource: URI | string): string { resource = URI.file(resource); } - const base = pathsBasename(resource.fsPath) || resource.fsPath /* can be empty string if '/' is passed in */; + const base = pathsBasename(resource.path) || (resource.scheme === Schemas.file ? resource.fsPath : resource.path) /* can be empty string if '/' is passed in */; // convert c: => C: if (hasDriveLetter(base)) { diff --git a/src/vs/base/common/paths.ts b/src/vs/base/common/paths.ts index a837ce51685..e5ed22fb7b9 100644 --- a/src/vs/base/common/paths.ts +++ b/src/vs/base/common/paths.ts @@ -19,9 +19,12 @@ export const sep = '/'; export const nativeSep = isWindows ? '\\' : '/'; /** + * @param path the path to get the dirname from + * @param separator the separator to use * @returns the directory name of a path. + * */ -export function dirname(path: string): string { +export function dirname(path: string, separator = nativeSep): string { const idx = ~path.lastIndexOf('/') || ~path.lastIndexOf('\\'); if (idx === 0) { return '.'; @@ -32,7 +35,7 @@ export function dirname(path: string): string { } else { let res = path.substring(0, ~idx); if (isWindows && res[res.length - 1] === ':') { - res += nativeSep; // make sure drive letters end with backslash + res += separator; // make sure drive letters end with backslash } return res; } @@ -328,7 +331,7 @@ export function isEqual(pathA: string, pathB: string, ignoreCase?: boolean): boo return equalsIgnoreCase(pathA, pathB); } -export function isEqualOrParent(path: string, candidate: string, ignoreCase?: boolean): boolean { +export function isEqualOrParent(path: string, candidate: string, ignoreCase?: boolean, separator = nativeSep): boolean { if (path === candidate) { return true; } @@ -352,15 +355,15 @@ export function isEqualOrParent(path: string, candidate: string, ignoreCase?: bo } let sepOffset = candidate.length; - if (candidate.charAt(candidate.length - 1) === nativeSep) { + if (candidate.charAt(candidate.length - 1) === separator) { sepOffset--; // adjust the expected sep offset in case our candidate already ends in separator character } - return path.charAt(sepOffset) === nativeSep; + return path.charAt(sepOffset) === separator; } - if (candidate.charAt(candidate.length - 1) !== nativeSep) { - candidate += nativeSep; + if (candidate.charAt(candidate.length - 1) !== separator) { + candidate += separator; } return path.indexOf(candidate) === 0; diff --git a/src/vs/base/common/performance.js b/src/vs/base/common/performance.js index baf730387ac..c89ace71f2c 100644 --- a/src/vs/base/common/performance.js +++ b/src/vs/base/common/performance.js @@ -42,31 +42,33 @@ define([], function () { function getEntries(type, name) { const result = []; const entries = global._performanceEntries; - for (let i = 0; i < entries.length; i += 4) { + for (let i = 0; i < entries.length; i += 5) { if (entries[i] === type && (name === void 0 || entries[i + 1] === name)) { result.push({ type: entries[i], name: entries[i + 1], startTime: entries[i + 2], duration: entries[i + 3], + seq: entries[i + 4], }); } } return result.sort((a, b) => { - return a.startTime - b.startTime; + return a.startTime - b.startTime || a.seq - b.seq; }); } function getEntry(type, name) { const entries = global._performanceEntries; - for (let i = 0; i < entries.length; i += 4) { + for (let i = 0; i < entries.length; i += 5) { if (entries[i] === type && entries[i + 1] === name) { return { type: entries[i], name: entries[i + 1], startTime: entries[i + 2], duration: entries[i + 3], + seq: entries[i + 4], }; } } @@ -76,7 +78,7 @@ define([], function () { const entries = global._performanceEntries; let name = from; let startTime = 0; - for (let i = 0; i < entries.length; i += 4) { + for (let i = 0; i < entries.length; i += 5) { if (entries[i + 1] === name) { if (name === from) { // found `from` (start of interval) @@ -91,8 +93,10 @@ define([], function () { return 0; } + let seq = 0; + function mark(name) { - global._performanceEntries.push('mark', name, _now(), 0); + global._performanceEntries.push('mark', name, _now(), 0, seq++); if (typeof console.timeStamp === 'function') { console.timeStamp(name); } @@ -121,9 +125,9 @@ define([], function () { function _getLastStartTime(name) { const entries = global._performanceEntries; - for (let i = entries.length - 1; i >= 0; i -= 4) { - if (entries[i - 2] === name) { - return entries[i - 1]; + for (let i = entries.length - 1; i >= 0; i -= 5) { + if (entries[i - 3] === name) { + return entries[i - 2]; } } diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index e7c4cb9d9c3..95d05f973df 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -5,26 +5,38 @@ 'use strict'; import * as paths from 'vs/base/common/paths'; -import uri from 'vs/base/common/uri'; +import URI from 'vs/base/common/uri'; import { equalsIgnoreCase } from 'vs/base/common/strings'; +import { Schemas } from 'vs/base/common/network'; +import { isLinux, isWindows } from 'vs/base/common/platform'; +import { CharCode } from 'vs/base/common/charCode'; -export function basenameOrAuthority(resource: uri): string { - return paths.basename(resource.path) || resource.authority; +export function getComparisonKey(resource: URI): string { + return hasToIgnoreCase(resource) ? resource.toString().toLowerCase() : resource.toString(); } -export function isEqualOrParent(resource: uri, candidate: uri, ignoreCase?: boolean): boolean { +export function hasToIgnoreCase(resource: URI): boolean { + // A file scheme resource is in the same platform as code, so ignore case for non linux platforms + // Resource can be from another platform. Lowering the case as an hack. Should come from File system provider + return resource && resource.scheme === Schemas.file ? !isLinux : true; +} + +export function basenameOrAuthority(resource: URI): string { + return basename(resource) || resource.authority; +} + +export function isEqualOrParent(resource: URI, candidate: URI, ignoreCase?: boolean): boolean { if (resource.scheme === candidate.scheme && resource.authority === candidate.authority) { - if (resource.scheme === 'file') { + if (resource.scheme === Schemas.file) { return paths.isEqualOrParent(resource.fsPath, candidate.fsPath, ignoreCase); } - - return paths.isEqualOrParent(resource.path, candidate.path, ignoreCase); + return paths.isEqualOrParent(resource.path, candidate.path, ignoreCase, '/'); } return false; } -export function isEqual(first: uri, second: uri, ignoreCase?: boolean): boolean { +export function isEqual(first: URI, second: URI, ignoreCase?: boolean): boolean { const identityEquals = (first === second); if (identityEquals) { return true; @@ -41,25 +53,71 @@ export function isEqual(first: uri, second: uri, ignoreCase?: boolean): boolean return first.toString() === second.toString(); } -export function dirname(resource: uri): uri { - const dirname = paths.dirname(resource.path); - if (resource.authority && dirname && !paths.isAbsolute(dirname)) { - return null; // If a URI contains an authority component, then the path component must either be empty or begin with a slash ("/") character - } +export function basename(resource: URI): string { + return paths.basename(resource.path); +} +/** + * Return a URI representing the directory of a URI path. + * + * @param resource The input URI. + * @returns The URI representing the directory of the input URI. + */ +export function dirname(resource: URI): URI { + let dirname = paths.dirname(resource.path, '/'); + if (resource.authority && dirname.length && dirname.charCodeAt(0) !== CharCode.Slash) { + return null; // If a URI contains an authority component, then the path component must either be empty or begin with a CharCode.Slash ("/") character + } return resource.with({ path: dirname }); } -export function joinPath(resource: uri, pathFragment: string): uri { - const joinedPath = paths.join(resource.path || '/', pathFragment); +/** + * Join a URI path with a path fragment and normalizes the resulting path. + * + * @param resource The input URI. + * @param pathFragment The path fragment to add to the URI path. + * @returns The resulting URI. + */ +export function joinPath(resource: URI, pathFragment: string): URI { + let joinedPath: string; + if (resource.scheme === Schemas.file) { + joinedPath = URI.file(paths.join(resource.fsPath, pathFragment)).path; + } else { + joinedPath = paths.join(resource.path, pathFragment); + } return resource.with({ path: joinedPath }); } -export function distinctParents(items: T[], resourceAccessor: (item: T) => uri): T[] { +/** + * Normalizes the path part of a URI: Resolves `.` and `..` elements with directory names. + * + * @param resource The URI to normalize the path. + * @returns The URI with the normalized path. + */ +export function normalizePath(resource: URI): URI { + let normalizedPath: string; + if (resource.scheme === Schemas.file) { + normalizedPath = URI.file(paths.normalize(resource.fsPath)).path; + } else { + normalizedPath = paths.normalize(resource.path); + } + return resource.with({ + path: normalizedPath + }); +} + +/** + * Returns true if the URI path is absolute. + */ +export function isAbsolutePath(resource: URI): boolean { + return paths.isAbsolute(resource.path); +} + +export function distinctParents(items: T[], resourceAccessor: (item: T) => URI): T[] { const distinctParents: T[] = []; for (let i = 0; i < items.length; i++) { const candidateResource = resourceAccessor(items[i]); @@ -78,3 +136,11 @@ export function distinctParents(items: T[], resourceAccessor: (item: T) => ur return distinctParents; } + +export function isMalformedFileUri(candidate: URI): URI | undefined { + if (!candidate.scheme || isWindows && candidate.scheme.match(/^[a-zA-Z]$/)) { + return URI.file((candidate.scheme ? candidate.scheme + ':' : '') + candidate.path); + } + return void 0; +} + diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 412efc17388..7ead50439d3 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -12,6 +12,12 @@ const _singleSlashStart = /^\//; const _doubleSlashStart = /^\/\//; function _validateUri(ret: URI): void { + + // // scheme, must be set + // if (!ret.scheme) { + // throw new Error('[UriError]: Scheme is missing.'); + // } + // scheme, https://tools.ietf.org/html/rfc3986#section-3.1 // ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) if (ret.scheme && !_schemePattern.test(ret.scheme)) { diff --git a/src/vs/base/common/winjs.base.d.ts b/src/vs/base/common/winjs.base.d.ts index f5b941a8873..c568d4d7b05 100644 --- a/src/vs/base/common/winjs.base.d.ts +++ b/src/vs/base/common/winjs.base.d.ts @@ -5,25 +5,21 @@ /// Interfaces for WinJS export type ErrorCallback = (error: any) => void; -export type ProgressCallback = (progress: TProgress) => void; -export declare class Promise { +export class Promise { constructor( executor: ( resolve: (value: T | PromiseLike) => void, - reject: (reason: any) => void, - progress: (progress: TProgress) => void) => void, + reject: (reason: any) => void) => void, oncancel?: () => void); public then( onfulfilled?: ((value: T) => TResult1 | PromiseLike) | null, - onrejected?: ((reason: any) => TResult2 | PromiseLike) | null, - onprogress?: (progress: TProgress) => void): Promise; + onrejected?: ((reason: any) => TResult2 | PromiseLike) | null): Promise; public done( onfulfilled?: (value: T) => void, - onrejected?: (reason: any) => void, - onprogress?: (progress: TProgress) => void): void; + onrejected?: (reason: any) => void): void; public cancel(): void; @@ -56,9 +52,7 @@ export type TValueCallback = (value: T | PromiseLike) => void; export { Promise as TPromise, - Promise as PPromise, - TValueCallback as ValueCallback, - ProgressCallback as TProgressCallback + TValueCallback as ValueCallback }; export interface IPromiseErrorDetail { diff --git a/src/vs/base/node/paths.ts b/src/vs/base/node/paths.ts index dfdc28def8a..66930cdaf4b 100644 --- a/src/vs/base/node/paths.ts +++ b/src/vs/base/node/paths.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import uri from 'vs/base/common/uri'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; interface IPaths { getAppDataPath(platform: string): string; getDefaultUserDataPath(platform: string): string; } -const pathsPath = uri.parse(require.toUrl('paths')).fsPath; +const pathsPath = getPathFromAmdModule(require, 'paths'); const paths = require.__$__nodeRequire(pathsPath); export const getAppDataPath = paths.getAppDataPath; -export const getDefaultUserDataPath = paths.getDefaultUserDataPath; \ No newline at end of file +export const getDefaultUserDataPath = paths.getDefaultUserDataPath; diff --git a/src/vs/base/node/processes.ts b/src/vs/base/node/processes.ts index ebe0dff4cc4..c08d79aaf2d 100644 --- a/src/vs/base/node/processes.ts +++ b/src/vs/base/node/processes.ts @@ -11,12 +11,12 @@ import * as nls from 'vs/nls'; import { TPromise, TValueCallback, ErrorCallback } from 'vs/base/common/winjs.base'; import * as Types from 'vs/base/common/types'; import { IStringDictionary } from 'vs/base/common/collections'; -import URI from 'vs/base/common/uri'; import * as Objects from 'vs/base/common/objects'; import * as TPath from 'vs/base/common/paths'; import * as Platform from 'vs/base/common/platform'; import { LineDecoder } from 'vs/base/node/decoder'; import { CommandOptions, ForkOptions, SuccessData, Source, TerminateResponse, TerminateResponseCode, Executable } from 'vs/base/common/processes'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; export { CommandOptions, ForkOptions, SuccessData, Source, TerminateResponse, TerminateResponseCode }; export type TProgressCallback = (progress: T) => void; @@ -54,7 +54,7 @@ export function terminateProcess(process: cp.ChildProcess, cwd?: string): Termin } } else if (Platform.isLinux || Platform.isMacintosh) { try { - let cmd = URI.parse(require.toUrl('vs/base/node/terminateProcess.sh')).fsPath; + let cmd = getPathFromAmdModule(require, 'vs/base/node/terminateProcess.sh'); let result = cp.spawnSync(cmd, [process.pid.toString()]); if (result.error) { return { success: false, error: result.error }; diff --git a/src/vs/base/node/ps.ts b/src/vs/base/node/ps.ts index a7d2d36d0ca..fc8c25a1153 100644 --- a/src/vs/base/node/ps.ts +++ b/src/vs/base/node/ps.ts @@ -6,7 +6,8 @@ 'use strict'; import { exec } from 'child_process'; -import URI from 'vs/base/common/uri'; + +import { getPathFromAmdModule } from 'vs/base/common/amd'; export interface ProcessItem { name: string; @@ -207,7 +208,7 @@ export function listProcesses(rootPid: number): Promise { // The cpu usage value reported on Linux is the average over the process lifetime, // recalculate the usage over a one second interval // JSON.stringify is needed to escape spaces, https://github.com/nodejs/node/issues/6803 - let cmd = JSON.stringify(URI.parse(require.toUrl('vs/base/node/cpuUsage.sh')).fsPath); + let cmd = JSON.stringify(getPathFromAmdModule(require, 'vs/base/node/cpuUsage.sh')); cmd += ' ' + pids.join(' '); exec(cmd, {}, (err, stdout, stderr) => { diff --git a/src/vs/base/node/stdFork.ts b/src/vs/base/node/stdFork.ts index c7c36559465..17782445328 100644 --- a/src/vs/base/node/stdFork.ts +++ b/src/vs/base/node/stdFork.ts @@ -9,7 +9,7 @@ import * as path from 'path'; import * as os from 'os'; import * as net from 'net'; import * as cp from 'child_process'; -import uri from 'vs/base/common/uri'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; export interface IForkOpts { cwd?: string; @@ -117,7 +117,7 @@ export function fork(modulePath: string, args: string[], options: IForkOpts, cal }; // Create the process - let bootstrapperPath = (uri.parse(require.toUrl('./stdForkStart.js')).fsPath); + let bootstrapperPath = (getPathFromAmdModule(require, './stdForkStart.js')); childProcess = cp.fork(bootstrapperPath, [modulePath].concat(args), { silent: true, cwd: options.cwd, diff --git a/src/vs/base/parts/ipc/common/ipc.ts b/src/vs/base/parts/ipc/common/ipc.ts index 37555c26b07..8543a0ba8bf 100644 --- a/src/vs/base/parts/ipc/common/ipc.ts +++ b/src/vs/base/parts/ipc/common/ipc.ts @@ -14,7 +14,6 @@ enum MessageType { RequestPromiseCancel, ResponseInitialize, ResponsePromiseSuccess, - ResponsePromiseProgress, ResponsePromiseError, ResponsePromiseErrorObj, @@ -24,7 +23,11 @@ enum MessageType { } function isResponse(messageType: MessageType): boolean { - return messageType >= MessageType.ResponseInitialize; + return messageType === MessageType.ResponseInitialize + || messageType === MessageType.ResponsePromiseSuccess + || messageType === MessageType.ResponsePromiseError + || messageType === MessageType.ResponsePromiseErrorObj + || messageType === MessageType.ResponseEventFire; } interface IRawMessage { @@ -172,8 +175,6 @@ export class ChannelServer implements IChannelServer, IDisposable { } delete this.activeRequests[request.id]; - }, data => { - this.protocol.send({ id, data, type: MessageType.ResponsePromiseProgress }); }); this.activeRequests[request.id] = toDisposable(() => requestPromise.cancel()); @@ -260,7 +261,8 @@ export class ChannelClient implements IChannelClient, IDisposable { let uninitializedPromise: TPromise | null = null; const emitter = new Emitter({ onFirstListenerAdd: () => { - uninitializedPromise = this.whenInitialized().then(() => { + uninitializedPromise = this.whenInitialized(); + uninitializedPromise.then(() => { uninitializedPromise = null; this.send(request.raw); }); @@ -282,7 +284,7 @@ export class ChannelClient implements IChannelClient, IDisposable { private doRequest(request: IRequest): Promise { const id = request.raw.id; - return new TPromise((c, e, p) => { + return new TPromise((c, e) => { this.handlers[id] = response => { switch (response.type) { case MessageType.ResponsePromiseSuccess: @@ -302,10 +304,6 @@ export class ChannelClient implements IChannelClient, IDisposable { delete this.handlers[id]; e(response.data); break; - - case MessageType.ResponsePromiseProgress: - p(response.data); - break; } }; @@ -317,12 +315,12 @@ export class ChannelClient implements IChannelClient, IDisposable { private bufferRequest(request: IRequest): Promise { let flushedRequest: Promise = null; - return new TPromise((c, e, p) => { + return new TPromise((c, e) => { this.bufferedRequests.push(request); request.flush = () => { request.flush = null; - flushedRequest = this.doRequest(request).then(c, e, p); + flushedRequest = this.doRequest(request).then(c, e); }; }, () => { request.flush = null; diff --git a/src/vs/base/parts/ipc/node/ipc.cp.ts b/src/vs/base/parts/ipc/node/ipc.cp.ts index 38a8a96f813..1221c2c7dfa 100644 --- a/src/vs/base/parts/ipc/node/ipc.cp.ts +++ b/src/vs/base/parts/ipc/node/ipc.cp.ts @@ -107,9 +107,8 @@ export class Client implements IChannelClient, IDisposable { const channel = this.channels[channelName] || (this.channels[channelName] = this.client.getChannel(channelName)); const request: TPromise = channel.call(name, arg); - // Progress doesn't propagate across 'then', we need to create a promise wrapper - const result = new TPromise((c, e, p) => { - request.then(c, e, p).done(() => { + const result = new TPromise((c, e) => { + request.then(c, e).done(() => { if (!this.activeRequests) { return; } @@ -150,6 +149,7 @@ export class Client implements IChannelClient, IDisposable { } this.activeRequests.splice(this.activeRequests.indexOf(listener), 1); + listener.dispose(); if (this.activeRequests.length === 0) { this.disposeDelayer.trigger(() => this.disposeClient()); diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts index 0be4208d0e9..e82503e1eb0 100644 --- a/src/vs/base/parts/ipc/node/ipc.net.ts +++ b/src/vs/base/parts/ipc/node/ipc.net.ts @@ -12,6 +12,8 @@ import { IMessagePassingProtocol, ClientConnectionEvent, IPCServer, IPCClient } import { join } from 'path'; import { tmpdir } from 'os'; import { generateUuid } from 'vs/base/common/uuid'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { TimeoutTimer } from 'vs/base/common/async'; export function generateRandomPipeName(): string { const randomSuffix = generateUuid(); @@ -23,17 +25,28 @@ export function generateRandomPipeName(): string { } } -export class Protocol implements IMessagePassingProtocol { +export class Protocol implements IDisposable, IMessagePassingProtocol { - private static readonly _headerLen = 17; + private static readonly _headerLen = 5; + + private _isDisposed: boolean; + private _chunks: Buffer[]; + + private _firstChunkTimer: TimeoutTimer; + private _socketDataListener: (data: Buffer) => void; + private _socketEndListener: () => void; + private _socketCloseListener: () => void; private _onMessage = new Emitter(); - readonly onMessage: Event = this._onMessage.event; + private _onClose = new Emitter(); + readonly onClose: Event = this._onClose.event; + constructor(private _socket: Socket, firstDataChunk?: Buffer) { + this._isDisposed = false; + this._chunks = []; - let chunks: Buffer[] = []; let totalLength = 0; const state = { @@ -44,16 +57,16 @@ export class Protocol implements IMessagePassingProtocol { const acceptChunk = (data: Buffer) => { - chunks.push(data); + this._chunks.push(data); totalLength += data.length; while (totalLength > 0) { if (state.readHead) { - // expecting header -> read 17bytes for header + // expecting header -> read 5bytes for header // information: `bodyIsJson` and `bodyLen` if (totalLength >= Protocol._headerLen) { - const all = Buffer.concat(chunks); + const all = Buffer.concat(this._chunks); state.bodyIsJson = all.readInt8(0) === 1; state.bodyLen = all.readInt32BE(1); @@ -61,7 +74,7 @@ export class Protocol implements IMessagePassingProtocol { const rest = all.slice(Protocol._headerLen); totalLength = rest.length; - chunks = [rest]; + this._chunks = [rest]; } else { break; @@ -73,21 +86,27 @@ export class Protocol implements IMessagePassingProtocol { // the actual message or wait for more data if (totalLength >= state.bodyLen) { - const all = Buffer.concat(chunks); + const all = Buffer.concat(this._chunks); let message = all.toString('utf8', 0, state.bodyLen); if (state.bodyIsJson) { message = JSON.parse(message); } - this._onMessage.fire(message); + // ensure the public getBuffer returns a valid value if invoked from the event listeners const rest = all.slice(state.bodyLen); totalLength = rest.length; - chunks = [rest]; + this._chunks = [rest]; state.bodyIsJson = false; state.bodyLen = -1; state.readHead = true; + this._onMessage.fire(message); + + if (this._isDisposed) { + // check if an event listener lead to our disposal + break; + } } else { break; } @@ -103,14 +122,43 @@ export class Protocol implements IMessagePassingProtocol { } }; - _socket.on('data', (data: Buffer) => { + // Make sure to always handle the firstDataChunk if no more `data` event comes in + this._firstChunkTimer = new TimeoutTimer(); + this._firstChunkTimer.setIfNotSet(() => { + acceptFirstDataChunk(); + }, 0); + + this._socketDataListener = (data: Buffer) => { acceptFirstDataChunk(); acceptChunk(data); - }); + }; + _socket.on('data', this._socketDataListener); - _socket.on('end', () => { + this._socketEndListener = () => { acceptFirstDataChunk(); - }); + }; + _socket.on('end', this._socketEndListener); + + this._socketCloseListener = () => { + this._onClose.fire(); + }; + _socket.once('close', this._socketCloseListener); + } + + public dispose(): void { + this._isDisposed = true; + this._firstChunkTimer.dispose(); + this._socket.removeListener('data', this._socketDataListener); + this._socket.removeListener('end', this._socketEndListener); + this._socket.removeListener('close', this._socketCloseListener); + } + + public end(): void { + this._socket.end(); + } + + public getBuffer(): Buffer { + return Buffer.concat(this._chunks); } public send(message: any): void { @@ -123,10 +171,10 @@ export class Protocol implements IMessagePassingProtocol { // ensure string if (typeof message !== 'string') { message = JSON.stringify(message); - header.writeInt8(1, 0); + header.writeInt8(1, 0, true); } const data = Buffer.from(message); - header.writeInt32BE(data.length, 1); + header.writeInt32BE(data.length, 1, true); this._writeSoon(header, data); } @@ -193,18 +241,19 @@ export class Server extends IPCServer { export class Client extends IPCClient { - private _onClose = new Emitter(); - get onClose(): Event { return this._onClose.event; } + public static fromSocket(socket: Socket, id: string): Client { + return new Client(new Protocol(socket), id); + } - constructor(private socket: Socket, id: string) { - super(new Protocol(socket), id); - socket.once('close', () => this._onClose.fire()); + get onClose(): Event { return this.protocol.onClose; } + + constructor(private protocol: Protocol, id: string) { + super(protocol, id); } dispose(): void { super.dispose(); - this.socket.end(); - this.socket = null; + this.protocol.end(); } } @@ -229,7 +278,7 @@ export function connect(hook: any, clientId: string): TPromise { return new TPromise((c, e) => { const socket = createConnection(hook, () => { socket.removeListener('error', e); - c(new Client(socket, clientId)); + c(Client.fromSocket(socket, clientId)); }); socket.once('error', e); diff --git a/src/vs/base/parts/ipc/test/node/ipc.net.test.ts b/src/vs/base/parts/ipc/test/node/ipc.net.test.ts index 6b48161d026..eb6ddffba02 100644 --- a/src/vs/base/parts/ipc/test/node/ipc.net.test.ts +++ b/src/vs/base/parts/ipc/test/node/ipc.net.test.ts @@ -87,4 +87,39 @@ suite('IPC, Socket Protocol', () => { }); }); }); + + test('can devolve to a socket and evolve again without losing data', () => { + let resolve: (v: void) => void; + let result = new TPromise((_resolve, _reject) => { + resolve = _resolve; + }); + const sender = new Protocol(stream); + const receiver1 = new Protocol(stream); + + assert.equal(stream.listenerCount('data'), 2); + assert.equal(stream.listenerCount('end'), 2); + + receiver1.onMessage((msg) => { + assert.equal(msg.value, 1); + + let buffer = receiver1.getBuffer(); + receiver1.dispose(); + + assert.equal(stream.listenerCount('data'), 1); + assert.equal(stream.listenerCount('end'), 1); + + const receiver2 = new Protocol(stream, buffer); + receiver2.onMessage((msg) => { + assert.equal(msg.value, 2); + resolve(void 0); + }); + }); + + const msg1 = { value: 1 }; + const msg2 = { value: 2 }; + sender.send(msg1); + sender.send(msg2); + + return result; + }); }); diff --git a/src/vs/base/parts/ipc/test/node/ipc.perf.ts b/src/vs/base/parts/ipc/test/node/ipc.perf.ts deleted file mode 100644 index 2e1fcbcbd83..00000000000 --- a/src/vs/base/parts/ipc/test/node/ipc.perf.ts +++ /dev/null @@ -1,114 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -'use strict'; - -import * as assert from 'assert'; -import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; -import uri from 'vs/base/common/uri'; -import { always } from 'vs/base/common/async'; -import { ITestChannel, TestServiceClient, ITestService } from './testService'; - -function createClient(): Client { - return new Client(uri.parse(require.toUrl('bootstrap')).fsPath, { - serverName: 'TestServer', - env: { AMD_ENTRYPOINT: 'vs/base/parts/ipc/test/node/testApp', verbose: true } - }); -} - -// Rename to ipc.perf.test.ts and run with ./scripts/test.sh --grep IPC.performance --timeout 60000 -suite('IPC performance', () => { - - test('increasing batch size', () => { - const client = createClient(); - const channel = client.getChannel('test'); - const service = new TestServiceClient(channel); - - const runs = [ - { batches: 250000, size: 1 }, - { batches: 2500, size: 100 }, - { batches: 500, size: 500 }, - { batches: 250, size: 1000 }, - { batches: 50, size: 5000 }, - { batches: 25, size: 10000 }, - // { batches: 10, size: 25000 }, - // { batches: 5, size: 50000 }, - // { batches: 1, size: 250000 }, - ]; - const dataSizes = [ - 100, - 250, - ]; - let i = 0, j = 0; - const result = measure(service, 10, 10, 250) // warm-up - .then(() => { - return (function nextRun() { - if (i >= runs.length) { - if (++j >= dataSizes.length) { - return; - } - i = 0; - } - const run = runs[i++]; - return measure(service, run.batches, run.size, dataSizes[j]) - .then(() => { - return nextRun(); - }); - })(); - }); - - return always(result, () => client.dispose()); - }); - - test('increasing raw data size', () => { - const client = createClient(); - const channel = client.getChannel('test'); - const service = new TestServiceClient(channel); - - const runs = [ - { batches: 250000, dataSize: 100 }, - { batches: 25000, dataSize: 1000 }, - { batches: 2500, dataSize: 10000 }, - { batches: 1250, dataSize: 20000 }, - { batches: 500, dataSize: 50000 }, - { batches: 250, dataSize: 100000 }, - { batches: 125, dataSize: 200000 }, - { batches: 50, dataSize: 500000 }, - { batches: 25, dataSize: 1000000 }, - ]; - let i = 0; - const result = measure(service, 10, 10, 250) // warm-up - .then(() => { - return (function nextRun() { - if (i >= runs.length) { - return; - } - const run = runs[i++]; - return measure(service, run.batches, 1, run.dataSize) - .then(() => { - return nextRun(); - }); - })(); - }); - - return always(result, () => client.dispose()); - }); - - function measure(service: ITestService, batches: number, size: number, dataSize: number) { - const start = Date.now(); - let hits = 0; - let count = 0; - return service.batchPerf(batches, size, dataSize) - .then(() => { - console.log(`Batches: ${batches}, size: ${size}, dataSize: ${dataSize}, n: ${batches * size * dataSize}, duration: ${Date.now() - start}`); - assert.strictEqual(hits, batches); - assert.strictEqual(count, batches * size); - }, err => assert.fail(err), - batch => { - hits++; - count += batch.length; - }); - } -}); \ No newline at end of file diff --git a/src/vs/base/parts/ipc/test/node/ipc.test.ts b/src/vs/base/parts/ipc/test/node/ipc.test.ts index 3091fce1d7f..01deb13302b 100644 --- a/src/vs/base/parts/ipc/test/node/ipc.test.ts +++ b/src/vs/base/parts/ipc/test/node/ipc.test.ts @@ -8,13 +8,13 @@ import * as assert from 'assert'; import { TPromise } from 'vs/base/common/winjs.base'; import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; -import uri from 'vs/base/common/uri'; import { always } from 'vs/base/common/async'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { ITestChannel, TestServiceClient } from './testService'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; function createClient(): Client { - return new Client(uri.parse(require.toUrl('bootstrap')).fsPath, { + return new Client(getPathFromAmdModule(require, 'bootstrap'), { serverName: 'TestServer', env: { AMD_ENTRYPOINT: 'vs/base/parts/ipc/test/node/testApp', verbose: true } }); @@ -101,4 +101,4 @@ suite('IPC', () => { return always(result, () => client.dispose()); }); }); -}); \ No newline at end of file +}); diff --git a/src/vs/base/parts/ipc/test/node/testService.ts b/src/vs/base/parts/ipc/test/node/testService.ts index a53ff5ed37b..f90d54708f6 100644 --- a/src/vs/base/parts/ipc/test/node/testService.ts +++ b/src/vs/base/parts/ipc/test/node/testService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { TPromise, PPromise } from 'vs/base/common/winjs.base'; +import { TPromise } from 'vs/base/common/winjs.base'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event, Emitter } from 'vs/base/common/event'; @@ -17,7 +17,6 @@ export interface ITestService { marco(): TPromise; pong(ping: string): TPromise<{ incoming: string, outgoing: string }>; cancelMe(): TPromise; - batchPerf(batches: number, size: number, dataSize: number): PPromise; } export class TestService implements ITestService { @@ -25,8 +24,6 @@ export class TestService implements ITestService { private _onMarco = new Emitter(); onMarco: Event = this._onMarco.event; - private _data = 'abcdefghijklmnopqrstuvwxyz'; - marco(): TPromise { this._onMarco.fire({ answer: 'polo' }); return TPromise.as('polo'); @@ -39,32 +36,6 @@ export class TestService implements ITestService { cancelMe(): TPromise { return TPromise.timeout(100).then(() => true); } - - batchPerf(batches: number, size: number, dataSize: number): PPromise { - while (this._data.length < dataSize) { - this._data += this._data; - } - const self = this; - return new PPromise((complete, error, progress) => { - let j = 0; - function send() { - if (j >= batches) { - complete(null); - return; - } - j++; - const batch = []; - for (let i = 0; i < size; i++) { - batch.push({ - prop: `${i}${self._data}`.substr(0, dataSize) - }); - } - progress(batch); - process.nextTick(send); - } - process.nextTick(send); - }); - } } export interface ITestChannel extends IChannel { @@ -74,7 +45,6 @@ export interface ITestChannel extends IChannel { call(command: 'marco'): TPromise; call(command: 'pong', ping: string): TPromise; call(command: 'cancelMe'): TPromise; - call(command: 'batchPerf', args: { batches: number; size: number; dataSize: number; }): PPromise; call(command: string, ...args: any[]): TPromise; } @@ -95,7 +65,6 @@ export class TestChannel implements ITestChannel { case 'pong': return this.testService.pong(args[0]); case 'cancelMe': return this.testService.cancelMe(); case 'marco': return this.testService.marco(); - case 'batchPerf': return this.testService.batchPerf(args[0].batches, args[0].size, args[0].dataSize); default: return TPromise.wrapError(new Error('command not found')); } } @@ -118,8 +87,4 @@ export class TestServiceClient implements ITestService { cancelMe(): TPromise { return this.channel.call('cancelMe'); } - - batchPerf(batches: number, size: number, dataSize: number): PPromise { - return this.channel.call('batchPerf', { batches, size, dataSize }); - } } \ No newline at end of file diff --git a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts index 74828296605..08e0701b847 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts @@ -92,7 +92,9 @@ export class QuickOpenEntry { * The label of the entry to use when a screen reader wants to read about the entry */ getAriaLabel(): string { - return this.getLabel(); + return [this.getLabel(), this.getDescription(), this.getDetail()] + .filter(s => !!s) + .join(', '); } /** @@ -501,7 +503,8 @@ export class QuickOpenModel implements IModel, IDataSource, IFilter, - IRunner + IRunner, + IAccessiblityProvider { private _entries: QuickOpenEntry[]; private _dataSource: IDataSource; diff --git a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts index 2a3a7f0d15a..ca63a6398d1 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts @@ -103,6 +103,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { private inputBox: InputBox; private inputContainer: Builder; private helpText: Builder; + private resultCount: Builder; private treeContainer: Builder; private progressBar: ProgressBar; private visible: boolean; @@ -152,6 +153,15 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { DOM.EventHelper.stop(e, true); this.hide(HideReason.CANCELED); + } else if (keyboardEvent.keyCode === KeyCode.Tab && !keyboardEvent.altKey && !keyboardEvent.ctrlKey && !keyboardEvent.metaKey) { + const stops = e.currentTarget.querySelectorAll('input, .monaco-tree, .monaco-tree-row.focused .action-label.icon'); + if (keyboardEvent.shiftKey && keyboardEvent.target === stops[0]) { + DOM.EventHelper.stop(e, true); + stops[stops.length - 1].focus(); + } else if (!keyboardEvent.shiftKey && keyboardEvent.target === stops[stops.length - 1]) { + DOM.EventHelper.stop(e, true); + stops[0].focus(); + } } }) .on(DOM.EventType.CONTEXT_MENU, (e: Event) => DOM.EventHelper.stop(e, true)) // Do this to fix an issue on Mac where the menu goes into the way @@ -223,6 +233,12 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { }); }); + // Result count for screen readers + this.resultCount = div.div({ + 'class': 'quick-open-result-count', + 'aria-live': 'polite' + }).clone(); + // Tree this.treeContainer = div.div({ 'class': 'quick-open-tree' @@ -243,7 +259,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { horizontalScrollMode: ScrollbarVisibility.Hidden, ariaLabel: nls.localize('treeAriaLabel', "Quick Picker"), keyboardSupport: this.options.keyboardSupport, - preventRootFocus: true + preventRootFocus: false })); this.treeElement = this.tree.getHTMLElement(); @@ -348,7 +364,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { if (keyboardEvent.keyCode === KeyCode.DownArrow || keyboardEvent.keyCode === KeyCode.UpArrow || keyboardEvent.keyCode === KeyCode.PageDown || keyboardEvent.keyCode === KeyCode.PageUp) { DOM.EventHelper.stop(e, true); this.navigateInTree(keyboardEvent.keyCode, keyboardEvent.shiftKey); - this.inputBox.inputElement.focus(); + this.treeElement.focus(); } }); return this.builder.getHTMLElement(); @@ -619,9 +635,12 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { // Indicate entries to tree this.tree.layout(); + const entries = input ? input.entries.filter(e => this.isElementVisible(input, e)) : []; + this.updateResultCount(entries.length); + // Handle auto focus - if (input && input.entries.some(e => this.isElementVisible(input, e))) { - this.autoFocus(input, autoFocus); + if (entries.length) { + this.autoFocus(input, entries, autoFocus); } }, errors.onUnexpectedError); } @@ -634,8 +653,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { return input.filter.isVisible(e); } - private autoFocus(input: IModel, autoFocus: IAutoFocus = {}): void { - const entries = input.entries.filter(e => this.isElementVisible(input, e)); + private autoFocus(input: IModel, entries: any[], autoFocus: IAutoFocus = {}): void { // First check for auto focus of prefix matches if (autoFocus.autoFocusPrefixMatch) { @@ -716,11 +734,13 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { // Indicate entries to tree this.tree.layout(); + const entries = input ? input.entries.filter(e => this.isElementVisible(input, e)) : []; + this.updateResultCount(entries.length); + // Handle auto focus if (autoFocus) { - let doAutoFocus = autoFocus && input && input.entries.some(e => this.isElementVisible(input, e)); - if (doAutoFocus) { - this.autoFocus(input, autoFocus); + if (entries.length) { + this.autoFocus(input, entries, autoFocus); } } }, errors.onUnexpectedError); @@ -760,6 +780,10 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { return height; } + updateResultCount(count: number) { + this.resultCount.text(nls.localize({ key: 'quickInput.visibleCount', comment: ['This tells the user how many items are shown in a list of items to select from. The items can be anything. Currently not visible, but read by screen readers.'] }, "{0} Results", count)); + } + hide(reason?: HideReason): void { if (!this.isVisible()) { return; diff --git a/src/vs/base/parts/quickopen/browser/quickopen.css b/src/vs/base/parts/quickopen/browser/quickopen.css index 97ef9224d98..97ddd6aab12 100644 --- a/src/vs/base/parts/quickopen/browser/quickopen.css +++ b/src/vs/base/parts/quickopen/browser/quickopen.css @@ -35,6 +35,11 @@ height: 25px; } +.monaco-quick-open-widget .quick-open-result-count { + position: absolute; + left: -10000px; +} + .monaco-quick-open-widget .quick-open-tree { line-height: 22px; } diff --git a/src/vs/base/parts/tree/browser/tree.ts b/src/vs/base/parts/tree/browser/tree.ts index efe1f73d95a..12df5ccd031 100644 --- a/src/vs/base/parts/tree/browser/tree.ts +++ b/src/vs/base/parts/tree/browser/tree.ts @@ -109,13 +109,6 @@ export interface ITree { */ collapseAll(elements?: any[], recursive?: boolean): WinJS.Promise; - /** - * Collapses several elements. - * Collapses all elements at the greatest tree depth that has expanded elements. - * The returned promise returns a boolean for whether the elements were collapsed or not. - */ - collapseDeepestExpandedLevel(): WinJS.Promise; - /** * Toggles an element's expansion state. */ diff --git a/src/vs/base/parts/tree/browser/treeDefaults.ts b/src/vs/base/parts/tree/browser/treeDefaults.ts index 032e70e54b7..ec975e735e2 100644 --- a/src/vs/base/parts/tree/browser/treeDefaults.ts +++ b/src/vs/base/parts/tree/browser/treeDefaults.ts @@ -134,6 +134,10 @@ export class DefaultController implements _.IController { return false; // Ignore event if target is a form input field (avoids browser specific issues) } + if (dom.findParentWithClass(event.target, 'scrollbar', 'monaco-tree')) { + return false; + } + if (dom.findParentWithClass(event.target, 'monaco-action-bar', 'row')) { // TODO@Joao not very nice way of checking for the action bar (implicit knowledge) return false; // Ignore event if target is over an action bar of the row } diff --git a/src/vs/base/parts/tree/browser/treeImpl.ts b/src/vs/base/parts/tree/browser/treeImpl.ts index f3b8c1415c7..dc008d1eede 100644 --- a/src/vs/base/parts/tree/browser/treeImpl.ts +++ b/src/vs/base/parts/tree/browser/treeImpl.ts @@ -184,10 +184,6 @@ export class Tree implements _.ITree { return this.model.collapseAll(elements, recursive); } - public collapseDeepestExpandedLevel(): WinJS.Promise { - return this.model.collapseDeepestExpandedLevel(); - } - public toggleExpansion(element: any, recursive: boolean = false): WinJS.Promise { return this.model.toggleExpansion(element, recursive); } @@ -215,7 +211,10 @@ export class Tree implements _.ITree { public getFirstVisibleElement(): any { return this.view.getFirstVisibleElement(); + } + public getLastVisibleElement(): any { + return this.view.getLastVisibleElement(); } public getScrollPosition(): number { diff --git a/src/vs/base/parts/tree/browser/treeModel.ts b/src/vs/base/parts/tree/browser/treeModel.ts index a9caa37d358..c2323599e0b 100644 --- a/src/vs/base/parts/tree/browser/treeModel.ts +++ b/src/vs/base/parts/tree/browser/treeModel.ts @@ -559,17 +559,6 @@ export class Item { return result; } - public getChildren(): Item[] { - var child = this.firstChild; - var results = []; - while (child) { - results.push(child); - child = child.next; - } - - return results; - } - private isAncestorOf(item: Item): boolean { while (item) { if (item.id === this.id) { @@ -1040,27 +1029,6 @@ export class TreeModel { return WinJS.Promise.join(promises); } - public collapseDeepestExpandedLevel(): WinJS.Promise { - var levelToCollapse = this.findDeepestExpandedLevel(this.input, 0); - - var items = [this.input]; - for (var i = 0; i < levelToCollapse; i++) { - items = arrays.flatten(items.map(node => node.getChildren())); - } - - var promises = items.map(child => this.collapse(child, false)); - return WinJS.Promise.join(promises); - } - - private findDeepestExpandedLevel(item: Item, currentLevel: number): number { - var expandedChildren = item.getChildren().filter(child => child.isExpanded()); - if (!expandedChildren.length) { - return currentLevel; - } - - return Math.max(...expandedChildren.map(child => this.findDeepestExpandedLevel(child, currentLevel + 1))); - } - public toggleExpansion(element: any, recursive: boolean = false): WinJS.Promise { return this.isExpanded(element) ? this.collapse(element, recursive) : this.expand(element); } diff --git a/src/vs/base/parts/tree/browser/treeUtils.ts b/src/vs/base/parts/tree/browser/treeUtils.ts new file mode 100644 index 00000000000..7187ca1bd2a --- /dev/null +++ b/src/vs/base/parts/tree/browser/treeUtils.ts @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import * as _ from 'vs/base/parts/tree/browser/tree'; + +export function collapseAll(tree: _.ITree, except?: any): void { + const nav = tree.getNavigator(); + let cur; + while (cur = nav.next()) { + if (!except || !isEqualOrParent(tree, except, cur)) { + tree.collapse(cur); + } + } +} + +export function isEqualOrParent(tree: _.ITree, element: any, candidateParent: any): boolean { + const nav = tree.getNavigator(element); + + do { + if (element === candidateParent) { + return true; + } + } while (element = nav.parent()); + + return false; +} + +export function expandAll(tree: _.ITree): void { + const nav = tree.getNavigator(); + let cur; + while (cur = nav.next()) { + tree.expand(cur); + } +} diff --git a/src/vs/base/parts/tree/browser/treeView.ts b/src/vs/base/parts/tree/browser/treeView.ts index cf6dc5ab8ff..bb125f1c905 100644 --- a/src/vs/base/parts/tree/browser/treeView.ts +++ b/src/vs/base/parts/tree/browser/treeView.ts @@ -220,7 +220,7 @@ export class ViewItem implements IViewItem { this.element.removeAttribute('id'); } if (this.model.hasChildren()) { - this.element.setAttribute('aria-expanded', String(!!this.model.isExpanded())); + this.element.setAttribute('aria-expanded', String(!!this._styles['expanded'])); } else { this.element.removeAttribute('aria-expanded'); } @@ -658,6 +658,11 @@ export class TreeView extends HeightMap { return item && item.model.getElement(); } + public getLastVisibleElement(): any { + const item = this.itemAtIndex(this.indexAt(this.lastRenderTop + this.lastRenderHeight)); + return item && item.model.getElement(); + } + private render(scrollTop: number, viewHeight: number, scrollLeft: number, viewWidth: number, scrollWidth: number): void { var i: number; var stop: number; diff --git a/src/vs/base/parts/tree/test/browser/treeModel.test.ts b/src/vs/base/parts/tree/test/browser/treeModel.test.ts index b65060dec56..7a23e564e4c 100644 --- a/src/vs/base/parts/tree/test/browser/treeModel.test.ts +++ b/src/vs/base/parts/tree/test/browser/treeModel.test.ts @@ -613,23 +613,6 @@ suite('TreeModel - Expansion', () => { }); }); - test('collapseDeepestExpandedLevel', () => { - return model.setInput(SAMPLE.DEEP2).then(() => { - return model.expand(SAMPLE.DEEP2.children[0]).then(() => { - return model.expand(SAMPLE.DEEP2.children[0].children[0]).then(() => { - - assert(model.isExpanded(SAMPLE.DEEP2.children[0])); - assert(model.isExpanded(SAMPLE.DEEP2.children[0].children[0])); - - return model.collapseDeepestExpandedLevel().then(() => { - assert(model.isExpanded(SAMPLE.DEEP2.children[0])); - assert(!model.isExpanded(SAMPLE.DEEP2.children[0].children[0])); - }); - }); - }); - }); - }); - test('auto expand single child folders', () => { return model.setInput(SAMPLE.DEEP).then(() => { return model.expand(SAMPLE.DEEP.children[0]).then(() => { diff --git a/src/vs/base/test/browser/ui/contextview/contextview.test.ts b/src/vs/base/test/browser/ui/contextview/contextview.test.ts new file mode 100644 index 00000000000..09214cfc06d --- /dev/null +++ b/src/vs/base/test/browser/ui/contextview/contextview.test.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { layout, LayoutAnchorPosition } from 'vs/base/browser/ui/contextview/contextview'; + +suite('Contextview', function () { + + test('layout', function () { + assert.equal(layout(200, 20, { offset: 0, size: 0, position: LayoutAnchorPosition.Before }), 0); + assert.equal(layout(200, 20, { offset: 50, size: 0, position: LayoutAnchorPosition.Before }), 50); + assert.equal(layout(200, 20, { offset: 200, size: 0, position: LayoutAnchorPosition.Before }), 180); + + assert.equal(layout(200, 20, { offset: 0, size: 0, position: LayoutAnchorPosition.After }), 0); + assert.equal(layout(200, 20, { offset: 50, size: 0, position: LayoutAnchorPosition.After }), 30); + assert.equal(layout(200, 20, { offset: 200, size: 0, position: LayoutAnchorPosition.After }), 180); + + assert.equal(layout(200, 20, { offset: 0, size: 50, position: LayoutAnchorPosition.Before }), 50); + assert.equal(layout(200, 20, { offset: 50, size: 50, position: LayoutAnchorPosition.Before }), 100); + assert.equal(layout(200, 20, { offset: 150, size: 50, position: LayoutAnchorPosition.Before }), 130); + + assert.equal(layout(200, 20, { offset: 0, size: 50, position: LayoutAnchorPosition.After }), 50); + assert.equal(layout(200, 20, { offset: 50, size: 50, position: LayoutAnchorPosition.After }), 30); + assert.equal(layout(200, 20, { offset: 150, size: 50, position: LayoutAnchorPosition.After }), 130); + }); +}); diff --git a/src/vs/base/test/common/async.test.ts b/src/vs/base/test/common/async.test.ts index 24ef5347f56..822d4063908 100644 --- a/src/vs/base/test/common/async.test.ts +++ b/src/vs/base/test/common/async.test.ts @@ -278,30 +278,6 @@ suite('Async', () => { return TPromise.join(promises); }); - test('Throttler - progress should work', function () { - let order = 0; - let factory = () => new TPromise((c, e, p) => { - TPromise.timeout(0).done(() => { - p(order++); - c(true); - }); - }); - - let throttler = new async.Throttler(); - let promises: TPromise[] = []; - let progresses: any[][] = [[], [], []]; - - promises.push(throttler.queue(factory).then(null, null, (p) => progresses[0].push(p))); - promises.push(throttler.queue(factory).then(null, null, (p) => progresses[1].push(p))); - promises.push(throttler.queue(factory).then(null, null, (p) => progresses[2].push(p))); - - return TPromise.join(promises).then(() => { - assert.deepEqual(progresses[0], [0]); - assert.deepEqual(progresses[1], [0]); - assert.deepEqual(progresses[2], [0]); - }); - }); - test('Delayer', function () { let count = 0; let factory = () => { @@ -453,54 +429,6 @@ suite('Async', () => { return p; }); - test('Delayer - progress should work', function () { - let order = 0; - let factory = () => new TPromise((c, e, p) => { - TPromise.timeout(0).done(() => { - p(order++); - c(true); - }); - }); - - let delayer = new async.Delayer(0); - let promises: TPromise[] = []; - let progresses: any[][] = [[], [], []]; - - promises.push(delayer.trigger(factory).then(null, null, (p) => progresses[0].push(p))); - promises.push(delayer.trigger(factory).then(null, null, (p) => progresses[1].push(p))); - promises.push(delayer.trigger(factory).then(null, null, (p) => progresses[2].push(p))); - - return TPromise.join(promises).then(() => { - assert.deepEqual(progresses[0], [0]); - assert.deepEqual(progresses[1], [0]); - assert.deepEqual(progresses[2], [0]); - }); - }); - - test('ThrottledDelayer - progress should work', function () { - let order = 0; - let factory = () => new TPromise((c, e, p) => { - TPromise.timeout(0).done(() => { - p(order++); - c(true); - }); - }); - - let delayer = new async.ThrottledDelayer(0); - let promises: TPromise[] = []; - let progresses: any[][] = [[], [], []]; - - promises.push(delayer.trigger(factory).then(null, null, (p) => progresses[0].push(p))); - promises.push(delayer.trigger(factory).then(null, null, (p) => progresses[1].push(p))); - promises.push(delayer.trigger(factory).then(null, null, (p) => progresses[2].push(p))); - - return TPromise.join(promises).then(() => { - assert.deepEqual(progresses[0], [0]); - assert.deepEqual(progresses[1], [0]); - assert.deepEqual(progresses[2], [0]); - }); - }); - test('Sequence', function () { let factoryFactory = (n: number) => () => { return TPromise.as(n); diff --git a/src/vs/base/test/common/network.test.ts b/src/vs/base/test/common/network.test.ts deleted file mode 100644 index 7aba51f153b..00000000000 --- a/src/vs/base/test/common/network.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import * as assert from 'assert'; -import URI from 'vs/base/common/uri'; - -function assertUrl(raw: string, scheme: string, domain: string, port: string, path: string, queryString: string, fragmentId: string): void { - // check for equivalent behaviour - const uri = URI.parse(raw); - assert.equal(uri.scheme, scheme); - assert.equal(uri.authority, port ? domain + ':' + port : domain); - assert.equal(uri.path, path); - assert.equal(uri.query, queryString); - assert.equal(uri.fragment, fragmentId); -} - -suite('Network', () => { - test('urls', () => { - assertUrl('http://www.test.com:8000/this/that/theother.html?query=foo#hash', - 'http', 'www.test.com', '8000', '/this/that/theother.html', 'query=foo', 'hash' - ); - - assertUrl('http://www.test.com:8000/this/that/theother.html?query=foo', - 'http', 'www.test.com', '8000', '/this/that/theother.html', 'query=foo', '' - ); - - assertUrl('http://www.test.com:8000/this/that/theother.html#hash', - 'http', 'www.test.com', '8000', '/this/that/theother.html', '', 'hash' - ); - - assertUrl('http://www.test.com:8000/#hash', - 'http', 'www.test.com', '8000', '/', '', 'hash' - ); - - assertUrl('http://www.test.com:8000#hash', - 'http', 'www.test.com', '8000', '/', '', 'hash' - ); - - assertUrl('http://www.test.com/#hash', - 'http', 'www.test.com', '', '/', '', 'hash' - ); - - assertUrl('http://www.test.com#hash', - 'http', 'www.test.com', '', '/', '', 'hash' - ); - - assertUrl('http://www.test.com:8000/this/that/theother.html', - 'http', 'www.test.com', '8000', '/this/that/theother.html', '', '' - ); - - assertUrl('http://www.test.com:8000/', - 'http', 'www.test.com', '8000', '/', '', '' - ); - - assertUrl('http://www.test.com:8000', - 'http', 'www.test.com', '8000', '/', '', '' - ); - - assertUrl('http://www.test.com/', - 'http', 'www.test.com', '', '/', '', '' - ); - - assertUrl('//www.test.com/', - '', 'www.test.com', '', '/', '', '' - ); - - assertUrl('//www.test.com:8000/this/that/theother.html?query=foo#hash', - '', 'www.test.com', '8000', '/this/that/theother.html', 'query=foo', 'hash' - ); - - assertUrl('//www.test.com/this/that/theother.html?query=foo#hash', - '', 'www.test.com', '', '/this/that/theother.html', 'query=foo', 'hash' - ); - - assertUrl('https://www.test.com:8000/this/that/theother.html?query=foo#hash', - 'https', 'www.test.com', '8000', '/this/that/theother.html', 'query=foo', 'hash' - ); - - assertUrl('f12://www.test.com:8000/this/that/theother.html?query=foo#hash', - 'f12', 'www.test.com', '8000', '/this/that/theother.html', 'query=foo', 'hash' - ); - - assertUrl('inmemory://model/0', - 'inmemory', 'model', '', '/0', '', '' - ); - - assertUrl('file:///c/far/boo/file.cs', 'file', '', '', '/c/far/boo/file.cs', '', ''); - }); -}); diff --git a/src/vs/base/test/common/resources.test.ts b/src/vs/base/test/common/resources.test.ts index 6a613730db3..e35a9bb438d 100644 --- a/src/vs/base/test/common/resources.test.ts +++ b/src/vs/base/test/common/resources.test.ts @@ -5,9 +5,9 @@ 'use strict'; import * as assert from 'assert'; -import { normalize } from 'vs/base/common/paths'; -import { dirname, distinctParents, joinPath } from 'vs/base/common/resources'; +import { dirname, basename, distinctParents, joinPath, isEqual, isEqualOrParent, hasToIgnoreCase, normalizePath, isAbsolutePath, isMalformedFileUri } from 'vs/base/common/resources'; import URI from 'vs/base/common/uri'; +import { isWindows } from 'vs/base/common/platform'; suite('Resources', () => { @@ -43,29 +43,185 @@ suite('Resources', () => { }); test('dirname', () => { - const f = URI.file('/some/file/test.txt'); - const d = dirname(f); - assert.equal(d.fsPath, normalize('/some/file', true)); + if (isWindows) { + assert.equal(dirname(URI.file('c:\\some\\file\\test.txt')).toString(), 'file:///c%3A/some/file'); + assert.equal(dirname(URI.file('c:\\some\\file')).toString(), 'file:///c%3A/some'); + assert.equal(dirname(URI.file('c:\\some\\file\\')).toString(), 'file:///c%3A/some'); + assert.equal(dirname(URI.file('c:\\some')).toString(), 'file:///c%3A/'); + } else { + assert.equal(dirname(URI.file('/some/file/test.txt')).toString(), 'file:///some/file'); + assert.equal(dirname(URI.file('/some/file/')).toString(), 'file:///some'); + assert.equal(dirname(URI.file('/some/file')).toString(), 'file:///some'); + } + assert.equal(dirname(URI.parse('foo://a/some/file/test.txt')).toString(), 'foo://a/some/file'); + assert.equal(dirname(URI.parse('foo://a/some/file/')).toString(), 'foo://a/some'); + assert.equal(dirname(URI.parse('foo://a/some/file')).toString(), 'foo://a/some'); + assert.equal(dirname(URI.parse('foo://a/some')).toString(), 'foo://a/'); // does not explode (https://github.com/Microsoft/vscode/issues/41987) dirname(URI.from({ scheme: 'file', authority: '/users/someone/portal.h' })); }); + test('basename', () => { + if (isWindows) { + assert.equal(basename(URI.file('c:\\some\\file\\test.txt')), 'test.txt'); + assert.equal(basename(URI.file('c:\\some\\file')), 'file'); + assert.equal(basename(URI.file('c:\\some\\file\\')), 'file'); + } else { + assert.equal(basename(URI.file('/some/file/test.txt')), 'test.txt'); + assert.equal(basename(URI.file('/some/file/')), 'file'); + assert.equal(basename(URI.file('/some/file')), 'file'); + assert.equal(basename(URI.file('/some')), 'some'); + } + assert.equal(basename(URI.parse('foo://a/some/file/test.txt')), 'test.txt'); + assert.equal(basename(URI.parse('foo://a/some/file/')), 'file'); + assert.equal(basename(URI.parse('foo://a/some/file')), 'file'); + assert.equal(basename(URI.parse('foo://a/some')), 'some'); + assert.equal(basename(URI.parse('foo://a/')), ''); + assert.equal(basename(URI.parse('foo://a')), ''); + }); + test('joinPath', () => { - assert.equal( - joinPath(URI.file('/foo/bar'), '/file.js').toString(), - 'file:///foo/bar/file.js'); - - assert.equal( - joinPath(URI.file('/foo/bar/'), '/file.js').toString(), - 'file:///foo/bar/file.js'); - - assert.equal( - joinPath(URI.file('/'), '/file.js').toString(), - 'file:///file.js'); + if (isWindows) { + assert.equal(joinPath(URI.file('c:\\foo\\bar'), '/file.js').toString(), 'file:///c%3A/foo/bar/file.js'); + assert.equal(joinPath(URI.file('c:\\foo\\bar\\'), 'file.js').toString(), 'file:///c%3A/foo/bar/file.js'); + assert.equal(joinPath(URI.file('c:\\foo\\bar\\'), '/file.js').toString(), 'file:///c%3A/foo/bar/file.js'); + assert.equal(joinPath(URI.file('c:\\'), '/file.js').toString(), 'file:///c%3A/file.js'); + assert.equal(joinPath(URI.file('c:\\'), 'bar/file.js').toString(), 'file:///c%3A/bar/file.js'); + assert.equal(joinPath(URI.file('c:\\foo'), './file.js').toString(), 'file:///c%3A/foo/file.js'); + assert.equal(joinPath(URI.file('c:\\foo'), '/./file.js').toString(), 'file:///c%3A/foo/file.js'); + assert.equal(joinPath(URI.file('c:\\foo'), '../file.js').toString(), 'file:///c%3A/file.js'); + assert.equal(joinPath(URI.file('c:\\foo\\.'), '../file.js').toString(), 'file:///c%3A/file.js'); + } else { + assert.equal(joinPath(URI.file('/foo/bar'), '/file.js').toString(), 'file:///foo/bar/file.js'); + assert.equal(joinPath(URI.file('/foo/bar'), 'file.js').toString(), 'file:///foo/bar/file.js'); + assert.equal(joinPath(URI.file('/foo/bar/'), '/file.js').toString(), 'file:///foo/bar/file.js'); + assert.equal(joinPath(URI.file('/'), '/file.js').toString(), 'file:///file.js'); + assert.equal(joinPath(URI.file('/foo/bar'), './file.js').toString(), 'file:///foo/bar/file.js'); + assert.equal(joinPath(URI.file('/foo/bar'), '/./file.js').toString(), 'file:///foo/bar/file.js'); + assert.equal(joinPath(URI.file('/foo/bar'), '../file.js').toString(), 'file:///foo/file.js'); + } + assert.equal(joinPath(URI.parse('foo://a/foo/bar'), '/file.js').toString(), 'foo://a/foo/bar/file.js'); + assert.equal(joinPath(URI.parse('foo://a/foo/bar'), 'file.js').toString(), 'foo://a/foo/bar/file.js'); + assert.equal(joinPath(URI.parse('foo://a/foo/bar/'), '/file.js').toString(), 'foo://a/foo/bar/file.js'); + assert.equal(joinPath(URI.parse('foo://a/'), '/file.js').toString(), 'foo://a/file.js'); + assert.equal(joinPath(URI.parse('foo://a/foo/bar/'), './file.js').toString(), 'foo://a/foo/bar/file.js'); + assert.equal(joinPath(URI.parse('foo://a/foo/bar/'), '/./file.js').toString(), 'foo://a/foo/bar/file.js'); + assert.equal(joinPath(URI.parse('foo://a/foo/bar/'), '../file.js').toString(), 'foo://a/foo/file.js'); assert.equal( joinPath(URI.from({ scheme: 'myScheme', authority: 'authority', path: '/path', query: 'query', fragment: 'fragment' }), '/file.js').toString(), 'myScheme://authority/path/file.js?query#fragment'); }); + + test('normalizePath', () => { + if (isWindows) { + assert.equal(normalizePath(URI.file('c:\\foo\\.\\bar')).toString(), 'file:///c%3A/foo/bar'); + assert.equal(normalizePath(URI.file('c:\\foo\\.')).toString(), 'file:///c%3A/foo'); + assert.equal(normalizePath(URI.file('c:\\foo\\.\\')).toString(), 'file:///c%3A/foo/'); + assert.equal(normalizePath(URI.file('c:\\foo\\..')).toString(), 'file:///c%3A/'); + assert.equal(normalizePath(URI.file('c:\\foo\\..\\bar')).toString(), 'file:///c%3A/bar'); + assert.equal(normalizePath(URI.file('c:\\foo\\..\\..\\bar')).toString(), 'file:///c%3A/bar'); + assert.equal(normalizePath(URI.file('c:\\foo\\foo\\..\\..\\bar')).toString(), 'file:///c%3A/bar'); + assert.equal(normalizePath(URI.file('c:\\foo\\foo\\.\\..\\..\\bar')).toString(), 'file:///c%3A/bar'); + assert.equal(normalizePath(URI.file('c:\\foo\\foo\\.\\..\\some\\..\\bar')).toString(), 'file:///c%3A/foo/bar'); + } else { + assert.equal(normalizePath(URI.file('/foo/./bar')).toString(), 'file:///foo/bar'); + assert.equal(normalizePath(URI.file('/foo/.')).toString(), 'file:///foo'); + assert.equal(normalizePath(URI.file('/foo/./')).toString(), 'file:///foo/'); + assert.equal(normalizePath(URI.file('/foo/..')).toString(), 'file:///'); + assert.equal(normalizePath(URI.file('/foo/../bar')).toString(), 'file:///bar'); + assert.equal(normalizePath(URI.file('/foo/../../bar')).toString(), 'file:///bar'); + assert.equal(normalizePath(URI.file('/foo/foo/../../bar')).toString(), 'file:///bar'); + assert.equal(normalizePath(URI.file('/foo/foo/./../../bar')).toString(), 'file:///bar'); + assert.equal(normalizePath(URI.file('/foo/foo/./../some/../bar')).toString(), 'file:///foo/bar'); + } + assert.equal(normalizePath(URI.parse('foo://a/foo/./bar')).toString(), 'foo://a/foo/bar'); + assert.equal(normalizePath(URI.parse('foo://a/foo/.')).toString(), 'foo://a/foo'); + assert.equal(normalizePath(URI.parse('foo://a/foo/./')).toString(), 'foo://a/foo/'); + assert.equal(normalizePath(URI.parse('foo://a/foo/..')).toString(), 'foo://a/'); + assert.equal(normalizePath(URI.parse('foo://a/foo/../bar')).toString(), 'foo://a/bar'); + assert.equal(normalizePath(URI.parse('foo://a/foo/../../bar')).toString(), 'foo://a/bar'); + assert.equal(normalizePath(URI.parse('foo://a/foo/foo/../../bar')).toString(), 'foo://a/bar'); + assert.equal(normalizePath(URI.parse('foo://a/foo/foo/./../../bar')).toString(), 'foo://a/bar'); + assert.equal(normalizePath(URI.parse('foo://a/foo/foo/./../some/../bar')).toString(), 'foo://a/foo/bar'); + }); + + test('isAbsolute', () => { + if (isWindows) { + assert.equal(isAbsolutePath(URI.file('c:\\foo\\')), true); + assert.equal(isAbsolutePath(URI.file('bar')), true); // URI normalizes all file URIs to be absolute + } else { + assert.equal(isAbsolutePath(URI.file('/foo/bar')), true); + assert.equal(isAbsolutePath(URI.file('bar')), true); // URI normalizes all file URIs to be absolute + } + assert.equal(isAbsolutePath(URI.parse('foo:foo')), false); + assert.equal(isAbsolutePath(URI.parse('foo://a/foo/.')), true); + }); + + test('isEqual', () => { + let fileURI = URI.file('/foo/bar'); + let fileURI2 = URI.file('/foo/Bar'); + assert.equal(isEqual(fileURI, fileURI, true), true); + assert.equal(isEqual(fileURI, fileURI, false), true); + assert.equal(isEqual(fileURI, fileURI, hasToIgnoreCase(fileURI)), true); + assert.equal(isEqual(fileURI, fileURI2, true), true); + assert.equal(isEqual(fileURI, fileURI2, false), false); + + let fileURI3 = URI.parse('foo://server:453/foo/bar'); + let fileURI4 = URI.parse('foo://server:453/foo/Bar'); + assert.equal(isEqual(fileURI3, fileURI3, true), true); + assert.equal(isEqual(fileURI3, fileURI3, false), true); + assert.equal(isEqual(fileURI3, fileURI3, hasToIgnoreCase(fileURI3)), true); + assert.equal(isEqual(fileURI3, fileURI4, true), true); + assert.equal(isEqual(fileURI3, fileURI4, false), false); + + + assert.equal(isEqual(fileURI, fileURI3, true), false); + }); + + test('isEqualOrParent', () => { + let fileURI = isWindows ? URI.file('c:\\foo\\bar') : URI.file('/foo/bar'); + let fileURI2 = isWindows ? URI.file('c:\\foo') : URI.file('/foo'); + let fileURI2b = isWindows ? URI.file('C:\\Foo\\') : URI.file('/Foo/'); + assert.equal(isEqualOrParent(fileURI, fileURI, true), true, '1'); + assert.equal(isEqualOrParent(fileURI, fileURI, false), true, '2'); + assert.equal(isEqualOrParent(fileURI, fileURI2, true), true, '3'); + assert.equal(isEqualOrParent(fileURI, fileURI2, false), true, '4'); + assert.equal(isEqualOrParent(fileURI, fileURI2b, true), true, '5'); + assert.equal(isEqualOrParent(fileURI, fileURI2b, false), false, '6'); + + assert.equal(isEqualOrParent(fileURI2, fileURI, false), false, '7'); + assert.equal(isEqualOrParent(fileURI2b, fileURI2, true), true, '8'); + + let fileURI3 = URI.parse('foo://server:453/foo/bar/goo'); + let fileURI4 = URI.parse('foo://server:453/foo/'); + let fileURI5 = URI.parse('foo://server:453/foo'); + assert.equal(isEqualOrParent(fileURI3, fileURI3, true), true, '11'); + assert.equal(isEqualOrParent(fileURI3, fileURI3, false), true, '12'); + assert.equal(isEqualOrParent(fileURI3, fileURI4, true), true, '13'); + assert.equal(isEqualOrParent(fileURI3, fileURI4, false), true, '14'); + assert.equal(isEqualOrParent(fileURI3, fileURI, true), false, '15'); + assert.equal(isEqualOrParent(fileURI5, fileURI5, true), true, '16'); + }); + + function assertMalformedFileUri(path: string, expected: string) { + const newURI = isMalformedFileUri(URI.parse(path)); + assert.equal(newURI && newURI.toString(), expected); + } + + test('isMalformedFileUri', () => { + if (isWindows) { + assertMalformedFileUri('c:/foo/bar', 'file:///c%3A/foo/bar'); + assertMalformedFileUri('c:\\foo\\bar', 'file:///c%3A/foo/bar'); + assertMalformedFileUri('\\\\localhost\\c$\\devel\\test', 'file://localhost/c%24/devel/test'); + } + assertMalformedFileUri('/foo/bar', 'file:///foo/bar'); + + assertMalformedFileUri('file:///foo/bar', void 0); + assertMalformedFileUri('file:///c%3A/foo/bar', void 0); + assertMalformedFileUri('file://localhost/c$/devel/test', void 0); + assertMalformedFileUri('foo://dadie/foo/bar', void 0); + assertMalformedFileUri('foo:///dadie/foo/bar', void 0); + }); }); \ No newline at end of file diff --git a/src/vs/base/test/common/uri.test.ts b/src/vs/base/test/common/uri.test.ts index 618a56a1493..e74f82046d0 100644 --- a/src/vs/base/test/common/uri.test.ts +++ b/src/vs/base/test/common/uri.test.ts @@ -65,8 +65,6 @@ suite('URI', () => { assert.equal(URI.from({ scheme: 'http', authority: 'www.MSFT.com', path: '/my/path' }).toString(), 'http://www.msft.com/my/path'); assert.equal(URI.from({ scheme: 'http', authority: '', path: 'my/path' }).toString(), 'http:/my/path'); assert.equal(URI.from({ scheme: 'http', authority: '', path: '/my/path' }).toString(), 'http:/my/path'); - assert.equal(URI.from({ scheme: '', authority: '', path: 'my/path' }).toString(), 'my/path'); - assert.equal(URI.from({ scheme: '', authority: '', path: '/my/path' }).toString(), '/my/path'); //http://a-test-site.com/#test=true assert.equal(URI.from({ scheme: 'http', authority: 'a-test-site.com', path: '/', query: 'test=true' }).toString(), 'http://a-test-site.com/?test%3Dtrue'); assert.equal(URI.from({ scheme: 'http', authority: 'a-test-site.com', path: '/', query: '', fragment: 'test=true' }).toString(), 'http://a-test-site.com/#test%3Dtrue'); @@ -75,7 +73,7 @@ suite('URI', () => { test('http#toString, encode=FALSE', () => { assert.equal(URI.from({ scheme: 'http', authority: 'a-test-site.com', path: '/', query: 'test=true' }).toString(true), 'http://a-test-site.com/?test=true'); assert.equal(URI.from({ scheme: 'http', authority: 'a-test-site.com', path: '/', query: '', fragment: 'test=true' }).toString(true), 'http://a-test-site.com/#test=true'); - assert.equal(URI.from({}).with({ scheme: 'http', path: '/api/files/test.me', query: 't=1234' }).toString(true), 'http:/api/files/test.me?t=1234'); + assert.equal(URI.from({ scheme: 'http', path: '/api/files/test.me', query: 't=1234' }).toString(true), 'http:/api/files/test.me?t=1234'); var value = URI.parse('file://shares/pröjects/c%23/#l12'); assert.equal(value.authority, 'shares'); @@ -107,12 +105,12 @@ suite('URI', () => { test('with, changes', () => { assert.equal(URI.parse('before:some/file/path').with({ scheme: 'after' }).toString(), 'after:some/file/path'); - assert.equal(URI.from({}).with({ scheme: 'http', path: '/api/files/test.me', query: 't=1234' }).toString(), 'http:/api/files/test.me?t%3D1234'); - assert.equal(URI.from({}).with({ scheme: 'http', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'http:/api/files/test.me?t%3D1234'); - assert.equal(URI.from({}).with({ scheme: 'https', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'https:/api/files/test.me?t%3D1234'); - assert.equal(URI.from({}).with({ scheme: 'HTTP', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'HTTP:/api/files/test.me?t%3D1234'); - assert.equal(URI.from({}).with({ scheme: 'HTTPS', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'HTTPS:/api/files/test.me?t%3D1234'); - assert.equal(URI.from({}).with({ scheme: 'boo', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'boo:/api/files/test.me?t%3D1234'); + assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'http', path: '/api/files/test.me', query: 't=1234' }).toString(), 'http:/api/files/test.me?t%3D1234'); + assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'http', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'http:/api/files/test.me?t%3D1234'); + assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'https', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'https:/api/files/test.me?t%3D1234'); + assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'HTTP', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'HTTP:/api/files/test.me?t%3D1234'); + assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'HTTPS', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'HTTPS:/api/files/test.me?t%3D1234'); + assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'boo', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'boo:/api/files/test.me?t%3D1234'); }); test('with, remove components #8465', () => { @@ -186,34 +184,13 @@ suite('URI', () => { assert.equal(value.query, ''); assert.equal(value.fragment, ''); - value = URI.parse('api/files/test'); - assert.equal(value.scheme, ''); + value = URI.parse('foo:api/files/test'); + assert.equal(value.scheme, 'foo'); assert.equal(value.authority, ''); assert.equal(value.path, 'api/files/test'); assert.equal(value.query, ''); assert.equal(value.fragment, ''); - value = URI.parse('api'); - assert.equal(value.scheme, ''); - assert.equal(value.authority, ''); - assert.equal(value.path, 'api'); - assert.equal(value.query, ''); - assert.equal(value.fragment, ''); - - value = URI.parse('/api/files/test'); - assert.equal(value.scheme, ''); - assert.equal(value.authority, ''); - assert.equal(value.path, '/api/files/test'); - assert.equal(value.query, ''); - assert.equal(value.fragment, ''); - - value = URI.parse('?test'); - assert.equal(value.scheme, ''); - assert.equal(value.authority, ''); - assert.equal(value.path, ''); - assert.equal(value.query, 'test'); - assert.equal(value.fragment, ''); - value = URI.parse('file:?q'); assert.equal(value.scheme, 'file'); assert.equal(value.authority, ''); @@ -221,13 +198,6 @@ suite('URI', () => { assert.equal(value.query, 'q'); assert.equal(value.fragment, ''); - value = URI.parse('#test'); - assert.equal(value.scheme, ''); - assert.equal(value.authority, ''); - assert.equal(value.path, ''); - assert.equal(value.query, ''); - assert.equal(value.fragment, 'test'); - value = URI.parse('file:#d'); assert.equal(value.scheme, 'file'); assert.equal(value.authority, ''); diff --git a/src/vs/base/test/common/utils.ts b/src/vs/base/test/common/utils.ts index f0e948976fa..ececd9b461c 100644 --- a/src/vs/base/test/common/utils.ts +++ b/src/vs/base/test/common/utils.ts @@ -5,10 +5,9 @@ 'use strict'; -import * as errors from 'vs/base/common/errors'; import * as paths from 'vs/base/common/paths'; import URI from 'vs/base/common/uri'; -import { PPromise, TProgressCallback, TPromise, TValueCallback } from 'vs/base/common/winjs.base'; +import { TPromise, TValueCallback } from 'vs/base/common/winjs.base'; export class DeferredTPromise extends TPromise { @@ -17,11 +16,11 @@ export class DeferredTPromise extends TPromise { private completeCallback: TValueCallback; private errorCallback: (err: any) => void; - constructor() { + constructor(oncancel?: any) { let captured: any; super((c, e) => { captured = { c, e }; - }, () => this.oncancel()); + }, oncancel ? oncancel : () => this.oncancel); this.canceled = false; this.completeCallback = captured.c; this.errorCallback = captured.e; @@ -40,39 +39,6 @@ export class DeferredTPromise extends TPromise { } } -export class DeferredPPromise extends PPromise { - - private completeCallback: TValueCallback; - private errorCallback: (err: any) => void; - private progressCallback: TProgressCallback

; - - constructor(init: (complete: TValueCallback, error: (err: any) => void, progress: TProgressCallback

) => void = (c, e, p) => { }, oncancel?: any) { - let captured: any; - super((c, e, p) => { - captured = { c, e, p }; - }, oncancel ? oncancel : () => this.oncancel); - this.completeCallback = captured.c; - this.errorCallback = captured.e; - this.progressCallback = captured.p; - } - - private oncancel(): void { - this.errorCallback(errors.canceled()); - } - - public complete(c: C) { - this.completeCallback(c); - } - - public progress(p: P) { - this.progressCallback(p); - } - - public error(e: any) { - this.errorCallback(e); - } -} - export function toResource(this: any, path: string) { return URI.file(paths.join('C:\\', Buffer.from(this.test.fullTitle()).toString('base64'), path)); } @@ -88,3 +54,7 @@ export function testRepeat(n: number, description: string, callback: (this: any, test(`${description} (iteration ${i})`, callback); } } + +export function testRepeatOnly(n: number, description: string, callback: (this: any, done: MochaDone) => any): void { + suite.only('repeat', () => testRepeat(n, description, callback)); +} diff --git a/src/vs/base/test/node/encoding/encoding.test.ts b/src/vs/base/test/node/encoding/encoding.test.ts index 252ee281e37..99d05d437a0 100644 --- a/src/vs/base/test/node/encoding/encoding.test.ts +++ b/src/vs/base/test/node/encoding/encoding.test.ts @@ -10,10 +10,11 @@ import * as fs from 'fs'; import * as encoding from 'vs/base/node/encoding'; import { readExactlyByFile } from 'vs/base/node/stream'; import { Readable } from 'stream'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; suite('Encoding', () => { test('detectBOM UTF-8', () => { - const file = require.toUrl('./fixtures/some_utf8.css'); + const file = getPathFromAmdModule(require, './fixtures/some_utf8.css'); return encoding.detectEncodingByBOM(file).then((encoding: string) => { assert.equal(encoding, 'utf8'); @@ -21,7 +22,7 @@ suite('Encoding', () => { }); test('detectBOM UTF-16 LE', () => { - const file = require.toUrl('./fixtures/some_utf16le.css'); + const file = getPathFromAmdModule(require, './fixtures/some_utf16le.css'); return encoding.detectEncodingByBOM(file).then((encoding: string) => { assert.equal(encoding, 'utf16le'); @@ -29,7 +30,7 @@ suite('Encoding', () => { }); test('detectBOM UTF-16 BE', () => { - const file = require.toUrl('./fixtures/some_utf16be.css'); + const file = getPathFromAmdModule(require, './fixtures/some_utf16be.css'); return encoding.detectEncodingByBOM(file).then((encoding: string) => { assert.equal(encoding, 'utf16be'); @@ -37,7 +38,7 @@ suite('Encoding', () => { }); test('detectBOM ANSI', function () { - const file = require.toUrl('./fixtures/some_ansi.css'); + const file = getPathFromAmdModule(require, './fixtures/some_ansi.css'); return encoding.detectEncodingByBOM(file).then((encoding: string) => { assert.equal(encoding, null); @@ -45,7 +46,7 @@ suite('Encoding', () => { }); test('detectBOM ANSI', function () { - const file = require.toUrl('./fixtures/empty.txt'); + const file = getPathFromAmdModule(require, './fixtures/empty.txt'); return encoding.detectEncodingByBOM(file).then((encoding: string) => { assert.equal(encoding, null); @@ -68,7 +69,7 @@ suite('Encoding', () => { }); test('detectEncodingFromBuffer (JSON saved as PNG)', function () { - const file = require.toUrl('./fixtures/some.json.png'); + const file = getPathFromAmdModule(require, './fixtures/some.json.png'); return readExactlyByFile(file, 512).then(buffer => { const mimes = encoding.detectEncodingFromBuffer(buffer); @@ -77,7 +78,7 @@ suite('Encoding', () => { }); test('detectEncodingFromBuffer (PNG saved as TXT)', function () { - const file = require.toUrl('./fixtures/some.png.txt'); + const file = getPathFromAmdModule(require, './fixtures/some.png.txt'); return readExactlyByFile(file, 512).then(buffer => { const mimes = encoding.detectEncodingFromBuffer(buffer); assert.equal(mimes.seemsBinary, true); @@ -85,7 +86,7 @@ suite('Encoding', () => { }); test('detectEncodingFromBuffer (XML saved as PNG)', function () { - const file = require.toUrl('./fixtures/some.xml.png'); + const file = getPathFromAmdModule(require, './fixtures/some.xml.png'); return readExactlyByFile(file, 512).then(buffer => { const mimes = encoding.detectEncodingFromBuffer(buffer); assert.equal(mimes.seemsBinary, false); @@ -93,7 +94,7 @@ suite('Encoding', () => { }); test('detectEncodingFromBuffer (QWOFF saved as TXT)', function () { - const file = require.toUrl('./fixtures/some.qwoff.txt'); + const file = getPathFromAmdModule(require, './fixtures/some.qwoff.txt'); return readExactlyByFile(file, 512).then(buffer => { const mimes = encoding.detectEncodingFromBuffer(buffer); assert.equal(mimes.seemsBinary, true); @@ -101,7 +102,7 @@ suite('Encoding', () => { }); test('detectEncodingFromBuffer (CSS saved as QWOFF)', function () { - const file = require.toUrl('./fixtures/some.css.qwoff'); + const file = getPathFromAmdModule(require, './fixtures/some.css.qwoff'); return readExactlyByFile(file, 512).then(buffer => { const mimes = encoding.detectEncodingFromBuffer(buffer); assert.equal(mimes.seemsBinary, false); @@ -109,7 +110,7 @@ suite('Encoding', () => { }); test('detectEncodingFromBuffer (PDF)', function () { - const file = require.toUrl('./fixtures/some.pdf'); + const file = getPathFromAmdModule(require, './fixtures/some.pdf'); return readExactlyByFile(file, 512).then(buffer => { const mimes = encoding.detectEncodingFromBuffer(buffer); assert.equal(mimes.seemsBinary, true); @@ -117,7 +118,7 @@ suite('Encoding', () => { }); test('detectEncodingFromBuffer (guess UTF-16 LE from content without BOM)', function () { - const file = require.toUrl('./fixtures/utf16_le_nobom.txt'); + const file = getPathFromAmdModule(require, './fixtures/utf16_le_nobom.txt'); return readExactlyByFile(file, 512).then(buffer => { const mimes = encoding.detectEncodingFromBuffer(buffer); assert.equal(mimes.encoding, encoding.UTF16le); @@ -126,7 +127,7 @@ suite('Encoding', () => { }); test('detectEncodingFromBuffer (guess UTF-16 BE from content without BOM)', function () { - const file = require.toUrl('./fixtures/utf16_be_nobom.txt'); + const file = getPathFromAmdModule(require, './fixtures/utf16_be_nobom.txt'); return readExactlyByFile(file, 512).then(buffer => { const mimes = encoding.detectEncodingFromBuffer(buffer); assert.equal(mimes.encoding, encoding.UTF16be); @@ -135,7 +136,7 @@ suite('Encoding', () => { }); test('autoGuessEncoding (ShiftJIS)', function () { - const file = require.toUrl('./fixtures/some.shiftjis.txt'); + const file = getPathFromAmdModule(require, './fixtures/some.shiftjis.txt'); return readExactlyByFile(file, 512 * 8).then(buffer => { return encoding.detectEncodingFromBuffer(buffer, true).then(mimes => { assert.equal(mimes.encoding, 'shiftjis'); @@ -144,7 +145,7 @@ suite('Encoding', () => { }); test('autoGuessEncoding (CP1252)', function () { - const file = require.toUrl('./fixtures/some.cp1252.txt'); + const file = getPathFromAmdModule(require, './fixtures/some.cp1252.txt'); return readExactlyByFile(file, 512 * 8).then(buffer => { return encoding.detectEncodingFromBuffer(buffer, true).then(mimes => { assert.equal(mimes.encoding, 'windows1252'); @@ -238,7 +239,7 @@ suite('Encoding', () => { test('toDecodeStream - encoding, utf16be', async function () { - let path = require.toUrl('./fixtures/some_utf16be.css'); + let path = getPathFromAmdModule(require, './fixtures/some_utf16be.css'); let source = fs.createReadStream(path); let { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 64 }); @@ -254,7 +255,7 @@ suite('Encoding', () => { test('toDecodeStream - empty file', async function () { - let path = require.toUrl('./fixtures/empty.txt'); + let path = getPathFromAmdModule(require, './fixtures/empty.txt'); let source = fs.createReadStream(path); let { detected, stream } = await encoding.toDecodeStream(source, {}); diff --git a/src/vs/base/test/node/extfs/extfs.test.ts b/src/vs/base/test/node/extfs/extfs.test.ts index d5a363f2b5c..d1740085fd0 100644 --- a/src/vs/base/test/node/extfs/extfs.test.ts +++ b/src/vs/base/test/node/extfs/extfs.test.ts @@ -14,6 +14,7 @@ import { canNormalize } from 'vs/base/common/normalization'; import { isLinux, isWindows } from 'vs/base/common/platform'; import * as uuid from 'vs/base/common/uuid'; import * as extfs from 'vs/base/node/extfs'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; @@ -169,7 +170,7 @@ suite('Extfs', () => { test('copy, move and delete', function (done) { const id = uuid.generateUuid(); const id2 = uuid.generateUuid(); - const sourceDir = require.toUrl('./fixtures'); + const sourceDir = getPathFromAmdModule(require, './fixtures'); const parentDir = path.join(os.tmpdir(), 'vsctests', 'extfs'); const targetDir = path.join(parentDir, id); const targetDir2 = path.join(parentDir, id2); @@ -320,7 +321,7 @@ suite('Extfs', () => { test('writeFileAndFlush (file stream)', function (done) { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const sourceFile = require.toUrl('./fixtures/index.html'); + const sourceFile = getPathFromAmdModule(require, './fixtures/index.html'); const newDir = path.join(parentDir, 'extfs', id); const testFile = path.join(newDir, 'flushed.txt'); @@ -453,7 +454,7 @@ suite('Extfs', () => { test('writeFileAndFlush (file stream, error handling)', function (done) { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); - const sourceFile = require.toUrl('./fixtures/index.html'); + const sourceFile = getPathFromAmdModule(require, './fixtures/index.html'); const newDir = path.join(parentDir, 'extfs', id); const testFile = path.join(newDir, 'flushed.txt'); diff --git a/src/vs/base/test/node/processes/processes.test.ts b/src/vs/base/test/node/processes/processes.test.ts index 332bdcdbff7..ae4f062bd1c 100644 --- a/src/vs/base/test/node/processes/processes.test.ts +++ b/src/vs/base/test/node/processes/processes.test.ts @@ -9,8 +9,8 @@ import * as assert from 'assert'; import * as cp from 'child_process'; import * as objects from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; -import URI from 'vs/base/common/uri'; import * as processes from 'vs/base/node/processes'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; function fork(id: string): cp.ChildProcess { const opts: any = { @@ -21,7 +21,7 @@ function fork(id: string): cp.ChildProcess { }) }; - return cp.fork(URI.parse(require.toUrl('bootstrap')).fsPath, ['--type=processTests'], opts); + return cp.fork(getPathFromAmdModule(require, 'bootstrap'), ['--type=processTests'], opts); } suite('Processes', () => { diff --git a/src/vs/base/test/node/stream/stream.test.ts b/src/vs/base/test/node/stream/stream.test.ts index c2c5f1b2f9a..d52ed4c43b1 100644 --- a/src/vs/base/test/node/stream/stream.test.ts +++ b/src/vs/base/test/node/stream/stream.test.ts @@ -8,10 +8,11 @@ import * as assert from 'assert'; import * as stream from 'vs/base/node/stream'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; suite('Stream', () => { test('readExactlyByFile - ANSI', function () { - const file = require.toUrl('./fixtures/file.css'); + const file = getPathFromAmdModule(require, './fixtures/file.css'); return stream.readExactlyByFile(file, 10).then(({ buffer, bytesRead }) => { assert.equal(bytesRead, 10); @@ -20,7 +21,7 @@ suite('Stream', () => { }); test('readExactlyByFile - empty', function () { - const file = require.toUrl('./fixtures/empty.txt'); + const file = getPathFromAmdModule(require, './fixtures/empty.txt'); return stream.readExactlyByFile(file, 10).then(({ bytesRead }) => { assert.equal(bytesRead, 0); @@ -28,7 +29,7 @@ suite('Stream', () => { }); test('readToMatchingString - ANSI', function () { - const file = require.toUrl('./fixtures/file.css'); + const file = getPathFromAmdModule(require, './fixtures/file.css'); return stream.readToMatchingString(file, '\n', 10, 100).then((result: string) => { // \r may be present on Windows @@ -37,10 +38,10 @@ suite('Stream', () => { }); test('readToMatchingString - empty', function () { - const file = require.toUrl('./fixtures/empty.txt'); + const file = getPathFromAmdModule(require, './fixtures/empty.txt'); return stream.readToMatchingString(file, '\n', 10, 100).then((result: string) => { assert.equal(result, null); }); }); -}); \ No newline at end of file +}); diff --git a/src/vs/base/test/node/uri.test.perf.ts b/src/vs/base/test/node/uri.test.perf.ts index 3689d197b4d..492a676322a 100644 --- a/src/vs/base/test/node/uri.test.perf.ts +++ b/src/vs/base/test/node/uri.test.perf.ts @@ -7,13 +7,14 @@ import * as assert from 'assert'; import URI from 'vs/base/common/uri'; import { readFileSync } from 'fs'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; suite('URI - perf', function () { let manyFileUris: URI[]; setup(function () { manyFileUris = []; - let data = readFileSync(URI.parse(require.toUrl('./uri.test.data.txt')).fsPath).toString(); + let data = readFileSync(getPathFromAmdModule(require, './uri.test.data.txt')).toString(); let lines = data.split('\n'); for (let line of lines) { manyFileUris.push(URI.file(line)); diff --git a/src/vs/base/test/node/zip/zip.test.ts b/src/vs/base/test/node/zip/zip.test.ts index f53894f5ebb..21adac26149 100644 --- a/src/vs/base/test/node/zip/zip.test.ts +++ b/src/vs/base/test/node/zip/zip.test.ts @@ -8,13 +8,13 @@ import * as assert from 'assert'; import * as path from 'path'; import * as os from 'os'; -import URI from 'vs/base/common/uri'; import { extract } from 'vs/base/node/zip'; import { generateUuid } from 'vs/base/common/uuid'; import { rimraf, exists } from 'vs/base/node/pfs'; import { NullLogService } from 'vs/platform/log/common/log'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; -const fixtures = URI.parse(require.toUrl('./fixtures')).fsPath; +const fixtures = getPathFromAmdModule(require, './fixtures'); suite('Zip', () => { @@ -27,4 +27,4 @@ suite('Zip', () => { .then(exists => assert(exists)) .then(() => rimraf(target)); }); -}); \ No newline at end of file +}); diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index cc305121e2c..2e25cb33248 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -86,7 +86,7 @@ export class IssueReporter extends Disposable { vscodeVersion: `${pkg.name} ${pkg.version} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'})`, os: `${os.type()} ${os.arch()} ${os.release()}` }, - extensionsDisabled: this.environmentService.disableExtensions, + extensionsDisabled: !!this.environmentService.disableExtensions, }); this.previewButton = new Button(document.getElementById('issue-reporter')); @@ -413,6 +413,16 @@ export class IssueReporter extends Disposable { if (cmdOrCtrlKey && e.keyCode === 189) { this.applyZoom(webFrame.getZoomLevel() - 1); } + + // With latest electron upgrade, cmd+a is no longer propagating correctly for inputs in this window on mac + // Manually perform the selection + if (platform.isMacintosh) { + if (cmdOrCtrlKey && e.keyCode === 65 && e.target) { + if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) { + (e.target).select(); + } + } + } }; } diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index f3ef70e0774..a52ce64b90d 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -63,7 +63,7 @@ import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver'; import { IMenubarService } from 'vs/platform/menubar/common/menubar'; import { MenubarService } from 'vs/platform/menubar/electron-main/menubarService'; import { MenubarChannel } from 'vs/platform/menubar/common/menubarIpc'; -// TODO@sbatten: Remove after conversion to new dynamic menubar +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; import { CodeMenu } from 'vs/code/electron-main/menus'; export class CodeApplication { @@ -87,7 +87,8 @@ export class CodeApplication { @ILifecycleService private lifecycleService: ILifecycleService, @IConfigurationService private configurationService: ConfigurationService, @IStateService private stateService: IStateService, - @IHistoryMainService private historyMainService: IHistoryMainService + @IHistoryMainService private historyMainService: IHistoryMainService, + @IUriDisplayService private uriDisplayService: IUriDisplayService ) { this.toDispose = [mainIpcServer, configurationService]; @@ -154,14 +155,14 @@ export class CodeApplication { }); }); - let macOpenFiles: string[] = []; + let macOpenFileURIs: URI[] = []; let runningTimeout: number = null; app.on('open-file', (event: Event, path: string) => { this.logService.trace('App#open-file: ', path); event.preventDefault(); // Keep in array because more might come! - macOpenFiles.push(path); + macOpenFileURIs.push(URI.file(path)); // Clear previous handler if any if (runningTimeout !== null) { @@ -175,10 +176,10 @@ export class CodeApplication { this.windowsMainService.open({ context: OpenContext.DOCK /* can also be opening from finder while app is running */, cli: this.environmentService.args, - pathsToOpen: macOpenFiles, + urisToOpen: macOpenFileURIs, preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */ }); - macOpenFiles = []; + macOpenFileURIs = []; runningTimeout = null; } }, 100); @@ -222,6 +223,10 @@ export class CodeApplication { } }); + ipc.on('vscode:uriDisplayRegisterFormater', (event: any, { scheme, formater }) => { + this.uriDisplayService.registerFormater(scheme, formater); + }); + // Keyboard layout changes KeyboardLayoutMonitor.INSTANCE.onDidChangeKeyboardLayout(() => { if (this.windowsMainService) { @@ -462,10 +467,10 @@ export class CodeApplication { // Open our first window const macOpenFiles = (global).macOpenFiles as string[]; const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP; - if (args['new-window'] && args._.length === 0) { + if (args['new-window'] && args._.length === 0 && (args['folder-uri'] || []).length === 0) { this.windowsMainService.open({ context, cli: args, forceNewWindow: true, forceEmpty: true, initialStartup: true }); // new window if "-n" was used without paths - } else if (macOpenFiles && macOpenFiles.length && (!args._ || !args._.length)) { - this.windowsMainService.open({ context: OpenContext.DOCK, cli: args, pathsToOpen: macOpenFiles, initialStartup: true }); // mac: open-file event received on startup + } else if (macOpenFiles && macOpenFiles.length && (!args._ || !args._.length || !args['folder-uri'] || !args['folder-uri'].length)) { + this.windowsMainService.open({ context: OpenContext.DOCK, cli: args, urisToOpen: macOpenFiles.map(file => URI.file(file)), initialStartup: true }); // mac: open-file event received on startup } else { this.windowsMainService.open({ context, cli: args, forceNewWindow: args['new-window'] || (!args._.length && args['unity-launch']), diffMode: args.diff, initialStartup: true }); // default: read paths from cli } @@ -511,7 +516,7 @@ export class CodeApplication { } } - // TODO@sbatten: Remove when menu is converted + // TODO@sbatten: Remove when switching back to dynamic menu // Install Menu const instantiationService = accessor.get(IInstantiationService); const configurationService = accessor.get(IConfigurationService); diff --git a/src/vs/code/electron-main/diagnostics.ts b/src/vs/code/electron-main/diagnostics.ts index 3db04d06bdc..3bb9e182899 100644 --- a/src/vs/code/electron-main/diagnostics.ts +++ b/src/vs/code/electron-main/diagnostics.ts @@ -16,6 +16,7 @@ import { repeat, pad } from 'vs/base/common/strings'; import { isWindows } from 'vs/base/common/platform'; import { app } from 'electron'; import { basename } from 'path'; +import URI from 'vs/base/common/uri'; export interface VersionInfo { vscodeVersion: string; @@ -50,29 +51,35 @@ export function getPerformanceInfo(info: IMainProcessInfo): Promise window.folders && window.folders.length > 0)) { + if (info.windows.some(window => window.folderURIs && window.folderURIs.length > 0)) { info.windows.forEach(window => { - if (window.folders.length === 0) { + if (window.folderURIs.length === 0) { return; } workspaceInfoMessages.push(`| Window (${window.title})`); - window.folders.forEach(folder => { - workspaceStatPromises.push(collectWorkspaceStats(folder, ['node_modules', '.git']).then(async stats => { + window.folderURIs.forEach(uriComponents => { + const folderUri = URI.revive(uriComponents); + if (folderUri.scheme === 'file') { + const folder = folderUri.fsPath; + workspaceStatPromises.push(collectWorkspaceStats(folder, ['node_modules', '.git']).then(async stats => { - let countMessage = `${stats.fileCount} files`; - if (stats.maxFilesReached) { - countMessage = `more than ${countMessage}`; - } - workspaceInfoMessages.push(`| Folder (${basename(folder)}): ${countMessage}`); - workspaceInfoMessages.push(formatWorkspaceStats(stats)); + let countMessage = `${stats.fileCount} files`; + if (stats.maxFilesReached) { + countMessage = `more than ${countMessage}`; + } + workspaceInfoMessages.push(`| Folder (${basename(folder)}): ${countMessage}`); + workspaceInfoMessages.push(formatWorkspaceStats(stats)); - const launchConfigs = await collectLaunchConfigs(folder); - if (launchConfigs.length > 0) { - workspaceInfoMessages.push(formatLaunchConfigs(launchConfigs)); - } - })); + const launchConfigs = await collectLaunchConfigs(folder); + if (launchConfigs.length > 0) { + workspaceInfoMessages.push(formatLaunchConfigs(launchConfigs)); + } + })); + } else { + workspaceInfoMessages.push(`| Folder (${folderUri.toString()}): RPerformance stats not available.`); + } }); }); } @@ -129,33 +136,39 @@ export function printDiagnostics(info: IMainProcessInfo): Promise { // Workspace Stats const workspaceStatPromises = []; - if (info.windows.some(window => window.folders && window.folders.length > 0)) { + if (info.windows.some(window => window.folderURIs && window.folderURIs.length > 0)) { console.log(''); console.log('Workspace Stats: '); info.windows.forEach(window => { - if (window.folders.length === 0) { + if (window.folderURIs.length === 0) { return; } console.log(`| Window (${window.title})`); - window.folders.forEach(folder => { - workspaceStatPromises.push(collectWorkspaceStats(folder, ['node_modules', '.git']).then(async stats => { - let countMessage = `${stats.fileCount} files`; - if (stats.maxFilesReached) { - countMessage = `more than ${countMessage}`; - } - console.log(`| Folder (${basename(folder)}): ${countMessage}`); - console.log(formatWorkspaceStats(stats)); - - await collectLaunchConfigs(folder).then(launchConfigs => { - if (launchConfigs.length > 0) { - console.log(formatLaunchConfigs(launchConfigs)); + window.folderURIs.forEach(uriComponents => { + const folderUri = URI.revive(uriComponents); + if (folderUri.scheme === 'file') { + const folder = folderUri.fsPath; + workspaceStatPromises.push(collectWorkspaceStats(folder, ['node_modules', '.git']).then(async stats => { + let countMessage = `${stats.fileCount} files`; + if (stats.maxFilesReached) { + countMessage = `more than ${countMessage}`; } - }); - }).catch(error => { - console.log(`| Error: Unable to collect workpsace stats for folder ${folder} (${error.toString()})`); - })); + console.log(`| Folder (${basename(folder)}): ${countMessage}`); + console.log(formatWorkspaceStats(stats)); + + await collectLaunchConfigs(folder).then(launchConfigs => { + if (launchConfigs.length > 0) { + console.log(formatLaunchConfigs(launchConfigs)); + } + }); + }).catch(error => { + console.log(`| Error: Unable to collect workspace stats for folder ${folder} (${error.toString()})`); + })); + } else { + console.log(`| Folder (${folderUri.toString()}): Workspace stats not available.`); + } }); }); } diff --git a/src/vs/code/electron-main/launch.ts b/src/vs/code/electron-main/launch.ts index d1781a1a89b..2cad34a8d84 100644 --- a/src/vs/code/electron-main/launch.ts +++ b/src/vs/code/electron-main/launch.ts @@ -16,9 +16,8 @@ import { OpenContext, IWindowSettings } from 'vs/platform/windows/common/windows import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; import { whenDeleted } from 'vs/base/node/pfs'; import { IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces'; -import { Schemas } from 'vs/base/common/network'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import URI from 'vs/base/common/uri'; +import URI, { UriComponents } from 'vs/base/common/uri'; import { BrowserWindow } from 'electron'; import { Event } from 'vs/base/common/event'; @@ -33,7 +32,7 @@ export interface IStartArguments { export interface IWindowInfo { pid: number; title: string; - folders: string[]; + folderURIs: UriComponents[]; } export interface IMainProcessInfo { @@ -179,7 +178,7 @@ export class LaunchService implements ILaunchService { } // Start without file/folder arguments - else if (args._.length === 0) { + else if (args._.length === 0 && (args['folder-uri'] || []).length === 0) { let openNewWindow = false; // Force new window @@ -275,27 +274,25 @@ export class LaunchService implements ILaunchService { } private codeWindowToInfo(window: ICodeWindow): IWindowInfo { - const folders: string[] = []; + const folderURIs: URI[] = []; - if (window.openedFolderPath) { - folders.push(window.openedFolderPath); + if (window.openedFolderUri) { + folderURIs.push(window.openedFolderUri); } else if (window.openedWorkspace) { const rootFolders = this.workspacesMainService.resolveWorkspaceSync(window.openedWorkspace.configPath).folders; rootFolders.forEach(root => { - if (root.uri.scheme === Schemas.file) { // todo@remote signal remote folders? - folders.push(root.uri.fsPath); - } + folderURIs.push(root.uri); }); } - return this.browserWindowToInfo(window.win, folders); + return this.browserWindowToInfo(window.win, folderURIs); } - private browserWindowToInfo(win: BrowserWindow, folders: string[] = []): IWindowInfo { + private browserWindowToInfo(win: BrowserWindow, folderURIs: URI[] = []): IWindowInfo { return { pid: win.webContents.getOSProcessId(), title: win.getTitle(), - folders + folderURIs } as IWindowInfo; } } diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 4349d32b4f8..060c2459c5f 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -35,7 +35,7 @@ import { IRequestService } from 'vs/platform/request/node/request'; import { RequestService } from 'vs/platform/request/electron-main/requestService'; import { IURLService } from 'vs/platform/url/common/url'; import { URLService } from 'vs/platform/url/common/urlService'; -import * as fs from 'original-fs'; +import * as fs from 'fs'; import { CodeApplication } from 'vs/code/electron-main/app'; import { HistoryMainService } from 'vs/platform/history/electron-main/historyMainService'; import { IHistoryMainService } from 'vs/platform/history/common/history'; @@ -50,6 +50,7 @@ import { uploadLogs } from 'vs/code/electron-main/logUploader'; import { setUnexpectedErrorHandler } from 'vs/base/common/errors'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { CommandLineDialogService } from 'vs/platform/dialogs/node/dialogService'; +import { IUriDisplayService, UriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; function createServices(args: ParsedArgs, bufferLogService: BufferLogService): IInstantiationService { const services = new ServiceCollection(); @@ -57,6 +58,7 @@ function createServices(args: ParsedArgs, bufferLogService: BufferLogService): I const environmentService = new EnvironmentService(args, process.execPath); const consoleLogService = new ConsoleLogMainService(getLogLevel(environmentService)); const logService = new MultiplexLogService([consoleLogService, bufferLogService]); + const uriDisplayService = new UriDisplayService(environmentService, undefined); process.once('exit', () => logService.dispose()); @@ -64,6 +66,7 @@ function createServices(args: ParsedArgs, bufferLogService: BufferLogService): I setTimeout(() => cleanupOlderLogs(environmentService).then(null, err => console.error(err)), 10000); services.set(IEnvironmentService, environmentService); + services.set(IUriDisplayService, uriDisplayService); services.set(ILogService, logService); services.set(IWorkspacesMainService, new SyncDescriptor(WorkspacesMainService)); services.set(IHistoryMainService, new SyncDescriptor(HistoryMainService)); diff --git a/src/vs/code/electron-main/menubar.ts b/src/vs/code/electron-main/menubar.ts index 5870ea0ad31..1e9c908a4e3 100644 --- a/src/vs/code/electron-main/menubar.ts +++ b/src/vs/code/electron-main/menubar.ts @@ -16,46 +16,30 @@ import { IUpdateService, StateType } from 'vs/platform/update/common/update'; import product from 'vs/platform/node/product'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { mnemonicMenuLabel as baseMnemonicLabel, unmnemonicLabel, getPathLabel } from 'vs/base/common/labels'; -import { KeybindingsResolver } from 'vs/code/electron-main/keyboard'; +import { mnemonicMenuLabel as baseMnemonicLabel, unmnemonicLabel } from 'vs/base/common/labels'; import { IWindowsMainService, IWindowsCountChangedEvent } from 'vs/platform/windows/electron-main/windows'; import { IHistoryMainService } from 'vs/platform/history/common/history'; -import { IWorkspaceIdentifier, getWorkspaceLabel, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; -import { IMenubarData, IMenubarMenuItemAction, IMenubarMenuItemSeparator } from 'vs/platform/menubar/common/menubar'; - -// interface IExtensionViewlet { -// id: string; -// label: string; -// } +import { IWorkspaceIdentifier, getWorkspaceLabel, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IMenubarData, IMenubarKeybinding, MenubarMenuItem, isMenubarMenuItemSeparator, isMenubarMenuItemSubmenu, isMenubarMenuItemAction } from 'vs/platform/menubar/common/menubar'; +import URI from 'vs/base/common/uri'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; const telemetryFrom = 'menu'; export class Menubar { private static readonly MAX_MENU_RECENT_ENTRIES = 10; - - // private keys = [ - // 'files.autoSave', - // 'editor.multiCursorModifier', - // 'workbench.sideBar.location', - // 'workbench.statusBar.visible', - // 'workbench.activityBar.visible', - // 'window.enableMenuBarMnemonics', - // 'window.nativeTabs' - // ]; - private isQuitting: boolean; private appMenuInstalled: boolean; + private closedLastWindow: boolean; private menuUpdater: RunOnceScheduler; - private keybindingsResolver: KeybindingsResolver; - - // private extensionViewlets: IExtensionViewlet[]; - private nativeTabMenuItems: Electron.MenuItem[]; - private menubarMenus: IMenubarData = {}; + private menubarMenus: IMenubarData; + + private keybindings: { [commandId: string]: IMenubarKeybinding }; constructor( @IUpdateService private updateService: IUpdateService, @@ -64,13 +48,14 @@ export class Menubar { @IWindowsMainService private windowsMainService: IWindowsMainService, @IEnvironmentService private environmentService: IEnvironmentService, @ITelemetryService private telemetryService: ITelemetryService, - @IHistoryMainService private historyMainService: IHistoryMainService + @IHistoryMainService private historyMainService: IHistoryMainService, + @IUriDisplayService private uriDisplayService: IUriDisplayService ) { - // this.extensionViewlets = []; - // this.nativeTabMenuItems = []; - this.menuUpdater = new RunOnceScheduler(() => this.doUpdateMenu(), 0); - this.keybindingsResolver = instantiationService.createInstance(KeybindingsResolver); + + this.keybindings = Object.create(null); + + this.closedLastWindow = false; this.install(); @@ -85,7 +70,7 @@ export class Menubar { }); // // Listen to some events from window service to update menu - // this.historyMainService.onRecentlyOpenedChange(() => this.updateMenu()); + this.historyMainService.onRecentlyOpenedChange(() => this.scheduleUpdateMenu()); this.windowsMainService.onWindowsCountChanged(e => this.onWindowsCountChanged(e)); // this.windowsMainService.onActiveWindowChanged(() => this.updateWorkspaceMenuItems()); // this.windowsMainService.onWindowReady(() => this.updateWorkspaceMenuItems()); @@ -111,9 +96,6 @@ export class Menubar { // Listen to update service // this.updateService.onStateChange(() => this.updateMenu()); - - // Listen to keybindings change - this.keybindingsResolver.onKeybindingsChanged(() => this.scheduleUpdateMenu()); } private get currentEnableMenuBarMnemonics(): boolean { @@ -133,8 +115,14 @@ export class Menubar { return enableNativeTabs; } - updateMenu(menus: IMenubarData, windowId: number) { + updateMenu(menus: IMenubarData, windowId: number, additionalKeybindings?: Array) { this.menubarMenus = menus; + if (additionalKeybindings) { + additionalKeybindings.forEach(keybinding => { + this.keybindings[keybinding.id] = keybinding; + }); + } + this.scheduleUpdateMenu(); } @@ -164,9 +152,9 @@ export class Menubar { return; } - // Update menu if window count goes from N > 0 or 0 > N to update menu item enablement if ((e.oldCount === 0 && e.newCount > 0) || (e.oldCount > 0 && e.newCount === 0)) { + this.closedLastWindow = e.newCount === 0; this.scheduleUpdateMenu(); } @@ -228,19 +216,6 @@ export class Menubar { menubar.append(editMenuItem); } - // Recent - const recentMenu = new Menu(); - const recentMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miRecent', comment: ['&& denotes a mnemonic'] }, "&&Recent")), submenu: recentMenu, enabled: recentMenu.items.length > 0 }); - if (this.shouldDrawMenu('Recent')) { - if (this.shouldFallback('Recent')) { - this.setFallbackMenuById(recentMenu, 'Recent'); - } else { - this.setMenuById(recentMenu, 'Recent'); - } - - menubar.append(recentMenuItem); - } - // Selection const selectionMenu = new Menu(); const selectionMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mSelection', comment: ['&& denotes a mnemonic'] }, "&&Selection")), submenu: selectionMenu }); @@ -277,6 +252,15 @@ export class Menubar { menubar.append(gotoMenuItem); } + // Terminal + const terminalMenu = new Menu(); + const terminalMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mTerminal', comment: ['&& denotes a mnemonic'] }, "Ter&&minal")), submenu: terminalMenu }); + + if (this.shouldDrawMenu('Terminal')) { + this.setMenuById(terminalMenu, 'Terminal'); + menubar.append(terminalMenuItem); + } + // Debug const debugMenu = new Menu(); const debugMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mDebug', comment: ['&& denotes a mnemonic'] }, "&&Debug")), submenu: debugMenu }); @@ -290,14 +274,14 @@ export class Menubar { const taskMenu = new Menu(); const taskMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mTask', comment: ['&& denotes a mnemonic'] }, "&&Tasks")), submenu: taskMenu }); - if (this.shouldDrawMenu('Task')) { - this.setMenuById(taskMenu, 'Task'); + if (this.shouldDrawMenu('Tasks')) { + this.setMenuById(taskMenu, 'Tasks'); menubar.append(taskMenuItem); } // Mac: Window let macWindowMenuItem: Electron.MenuItem; - if (isMacintosh) { + if (this.shouldDrawMenu('Window')) { const windowMenu = new Menu(); macWindowMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize('mWindow', "Window")), submenu: windowMenu, role: 'window' }); this.setMacWindowMenu(windowMenu); @@ -308,16 +292,18 @@ export class Menubar { } // Preferences - const preferencesMenu = new Menu(); - const preferencesMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences")), submenu: preferencesMenu }); + if (!isMacintosh) { + const preferencesMenu = new Menu(); + const preferencesMenuItem = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'mPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences")), submenu: preferencesMenu }); - if (this.shouldDrawMenu('Preferences')) { - if (this.shouldFallback('Preferences')) { - this.setFallbackMenuById(preferencesMenu, 'Preferences'); - } else { - this.setMenuById(preferencesMenu, 'Preferences'); + if (this.shouldDrawMenu('Preferences')) { + if (this.shouldFallback('Preferences')) { + this.setFallbackMenuById(preferencesMenu, 'Preferences'); + } else { + this.setMenuById(preferencesMenu, 'Preferences'); + } + menubar.append(preferencesMenuItem); } - menubar.append(preferencesMenuItem); } // Help @@ -333,12 +319,24 @@ export class Menubar { menubar.append(helpMenuItem); } - Menu.setApplicationMenu(menubar); + if (menubar.items && menubar.items.length > 0) { + Menu.setApplicationMenu(menubar); + } else { + Menu.setApplicationMenu(null); + } } private setMacApplicationMenu(macApplicationMenu: Electron.Menu): void { const about = new MenuItem({ label: nls.localize('mAbout', "About {0}", product.nameLong), role: 'about' }); const checkForUpdates = this.getUpdateMenuItems(); + + let preferences; + if (this.shouldDrawMenu('Preferences')) { + const preferencesMenu = new Menu(); + this.setMenuById(preferencesMenu, 'Preferences'); + preferences = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences")), submenu: preferencesMenu }); + } + const servicesMenu = new Menu(); const services = new MenuItem({ label: nls.localize('mServices', "Services"), role: 'services', submenu: servicesMenu }); const hide = new MenuItem({ label: nls.localize('mHide', "Hide {0}", product.nameLong), role: 'hide', accelerator: 'Command+H' }); @@ -354,6 +352,14 @@ export class Menubar { const actions = [about]; actions.push(...checkForUpdates); + + if (preferences) { + actions.push(...[ + __separator__(), + preferences + ]); + } + actions.push(...[ __separator__(), services, @@ -369,18 +375,30 @@ export class Menubar { } private shouldDrawMenu(menuId: string): boolean { + // We need to draw an empty menu to override the electron default + if (!isMacintosh && this.configurationService.getValue('window.titleBarStyle') === 'custom') { + return false; + } + switch (menuId) { case 'File': - case 'Recent': case 'Help': - return true; + if (isMacintosh) { + return (this.windowsMainService.getWindowCount() === 0 && this.closedLastWindow) || (!!this.menubarMenus && !!this.menubarMenus[menuId]); + } + + case 'Window': + if (isMacintosh) { + return (this.windowsMainService.getWindowCount() === 0 && this.closedLastWindow) || !!this.menubarMenus; + } + default: - return this.windowsMainService.getWindowCount() > 0 && !!this.menubarMenus[menuId]; + return this.windowsMainService.getWindowCount() > 0 && (!!this.menubarMenus && !!this.menubarMenus[menuId]); } } private shouldFallback(menuId: string): boolean { - return this.shouldDrawMenu(menuId) && (this.windowsMainService.getWindowCount() === 0 || !this.menubarMenus[menuId]); + return this.shouldDrawMenu(menuId) && (this.windowsMainService.getWindowCount() === 0 && this.closedLastWindow && isMacintosh); } private setFallbackMenuById(menu: Electron.Menu, menuId: string): void { @@ -394,43 +412,28 @@ export class Menubar { const openWorkspace = new MenuItem(this.likeAction('workbench.action.openWorkspace', { label: this.mnemonicLabel(nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "Open Wor&&kspace...")), click: (menuItem, win, event) => this.windowsMainService.pickWorkspaceAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) })); + const openRecentMenu = new Menu(); + this.setFallbackMenuById(openRecentMenu, 'Recent'); + const openRecent = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miOpenRecent', comment: ['&& denotes a mnemonic'] }, "Open &&Recent")), submenu: openRecentMenu }); + menu.append(newFile); menu.append(newWindow); menu.append(__separator__()); menu.append(open); menu.append(openWorkspace); + menu.append(openRecent); break; case 'Recent': menu.append(this.createMenuItem(nls.localize({ key: 'miReopenClosedEditor', comment: ['&& denotes a mnemonic'] }, "&&Reopen Closed Editor"), 'workbench.action.reopenClosedEditor')); - const { workspaces, files } = this.historyMainService.getRecentlyOpened(); + this.insertRecentMenuItems(menu); - // Workspaces - if (workspaces.length > 0) { - menu.append(__separator__()); - - for (let i = 0; i < Menubar.MAX_MENU_RECENT_ENTRIES && i < workspaces.length; i++) { - menu.append(this.createOpenRecentMenuItem(workspaces[i], 'openRecentWorkspace', false)); - } - } - - // Files - if (files.length > 0) { - menu.append(__separator__()); - - for (let i = 0; i < Menubar.MAX_MENU_RECENT_ENTRIES && i < files.length; i++) { - menu.append(this.createOpenRecentMenuItem(files[i], 'openRecentFile', true)); - } - } - - if (workspaces.length || files.length) { - menu.append(__separator__()); - menu.append(this.createMenuItem(nls.localize({ key: 'miMore', comment: ['&& denotes a mnemonic'] }, "&&More..."), 'workbench.action.openRecent')); - menu.append(__separator__()); - menu.append(new MenuItem(this.likeAction('workbench.action.clearRecentFiles', { label: this.mnemonicLabel(nls.localize({ key: 'miClearRecentOpen', comment: ['&& denotes a mnemonic'] }, "&&Clear Recently Opened")), click: () => this.historyMainService.clearRecentlyOpened() }))); - } + menu.append(__separator__()); + menu.append(this.createMenuItem(nls.localize({ key: 'miMore', comment: ['&& denotes a mnemonic'] }, "&&More..."), 'workbench.action.openRecent')); + menu.append(__separator__()); + menu.append(new MenuItem(this.likeAction('workbench.action.clearRecentFiles', { label: this.mnemonicLabel(nls.localize({ key: 'miClearRecentOpen', comment: ['&& denotes a mnemonic'] }, "&&Clear Recently Opened")), click: () => this.historyMainService.clearRecentlyOpened() }))); break; @@ -480,46 +483,94 @@ export class Menubar { }); } - const openProcessExplorer = new MenuItem({ label: this.mnemonicLabel(nls.localize({ key: 'miOpenProcessExplorerer', comment: ['&& denotes a mnemonic'] }, "Open &&Process Explorer")), click: () => this.runActionInRenderer('workbench.action.openProcessExplorer') }); - if (twitterItem) { menu.append(twitterItem); } if (featureRequestsItem) { menu.append(featureRequestsItem); } if (reportIssuesItem) { menu.append(reportIssuesItem); } - if (twitterItem || featureRequestsItem || reportIssuesItem) { menu.append(__separator__()); } + if ((twitterItem || featureRequestsItem || reportIssuesItem) && (licenseItem || privacyStatementItem)) { menu.append(__separator__()); } if (licenseItem) { menu.append(licenseItem); } if (privacyStatementItem) { menu.append(privacyStatementItem); } - if (licenseItem || privacyStatementItem) { menu.append(__separator__()); } - menu.append(openProcessExplorer); break; } } - private setMenuById(menu: Electron.Menu, menuId: string): void { - console.log(`Attempting to set menu for ${menuId}`); - - // Build dynamic menu - this.menubarMenus[menuId].items.forEach((item: IMenubarMenuItemAction | IMenubarMenuItemSeparator) => { - if (item.id === 'vscode.menubar.separator') { + private setMenu(menu: Electron.Menu, items: Array) { + items.forEach((item: MenubarMenuItem) => { + if (isMenubarMenuItemSeparator(item)) { menu.append(__separator__()); - } else { - let menuItem: Electron.MenuItem; - let action: IMenubarMenuItemAction = item; - menuItem = this.createMenuItem(action.label, action.id, action.enabled, action.checked); + } else if (isMenubarMenuItemSubmenu(item)) { + const submenu = new Menu(); + const submenuItem = new MenuItem({ label: this.mnemonicLabel(item.label), submenu: submenu }); + this.setMenu(submenu, item.submenu.items); + menu.append(submenuItem); + } else if (isMenubarMenuItemAction(item)) { + if (item.id === 'workbench.action.openRecent') { + this.insertRecentMenuItems(menu); + } else if (item.id === 'workbench.action.showAboutDialog') { + this.insertCheckForUpdatesItems(menu); + } + + // Store the keybinding + if (item.keybinding) { + this.keybindings[item.id] = item.keybinding; + } else if (this.keybindings[item.id]) { + this.keybindings[item.id] = undefined; + } + + const menuItem = this.createMenuItem(item.label, item.id, item.enabled, item.checked); menu.append(menuItem); } }); } + private setMenuById(menu: Electron.Menu, menuId: string): void { + if (this.menubarMenus && this.menubarMenus[menuId]) { + this.setMenu(menu, this.menubarMenus[menuId].items); + } + } + + private insertCheckForUpdatesItems(menu: Electron.Menu) { + const updateItems = this.getUpdateMenuItems(); + if (updateItems.length) { + updateItems.forEach(i => menu.append(i)); + menu.append(__separator__()); + } + } + + private insertRecentMenuItems(menu: Electron.Menu) { + const { workspaces, files } = this.historyMainService.getRecentlyOpened(); + + // Workspaces + if (workspaces.length > 0) { + for (let i = 0; i < Menubar.MAX_MENU_RECENT_ENTRIES && i < workspaces.length; i++) { + menu.append(this.createOpenRecentMenuItem(workspaces[i], 'openRecentWorkspace', false)); + } + + menu.append(__separator__()); + } + + // Files + if (files.length > 0) { + for (let i = 0; i < Menubar.MAX_MENU_RECENT_ENTRIES && i < files.length; i++) { + menu.append(this.createOpenRecentMenuItem(files[i], 'openRecentFile', true)); + } + + menu.append(__separator__()); + } + } + private createOpenRecentMenuItem(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string, commandId: string, isFile: boolean): Electron.MenuItem { let label: string; - let path: string; - if (isSingleFolderWorkspaceIdentifier(workspace) || typeof workspace === 'string') { - label = unmnemonicLabel(getPathLabel(workspace, this.environmentService, null)); - path = workspace; + let uri: URI; + if (isSingleFolderWorkspaceIdentifier(workspace)) { + label = unmnemonicLabel(getWorkspaceLabel(workspace, this.environmentService, this.uriDisplayService, { verbose: true })); + uri = workspace; + } else if (isWorkspaceIdentifier(workspace)) { + label = getWorkspaceLabel(workspace, this.environmentService, this.uriDisplayService, { verbose: true }); + uri = URI.file(workspace.configPath); } else { - label = getWorkspaceLabel(workspace, this.environmentService, { verbose: true }); - path = workspace.configPath; + uri = URI.file(workspace); + label = unmnemonicLabel(this.uriDisplayService.getLabel(uri)); } return new MenuItem(this.likeAction(commandId, { @@ -529,12 +580,13 @@ export class Menubar { const success = this.windowsMainService.open({ context: OpenContext.MENU, cli: this.environmentService.args, - pathsToOpen: [path], forceNewWindow: openInNewWindow, + urisToOpen: [uri], + forceNewWindow: openInNewWindow, forceOpenWorkspaceAsFile: isFile }).length > 0; if (!success) { - this.historyMainService.removeFromRecentlyOpened([isSingleFolderWorkspaceIdentifier(workspace) ? workspace : workspace.configPath]); + this.historyMainService.removeFromRecentlyOpened([workspace]); } } }, false)); @@ -555,6 +607,7 @@ export class Menubar { if (this.currentEnableNativeTabs) { const hasMultipleWindows = this.windowsMainService.getWindowCount() > 1; + this.nativeTabMenuItems.push(this.createMenuItem(nls.localize('mNewTab', "New Tab"), 'workbench.action.newWindowTab')); this.nativeTabMenuItems.push(this.createMenuItem(nls.localize('mShowPreviousTab', "Show Previous Tab"), 'workbench.action.showPreviousWindowTab', hasMultipleWindows)); this.nativeTabMenuItems.push(this.createMenuItem(nls.localize('mShowNextTab', "Show Next Tab"), 'workbench.action.showNextWindowTab', hasMultipleWindows)); this.nativeTabMenuItems.push(this.createMenuItem(nls.localize('mMoveTabToNewWindow', "Move Tab to New Window"), 'workbench.action.moveWindowTabToNewWindow', hasMultipleWindows)); @@ -660,6 +713,17 @@ export class Menubar { commandId = arg2[0]; } + // Add role for special case menu items + if (isMacintosh) { + if (commandId === 'editor.action.clipboardCutAction') { + options['role'] = 'cut'; + } else if (commandId === 'editor.action.clipboardCopyAction') { + options['role'] = 'copy'; + } else if (commandId === 'editor.action.clipboardPasteAction') { + options['role'] = 'paste'; + } + } + return new MenuItem(this.withKeybinding(commandId, options)); } @@ -674,7 +738,7 @@ export class Menubar { } private withKeybinding(commandId: string, options: Electron.MenuItemConstructorOptions): Electron.MenuItemConstructorOptions { - const binding = this.keybindingsResolver.getKeybinding(commandId); + const binding = this.keybindings[commandId]; // Apply binding if there is one if (binding && binding.label) { diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts index 998479e6247..f2cd26b67f1 100644 --- a/src/vs/code/electron-main/menus.ts +++ b/src/vs/code/electron-main/menus.ts @@ -18,11 +18,13 @@ import { IUpdateService, StateType } from 'vs/platform/update/common/update'; import product from 'vs/platform/node/product'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { mnemonicMenuLabel as baseMnemonicLabel, unmnemonicLabel, getPathLabel } from 'vs/base/common/labels'; +import { mnemonicMenuLabel as baseMnemonicLabel, unmnemonicLabel } from 'vs/base/common/labels'; import { KeybindingsResolver } from 'vs/code/electron-main/keyboard'; import { IWindowsMainService, IWindowsCountChangedEvent } from 'vs/platform/windows/electron-main/windows'; import { IHistoryMainService } from 'vs/platform/history/common/history'; -import { IWorkspaceIdentifier, getWorkspaceLabel, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, getWorkspaceLabel, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import URI from 'vs/base/common/uri'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; interface IMenuItemClickHandler { inDevTools: (contents: Electron.WebContents) => void; @@ -65,7 +67,8 @@ export class CodeMenu { @IWindowsService private windowsService: IWindowsService, @IEnvironmentService private environmentService: IEnvironmentService, @ITelemetryService private telemetryService: ITelemetryService, - @IHistoryMainService private historyMainService: IHistoryMainService + @IHistoryMainService private historyMainService: IHistoryMainService, + @IUriDisplayService private uriDisplayService: IUriDisplayService ) { this.nativeTabMenuItems = []; @@ -194,7 +197,7 @@ export class CodeMenu { private updateWorkspaceMenuItems(): void { const window = this.windowsMainService.getLastActiveWindow(); const isInWorkspaceContext = window && !!window.openedWorkspace; - const isInFolderContext = window && !!window.openedFolderPath; + const isInFolderContext = window && !!window.openedFolderUri; this.closeWorkspace.visible = isInWorkspaceContext; this.closeFolder.visible = !isInWorkspaceContext; @@ -489,13 +492,16 @@ export class CodeMenu { private createOpenRecentMenuItem(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string, commandId: string, isFile: boolean): Electron.MenuItem { let label: string; - let path: string; - if (isSingleFolderWorkspaceIdentifier(workspace) || typeof workspace === 'string') { - label = unmnemonicLabel(getPathLabel(workspace, this.environmentService)); - path = workspace; + let uri: URI; + if (isSingleFolderWorkspaceIdentifier(workspace)) { + label = unmnemonicLabel(getWorkspaceLabel(workspace, this.environmentService, this.uriDisplayService, { verbose: true })); + uri = workspace; + } else if (isWorkspaceIdentifier(workspace)) { + label = getWorkspaceLabel(workspace, this.environmentService, this.uriDisplayService, { verbose: true }); + uri = URI.file(workspace.configPath); } else { - label = getWorkspaceLabel(workspace, this.environmentService, { verbose: true }); - path = workspace.configPath; + uri = URI.file(workspace); + label = unmnemonicLabel(this.uriDisplayService.getLabel(uri)); } return new MenuItem(this.likeAction(commandId, { @@ -505,12 +511,13 @@ export class CodeMenu { const success = this.windowsMainService.open({ context: OpenContext.MENU, cli: this.environmentService.args, - pathsToOpen: [path], forceNewWindow: openInNewWindow, + urisToOpen: [uri], + forceNewWindow: openInNewWindow, forceOpenWorkspaceAsFile: isFile }).length > 0; if (!success) { - this.historyMainService.removeFromRecentlyOpened([isSingleFolderWorkspaceIdentifier(workspace) ? workspace : workspace.configPath]); + this.historyMainService.removeFromRecentlyOpened([workspace]); } } }, false)); @@ -740,7 +747,7 @@ export class CodeMenu { const twoRowsRightEditorLayout = this.createMenuItem(nls.localize({ key: 'miTwoRowsRightEditorLayout', comment: ['&& denotes a mnemonic'] }, "Two R&&ows Right"), 'workbench.action.editorLayoutTwoRowsRight'); const twoColumnsBottomEditorLayout = this.createMenuItem(nls.localize({ key: 'miTwoColumnsBottomEditorLayout', comment: ['&& denotes a mnemonic'] }, "Two &&Columns Bottom"), 'workbench.action.editorLayoutTwoColumnsBottom'); - const toggleEditorLayout = this.createMenuItem(nls.localize({ key: 'miToggleEditorLayout', comment: ['&& denotes a mnemonic'] }, "Toggle Vertical/Horizontal &&Layout"), 'workbench.action.toggleEditorGroupLayout'); + const toggleEditorLayout = this.createMenuItem(nls.localize({ key: 'miToggleEditorLayout', comment: ['&& denotes a mnemonic'] }, "Flip &&Layout"), 'workbench.action.toggleEditorGroupLayout'); [ splitEditorUp, @@ -766,6 +773,7 @@ export class CodeMenu { const toggleMinimap = this.createMenuItem(nls.localize({ key: 'miToggleMinimap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Minimap"), 'editor.action.toggleMinimap'); const toggleRenderWhitespace = this.createMenuItem(nls.localize({ key: 'miToggleRenderWhitespace', comment: ['&& denotes a mnemonic'] }, "Toggle &&Render Whitespace"), 'editor.action.toggleRenderWhitespace'); const toggleRenderControlCharacters = this.createMenuItem(nls.localize({ key: 'miToggleRenderControlCharacters', comment: ['&& denotes a mnemonic'] }, "Toggle &&Control Characters"), 'editor.action.toggleRenderControlCharacter'); + const toggleBreadcrumbs = this.createMenuItem(nls.localize({ key: 'miToggleBreadcrumbs', comment: ['&& denotes a mnemonic'] }, "Toggle &&Breadcrumbs"), 'breadcrumbs.toggle'); arrays.coalesce([ commands, @@ -788,7 +796,8 @@ export class CodeMenu { toggleWordWrap, toggleMinimap, toggleRenderWhitespace, - toggleRenderControlCharacters + toggleRenderControlCharacters, + toggleBreadcrumbs ]).forEach(item => viewMenu.append(item)); } @@ -962,6 +971,7 @@ export class CodeMenu { if (this.currentEnableNativeTabs) { const hasMultipleWindows = this.windowsMainService.getWindowCount() > 1; + this.nativeTabMenuItems.push(this.createMenuItem(nls.localize('mNewTab', "New Tab"), 'workbench.action.newWindowTab')); this.nativeTabMenuItems.push(this.createMenuItem(nls.localize('mShowPreviousTab', "Show Previous Tab"), 'workbench.action.showPreviousWindowTab', hasMultipleWindows)); this.nativeTabMenuItems.push(this.createMenuItem(nls.localize('mShowNextTab', "Show Next Tab"), 'workbench.action.showNextWindowTab', hasMultipleWindows)); this.nativeTabMenuItems.push(this.createMenuItem(nls.localize('mMoveTabToNewWindow', "Move Tab to New Window"), 'workbench.action.moveWindowTabToNewWindow', hasMultipleWindows)); @@ -1307,4 +1317,4 @@ export class CodeMenu { function __separator__(): Electron.MenuItem { return new MenuItem({ type: 'separator' }); -} +} \ No newline at end of file diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 0437bb840e1..d0151137c0b 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -280,8 +280,8 @@ export class CodeWindow implements ICodeWindow { return this.currentConfig ? this.currentConfig.workspace : void 0; } - get openedFolderPath(): string { - return this.currentConfig ? this.currentConfig.folderPath : void 0; + get openedFolderUri(): URI { + return this.currentConfig ? this.currentConfig.folderUri : void 0; } setReady(): void { @@ -500,6 +500,12 @@ export class CodeWindow implements ICodeWindow { }); } + addTabbedWindow(window: ICodeWindow): void { + if (isMacintosh) { + this._win.addTabbedWindow(window.win); + } + } + load(config: IWindowConfiguration, isReload?: boolean, disableExtensions?: boolean): void { // If this is the first time the window is loaded, we associate the paths diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 6e6fbed43cd..e4e44ab6d10 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -6,7 +6,7 @@ 'use strict'; import { basename, normalize, join, dirname } from 'path'; -import * as fs from 'original-fs'; +import * as fs from 'fs'; import { localize } from 'vs/nls'; import * as arrays from 'vs/base/common/arrays'; import { assign, mixin, equals } from 'vs/base/common/objects'; @@ -20,16 +20,15 @@ import { ILifecycleService, UnloadReason, IWindowUnloadEvent } from 'vs/platform import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, INativeOpenDialogOptions, ReadyState, IPathsToWaitFor, IEnterWorkspaceResult, IMessageBoxResult } from 'vs/platform/windows/common/windows'; -import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderPath } from 'vs/code/node/windowsFinder'; +import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderUri } from 'vs/code/node/windowsFinder'; import { Event as CommonEvent, Emitter } from 'vs/base/common/event'; import product from 'vs/platform/node/product'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { isEqual } from 'vs/base/common/paths'; import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent, ICodeWindow, IWindowState as ISingleWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows'; import { IHistoryMainService } from 'vs/platform/history/common/history'; import { IProcessEnvironment, isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IWorkspacesMainService, IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, WORKSPACE_FILTER, isSingleFolderWorkspaceIdentifier, IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspacesMainService, IWorkspaceIdentifier, WORKSPACE_FILTER, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { Schemas } from 'vs/base/common/network'; @@ -37,6 +36,7 @@ import { normalizeNFC } from 'vs/base/common/normalization'; import URI from 'vs/base/common/uri'; import { Queue } from 'vs/base/common/async'; import { exists } from 'vs/base/node/pfs'; +import { getComparisonKey, isEqual, hasToIgnoreCase } from 'vs/base/common/resources'; enum WindowError { UNRESPONSIVE, @@ -49,11 +49,15 @@ interface INewWindowState extends ISingleWindowState { interface IWindowState { workspace?: IWorkspaceIdentifier; - folderPath?: string; + folderUri?: URI; backupPath: string; uiState: ISingleWindowState; } +interface IBackwardCompatibleWindowState extends IWindowState { + folderPath?: string; +} + interface IWindowsState { lastActiveWindow?: IWindowState; lastPluginDevelopmentHostWindow?: IWindowState; @@ -67,7 +71,7 @@ interface IOpenBrowserWindowOptions { cli?: ParsedArgs; workspace?: IWorkspaceIdentifier; - folderPath?: string; + folderUri?: URI; initialStartup?: boolean; @@ -77,6 +81,7 @@ interface IOpenBrowserWindowOptions { filesToWait?: IPathsToWaitFor; forceNewWindow?: boolean; + forceNewTabbedWindow?: boolean; windowToUse?: ICodeWindow; emptyWindowBackupFolder?: string; @@ -88,7 +93,7 @@ interface IPathToOpen extends IPath { workspace?: IWorkspaceIdentifier; // the folder path for a Code instance to open - folderPath?: string; + folderUri?: URI; // the backup spath for a Code instance to use backupPath?: string; @@ -144,7 +149,7 @@ export class WindowsManager implements IWindowsMainService { @IWorkspacesMainService private workspacesMainService: IWorkspacesMainService, @IInstantiationService private instantiationService: IInstantiationService ) { - this.windowsState = this.stateService.getItem(WindowsManager.windowsStateStorageKey) || { openedWindows: [] }; + this.windowsState = this.getWindowsState(); if (!Array.isArray(this.windowsState.openedWindows)) { this.windowsState.openedWindows = []; } @@ -153,6 +158,30 @@ export class WindowsManager implements IWindowsMainService { this.workspacesManager = new WorkspacesManager(workspacesMainService, backupMainService, environmentService, this); } + private getWindowsState(): IWindowsState { + const windowsState = this.stateService.getItem(WindowsManager.windowsStateStorageKey) || { openedWindows: [] }; + if (windowsState.lastActiveWindow) { + windowsState.lastActiveWindow = this.revive(windowsState.lastActiveWindow); + } + if (windowsState.lastPluginDevelopmentHostWindow) { + windowsState.lastPluginDevelopmentHostWindow = this.revive(windowsState.lastPluginDevelopmentHostWindow); + } + if (windowsState.openedWindows) { + windowsState.openedWindows = windowsState.openedWindows.map(windowState => this.revive(windowState)); + } + return windowsState; + } + + private revive(windowState: IWindowState): IWindowState { + if (windowState.folderUri) { + windowState.folderUri = URI.revive(windowState.folderUri); + } + if ((windowState).folderPath) { + windowState.folderUri = URI.file((windowState).folderPath); + } + return windowState; + } + ready(initialUserEnv: IProcessEnvironment): void { this.initialUserEnv = initialUserEnv; @@ -294,10 +323,10 @@ export class WindowsManager implements IWindowsMainService { } // Any non extension host window with same workspace or folder - else if (!win.isExtensionDevelopmentHost && (!!win.openedWorkspace || !!win.openedFolderPath)) { + else if (!win.isExtensionDevelopmentHost && (!!win.openedWorkspace || !!win.openedFolderUri)) { this.windowsState.openedWindows.forEach(o => { const sameWorkspace = win.openedWorkspace && o.workspace && o.workspace.id === win.openedWorkspace.id; - const sameFolder = win.openedFolderPath && isEqual(o.folderPath, win.openedFolderPath, !isLinux /* ignorecase */); + const sameFolder = win.openedFolderUri && o.folderUri && isEqual(o.folderUri, win.openedFolderUri, hasToIgnoreCase(o.folderUri)); if (sameWorkspace || sameFolder) { o.uiState = state.uiState; @@ -317,23 +346,24 @@ export class WindowsManager implements IWindowsMainService { private toWindowState(win: ICodeWindow): IWindowState { return { workspace: win.openedWorkspace, - folderPath: win.openedFolderPath, + folderUri: win.openedFolderUri, backupPath: win.backupPath, uiState: win.serializeWindowState() }; } open(openConfig: IOpenConfiguration): ICodeWindow[] { + this.logService.trace('windowsManager#open'); openConfig = this.validateOpenConfig(openConfig); let pathsToOpen = this.getPathsToOpen(openConfig); // When run with --add, take the folders that are to be opened as // folders that should be added to the currently active window. - let foldersToAdd: IPath[] = []; + let foldersToAdd: URI[] = []; if (openConfig.addMode) { - foldersToAdd = pathsToOpen.filter(path => !!path.folderPath).map(path => ({ filePath: path.folderPath })); - pathsToOpen = pathsToOpen.filter(path => !path.folderPath); + foldersToAdd = pathsToOpen.filter(path => !!path.folderUri).map(path => path.folderUri); + pathsToOpen = pathsToOpen.filter(path => !path.folderUri); } let filesToOpen = pathsToOpen.filter(path => !!path.filePath && !path.createFilePath); @@ -362,12 +392,12 @@ export class WindowsManager implements IWindowsMainService { // // These are windows to open to show either folders or files (including diffing files or creating them) // - const foldersToOpen = arrays.distinct(pathsToOpen.filter(win => win.folderPath && !win.filePath).map(win => win.folderPath), folder => isLinux ? folder : folder.toLowerCase()); // prevent duplicates + const foldersToOpen = arrays.distinct(pathsToOpen.filter(win => win.folderUri && !win.filePath).map(win => win.folderUri), folder => getComparisonKey(folder)); // prevent duplicates // // These are windows to restore because of hot-exit or from previous session (only performed once on startup!) // - let foldersToRestore: string[] = []; + let foldersToRestore: URI[] = []; let workspacesToRestore: IWorkspaceIdentifier[] = []; let emptyToRestore: string[] = []; if (openConfig.initialStartup && !openConfig.cli.extensionDevelopmentPath && !openConfig.cli['disable-restore-windows']) { @@ -377,21 +407,22 @@ export class WindowsManager implements IWindowsMainService { workspacesToRestore.push(...this.workspacesMainService.getUntitledWorkspacesSync()); // collect from previous window session emptyToRestore = this.backupMainService.getEmptyWindowBackupPaths(); - emptyToRestore.push(...pathsToOpen.filter(w => !w.workspace && !w.folderPath && w.backupPath).map(w => basename(w.backupPath))); // add empty windows with backupPath + emptyToRestore.push(...pathsToOpen.filter(w => !w.workspace && !w.folderUri && w.backupPath).map(w => basename(w.backupPath))); // add empty windows with backupPath emptyToRestore = arrays.distinct(emptyToRestore); // prevent duplicates } // // These are empty windows to open // - const emptyToOpen = pathsToOpen.filter(win => !win.workspace && !win.folderPath && !win.filePath && !win.backupPath).length; + const emptyToOpen = pathsToOpen.filter(win => !win.workspace && !win.folderUri && !win.filePath && !win.backupPath).length; // Open based on config const usedWindows = this.doOpen(openConfig, workspacesToOpen, workspacesToRestore, foldersToOpen, foldersToRestore, emptyToRestore, emptyToOpen, filesToOpen, filesToCreate, filesToDiff, filesToWait, foldersToAdd); // Make sure to pass focus to the most relevant of the windows if we open multiple if (usedWindows.length > 1) { - let focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && !openConfig.cli._.length && (!openConfig.pathsToOpen || !openConfig.pathsToOpen.length); + + let focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && !openConfig.cli._.length && !(openConfig.cli['folder-uri'] || []).length && !(openConfig.urisToOpen || []).length; let focusLastOpened = true; let focusLastWindow = true; @@ -410,9 +441,9 @@ export class WindowsManager implements IWindowsMainService { for (let i = usedWindows.length - 1; i >= 0; i--) { const usedWindow = usedWindows[i]; if ( - (usedWindow.openedWorkspace && workspacesToRestore.some(workspace => workspace.id === usedWindow.openedWorkspace.id)) || // skip over restored workspace - (usedWindow.openedFolderPath && foldersToRestore.some(folder => folder === usedWindow.openedFolderPath)) || // skip over restored folder - (usedWindow.backupPath && emptyToRestore.some(empty => empty === basename(usedWindow.backupPath))) // skip over restored empty window + (usedWindow.openedWorkspace && workspacesToRestore.some(workspace => workspace.id === usedWindow.openedWorkspace.id)) || // skip over restored workspace + (usedWindow.openedFolderUri && foldersToRestore.some(folder => isEqual(folder, usedWindow.openedFolderUri, hasToIgnoreCase(folder)))) || // skip over restored folder + (usedWindow.backupPath && emptyToRestore.some(empty => empty === basename(usedWindow.backupPath))) // skip over restored empty window ) { continue; } @@ -436,8 +467,8 @@ export class WindowsManager implements IWindowsMainService { const recentlyOpenedFiles: string[] = []; pathsToOpen.forEach(win => { - if (win.workspace || win.folderPath) { - recentlyOpenedWorkspaces.push(win.workspace || win.folderPath); + if (win.workspace || win.folderUri) { + recentlyOpenedWorkspaces.push(win.workspace || win.folderUri); } else if (win.filePath) { recentlyOpenedFiles.push(win.filePath); } @@ -472,15 +503,15 @@ export class WindowsManager implements IWindowsMainService { openConfig: IOpenConfiguration, workspacesToOpen: IWorkspaceIdentifier[], workspacesToRestore: IWorkspaceIdentifier[], - foldersToOpen: string[], - foldersToRestore: string[], + foldersToOpen: URI[], + foldersToRestore: URI[], emptyToRestore: string[], emptyToOpen: number, filesToOpen: IPath[], filesToCreate: IPath[], filesToDiff: IPath[], filesToWait: IPathsToWaitFor, - foldersToAdd: IPath[] + foldersToAdd: URI[] ) { const usedWindows: ICodeWindow[] = []; @@ -516,6 +547,8 @@ export class WindowsManager implements IWindowsMainService { // Special case: we started with --wait and we got back a folder to open. In this case // we actually prefer to not open the folder but operate purely on the file. if (typeof bestWindowOrFolder === 'string' && filesToWait) { + //TODO@Ben: #54483 This should not happen + console.error(`This should not happen`, bestWindowOrFolder, WindowsManager.WINDOWS); bestWindowOrFolder = !openFilesInNewWindow ? this.getLastActiveWindow() : null; } @@ -528,8 +561,8 @@ export class WindowsManager implements IWindowsMainService { } // Window is single folder - else if (bestWindowOrFolder.openedFolderPath) { - foldersToOpen.push(bestWindowOrFolder.openedFolderPath); + else if (bestWindowOrFolder.openedFolderUri) { + foldersToOpen.push(bestWindowOrFolder.openedFolderUri); } // Window is empty @@ -548,7 +581,9 @@ export class WindowsManager implements IWindowsMainService { // We found a suitable folder to open: add it to foldersToOpen else if (typeof bestWindowOrFolder === 'string') { - foldersToOpen.push(bestWindowOrFolder); + //TODO@Ben: #54483 Ben This should not happen + // foldersToOpen.push(bestWindowOrFolder); + console.error(`This should not happen`, bestWindowOrFolder, WindowsManager.WINDOWS); } // Finally, if no window or folder is found, just open the files in an empty window @@ -561,7 +596,8 @@ export class WindowsManager implements IWindowsMainService { filesToCreate, filesToDiff, filesToWait, - forceNewWindow: true + forceNewWindow: true, + forceNewTabbedWindow: openConfig.forceNewTabbedWindow })); // Reset these because we handled them @@ -613,7 +649,8 @@ export class WindowsManager implements IWindowsMainService { } // Handle folders to open (instructed and to restore) - const allFoldersToOpen = arrays.distinct([...foldersToRestore, ...foldersToOpen], folder => isLinux ? folder : folder.toLowerCase()); // prevent duplicates + const allFoldersToOpen = arrays.distinct([...foldersToRestore, ...foldersToOpen], folder => getComparisonKey(folder)); // prevent duplicates + if (allFoldersToOpen.length > 0) { // Check for existing instances @@ -635,12 +672,13 @@ export class WindowsManager implements IWindowsMainService { // Open remaining ones allFoldersToOpen.forEach(folderToOpen => { - if (windowsOnFolderPath.some(win => isEqual(win.openedFolderPath, folderToOpen, !isLinux /* ignorecase */))) { + + if (windowsOnFolderPath.some(win => isEqual(win.openedFolderUri, folderToOpen, hasToIgnoreCase(win.openedFolderUri)))) { return; // ignore folders that are already open } // Do open folder - usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, { folderPath: folderToOpen }, openFolderInNewWindow, filesToOpen, filesToCreate, filesToDiff, filesToWait)); + usedWindows.push(this.doOpenFolderOrWorkspace(openConfig, { folderUri: folderToOpen }, openFolderInNewWindow, filesToOpen, filesToCreate, filesToDiff, filesToWait)); // Reset these because we handled them filesToOpen = []; @@ -664,6 +702,7 @@ export class WindowsManager implements IWindowsMainService { filesToDiff, filesToWait, forceNewWindow: true, + forceNewTabbedWindow: openConfig.forceNewTabbedWindow, emptyWindowBackupFolder })); @@ -684,7 +723,8 @@ export class WindowsManager implements IWindowsMainService { userEnv: openConfig.userEnv, cli: openConfig.cli, initialStartup: openConfig.initialStartup, - forceNewWindow: openFolderInNewWindow + forceNewWindow: openFolderInNewWindow, + forceNewTabbedWindow: openConfig.forceNewTabbedWindow })); openFolderInNewWindow = true; // any other window to open must open in new window then @@ -705,7 +745,7 @@ export class WindowsManager implements IWindowsMainService { return window; } - private doAddFoldersToExistingWidow(window: ICodeWindow, foldersToAdd: IPath[]): ICodeWindow { + private doAddFoldersToExistingWidow(window: ICodeWindow, foldersToAdd: URI[]): ICodeWindow { window.focus(); // make sure window has focus window.ready().then(readyWindow => { @@ -725,12 +765,13 @@ export class WindowsManager implements IWindowsMainService { cli: openConfig.cli, initialStartup: openConfig.initialStartup, workspace: folderOrWorkspace.workspace, - folderPath: folderOrWorkspace.folderPath, + folderUri: folderOrWorkspace.folderUri, filesToOpen, filesToCreate, filesToDiff, filesToWait, forceNewWindow, + forceNewTabbedWindow: openConfig.forceNewTabbedWindow, windowToUse }); @@ -742,7 +783,7 @@ export class WindowsManager implements IWindowsMainService { let isCommandLineOrAPICall = false; // Extract paths: from API - if (openConfig.pathsToOpen && openConfig.pathsToOpen.length > 0) { + if (openConfig.urisToOpen && openConfig.urisToOpen.length > 0) { windowsToOpen = this.doExtractPathsFromAPI(openConfig); isCommandLineOrAPICall = true; } @@ -753,7 +794,7 @@ export class WindowsManager implements IWindowsMainService { } // Extract paths: from CLI - else if (openConfig.cli._.length > 0) { + else if (openConfig.cli._.length > 0 || (openConfig.cli['folder-uri'] || []).length > 0) { windowsToOpen = this.doExtractPathsFromCLI(openConfig.cli); isCommandLineOrAPICall = true; } @@ -768,13 +809,13 @@ export class WindowsManager implements IWindowsMainService { // If we are in addMode, we should not do this because in that case all // folders should be added to the existing window. if (!openConfig.addMode && isCommandLineOrAPICall) { - const foldersToOpen = windowsToOpen.filter(path => !!path.folderPath); + const foldersToOpen = windowsToOpen.filter(path => !!path.folderUri); if (foldersToOpen.length > 1) { - const workspace = this.workspacesMainService.createWorkspaceSync(foldersToOpen.map(folder => ({ uri: URI.file(folder.folderPath) }))); + const workspace = this.workspacesMainService.createWorkspaceSync(foldersToOpen.map(folder => ({ uri: folder.folderUri }))); // Add workspace and remove folders thereby windowsToOpen.push({ workspace }); - windowsToOpen = windowsToOpen.filter(path => !path.folderPath); + windowsToOpen = windowsToOpen.filter(path => !path.folderUri); } } @@ -782,8 +823,8 @@ export class WindowsManager implements IWindowsMainService { } private doExtractPathsFromAPI(openConfig: IOpenConfiguration): IPath[] { - let pathsToOpen = openConfig.pathsToOpen.map(pathToOpen => { - const path = this.parsePath(pathToOpen, { gotoLineMode: openConfig.cli && openConfig.cli.goto, forceOpenWorkspaceAsFile: openConfig.forceOpenWorkspaceAsFile }); + let pathsToOpen = openConfig.urisToOpen.map(pathToOpen => { + const path = this.parseUri(pathToOpen, { gotoLineMode: openConfig.cli && openConfig.cli.goto, forceOpenWorkspaceAsFile: openConfig.forceOpenWorkspaceAsFile }); // Warn if the requested path to open does not exist if (!path) { @@ -792,7 +833,7 @@ export class WindowsManager implements IWindowsMainService { type: 'info', buttons: [localize('ok', "OK")], message: localize('pathNotExistTitle', "Path does not exist"), - detail: localize('pathNotExistDetail', "The path '{0}' does not seem to exist anymore on disk.", pathToOpen), + detail: localize('pathNotExistDetail', "The path '{0}' does not seem to exist anymore on disk.", pathToOpen.scheme === Schemas.file ? pathToOpen.fsPath : pathToOpen.path), noLink: true }; @@ -809,7 +850,20 @@ export class WindowsManager implements IWindowsMainService { } private doExtractPathsFromCLI(cli: ParsedArgs): IPath[] { - const pathsToOpen = arrays.coalesce(cli._.map(candidate => this.parsePath(candidate, { ignoreFileNotFound: true, gotoLineMode: cli.goto }))); + const pathsToOpen = []; + + // folder uris + if (cli['folder-uri'] && cli['folder-uri'].length) { + const arg = cli['folder-uri']; + const folderUris: string[] = typeof arg === 'string' ? [arg] : arg; + pathsToOpen.push(...arrays.coalesce(folderUris.map(candidate => this.parseUri(this.parseFolderUriArg(candidate), { ignoreFileNotFound: true, gotoLineMode: cli.goto })))); + } + + // folder or file paths + if (cli._ && cli._.length) { + pathsToOpen.push(...arrays.coalesce(cli._.map(candidate => this.parsePath(candidate, { ignoreFileNotFound: true, gotoLineMode: cli.goto })))); + } + if (pathsToOpen.length > 0) { return pathsToOpen; } @@ -842,9 +896,9 @@ export class WindowsManager implements IWindowsMainService { } // folder (if path is valid) - else if (lastActiveWindow.folderPath) { - const validatedFolder = this.parsePath(lastActiveWindow.folderPath); - if (validatedFolder && validatedFolder.folderPath) { + else if (lastActiveWindow.folderUri) { + const validatedFolder = this.parseUri(lastActiveWindow.folderUri); + if (validatedFolder && validatedFolder.folderUri) { return [validatedFolder]; } } @@ -870,16 +924,16 @@ export class WindowsManager implements IWindowsMainService { windowsToOpen.push(...workspaceCandidates.map(candidate => this.parsePath(candidate.configPath)).filter(window => window && window.workspace)); // Folders - const folderCandidates = this.windowsState.openedWindows.filter(w => !!w.folderPath).map(w => w.folderPath); - if (lastActiveWindow && lastActiveWindow.folderPath) { - folderCandidates.push(lastActiveWindow.folderPath); + const folderCandidates = this.windowsState.openedWindows.filter(w => !!w.folderUri).map(w => w.folderUri); + if (lastActiveWindow && lastActiveWindow.folderUri) { + folderCandidates.push(lastActiveWindow.folderUri); } - windowsToOpen.push(...folderCandidates.map(candidate => this.parsePath(candidate)).filter(window => window && window.folderPath)); + windowsToOpen.push(...folderCandidates.map(candidate => this.parseUri(candidate)).filter(window => window && window.folderUri)); // Windows that were Empty if (restoreWindows === 'all') { - const lastOpenedEmpty = this.windowsState.openedWindows.filter(w => !w.workspace && !w.folderPath && w.backupPath).map(w => w.backupPath); - const lastActiveEmpty = lastActiveWindow && !lastActiveWindow.workspace && !lastActiveWindow.folderPath && lastActiveWindow.backupPath; + const lastOpenedEmpty = this.windowsState.openedWindows.filter(w => !w.workspace && !w.folderUri && w.backupPath).map(w => w.backupPath); + const lastActiveEmpty = lastActiveWindow && !lastActiveWindow.workspace && !lastActiveWindow.folderUri && lastActiveWindow.backupPath; if (lastActiveEmpty) { lastOpenedEmpty.push(lastActiveEmpty); } @@ -914,6 +968,28 @@ export class WindowsManager implements IWindowsMainService { return restoreWindows; } + private parseFolderUriArg(arg: string): URI { + // Do not support if user has passed folder path on Windows + if (isWindows && /^([a-z])\:(.*)$/i.test(arg)) { + return null; + } + return URI.parse(arg); + } + + private parseUri(anyUri: URI, options?: { ignoreFileNotFound?: boolean, gotoLineMode?: boolean, forceOpenWorkspaceAsFile?: boolean; }): IPathToOpen { + if (!anyUri || !anyUri.scheme) { + return null; + } + + if (anyUri.scheme === Schemas.file) { + return this.parsePath(anyUri.fsPath, options); + } + + return { + folderUri: anyUri + }; + } + private parsePath(anyPath: string, options?: { ignoreFileNotFound?: boolean, gotoLineMode?: boolean, forceOpenWorkspaceAsFile?: boolean; }): IPathToOpen { if (!anyPath) { return null; @@ -954,7 +1030,7 @@ export class WindowsManager implements IWindowsMainService { // over to us) else if (candidateStat.isDirectory()) { return { - folderPath: candidate + folderUri: URI.file(candidate) }; } } @@ -1024,21 +1100,36 @@ export class WindowsManager implements IWindowsMainService { } // Fill in previously opened workspace unless an explicit path is provided and we are not unit testing - if (openConfig.cli._.length === 0 && !openConfig.cli.extensionTestsPath) { + if (openConfig.cli._.length === 0 && (openConfig.cli['folder-uri'] || []).length === 0 && !openConfig.cli.extensionTestsPath) { const extensionDevelopmentWindowState = this.windowsState.lastPluginDevelopmentHostWindow; - const workspaceToOpen = extensionDevelopmentWindowState && (extensionDevelopmentWindowState.workspace || extensionDevelopmentWindowState.folderPath); + const workspaceToOpen = extensionDevelopmentWindowState && (extensionDevelopmentWindowState.workspace || extensionDevelopmentWindowState.folderUri); if (workspaceToOpen) { - openConfig.cli._ = [isSingleFolderWorkspaceIdentifier(workspaceToOpen) ? workspaceToOpen : workspaceToOpen.configPath]; + if (isSingleFolderWorkspaceIdentifier(workspaceToOpen)) { + if (workspaceToOpen.scheme === Schemas.file) { + openConfig.cli._ = [workspaceToOpen.fsPath]; + } else { + openConfig.cli['folder-uri'] = [workspaceToOpen.toString()]; + } + } else { + openConfig.cli._ = [workspaceToOpen.configPath]; + } } } // Make sure we are not asked to open a workspace or folder that is already opened - if (openConfig.cli._.some(path => !!findWindowOnWorkspaceOrFolderPath(WindowsManager.WINDOWS, path))) { + if (openConfig.cli._.some(path => !!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, URI.file(path)))) { openConfig.cli._ = []; } + if (openConfig.cli['folder-uri']) { + const arg = openConfig.cli['folder-uri']; + const folderUris: string[] = typeof arg === 'string' ? [arg] : arg; + if (folderUris.some(uri => !!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, this.parseFolderUriArg(uri)))) { + openConfig.cli['folder-uri'] = []; + } + } // Open it - this.open({ context: openConfig.context, cli: openConfig.cli, forceNewWindow: true, forceEmpty: openConfig.cli._.length === 0, userEnv: openConfig.userEnv }); + this.open({ context: openConfig.context, cli: openConfig.cli, forceNewWindow: true, forceEmpty: openConfig.cli._.length === 0 && (openConfig.cli['folder-uri'] || []).length === 0, userEnv: openConfig.userEnv }); } private openInBrowserWindow(options: IOpenBrowserWindowOptions): ICodeWindow { @@ -1051,7 +1142,7 @@ export class WindowsManager implements IWindowsMainService { configuration.userEnv = assign({}, this.initialUserEnv, options.userEnv || {}); configuration.isInitialStartup = options.initialStartup; configuration.workspace = options.workspace; - configuration.folderPath = options.folderPath; + configuration.folderUri = options.folderUri; configuration.filesToOpen = options.filesToOpen; configuration.filesToCreate = options.filesToCreate; configuration.filesToDiff = options.filesToDiff; @@ -1067,7 +1158,7 @@ export class WindowsManager implements IWindowsMainService { } let window: ICodeWindow; - if (!options.forceNewWindow) { + if (!options.forceNewWindow && !options.forceNewTabbedWindow) { window = options.windowToUse || this.getLastActiveWindow(); if (window) { window.focus(); @@ -1094,12 +1185,21 @@ export class WindowsManager implements IWindowsMainService { state.mode = WindowMode.Normal; } + // Create the window window = this.instantiationService.createInstance(CodeWindow, { state, extensionDevelopmentPath: configuration.extensionDevelopmentPath, isExtensionTestHost: !!configuration.extensionTestsPath }); + // Add as window tab if configured (macOS only) + if (options.forceNewTabbedWindow) { + const activeWindow = this.getLastActiveWindow(); + if (activeWindow) { + activeWindow.addTabbedWindow(window); + } + } + // Add to our list of windows WindowsManager.WINDOWS.push(window); @@ -1141,8 +1241,8 @@ export class WindowsManager implements IWindowsMainService { if (!configuration.extensionDevelopmentPath) { if (configuration.workspace) { configuration.backupPath = this.backupMainService.registerWorkspaceBackupSync(configuration.workspace); - } else if (configuration.folderPath) { - configuration.backupPath = this.backupMainService.registerFolderBackupSync(configuration.folderPath); + } else if (configuration.folderUri) { + configuration.backupPath = this.backupMainService.registerFolderBackupSync(configuration.folderUri); } else { configuration.backupPath = this.backupMainService.registerEmptyWindowBackupSync(options.emptyWindowBackupFolder); } @@ -1179,8 +1279,8 @@ export class WindowsManager implements IWindowsMainService { } // Known Folder - load from stored settings - if (configuration.folderPath) { - const stateForFolder = this.windowsState.openedWindows.filter(o => isEqual(o.folderPath, configuration.folderPath, !isLinux /* ignorecase */)).map(o => o.uiState); + if (configuration.folderUri) { + const stateForFolder = this.windowsState.openedWindows.filter(o => o.folderUri && isEqual(o.folderUri, configuration.folderUri, hasToIgnoreCase(o.folderUri))).map(o => o.uiState); if (stateForFolder.length) { return stateForFolder[0]; } @@ -1390,6 +1490,10 @@ export class WindowsManager implements IWindowsMainService { return this.open({ context, cli: this.environmentService.args, forceNewWindow: true, forceEmpty: true }); } + openNewTabbedWindow(context: OpenContext): ICodeWindow[] { + return this.open({ context, cli: this.environmentService.args, forceNewTabbedWindow: true, forceEmpty: true }); + } + waitForWindowCloseOrLoad(windowId: number): TPromise { return new TPromise(c => { function handler(id: number) { @@ -1613,7 +1717,7 @@ class Dialogs { } pickAndOpen(options: INativeOpenDialogOptions): void { - this.getFileOrFolderPaths(options).then(paths => { + this.getFileOrFolderUris(options).then(paths => { const numberOfPaths = paths ? paths.length : 0; // Telemetry @@ -1631,7 +1735,7 @@ class Dialogs { this.windowsMainService.open({ context: OpenContext.DIALOG, cli: this.environmentService.args, - pathsToOpen: paths, + urisToOpen: paths, forceNewWindow: options.forceNewWindow, forceOpenWorkspaceAsFile: options.dialogOptions && !equals(options.dialogOptions.filters, WORKSPACE_FILTER) }); @@ -1639,7 +1743,7 @@ class Dialogs { }); } - private getFileOrFolderPaths(options: IInternalNativeOpenDialogOptions): TPromise { + private getFileOrFolderUris(options: IInternalNativeOpenDialogOptions): TPromise { // Ensure dialog options if (!options.dialogOptions) { @@ -1677,7 +1781,7 @@ class Dialogs { // Remember path in storage for next time this.stateService.setItem(Dialogs.workingDirPickerStorageKey, dirname(paths[0])); - return paths; + return paths.map(path => URI.file(path)); } return void 0; @@ -1860,7 +1964,7 @@ class WorkspacesManager { } // Update window configuration properly based on transition to workspace - window.config.folderPath = void 0; + window.config.folderUri = void 0; window.config.workspace = workspace; window.config.backupPath = backupPath; @@ -1953,7 +2057,7 @@ class WorkspacesManager { private getUntitledWorkspaceSaveDialogDefaultPath(workspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): string { if (workspace) { if (isSingleFolderWorkspaceIdentifier(workspace)) { - return dirname(workspace); + return workspace.scheme === Schemas.file ? dirname(workspace.fsPath) : void 0; } const resolvedWorkspace = this.workspacesMainService.resolveWorkspaceSync(workspace.configPath); diff --git a/src/vs/code/node/windowsFinder.ts b/src/vs/code/node/windowsFinder.ts index d91192d413e..cd9bca97297 100644 --- a/src/vs/code/node/windowsFinder.ts +++ b/src/vs/code/node/windowsFinder.ts @@ -8,12 +8,14 @@ import * as platform from 'vs/base/common/platform'; import * as paths from 'vs/base/common/paths'; import { OpenContext } from 'vs/platform/windows/common/windows'; -import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IResolvedWorkspace } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, IResolvedWorkspace, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { Schemas } from 'vs/base/common/network'; +import URI from 'vs/base/common/uri'; +import { hasToIgnoreCase, isEqual } from 'vs/base/common/resources'; export interface ISimpleWindow { openedWorkspace?: IWorkspaceIdentifier; - openedFolderPath?: string; + openedFolderUri?: URI; openedFilePath?: string; extensionDevelopmentPath?: string; lastFocusTime: number; @@ -30,7 +32,7 @@ export interface IBestWindowOrFolderOptions { workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace; } -export function findBestWindowOrFolderForFile({ windows, newWindow, reuseWindow, context, filePath, workspaceResolver }: IBestWindowOrFolderOptions): W | string { +export function findBestWindowOrFolderForFile({ windows, newWindow, reuseWindow, context, filePath, workspaceResolver }: IBestWindowOrFolderOptions): W { if (!newWindow && filePath && (context === OpenContext.DESKTOP || context === OpenContext.CLI || context === OpenContext.DOCK)) { const windowOnFilePath = findWindowOnFilePath(windows, filePath, workspaceResolver); if (windowOnFilePath) { @@ -54,9 +56,9 @@ function findWindowOnFilePath(windows: W[], filePath: s } // Then go with single folder windows that are parent of the provided file path - const singleFolderWindowsOnFilePath = windows.filter(window => typeof window.openedFolderPath === 'string' && paths.isEqualOrParent(filePath, window.openedFolderPath, !platform.isLinux /* ignorecase */)); + const singleFolderWindowsOnFilePath = windows.filter(window => window.openedFolderUri && window.openedFolderUri.scheme === Schemas.file && paths.isEqualOrParent(filePath, window.openedFolderUri.fsPath, !platform.isLinux /* ignorecase */)); if (singleFolderWindowsOnFilePath.length) { - return singleFolderWindowsOnFilePath.sort((a, b) => -(a.openedFolderPath.length - b.openedFolderPath.length))[0]; + return singleFolderWindowsOnFilePath.sort((a, b) => -(a.openedFolderUri.path.length - b.openedFolderUri.path.length))[0]; } return null; @@ -73,7 +75,7 @@ export function findWindowOnWorkspace(windows: W[], wor // match on folder if (isSingleFolderWorkspaceIdentifier(workspace)) { - if (typeof window.openedFolderPath === 'string' && (paths.isEqual(window.openedFolderPath, workspace, !platform.isLinux /* ignorecase */))) { + if (window.openedFolderUri && isEqual(window.openedFolderUri, workspace, hasToIgnoreCase(window.openedFolderUri))) { return true; } } @@ -101,16 +103,16 @@ export function findWindowOnExtensionDevelopmentPath(wi })[0]; } -export function findWindowOnWorkspaceOrFolderPath(windows: W[], path: string): W { +export function findWindowOnWorkspaceOrFolderUri(windows: W[], uri: URI): W { return windows.filter(window => { // check for workspace config path - if (window.openedWorkspace && paths.isEqual(window.openedWorkspace.configPath, path, !platform.isLinux /* ignorecase */)) { + if (window.openedWorkspace && isEqual(URI.file(window.openedWorkspace.configPath), uri, !platform.isLinux /* ignorecase */)) { return true; } // check for folder path - if (window.openedFolderPath && paths.isEqual(window.openedFolderPath, path, !platform.isLinux /* ignorecase */)) { + if (window.openedFolderUri && isEqual(window.openedFolderUri, uri, hasToIgnoreCase(uri))) { return true; } diff --git a/src/vs/code/test/node/windowsFinder.test.ts b/src/vs/code/test/node/windowsFinder.test.ts index 084d8546224..d9d9a683643 100644 --- a/src/vs/code/test/node/windowsFinder.test.ts +++ b/src/vs/code/test/node/windowsFinder.test.ts @@ -10,8 +10,10 @@ import { findBestWindowOrFolderForFile, ISimpleWindow, IBestWindowOrFolderOption import { OpenContext } from 'vs/platform/windows/common/windows'; import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; +import URI from 'vs/base/common/uri'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; -const fixturesFolder = require.toUrl('./fixtures'); +const fixturesFolder = getPathFromAmdModule(require, './fixtures'); const testWorkspace: IWorkspaceIdentifier = { id: Date.now().toString(), @@ -30,10 +32,10 @@ function options(custom?: Partial>): I }; } -const vscodeFolderWindow = { lastFocusTime: 1, openedFolderPath: path.join(fixturesFolder, 'vscode_folder') }; -const lastActiveWindow = { lastFocusTime: 3, openedFolderPath: null }; -const noVscodeFolderWindow = { lastFocusTime: 2, openedFolderPath: path.join(fixturesFolder, 'no_vscode_folder') }; -const windows = [ +const vscodeFolderWindow: ISimpleWindow = { lastFocusTime: 1, openedFolderUri: URI.file(path.join(fixturesFolder, 'vscode_folder')) }; +const lastActiveWindow: ISimpleWindow = { lastFocusTime: 3, openedFolderUri: null }; +const noVscodeFolderWindow: ISimpleWindow = { lastFocusTime: 2, openedFolderUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder')) }; +const windows: ISimpleWindow[] = [ vscodeFolderWindow, lastActiveWindow, noVscodeFolderWindow, @@ -103,7 +105,7 @@ suite('WindowsFinder', () => { windows, filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt') })), vscodeFolderWindow); - const window = { lastFocusTime: 1, openedFolderPath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder') }; + const window: ISimpleWindow = { lastFocusTime: 1, openedFolderUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'nested_folder')) }; assert.equal(findBestWindowOrFolderForFile(options({ windows: [window], filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder', 'subfolder', 'file.txt') @@ -111,8 +113,8 @@ suite('WindowsFinder', () => { }); test('More specific existing window wins', () => { - const window = { lastFocusTime: 2, openedFolderPath: path.join(fixturesFolder, 'no_vscode_folder') }; - const nestedFolderWindow = { lastFocusTime: 1, openedFolderPath: path.join(fixturesFolder, 'no_vscode_folder', 'nested_folder') }; + const window: ISimpleWindow = { lastFocusTime: 2, openedFolderUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder')) }; + const nestedFolderWindow: ISimpleWindow = { lastFocusTime: 1, openedFolderUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder', 'nested_folder')) }; assert.equal(findBestWindowOrFolderForFile(options({ windows: [window, nestedFolderWindow], filePath: path.join(fixturesFolder, 'no_vscode_folder', 'nested_folder', 'subfolder', 'file.txt') @@ -120,7 +122,7 @@ suite('WindowsFinder', () => { }); test('Workspace folder wins', () => { - const window = { lastFocusTime: 1, openedWorkspace: testWorkspace }; + const window: ISimpleWindow = { lastFocusTime: 1, openedWorkspace: testWorkspace }; assert.equal(findBestWindowOrFolderForFile(options({ windows: [window], filePath: path.join(fixturesFolder, 'vscode_workspace_2_folder', 'nested_vscode_folder', 'subfolder', 'file.txt') diff --git a/src/vs/editor/browser/controller/coreCommands.ts b/src/vs/editor/browser/controller/coreCommands.ts index 05c024d8d8c..46a8671d6f4 100644 --- a/src/vs/editor/browser/controller/coreCommands.ts +++ b/src/vs/editor/browser/controller/coreCommands.ts @@ -5,6 +5,7 @@ 'use strict'; +import * as nls from 'vs/nls'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; @@ -16,7 +17,7 @@ import { registerEditorCommand, ICommandOptions, EditorCommand, Command } from ' import { IColumnSelectResult, ColumnSelection } from 'vs/editor/common/controller/cursorColumnSelection'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import H = editorCommon.Handler; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -26,8 +27,9 @@ import { TypeOperations } from 'vs/editor/common/controller/cursorTypeOperations import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations'; import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { MenuId } from 'vs/platform/actions/common/actions'; -const CORE_WEIGHT = KeybindingsRegistry.WEIGHT.editorCore(); +const CORE_WEIGHT = KeybindingWeight.EditorCore; export abstract class CoreEditorCommand extends EditorCommand { public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void { @@ -1620,7 +1622,7 @@ function findFocusedEditor(accessor: ServicesAccessor): ICodeEditor { } function registerCommand(command: Command) { - KeybindingsRegistry.registerCommandAndKeybindingRule(command.toCommandAndKeybindingRule(CORE_WEIGHT)); + command.register(); } /** @@ -1704,11 +1706,17 @@ registerCommand(new EditorOrNativeTextInputCommand({ editorHandler: CoreNavigationCommands.SelectAll, inputHandler: 'selectAll', id: 'editor.action.selectAll', - precondition: EditorContextKeys.focus, + precondition: EditorContextKeys.textInputFocus, kbOpts: { weight: CORE_WEIGHT, kbExpr: null, primary: KeyMod.CtrlCmd | KeyCode.KEY_A + }, + menubarOpts: { + menuId: MenuId.MenubarSelectionMenu, + group: '1_basic', + title: nls.localize({ key: 'miSelectAll', comment: ['&& denotes a mnemonic'] }, "&&Select All"), + order: 1 } })); @@ -1721,6 +1729,12 @@ registerCommand(new EditorOrNativeTextInputCommand({ weight: CORE_WEIGHT, kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyCode.KEY_Z + }, + menubarOpts: { + menuId: MenuId.MenubarEditMenu, + group: '1_do', + title: nls.localize({ key: 'miUndo', comment: ['&& denotes a mnemonic'] }, "&&Undo"), + order: 1 } })); registerCommand(new EditorHandlerCommand('default:' + H.Undo, H.Undo)); @@ -1736,6 +1750,12 @@ registerCommand(new EditorOrNativeTextInputCommand({ primary: KeyMod.CtrlCmd | KeyCode.KEY_Y, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z], mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z } + }, + menubarOpts: { + menuId: MenuId.MenubarEditMenu, + group: '1_do', + title: nls.localize({ key: 'miRedo', comment: ['&& denotes a mnemonic'] }, "&&Redo"), + order: 2 } })); registerCommand(new EditorHandlerCommand('default:' + H.Redo, H.Redo)); diff --git a/src/vs/editor/browser/controller/textAreaState.ts b/src/vs/editor/browser/controller/textAreaState.ts index 09f54a8a013..fb3249c7903 100644 --- a/src/vs/editor/browser/controller/textAreaState.ts +++ b/src/vs/editor/browser/controller/textAreaState.ts @@ -121,7 +121,7 @@ export class TextAreaState { // See https://github.com/Microsoft/vscode/issues/42251 // where typing always happens at offset 0 in the textarea // when using a custom title area in OSX and moving the window - if (strings.endsWith(currentValue, previousValue)) { + if (!strings.startsWith(currentValue, previousValue) && strings.endsWith(currentValue, previousValue)) { // Looks like something was typed at offset 0 // ==> pretend we placed the cursor at offset 0 to begin with... previousSelectionStart = 0; diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index 6d9e1f04119..1d45a7e2999 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -9,13 +9,13 @@ import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { ServicesAccessor, IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation'; import { CommandsRegistry, ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; -import { KeybindingsRegistry, ICommandAndKeybindingRule, IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { Position } from 'vs/editor/common/core/position'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { MenuId, MenuRegistry, IMenuItem } from 'vs/platform/actions/common/actions'; +import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -29,53 +29,83 @@ export type IEditorContributionCtor = IConstructorSignature1 this.runCommand(accessor, args), - weight: weight, - when: kbWhen, - primary: kbOpts.primary, - secondary: kbOpts.secondary, - win: kbOpts.win, - linux: kbOpts.linux, - mac: kbOpts.mac, - description: this._description - }; + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: this.id, + handler: (accessor, args) => this.runCommand(accessor, args), + weight: this._kbOpts.weight, + when: kbWhen, + primary: this._kbOpts.primary, + secondary: this._kbOpts.secondary, + win: this._kbOpts.win, + linux: this._kbOpts.linux, + mac: this._kbOpts.mac, + description: this._description + }); + + } else { + + CommandsRegistry.registerCommand({ + id: this.id, + handler: (accessor, args) => this.runCommand(accessor, args), + description: this._description + }); + } } public abstract runCommand(accessor: ServicesAccessor, args: any): void | TPromise; @@ -144,8 +174,8 @@ export abstract class EditorCommand extends Command { //#region EditorAction export interface IEditorCommandMenuOptions { - group?: string; - order?: number; + group: string; + order: number; when?: ContextKeyExpr; } export interface IActionOptions extends ICommandOptions { @@ -166,20 +196,21 @@ export abstract class EditorAction extends EditorCommand { this.menuOpts = opts.menuOpts; } - public toMenuItem(): IMenuItem { - if (!this.menuOpts) { - return null; + public register(): void { + + if (this.menuOpts) { + MenuRegistry.appendMenuItem(MenuId.EditorContext, { + command: { + id: this.id, + title: this.label + }, + when: ContextKeyExpr.and(this.precondition, this.menuOpts.when), + group: this.menuOpts.group, + order: this.menuOpts.order + }); } - return { - command: { - id: this.id, - title: this.label - }, - when: ContextKeyExpr.and(this.precondition, this.menuOpts.when), - group: this.menuOpts.group, - order: this.menuOpts.order - }; + super.register(); } public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void | TPromise { @@ -295,14 +326,7 @@ class EditorContributionRegistry { } public registerEditorAction(action: EditorAction) { - - let menuItem = action.toMenuItem(); - if (menuItem) { - MenuRegistry.appendMenuItem(MenuId.EditorContext, menuItem); - } - - KeybindingsRegistry.registerCommandAndKeybindingRule(action.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); - + action.register(); this.editorActions.push(action); } @@ -315,7 +339,7 @@ class EditorContributionRegistry { } public registerEditorCommand(editorCommand: EditorCommand) { - KeybindingsRegistry.registerCommandAndKeybindingRule(editorCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); + editorCommand.register(); this.editorCommands[editorCommand.id] = editorCommand; } diff --git a/src/vs/editor/browser/services/codeEditorServiceImpl.ts b/src/vs/editor/browser/services/codeEditorServiceImpl.ts index 9fad9a52fe4..aec6b1e4030 100644 --- a/src/vs/editor/browser/services/codeEditorServiceImpl.ts +++ b/src/vs/editor/browser/services/codeEditorServiceImpl.ts @@ -212,7 +212,7 @@ class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { const _CSS_MAP: { [prop: string]: string; } = { color: 'color:{0} !important;', - opacity: 'opacity:{0};', + opacity: 'opacity:{0}; will-change: opacity;', // TODO@Ben: 'will-change: opacity' is a workaround for https://github.com/Microsoft/vscode/issues/52196 backgroundColor: 'background-color:{0};', outline: 'outline:{0};', diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index 931531d2719..d5797c01a92 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -6,11 +6,11 @@ import URI from 'vs/base/common/uri'; import * as dom from 'vs/base/browser/dom'; +import * as resources from 'vs/base/common/resources'; import { parse } from 'vs/base/common/marshalling'; import { Schemas } from 'vs/base/common/network'; import { TPromise } from 'vs/base/common/winjs.base'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { normalize } from 'vs/base/common/paths'; import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -79,7 +79,7 @@ export class OpenerService implements IOpenerService { return TPromise.as(undefined); } else if (resource.scheme === Schemas.file) { - resource = resource.with({ path: normalize(resource.path) }); // workaround for non-normalized paths (https://github.com/Microsoft/vscode/issues/12954) + resource = resources.normalizePath(resource); // workaround for non-normalized paths (https://github.com/Microsoft/vscode/issues/12954) } promise = this._editorService.openCodeEditor({ resource, options: { selection, } }, this._editorService.getFocusedCodeEditor(), options && options.openToSide); } diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index fcccb8daffc..3f20b8b4a2d 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -1356,22 +1356,22 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE if (this.isSimpleWidget) { commandDelegate = { paste: (source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[]) => { - this.cursor.trigger(source, editorCommon.Handler.Paste, { text, pasteOnNewLine, multicursorText }); + this.trigger(source, editorCommon.Handler.Paste, { text, pasteOnNewLine, multicursorText }); }, type: (source: string, text: string) => { - this.cursor.trigger(source, editorCommon.Handler.Type, { text }); + this.trigger(source, editorCommon.Handler.Type, { text }); }, replacePreviousChar: (source: string, text: string, replaceCharCnt: number) => { - this.cursor.trigger(source, editorCommon.Handler.ReplacePreviousChar, { text, replaceCharCnt }); + this.trigger(source, editorCommon.Handler.ReplacePreviousChar, { text, replaceCharCnt }); }, compositionStart: (source: string) => { - this.cursor.trigger(source, editorCommon.Handler.CompositionStart, undefined); + this.trigger(source, editorCommon.Handler.CompositionStart, undefined); }, compositionEnd: (source: string) => { - this.cursor.trigger(source, editorCommon.Handler.CompositionEnd, undefined); + this.trigger(source, editorCommon.Handler.CompositionEnd, undefined); }, cut: (source: string) => { - this.cursor.trigger(source, editorCommon.Handler.Cut, undefined); + this.trigger(source, editorCommon.Handler.Cut, undefined); } }; } else { @@ -1550,6 +1550,8 @@ class EditorContextKeysManager extends Disposable { private _editorReadonly: IContextKey; private _hasMultipleSelections: IContextKey; private _hasNonEmptySelection: IContextKey; + private _canUndo: IContextKey; + private _canRedo: IContextKey; constructor( editor: CodeEditorWidget, @@ -1567,6 +1569,8 @@ class EditorContextKeysManager extends Disposable { this._editorReadonly = EditorContextKeys.readOnly.bindTo(contextKeyService); this._hasMultipleSelections = EditorContextKeys.hasMultipleSelections.bindTo(contextKeyService); this._hasNonEmptySelection = EditorContextKeys.hasNonEmptySelection.bindTo(contextKeyService); + this._canUndo = EditorContextKeys.canUndo.bindTo(contextKeyService); + this._canRedo = EditorContextKeys.canRedo.bindTo(contextKeyService); this._register(this._editor.onDidChangeConfiguration(() => this._updateFromConfig())); this._register(this._editor.onDidChangeCursorSelection(() => this._updateFromSelection())); @@ -1574,10 +1578,13 @@ class EditorContextKeysManager extends Disposable { this._register(this._editor.onDidBlurEditorWidget(() => this._updateFromFocus())); this._register(this._editor.onDidFocusEditorText(() => this._updateFromFocus())); this._register(this._editor.onDidBlurEditorText(() => this._updateFromFocus())); + this._register(this._editor.onDidChangeModel(() => this._updateFromModel())); + this._register(this._editor.onDidChangeConfiguration(() => this._updateFromModel())); this._updateFromConfig(); this._updateFromSelection(); this._updateFromFocus(); + this._updateFromModel(); } private _updateFromConfig(): void { @@ -1603,6 +1610,12 @@ class EditorContextKeysManager extends Disposable { this._editorTextFocus.set(this._editor.hasTextFocus() && !this._editor.isSimpleWidget); this._textInputFocus.set(this._editor.hasTextFocus()); } + + private _updateFromModel(): void { + const model = this._editor.getModel(); + this._canUndo.set(model && model.canUndo()); + this._canRedo.set(model && model.canRedo()); + } } export class EditorModeContext extends Disposable { @@ -1805,7 +1818,7 @@ registerThemingParticipant((theme, collector) => { const unnecessaryForeground = theme.getColor(editorUnnecessaryCodeOpacity); if (unnecessaryForeground) { - collector.addRule(`.${SHOW_UNUSED_ENABLED_CLASS} .monaco-editor .${ClassName.EditorUnnecessaryInlineDecoration} { opacity: ${unnecessaryForeground.rgba.a}; }`); + collector.addRule(`.${SHOW_UNUSED_ENABLED_CLASS} .monaco-editor .${ClassName.EditorUnnecessaryInlineDecoration} { opacity: ${unnecessaryForeground.rgba.a}; will-change: opacity; }`); // TODO@Ben: 'will-change: opacity' is a workaround for https://github.com/Microsoft/vscode/issues/52196 } const unnecessaryBorder = theme.getColor(editorUnnecessaryCodeBorder); diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index a3f19a76245..dbd07dd8a74 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -1177,7 +1177,7 @@ interface IDataSource { getModifiedEditor(): editorBrowser.ICodeEditor; } -abstract class DiffEditorWidgetStyle extends Disposable { +abstract class DiffEditorWidgetStyle extends Disposable implements IDiffEditorWidgetStyle { _dataSource: IDataSource; _insertColor: Color; @@ -1228,6 +1228,9 @@ abstract class DiffEditorWidgetStyle extends Disposable { protected abstract _getViewZones(lineChanges: editorCommon.ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor, renderIndicators: boolean): IEditorsZones; protected abstract _getOriginalEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations; protected abstract _getModifiedEditorDecorations(lineChanges: editorCommon.ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, originalEditor: editorBrowser.ICodeEditor, modifiedEditor: editorBrowser.ICodeEditor): IEditorDiffDecorations; + + public abstract setEnableSplitViewResizing(enableSplitViewResizing: boolean): void; + public abstract layout(): number; } interface IMyViewZone extends editorBrowser.IViewZone { @@ -1529,10 +1532,6 @@ class DiffEdtorWidgetSideBySide extends DiffEditorWidgetStyle implements IDiffEd this._sash.onDidReset(() => this.onSashReset()); } - public dispose(): void { - super.dispose(); - } - public setEnableSplitViewResizing(enableSplitViewResizing: boolean): void { let newDisableSash = (enableSplitViewResizing === false); if (this._disableSash !== newDisableSash) { @@ -1778,10 +1777,6 @@ class DiffEdtorWidgetInline extends DiffEditorWidgetStyle implements IDiffEditor })); } - public dispose(): void { - super.dispose(); - } - public setEnableSplitViewResizing(enableSplitViewResizing: boolean): void { // Nothing to do.. } diff --git a/src/vs/editor/browser/widget/diffReview.ts b/src/vs/editor/browser/widget/diffReview.ts index c06799df4c5..fe42bf6cc0d 100644 --- a/src/vs/editor/browser/widget/diffReview.ts +++ b/src/vs/editor/browser/widget/diffReview.ts @@ -30,6 +30,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ITextModel, TextModelResolvedOptions } from 'vs/editor/common/model'; import { ViewLineRenderingData } from 'vs/editor/common/viewModel/viewModel'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; const DIFF_LINES_PADDING = 3; @@ -810,7 +811,8 @@ class DiffReviewNext extends EditorAction { precondition: ContextKeyExpr.has('isInDiffEditor'), kbOpts: { kbExpr: null, - primary: KeyCode.F7 + primary: KeyCode.F7, + weight: KeybindingWeight.EditorContrib } }); } @@ -832,7 +834,8 @@ class DiffReviewPrev extends EditorAction { precondition: ContextKeyExpr.has('isInDiffEditor'), kbOpts: { kbExpr: null, - primary: KeyMod.Shift | KeyCode.F7 + primary: KeyMod.Shift | KeyCode.F7, + weight: KeybindingWeight.EditorContrib } }); } diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index 556665f4a6e..07380bbb371 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -83,6 +83,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed this._rawOptions.minimap = objects.mixin({}, this._rawOptions.minimap || {}); this._rawOptions.find = objects.mixin({}, this._rawOptions.find || {}); this._rawOptions.hover = objects.mixin({}, this._rawOptions.hover || {}); + this._rawOptions.parameterHints = objects.mixin({}, this._rawOptions.parameterHints || {}); this._validatedOptions = editorOptions.EditorOptionsValidator.validate(this._rawOptions, EDITOR_DEFAULTS); this.editor = null; @@ -246,7 +247,7 @@ const editorConfiguration: IConfigurationNode = { 'editor.lineHeight': { 'type': 'number', 'default': EDITOR_FONT_DEFAULTS.lineHeight, - 'description': nls.localize('lineHeight', "Controls the line height. Use 0 to compute the lineHeight from the fontSize.") + 'description': nls.localize('lineHeight', "Controls the line height. Use 0 to compute the line height from the font size.") }, 'editor.letterSpacing': { 'type': 'number', @@ -271,55 +272,55 @@ const editorConfiguration: IConfigurationNode = { 'type': 'number' }, 'default': EDITOR_DEFAULTS.viewInfo.rulers, - 'description': nls.localize('rulers', "Render vertical rulers after a certain number of monospace characters. Use multiple values for multiple rulers. No rulers are drawn if array is empty") + 'description': nls.localize('rulers', "Render vertical rulers after a certain number of monospace characters. Use multiple values for multiple rulers. No rulers are drawn if array is empty.") }, 'editor.wordSeparators': { 'type': 'string', 'default': EDITOR_DEFAULTS.wordSeparators, - 'description': nls.localize('wordSeparators', "Characters that will be used as word separators when doing word related navigations or operations") + 'description': nls.localize('wordSeparators', "Characters that will be used as word separators when doing word related navigations or operations.") }, 'editor.tabSize': { 'type': 'number', 'default': EDITOR_MODEL_DEFAULTS.tabSize, 'minimum': 1, - 'description': nls.localize('tabSize', "The number of spaces a tab is equal to. This setting is overridden based on the file contents when [`editor.detectIndentation`](#editor.detectIndentation) is on."), + 'markdownDescription': nls.localize('tabSize', "The number of spaces a tab is equal to. This setting is overridden based on the file contents when `#editor.detectIndentation#` is on."), 'errorMessage': nls.localize('tabSize.errorMessage', "Expected 'number'. Note that the value \"auto\" has been replaced by the `editor.detectIndentation` setting.") }, 'editor.insertSpaces': { 'type': 'boolean', 'default': EDITOR_MODEL_DEFAULTS.insertSpaces, - 'description': nls.localize('insertSpaces', "Insert spaces when pressing Tab. This setting is overridden based on the file contents when [`editor.detectIndentation`](#editor.detectIndentation) is on."), + 'markdownDescription': nls.localize('insertSpaces', "Insert spaces when pressing `Tab`. This setting is overridden based on the file contents when `#editor.detectIndentation#` is on."), 'errorMessage': nls.localize('insertSpaces.errorMessage', "Expected 'boolean'. Note that the value \"auto\" has been replaced by the `editor.detectIndentation` setting.") }, 'editor.detectIndentation': { 'type': 'boolean', 'default': EDITOR_MODEL_DEFAULTS.detectIndentation, - 'description': nls.localize('detectIndentation', "When opening a file, [`editor.tabSize`](#editor.tabSize) and [`editor.insertSpaces`](#editor.insertSpaces) will be detected based on the file contents.") + 'markdownDescription': nls.localize('detectIndentation', "Controls whether `#editor.tabSize#` and `#editor.insertSpaces#` will be automatically detected when a file is opened based on the file contents.") }, 'editor.roundedSelection': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.viewInfo.roundedSelection, - 'description': nls.localize('roundedSelection', "Controls if selections have rounded corners") + 'description': nls.localize('roundedSelection', "Controls whether selections should have rounded corners.") }, 'editor.scrollBeyondLastLine': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.viewInfo.scrollBeyondLastLine, - 'description': nls.localize('scrollBeyondLastLine', "Controls if the editor will scroll beyond the last line") + 'description': nls.localize('scrollBeyondLastLine', "Controls whether the editor will scroll beyond the last line.") }, 'editor.scrollBeyondLastColumn': { 'type': 'number', 'default': EDITOR_DEFAULTS.viewInfo.scrollBeyondLastColumn, - 'description': nls.localize('scrollBeyondLastColumn', "Controls the number of extra characters beyond which the editor will scroll horizontally") + 'description': nls.localize('scrollBeyondLastColumn', "Controls the number of extra characters beyond which the editor will scroll horizontally.") }, 'editor.smoothScrolling': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.viewInfo.smoothScrolling, - 'description': nls.localize('smoothScrolling', "Controls if the editor will scroll using an animation") + 'description': nls.localize('smoothScrolling', "Controls whether the editor will scroll using an animation.") }, 'editor.minimap.enabled': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.viewInfo.minimap.enabled, - 'description': nls.localize('minimap.enabled', "Controls if the minimap is shown") + 'description': nls.localize('minimap.enabled', "Controls whether the minimap is shown.") }, 'editor.minimap.side': { 'type': 'string', @@ -336,48 +337,48 @@ const editorConfiguration: IConfigurationNode = { 'editor.minimap.renderCharacters': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.viewInfo.minimap.renderCharacters, - 'description': nls.localize('minimap.renderCharacters', "Render the actual characters on a line (as opposed to color blocks)") + 'description': nls.localize('minimap.renderCharacters', "Render the actual characters on a line as opposed to color blocks.") }, 'editor.minimap.maxColumn': { 'type': 'number', 'default': EDITOR_DEFAULTS.viewInfo.minimap.maxColumn, - 'description': nls.localize('minimap.maxColumn', "Limit the width of the minimap to render at most a certain number of columns") + 'description': nls.localize('minimap.maxColumn', "Limit the width of the minimap to render at most a certain number of columns.") }, 'editor.hover.enabled': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.contribInfo.hover.enabled, - 'description': nls.localize('hover.enabled', "Controls if the hover is shown") + 'description': nls.localize('hover.enabled', "Controls whether the hover is shown.") }, 'editor.hover.delay': { 'type': 'number', 'default': EDITOR_DEFAULTS.contribInfo.hover.delay, - 'description': nls.localize('hover.delay', "Controls the delay after which to show the hover") + 'description': nls.localize('hover.delay', "Time delay in milliseconds after which to the hover is shown.") }, 'editor.hover.sticky': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.contribInfo.hover.sticky, - 'description': nls.localize('hover.sticky', "Controls if the hover should remain visible when mouse is moved over it") + 'description': nls.localize('hover.sticky', "Controls whether the hover should remain visible when mouse is moved over it.") }, 'editor.find.seedSearchStringFromSelection': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.contribInfo.find.seedSearchStringFromSelection, - 'description': nls.localize('find.seedSearchStringFromSelection', "Controls if we seed the search string in Find Widget from editor selection") + 'description': nls.localize('find.seedSearchStringFromSelection', "Controls whether the search string in the Find Widget is seeded from the editor selection.") }, 'editor.find.autoFindInSelection': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.contribInfo.find.autoFindInSelection, - 'description': nls.localize('find.autoFindInSelection', "Controls if Find in Selection flag is turned on when multiple characters or lines of text are selected in the editor") + 'description': nls.localize('find.autoFindInSelection', "Controls whether the find operation is carried on selected text or the entire file in the editor.") }, 'editor.find.globalFindClipboard': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.contribInfo.find.globalFindClipboard, - 'description': nls.localize('find.globalFindClipboard', "Controls if the Find Widget should read or modify the shared find clipboard on macOS"), + 'description': nls.localize('find.globalFindClipboard', "Controls whether the Find Widget should read or modify the shared find clipboard on macOS."), 'included': platform.isMacintosh }, 'editor.wordWrap': { 'type': 'string', 'enum': ['off', 'on', 'wordWrapColumn', 'bounded'], - 'enumDescriptions': [ + 'markdownEnumDescriptions': [ nls.localize('wordWrap.off', "Lines will never wrap."), nls.localize('wordWrap.on', "Lines will wrap at the viewport width."), nls.localize({ @@ -385,14 +386,14 @@ const editorConfiguration: IConfigurationNode = { comment: [ '- `editor.wordWrapColumn` refers to a different setting and should not be localized.' ] - }, "Lines will wrap at `editor.wordWrapColumn`."), + }, "Lines will wrap at `#editor.wordWrapColumn#`."), nls.localize({ key: 'wordWrap.bounded', comment: [ '- viewport means the edge of the visible window size.', '- `editor.wordWrapColumn` refers to a different setting and should not be localized.' ] - }, "Lines will wrap at the minimum of viewport and `editor.wordWrapColumn`."), + }, "Lines will wrap at the minimum of viewport and `#editor.wordWrapColumn#`."), ], 'default': EDITOR_DEFAULTS.wordWrap, 'description': nls.localize({ @@ -401,30 +402,36 @@ const editorConfiguration: IConfigurationNode = { '- \'off\', \'on\', \'wordWrapColumn\' and \'bounded\' refer to values the setting can take and should not be localized.', '- `editor.wordWrapColumn` refers to a different setting and should not be localized.' ] - }, "Controls how lines should wrap. Can be:\n - 'off' (disable wrapping),\n - 'on' (viewport wrapping),\n - 'wordWrapColumn' (wrap at `editor.wordWrapColumn`) or\n - 'bounded' (wrap at minimum of viewport and `editor.wordWrapColumn`).") + }, "Controls how lines should wrap.") }, 'editor.wordWrapColumn': { 'type': 'integer', 'default': EDITOR_DEFAULTS.wordWrapColumn, 'minimum': 1, - 'description': nls.localize({ + 'markdownDescription': nls.localize({ key: 'wordWrapColumn', comment: [ '- `editor.wordWrap` refers to a different setting and should not be localized.', '- \'wordWrapColumn\' and \'bounded\' refer to values the different setting can take and should not be localized.' ] - }, "Controls the wrapping column of the editor when `editor.wordWrap` is 'wordWrapColumn' or 'bounded'.") + }, "Controls the wrapping column of the editor when `#editor.wordWrap#` is `wordWrapColumn` or `bounded`.") }, 'editor.wrappingIndent': { 'type': 'string', 'enum': ['none', 'same', 'indent', 'deepIndent'], + enumDescriptions: [ + nls.localize('wrappingIndent.none', "No indentation. Wrapped lines begin at column 1."), + nls.localize('wrappingIndent.same', "Wrapped lines get the same indentation as the parent."), + nls.localize('wrappingIndent.indent', "Wrapped lines get +1 indentation toward the parent."), + nls.localize('wrappingIndent.deepIndent', "Wrapped lines get +2 indentation toward the parent."), + ], 'default': 'same', - 'description': nls.localize('wrappingIndent', "Controls the indentation of wrapped lines. Can be one of 'none', 'same', 'indent' or 'deepIndent'.") + 'description': nls.localize('wrappingIndent', "Controls the indentation of wrapped lines."), }, 'editor.mouseWheelScrollSensitivity': { 'type': 'number', 'default': EDITOR_DEFAULTS.viewInfo.scrollbar.mouseWheelScrollSensitivity, - 'description': nls.localize('mouseWheelScrollSensitivity', "A multiplier to be used on the `deltaX` and `deltaY` of mouse wheel scroll events") + 'markdownDescription': nls.localize('mouseWheelScrollSensitivity', "A multiplier to be used on the `deltaX` and `deltaY` of mouse wheel scroll events.") }, 'editor.multiCursorModifier': { 'type': 'string', @@ -434,13 +441,13 @@ const editorConfiguration: IConfigurationNode = { nls.localize('multiCursorModifier.alt', "Maps to `Alt` on Windows and Linux and to `Option` on macOS.") ], 'default': 'alt', - 'description': nls.localize({ + 'markdownDescription': nls.localize({ key: 'multiCursorModifier', comment: [ '- `ctrlCmd` refers to a value the setting can take and should not be localized.', '- `Control` and `Command` refer to the modifier keys Ctrl or Cmd on the keyboard and can be localized.' ] - }, "The modifier to be used to add multiple cursors with the mouse. `ctrlCmd` maps to `Control` on Windows and Linux and to `Command` on macOS. The Go To Definition and Open Link mouse gestures will adapt such that they do not conflict with the multicursor modifier. [Read more](https://code.visualstudio.com/docs/editor/codebasics#_multicursor-modifier)") + }, "The modifier to be used to add multiple cursors with the mouse. The Go To Definition and Open Link mouse gestures will adapt such that they do not conflict with the multicursor modifier. [Read more](https://code.visualstudio.com/docs/editor/codebasics#_multicursor-modifier).") }, 'editor.multiCursorMergeOverlapping': { 'type': 'boolean', @@ -474,54 +481,64 @@ const editorConfiguration: IConfigurationNode = { } ], 'default': EDITOR_DEFAULTS.contribInfo.quickSuggestions, - 'description': nls.localize('quickSuggestions', "Controls if suggestions should automatically show up while typing") + 'description': nls.localize('quickSuggestions', "Controls whether suggestions should automatically show up while typing.") }, 'editor.quickSuggestionsDelay': { 'type': 'integer', 'default': EDITOR_DEFAULTS.contribInfo.quickSuggestionsDelay, 'minimum': 0, - 'description': nls.localize('quickSuggestionsDelay', "Controls the delay in ms after which quick suggestions will show up") + 'description': nls.localize('quickSuggestionsDelay', "Controls the delay in milliseconds after which quick suggestions will show up.") }, - 'editor.parameterHints': { + 'editor.parameterHints.enabled': { 'type': 'boolean', - 'default': EDITOR_DEFAULTS.contribInfo.parameterHints, - 'description': nls.localize('parameterHints', "Enables pop-up that shows parameter documentation and type information as you type") + 'default': EDITOR_DEFAULTS.contribInfo.parameterHints.enabled, + 'description': nls.localize('parameterHints.enabled', "Enables a pop-up that shows parameter documentation and type information as you type.") + }, + 'editor.parameterHints.cycle': { + 'type': 'boolean', + 'default': EDITOR_DEFAULTS.contribInfo.parameterHints.cycle, + 'description': nls.localize('parameterHints.cycle', "Controls whether the parameter hints menu cycles or closes when reaching the end of the list.") }, 'editor.autoClosingBrackets': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.autoClosingBrackets, - 'description': nls.localize('autoClosingBrackets', "Controls if the editor should automatically close brackets after opening them") + 'description': nls.localize('autoClosingBrackets', "Controls whether the editor should automatically close brackets after the user adds an opening bracket.") }, 'editor.formatOnType': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.contribInfo.formatOnType, - 'description': nls.localize('formatOnType', "Controls if the editor should automatically format the line after typing") + 'description': nls.localize('formatOnType', "Controls whether the editor should automatically format the line after typing.") }, 'editor.formatOnPaste': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.contribInfo.formatOnPaste, - 'description': nls.localize('formatOnPaste', "Controls if the editor should automatically format the pasted content. A formatter must be available and the formatter should be able to format a range in a document.") + 'description': nls.localize('formatOnPaste', "Controls whether the editor should automatically format the pasted content. A formatter must be available and the formatter should be able to format a range in a document.") }, 'editor.autoIndent': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.autoIndent, - 'description': nls.localize('autoIndent', "Controls if the editor should automatically adjust the indentation when users type, paste or move lines. Indentation rules of the language must be available.") + 'description': nls.localize('autoIndent', "Controls whether the editor should automatically adjust the indentation when users type, paste or move lines. Extensions with indentation rules of the language must be available.") }, 'editor.suggestOnTriggerCharacters': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.contribInfo.suggestOnTriggerCharacters, - 'description': nls.localize('suggestOnTriggerCharacters', "Controls if suggestions should automatically show up when typing trigger characters") + 'description': nls.localize('suggestOnTriggerCharacters', "Controls whether suggestions should automatically show up when typing trigger characters.") }, 'editor.acceptSuggestionOnEnter': { 'type': 'string', 'enum': ['on', 'smart', 'off'], 'default': EDITOR_DEFAULTS.contribInfo.acceptSuggestionOnEnter, - 'description': nls.localize('acceptSuggestionOnEnter', "Controls if suggestions should be accepted on 'Enter' - in addition to 'Tab'. Helps to avoid ambiguity between inserting new lines or accepting suggestions. The value 'smart' means only accept a suggestion with Enter when it makes a textual change") + 'enumDescriptions': [ + '', + nls.localize('acceptSuggestionOnEnterSmart', "Only accept a suggestion with `Enter` when it makes a textual change."), + '' + ], + 'markdownDescription': nls.localize('acceptSuggestionOnEnter', "Controls whether suggestions should be accepted on `Enter`, in addition to `Tab`. Helps to avoid ambiguity between inserting new lines or accepting suggestions.") }, 'editor.acceptSuggestionOnCommitCharacter': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.contribInfo.acceptSuggestionOnCommitCharacter, - 'description': nls.localize('acceptSuggestionOnCommitCharacter', "Controls if suggestions should be accepted on commit characters. For instance in JavaScript the semi-colon (';') can be a commit character that accepts a suggestion and types that character.") + 'markdownDescription': nls.localize('acceptSuggestionOnCommitCharacter', "Controls whether suggestions should be accepted on commit characters. For example, in JavaScript, the semi-colon (`;`) can be a commit character that accepts a suggestion and types that character.") }, 'editor.snippetSuggestions': { 'type': 'string', @@ -560,13 +577,13 @@ const editorConfiguration: IConfigurationNode = { 'type': 'integer', 'default': 0, 'minimum': 0, - 'description': nls.localize('suggestFontSize', "Font size for the suggest widget") + 'description': nls.localize('suggestFontSize', "Font size for the suggest widget.") }, 'editor.suggestLineHeight': { 'type': 'integer', 'default': 0, 'minimum': 0, - 'description': nls.localize('suggestLineHeight', "Line height for the suggest widget") + 'description': nls.localize('suggestLineHeight', "Line height for the suggest widget.") }, 'editor.suggest.filterGraceful': { type: 'boolean', @@ -581,22 +598,22 @@ const editorConfiguration: IConfigurationNode = { 'editor.selectionHighlight': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.contribInfo.selectionHighlight, - 'description': nls.localize('selectionHighlight', "Controls whether the editor should highlight similar matches to the selection") + 'description': nls.localize('selectionHighlight', "Controls whether the editor should highlight matches similar to the selection") }, 'editor.occurrencesHighlight': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.contribInfo.occurrencesHighlight, - 'description': nls.localize('occurrencesHighlight', "Controls whether the editor should highlight semantic symbol occurrences") + 'description': nls.localize('occurrencesHighlight', "Controls whether the editor should highlight semantic symbol occurrences.") }, 'editor.overviewRulerLanes': { 'type': 'integer', 'default': 3, - 'description': nls.localize('overviewRulerLanes', "Controls the number of decorations that can show up at the same position in the overview ruler") + 'description': nls.localize('overviewRulerLanes', "Controls the number of decorations that can show up at the same position in the overview ruler.") }, 'editor.overviewRulerBorder': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.viewInfo.overviewRulerBorder, - 'description': nls.localize('overviewRulerBorder', "Controls if a border should be drawn around the overview ruler.") + 'description': nls.localize('overviewRulerBorder', "Controls whether a border should be drawn around the overview ruler.") }, 'editor.cursorBlinking': { 'type': 'string', @@ -607,60 +624,71 @@ const editorConfiguration: IConfigurationNode = { 'editor.mouseWheelZoom': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.viewInfo.mouseWheelZoom, - 'description': nls.localize('mouseWheelZoom', "Zoom the font of the editor when using mouse wheel and holding Ctrl") + 'markdownDescription': nls.localize('mouseWheelZoom', "Zoom the font of the editor when using mouse wheel and holding `Ctrl`.") }, 'editor.cursorStyle': { 'type': 'string', 'enum': ['block', 'block-outline', 'line', 'line-thin', 'underline', 'underline-thin'], 'default': editorOptions.cursorStyleToString(EDITOR_DEFAULTS.viewInfo.cursorStyle), - 'description': nls.localize('cursorStyle', "Controls the cursor style, accepted values are 'block', 'block-outline', 'line', 'line-thin', 'underline' and 'underline-thin'") + 'description': nls.localize('cursorStyle', "Controls the cursor style.") }, 'editor.cursorWidth': { 'type': 'integer', 'default': EDITOR_DEFAULTS.viewInfo.cursorWidth, - 'description': nls.localize('cursorWidth', "Controls the width of the cursor when editor.cursorStyle is set to 'line'") + 'markdownDescription': nls.localize('cursorWidth', "Controls the width of the cursor when `#editor.cursorStyle#` is set to `line`.") }, 'editor.fontLigatures': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.viewInfo.fontLigatures, - 'description': nls.localize('fontLigatures', "Enables font ligatures") + 'description': nls.localize('fontLigatures', "Enables/Disables font ligatures.") }, 'editor.hideCursorInOverviewRuler': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.viewInfo.hideCursorInOverviewRuler, - 'description': nls.localize('hideCursorInOverviewRuler', "Controls if the cursor should be hidden in the overview ruler.") + 'description': nls.localize('hideCursorInOverviewRuler', "Controls whether the cursor should be hidden in the overview ruler.") }, 'editor.renderWhitespace': { 'type': 'string', 'enum': ['none', 'boundary', 'all'], + 'enumDescriptions': [ + '', + nls.localize('renderWhiteSpace.boundary', "Render whitespace characters except for single spaces between words."), + '' + ], default: EDITOR_DEFAULTS.viewInfo.renderWhitespace, - description: nls.localize('renderWhitespace', "Controls how the editor should render whitespace characters, possibilities are 'none', 'boundary', and 'all'. The 'boundary' option does not render single spaces between words.") + description: nls.localize('renderWhitespace', "Controls how the editor should render whitespace characters.") }, 'editor.renderControlCharacters': { 'type': 'boolean', default: EDITOR_DEFAULTS.viewInfo.renderControlCharacters, - description: nls.localize('renderControlCharacters', "Controls whether the editor should render control characters") + description: nls.localize('renderControlCharacters', "Controls whether the editor should render control characters.") }, 'editor.renderIndentGuides': { 'type': 'boolean', default: EDITOR_DEFAULTS.viewInfo.renderIndentGuides, - description: nls.localize('renderIndentGuides', "Controls whether the editor should render indent guides") + description: nls.localize('renderIndentGuides', "Controls whether the editor should render indent guides.") }, 'editor.highlightActiveIndentGuide': { 'type': 'boolean', default: EDITOR_DEFAULTS.viewInfo.highlightActiveIndentGuide, - description: nls.localize('highlightActiveIndentGuide', "Controls whether the editor should highlight the active indent guide") + description: nls.localize('highlightActiveIndentGuide', "Controls whether the editor should highlight the active indent guide.") }, 'editor.renderLineHighlight': { 'type': 'string', 'enum': ['none', 'gutter', 'line', 'all'], + 'enumDescriptions': [ + '', + '', + '', + nls.localize('renderLineHighlight.all', "Highlights both the gutter and the current line."), + ], default: EDITOR_DEFAULTS.viewInfo.renderLineHighlight, - description: nls.localize('renderLineHighlight', "Controls how the editor should render the current line highlight, possibilities are 'none', 'gutter', 'line', and 'all'.") + description: nls.localize('renderLineHighlight', "Controls how the editor should render the current line highlight.") }, 'editor.codeLens': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.contribInfo.codeLens, - 'description': nls.localize('codeLens', "Controls if the editor shows CodeLens") + 'description': nls.localize('codeLens', "Controls whether the editor shows CodeLens") }, 'editor.folding': { 'type': 'boolean', @@ -670,12 +698,8 @@ const editorConfiguration: IConfigurationNode = { 'editor.foldingStrategy': { 'type': 'string', 'enum': ['auto', 'indentation'], - 'enumDescriptions': [ - nls.localize('foldingStrategyAuto', 'If available, use a language specific folding strategy, otherwise falls back to the indentation based strategy.'), - nls.localize('foldingStrategyIndentation', 'Always use the indentation based folding strategy') - ], 'default': EDITOR_DEFAULTS.contribInfo.foldingStrategy, - 'description': nls.localize('foldingStrategy', "Controls the way folding ranges are computed. 'auto' picks uses a language specific folding strategy, if available. 'indentation' forces that the indentation based folding strategy is used.") + 'markdownDescription': nls.localize('foldingStrategy', "Controls the strategy for computing folding ranges. `auto` uses a language specific folding strategy, if available. `indentation` uses the indentation based folding strategy.") }, 'editor.showFoldingControls': { 'type': 'string', @@ -696,22 +720,22 @@ const editorConfiguration: IConfigurationNode = { 'editor.useTabStops': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.useTabStops, - 'description': nls.localize('useTabStops', "Inserting and deleting whitespace follows tab stops") + 'description': nls.localize('useTabStops', "Inserting and deleting whitespace follows tab stops.") }, 'editor.trimAutoWhitespace': { 'type': 'boolean', 'default': EDITOR_MODEL_DEFAULTS.trimAutoWhitespace, - 'description': nls.localize('trimAutoWhitespace', "Remove trailing auto inserted whitespace") + 'description': nls.localize('trimAutoWhitespace', "Remove trailing auto inserted whitespace.") }, 'editor.stablePeek': { 'type': 'boolean', 'default': false, - 'description': nls.localize('stablePeek', "Keep peek editors open even when double clicking their content or when hitting Escape.") + 'markdownDescription': nls.localize('stablePeek', "Keep peek editors open even when double clicking their content or when hitting `Escape`.") }, 'editor.dragAndDrop': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.dragAndDrop, - 'description': nls.localize('dragAndDrop', "Controls if the editor should allow to move selections via drag and drop.") + 'description': nls.localize('dragAndDrop', "Controls whether the editor should allow moving selections via drag and drop.") }, 'editor.accessibilitySupport': { 'type': 'string', @@ -732,7 +756,7 @@ const editorConfiguration: IConfigurationNode = { 'editor.links': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.contribInfo.links, - 'description': nls.localize('links', "Controls whether the editor should detect links and make them clickable") + 'description': nls.localize('links', "Controls whether the editor should detect links and make them clickable.") }, 'editor.colorDecorators': { 'type': 'boolean', @@ -742,14 +766,14 @@ const editorConfiguration: IConfigurationNode = { 'editor.lightbulb.enabled': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.contribInfo.lightbulbEnabled, - 'description': nls.localize('codeActions', "Enables the code action lightbulb") + 'description': nls.localize('codeActions', "Enables the code action lightbulb in the editor.") }, 'editor.codeActionsOnSave': { 'type': 'object', 'properties': { 'source.organizeImports': { 'type': 'boolean', - 'description': nls.localize('codeActionsOnSave.organizeImports', "Run organize imports on save?") + 'description': nls.localize('codeActionsOnSave.organizeImports', "Controls whether organize imports action should be run on file save.") } }, 'additionalProperties': { @@ -761,23 +785,23 @@ const editorConfiguration: IConfigurationNode = { 'editor.codeActionsOnSaveTimeout': { 'type': 'number', 'default': EDITOR_DEFAULTS.contribInfo.codeActionsOnSaveTimeout, - 'description': nls.localize('codeActionsOnSaveTimeout', "Timeout for code actions run on save.") + 'description': nls.localize('codeActionsOnSaveTimeout', "Timeout in milliseconds after which the code actions that are run on save are cancelled.") }, 'editor.selectionClipboard': { 'type': 'boolean', 'default': EDITOR_DEFAULTS.contribInfo.selectionClipboard, - 'description': nls.localize('selectionClipboard', "Controls if the Linux primary clipboard should be supported."), + 'description': nls.localize('selectionClipboard', "Controls whether the Linux primary clipboard should be supported."), 'included': platform.isLinux }, 'diffEditor.renderSideBySide': { 'type': 'boolean', 'default': true, - 'description': nls.localize('sideBySide', "Controls if the diff editor shows the diff side by side or inline") + 'description': nls.localize('sideBySide', "Controls whether the diff editor shows the diff side by side or inline.") }, 'diffEditor.ignoreTrimWhitespace': { 'type': 'boolean', 'default': true, - 'description': nls.localize('ignoreTrimWhitespace', "Controls if the diff editor shows changes in leading or trailing whitespace as diffs") + 'description': nls.localize('ignoreTrimWhitespace', "Controls whether the diff editor shows changes in leading or trailing whitespace as diffs.") }, 'editor.largeFileOptimizations': { 'type': 'boolean', @@ -787,7 +811,7 @@ const editorConfiguration: IConfigurationNode = { 'diffEditor.renderIndicators': { 'type': 'boolean', 'default': true, - 'description': nls.localize('renderIndicators', "Controls if the diff editor shows +/- indicators for added/removed changes") + 'description': nls.localize('renderIndicators', "Controls whether the diff editor shows +/- indicators for added/removed changes.") } } }; diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 14365ee9925..3a510380338 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -158,6 +158,22 @@ export interface IEditorHoverOptions { sticky?: boolean; } +/** + * Configuration options for parameter hints + */ +export interface IEditorParameterHintOptions { + /** + * Enable parameter hints. + * Defaults to true. + */ + enabled?: boolean; + /** + * Enable cycling of parameter hints. + * Defaults to false. + */ + cycle?: boolean; +} + export interface ISuggestOptions { /** * Enable graceful matching. Defaults to true. @@ -451,9 +467,9 @@ export interface IEditorOptions { */ quickSuggestionsDelay?: number; /** - * Enables parameter hints + * Parameter hint options. */ - parameterHints?: boolean; + parameterHints?: IEditorParameterHintOptions; /** * Render icons in suggestions box. * Defaults to true. @@ -851,6 +867,11 @@ export interface InternalSuggestOptions { readonly snippetsPreventQuickSuggestions: boolean; } +export interface InternalParameterHintOptions { + readonly enabled: boolean; + readonly cycle: boolean; +} + export interface EditorWrappingInfo { readonly inDiffEditor: boolean; readonly isDominatedByLongLines: boolean; @@ -911,7 +932,7 @@ export interface EditorContribOptions { readonly contextmenu: boolean; readonly quickSuggestions: boolean | { other: boolean, comments: boolean, strings: boolean }; readonly quickSuggestionsDelay: number; - readonly parameterHints: boolean; + readonly parameterHints: InternalParameterHintOptions; readonly iconsInSuggestions: boolean; readonly formatOnType: boolean; readonly formatOnPaste: boolean; @@ -1237,6 +1258,16 @@ export class InternalEditorOptions { ); } + /** + * @internal + */ + private static _equalsParameterHintOptions(a: InternalParameterHintOptions, b: InternalParameterHintOptions): boolean { + return ( + a.enabled === b.enabled + && a.cycle === b.cycle + ); + } + /** * @internal */ @@ -1291,7 +1322,7 @@ export class InternalEditorOptions { && a.contextmenu === b.contextmenu && InternalEditorOptions._equalsQuickSuggestions(a.quickSuggestions, b.quickSuggestions) && a.quickSuggestionsDelay === b.quickSuggestionsDelay - && a.parameterHints === b.parameterHints + && this._equalsParameterHintOptions(a.parameterHints, b.parameterHints) && a.iconsInSuggestions === b.iconsInSuggestions && a.formatOnType === b.formatOnType && a.formatOnPaste === b.formatOnPaste @@ -1732,6 +1763,17 @@ export class EditorOptionsValidator { }; } + private static _sanitizeParameterHintOpts(opts: IEditorParameterHintOptions, defaults: InternalParameterHintOptions): InternalParameterHintOptions { + if (typeof opts !== 'object') { + return defaults; + } + + return { + enabled: _boolean(opts.enabled, defaults.enabled), + cycle: _boolean(opts.cycle, defaults.cycle) + }; + } + private static _santizeHoverOpts(_opts: boolean | IEditorHoverOptions, defaults: InternalEditorHoverOptions): InternalEditorHoverOptions { let opts: IEditorHoverOptions; if (typeof _opts === 'boolean') { @@ -1752,13 +1794,11 @@ export class EditorOptionsValidator { } private static _sanitizeSuggestOpts(opts: IEditorOptions, defaults: InternalSuggestOptions): InternalSuggestOptions { - if (!opts.suggest) { - return defaults; - } + const suggestOpts = opts.suggest || {}; return { - filterGraceful: _boolean(opts.suggest.filterGraceful, defaults.filterGraceful), + filterGraceful: _boolean(suggestOpts.filterGraceful, defaults.filterGraceful), snippets: _stringSet<'top' | 'bottom' | 'inline' | 'none'>(opts.snippetSuggestions, defaults.snippets, ['top', 'bottom', 'inline', 'none']), - snippetsPreventQuickSuggestions: _boolean(opts.suggest.snippetsPreventQuickSuggestions, defaults.filterGraceful), + snippetsPreventQuickSuggestions: _boolean(suggestOpts.snippetsPreventQuickSuggestions, defaults.filterGraceful), }; } @@ -1886,7 +1926,7 @@ export class EditorOptionsValidator { contextmenu: _boolean(opts.contextmenu, defaults.contextmenu), quickSuggestions: quickSuggestions, quickSuggestionsDelay: _clampedInt(opts.quickSuggestionsDelay, defaults.quickSuggestionsDelay, Constants.MIN_SAFE_SMALL_INTEGER, Constants.MAX_SAFE_SMALL_INTEGER), - parameterHints: _boolean(opts.parameterHints, defaults.parameterHints), + parameterHints: this._sanitizeParameterHintOpts(opts.parameterHints, defaults.parameterHints), iconsInSuggestions: _boolean(opts.iconsInSuggestions, defaults.iconsInSuggestions), formatOnType: _boolean(opts.formatOnType, defaults.formatOnType), formatOnPaste: _boolean(opts.formatOnPaste, defaults.formatOnPaste), @@ -2465,7 +2505,10 @@ export const EDITOR_DEFAULTS: IValidatedEditorOptions = { contextmenu: true, quickSuggestions: { other: true, comments: false, strings: false }, quickSuggestionsDelay: 10, - parameterHints: true, + parameterHints: { + enabled: true, + cycle: false + }, iconsInSuggestions: true, formatOnType: false, formatOnPaste: false, diff --git a/src/vs/editor/common/controller/cursorWordOperations.ts b/src/vs/editor/common/controller/cursorWordOperations.ts index b69d9799b53..da290068cf9 100644 --- a/src/vs/editor/common/controller/cursorWordOperations.ts +++ b/src/vs/editor/common/controller/cursorWordOperations.ts @@ -466,22 +466,37 @@ export class WordOperations { } export function _lastWordPartEnd(str: string, startIndex: number = str.length - 1): number { + let ignoreUpperCase = !strings.isLowerAsciiLetter(str.charCodeAt(startIndex + 1)); for (let i = startIndex; i >= 0; i--) { let chCode = str.charCodeAt(i); - if (chCode === CharCode.Space || chCode === CharCode.Tab || strings.isUpperAsciiLetter(chCode) || chCode === CharCode.Underline) { + if (chCode === CharCode.Space || chCode === CharCode.Tab || (!ignoreUpperCase && strings.isUpperAsciiLetter(chCode)) || chCode === CharCode.Underline) { return i - 1; } + if (ignoreUpperCase && i < startIndex && strings.isLowerAsciiLetter(chCode)) { + return i; + } + ignoreUpperCase = ignoreUpperCase && strings.isUpperAsciiLetter(chCode); } return -1; } -export function _nextWordPartBegin(str: string, startIndex: number = str.length - 1): number { - const checkLowerCase = str.charCodeAt(startIndex - 1) === CharCode.Space; // does a lc char count as a part start? +export function _nextWordPartBegin(str: string, startIndex: number = 0): number { + let prevChCode = str.charCodeAt(startIndex - 1); + let chCode = str.charCodeAt(startIndex); + // handle the special case ' X' and ' x' which is different from the standard methods + if ((prevChCode === CharCode.Space || prevChCode === CharCode.Tab) && (strings.isLowerAsciiLetter(chCode) || strings.isUpperAsciiLetter(chCode))) { + return startIndex + 1; + } + let ignoreUpperCase = strings.isUpperAsciiLetter(chCode); for (let i = startIndex; i < str.length; ++i) { - let chCode = str.charCodeAt(i); - if (chCode === CharCode.Space || chCode === CharCode.Tab || strings.isUpperAsciiLetter(chCode) || (checkLowerCase && strings.isLowerAsciiLetter(chCode))) { + chCode = str.charCodeAt(i); + if (chCode === CharCode.Space || chCode === CharCode.Tab || (!ignoreUpperCase && strings.isUpperAsciiLetter(chCode))) { return i + 1; } + if (ignoreUpperCase && strings.isLowerAsciiLetter(chCode)) { + return i; // multiple UPPERCase : assume an upper case word and a CamelCase word - like DSLModel + } + ignoreUpperCase = ignoreUpperCase && strings.isUpperAsciiLetter(chCode); if (chCode === CharCode.Underline) { return i + 2; } diff --git a/src/vs/editor/common/editorContextKeys.ts b/src/vs/editor/common/editorContextKeys.ts index e6cd1d9caf5..c306f76352c 100644 --- a/src/vs/editor/common/editorContextKeys.ts +++ b/src/vs/editor/common/editorContextKeys.ts @@ -30,6 +30,8 @@ export namespace EditorContextKeys { export const tabMovesFocus = new RawContextKey('editorTabMovesFocus', false); export const tabDoesNotMoveFocus: ContextKeyExpr = tabMovesFocus.toNegated(); export const isInEmbeddedEditor = new RawContextKey('isInEmbeddedEditor', undefined); + export const canUndo = new RawContextKey('canUndo', false); + export const canRedo = new RawContextKey('canRedo', false); // -- mode context keys export const languageId = new RawContextKey('editorLangId', undefined); diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index e4a6ba3feed..d37319c25b5 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -1030,6 +1030,12 @@ export interface ITextModel { */ undo(): Selection[]; + /** + * Is there anything in the undo stack? + * @internal + */ + canUndo(): boolean; + /** * Redo edit operations until the next stop point created by `pushStackElement`. * The inverse edit operations will be pushed on the undo stack. @@ -1037,6 +1043,12 @@ export interface ITextModel { */ redo(): Selection[]; + /** + * Is there anything in the redo stack? + * @internal + */ + canRedo(): boolean; + /** * @deprecated Please use `onDidChangeContent` instead. * An event emitted when the contents of the model have changed. diff --git a/src/vs/editor/common/model/editStack.ts b/src/vs/editor/common/model/editStack.ts index 13a955cf522..5f5e3066845 100644 --- a/src/vs/editor/common/model/editStack.ts +++ b/src/vs/editor/common/model/editStack.ts @@ -210,6 +210,10 @@ export class EditStack { return null; } + public canUndo(): boolean { + return (this.past.length > 0); + } + public redo(): IUndoRedoResult { if (this.future.length > 0) { @@ -233,4 +237,8 @@ export class EditStack { return null; } + + public canRedo(): boolean { + return (this.future.length > 0); + } } diff --git a/src/vs/editor/common/model/intervalTree.ts b/src/vs/editor/common/model/intervalTree.ts index 8f8c9de9ba5..28839e413af 100644 --- a/src/vs/editor/common/model/intervalTree.ts +++ b/src/vs/editor/common/model/intervalTree.ts @@ -12,14 +12,14 @@ import { IModelDecoration, TrackedRangeStickiness as ActualTrackedRangeStickines // The red-black tree is based on the "Introduction to Algorithms" by Cormen, Leiserson and Rivest. // -export const ClassName = { - EditorHintDecoration: 'squiggly-hint', - EditorInfoDecoration: 'squiggly-info', - EditorWarningDecoration: 'squiggly-warning', - EditorErrorDecoration: 'squiggly-error', - EditorUnnecessaryDecoration: 'squiggly-unnecessary', - EditorUnnecessaryInlineDecoration: 'squiggly-inline-unnecessary' -}; +export const enum ClassName { + EditorHintDecoration = 'squiggly-hint', + EditorInfoDecoration = 'squiggly-info', + EditorWarningDecoration = 'squiggly-warning', + EditorErrorDecoration = 'squiggly-error', + EditorUnnecessaryDecoration = 'squiggly-unnecessary', + EditorUnnecessaryInlineDecoration = 'squiggly-inline-unnecessary' +} /** * Describes the behavior of decorations when typing/editing near their edges. diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 1a4005d6a1b..7bddfeb4441 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -1416,6 +1416,10 @@ export class TextModel extends Disposable implements model.ITextModel { } } + public canUndo(): boolean { + return this._commandManager.canUndo(); + } + private _redo(): Selection[] { this._isRedoing = true; let r = this._commandManager.redo(); @@ -1441,6 +1445,10 @@ export class TextModel extends Disposable implements model.ITextModel { } } + public canRedo(): boolean { + return this._commandManager.canRedo(); + } + //#endregion //#region Decorations diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 6b8d3fb2aff..0f643685413 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -554,7 +554,7 @@ export interface DefinitionProvider { /** * Provide the definition of the symbol at the given position and document. */ - provideDefinition(model: model.ITextModel, position: Position, token: CancellationToken): DefinitionLink | Thenable; + provideDefinition(model: model.ITextModel, position: Position, token: CancellationToken): Definition | DefinitionLink[] | Thenable; } /** @@ -565,7 +565,7 @@ export interface ImplementationProvider { /** * Provide the implementation of the symbol at the given position and document. */ - provideImplementation(model: model.ITextModel, position: Position, token: CancellationToken): Definition | Thenable; + provideImplementation(model: model.ITextModel, position: Position, token: CancellationToken): Definition | DefinitionLink[] | Thenable; } /** @@ -576,7 +576,7 @@ export interface TypeDefinitionProvider { /** * Provide the type definition of the symbol at the given position and document. */ - provideTypeDefinition(model: model.ITextModel, position: Position, token: CancellationToken): Definition | Thenable; + provideTypeDefinition(model: model.ITextModel, position: Position, token: CancellationToken): Definition | DefinitionLink[] | Thenable; } /** @@ -646,7 +646,7 @@ export const symbolKindToCssClass = (function () { _fromMapping[SymbolKind.TypeParameter] = 'type-parameter'; return function toCssClassName(kind: SymbolKind): string { - return _fromMapping[kind] || 'property'; + return `symbol-icon ${_fromMapping[kind] || 'property'}`; }; })(); @@ -848,12 +848,12 @@ export interface FoldingRangeProvider { export interface FoldingRange { /** - * The zero-based start line of the range to fold. The folded area starts after the line's last character. + * The one-based start line of the range to fold. The folded area starts after the line's last character. */ start: number; /** - * The zero-based end line of the range to fold. The folded area ends with the line's last character. + * The one-based end line of the range to fold. The folded area ends with the line's last character. */ end: number; diff --git a/src/vs/editor/common/modes/languageConfigurationRegistry.ts b/src/vs/editor/common/modes/languageConfigurationRegistry.ts index a184362fd69..842df862701 100644 --- a/src/vs/editor/common/modes/languageConfigurationRegistry.ts +++ b/src/vs/editor/common/modes/languageConfigurationRegistry.ts @@ -13,7 +13,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { ITextModel } from 'vs/editor/common/model'; import { onUnexpectedError } from 'vs/base/common/errors'; import * as strings from 'vs/base/common/strings'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { DEFAULT_WORD_REGEXP, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper'; import { createScopedLineTokens } from 'vs/editor/common/modes/supports'; import { LineTokens } from 'vs/editor/common/core/lineTokens'; @@ -189,14 +189,12 @@ export class LanguageConfigurationRegistryImpl { let current = new RichEditSupport(languageIdentifier, previous, configuration); this._entries[languageIdentifier.id] = current; this._onDidChange.fire({ languageIdentifier }); - return { - dispose: () => { - if (this._entries[languageIdentifier.id] === current) { - this._entries[languageIdentifier.id] = previous; - this._onDidChange.fire({ languageIdentifier }); - } + return toDisposable(() => { + if (this._entries[languageIdentifier.id] === current) { + this._entries[languageIdentifier.id] = previous; + this._onDidChange.fire({ languageIdentifier }); } - }; + }); } private _getRichEditSupport(languageId: LanguageId): RichEditSupport { diff --git a/src/vs/editor/common/modes/languageFeatureRegistry.ts b/src/vs/editor/common/modes/languageFeatureRegistry.ts index c5e4e7010d8..9dfd73755f2 100644 --- a/src/vs/editor/common/modes/languageFeatureRegistry.ts +++ b/src/vs/editor/common/modes/languageFeatureRegistry.ts @@ -6,7 +6,7 @@ 'use strict'; import { Event, Emitter } from 'vs/base/common/event'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ITextModel } from 'vs/editor/common/model'; import { LanguageSelector, score } from 'vs/editor/common/modes/languageSelector'; import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService'; @@ -54,19 +54,17 @@ export default class LanguageFeatureRegistry { this._lastCandidate = undefined; this._onDidChange.fire(this._entries.length); - return { - dispose: () => { - if (entry) { - let idx = this._entries.indexOf(entry); - if (idx >= 0) { - this._entries.splice(idx, 1); - this._lastCandidate = undefined; - this._onDidChange.fire(this._entries.length); - entry = undefined; - } + return toDisposable(() => { + if (entry) { + let idx = this._entries.indexOf(entry); + if (idx >= 0) { + this._entries.splice(idx, 1); + this._lastCandidate = undefined; + this._onDidChange.fire(this._entries.length); + entry = undefined; } } - }; + }); } has(model: ITextModel): boolean { diff --git a/src/vs/editor/common/modes/tokenizationRegistry.ts b/src/vs/editor/common/modes/tokenizationRegistry.ts index 660ef056881..dd13f213e76 100644 --- a/src/vs/editor/common/modes/tokenizationRegistry.ts +++ b/src/vs/editor/common/modes/tokenizationRegistry.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { ColorId, ITokenizationRegistry, ITokenizationSupport, ITokenizationSupportChangedEvent } from 'vs/editor/common/modes'; import { Color } from 'vs/base/common/color'; @@ -33,15 +33,13 @@ export class TokenizationRegistryImpl implements ITokenizationRegistry { public register(language: string, support: ITokenizationSupport): IDisposable { this._map[language] = support; this.fire([language]); - return { - dispose: () => { - if (this._map[language] !== support) { - return; - } - delete this._map[language]; - this.fire([language]); + return toDisposable(() => { + if (this._map[language] !== support) { + return; } - }; + delete this._map[language]; + this.fire([language]); + }); } public get(language: string): ITokenizationSupport { diff --git a/src/vs/editor/common/services/editorSimpleWorker.ts b/src/vs/editor/common/services/editorSimpleWorker.ts index 5e5f192fb1a..d478fef73c6 100644 --- a/src/vs/editor/common/services/editorSimpleWorker.ts +++ b/src/vs/editor/common/services/editorSimpleWorker.ts @@ -16,7 +16,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; import { Position, IPosition } from 'vs/editor/common/core/position'; import { MirrorTextModel as BaseMirrorModel, IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel'; import { IInplaceReplaceSupportResult, ILink, ISuggestResult, ISuggestion, TextEdit } from 'vs/editor/common/modes'; -import { computeLinks } from 'vs/editor/common/modes/linkComputer'; +import { computeLinks, ILinkComputerTarget } from 'vs/editor/common/modes/linkComputer'; import { BasicInplaceReplace } from 'vs/editor/common/modes/supports/inplaceReplaceSupport'; import { getWordAtText, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper'; import { createMonacoBaseAPI } from 'vs/editor/common/standalone/standaloneBase'; @@ -50,7 +50,7 @@ export interface IRawModelData { /** * @internal */ -export interface ICommonModel { +export interface ICommonModel extends ILinkComputerTarget, IMirrorModel { uri: URI; version: number; eol: string; diff --git a/src/vs/editor/common/services/editorWorkerServiceImpl.ts b/src/vs/editor/common/services/editorWorkerServiceImpl.ts index efbf104f703..b03f956e982 100644 --- a/src/vs/editor/common/services/editorWorkerServiceImpl.ts +++ b/src/vs/editor/common/services/editorWorkerServiceImpl.ts @@ -5,7 +5,7 @@ 'use strict'; import { IntervalTimer, ShallowCancelThenPromise, wireCancellationToken } from 'vs/base/common/async'; -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { SimpleWorkerClient, logOnceWebWorkerWarning } from 'vs/base/common/worker/simpleWorker'; @@ -285,11 +285,9 @@ class EditorModelManager extends Disposable { toDispose.push(model.onWillDispose(() => { this._stopModelSync(modelUrl); })); - toDispose.push({ - dispose: () => { - this._proxy.acceptRemovedModel(modelUrl); - } - }); + toDispose.push(toDisposable(() => { + this._proxy.acceptRemovedModel(modelUrl); + })); this._syncedModels[modelUrl] = toDispose; } diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index 56a282da585..560ed428507 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -85,9 +85,12 @@ class ModelMarkerHandler { let ret = Range.lift(rawMarker); - if (rawMarker.severity === MarkerSeverity.Hint && Range.spansMultipleLines(ret)) { - // never render hints on multiple lines - ret = ret.setEndPosition(ret.startLineNumber, ret.startColumn); + if (rawMarker.severity === MarkerSeverity.Hint) { + // * never render hints on multiple lines + // * make enough space for three dots + if (Range.spansMultipleLines(ret) || ret.endColumn - ret.startColumn < 2) { + ret = ret.setEndPosition(ret.startLineNumber, ret.startColumn + 2); + } } ret = model.validateRange(ret); diff --git a/src/vs/editor/common/view/editorColorRegistry.ts b/src/vs/editor/common/view/editorColorRegistry.ts index 74dfb7da133..c4c369cb0d2 100644 --- a/src/vs/editor/common/view/editorColorRegistry.ts +++ b/src/vs/editor/common/view/editorColorRegistry.ts @@ -21,9 +21,9 @@ export const editorCursorBackground = registerColor('editorCursor.background', n export const editorWhitespaces = registerColor('editorWhitespace.foreground', { dark: '#e3e4e229', light: '#33333333', hc: '#e3e4e229' }, nls.localize('editorWhitespaces', 'Color of whitespace characters in the editor.')); export const editorIndentGuides = registerColor('editorIndentGuide.background', { dark: editorWhitespaces, light: editorWhitespaces, hc: editorWhitespaces }, nls.localize('editorIndentGuides', 'Color of the editor indentation guides.')); export const editorActiveIndentGuides = registerColor('editorIndentGuide.activeBackground', { dark: editorWhitespaces, light: editorWhitespaces, hc: editorWhitespaces }, nls.localize('editorActiveIndentGuide', 'Color of the active editor indentation guides.')); -export const editorLineNumbers = registerColor('editorLineNumber.foreground', { dark: '#5A5A5A', light: '#2B91AF', hc: Color.white }, nls.localize('editorLineNumbers', 'Color of editor line numbers.')); +export const editorLineNumbers = registerColor('editorLineNumber.foreground', { dark: '#858585', light: '#237893', hc: Color.white }, nls.localize('editorLineNumbers', 'Color of editor line numbers.')); -const deprecatedEditorActiveLineNumber = registerColor('editorActiveLineNumber.foreground', { dark: '#AAAAAA', light: '#0B216F', hc: activeContrastBorder }, nls.localize('editorActiveLineNumber', 'Color of editor active line number'), false, nls.localize('deprecatedEditorActiveLineNumber', 'Id is deprecated. Use \'editorLineNumber.activeForeground\' instead.')); +const deprecatedEditorActiveLineNumber = registerColor('editorActiveLineNumber.foreground', { dark: '#c6c6c6', light: '#0B216F', hc: activeContrastBorder }, nls.localize('editorActiveLineNumber', 'Color of editor active line number'), false, nls.localize('deprecatedEditorActiveLineNumber', 'Id is deprecated. Use \'editorLineNumber.activeForeground\' instead.')); export const editorActiveLineNumber = registerColor('editorLineNumber.activeForeground', { dark: deprecatedEditorActiveLineNumber, light: deprecatedEditorActiveLineNumber, hc: deprecatedEditorActiveLineNumber }, nls.localize('editorActiveLineNumber', 'Color of editor active line number')); export const editorRuler = registerColor('editorRuler.foreground', { dark: '#5A5A5A', light: Color.lightgrey, hc: Color.white }, nls.localize('editorRuler', 'Color of the editor rulers.')); diff --git a/src/vs/editor/common/view/viewEvents.ts b/src/vs/editor/common/view/viewEvents.ts index d28e49abcfe..a406a7bddcf 100644 --- a/src/vs/editor/common/view/viewEvents.ts +++ b/src/vs/editor/common/view/viewEvents.ts @@ -9,7 +9,7 @@ import { Selection } from 'vs/editor/common/core/selection'; import { ScrollEvent } from 'vs/base/common/scrollable'; import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; import * as errors from 'vs/base/common/errors'; -import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { ScrollType } from 'vs/editor/common/editorCommon'; export const enum ViewEventType { @@ -354,17 +354,15 @@ export class ViewEventEmitter extends Disposable { public addEventListener(listener: (events: ViewEvent[]) => void): IDisposable { this._listeners.push(listener); - return { - dispose: () => { - let listeners = this._listeners; - for (let i = 0, len = listeners.length; i < len; i++) { - if (listeners[i] === listener) { - listeners.splice(i, 1); - break; - } + return toDisposable(() => { + let listeners = this._listeners; + for (let i = 0, len = listeners.length; i < len; i++) { + if (listeners[i] === listener) { + listeners.splice(i, 1); + break; } } - }; + }); } } diff --git a/src/vs/editor/contrib/bracketMatching/bracketMatching.ts b/src/vs/editor/contrib/bracketMatching/bracketMatching.ts index b2573d26fa0..ed17ffb44dc 100644 --- a/src/vs/editor/contrib/bracketMatching/bracketMatching.ts +++ b/src/vs/editor/contrib/bracketMatching/bracketMatching.ts @@ -22,6 +22,7 @@ import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { registerColor } from 'vs/platform/theme/common/colorRegistry'; import { TrackedRangeStickiness, IModelDeltaDecoration, OverviewRulerLane } from 'vs/editor/common/model'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; const overviewRulerBracketMatchForeground = registerColor('editorOverviewRuler.bracketMatchForeground', { dark: '#A0A0A0', light: '#A0A0A0', hc: '#A0A0A0' }, nls.localize('overviewRulerBracketMatchForeground', 'Overview ruler marker color for matching brackets.')); @@ -34,7 +35,8 @@ class JumpToBracketAction extends EditorAction { precondition: null, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_BACKSLASH + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_BACKSLASH, + weight: KeybindingWeight.EditorContrib } }); } @@ -116,7 +118,13 @@ export class BracketMatchingController extends Disposable implements editorCommo this._updateBracketsSoon.schedule(); })); - this._register(editor.onDidChangeModel((e) => { this._decorations = []; this._updateBracketsSoon.schedule(); })); + this._register(editor.onDidChangeModelContent((e) => { + this._updateBracketsSoon.schedule(); + })); + this._register(editor.onDidChangeModel((e) => { + this._decorations = []; + this._updateBracketsSoon.schedule(); + })); this._register(editor.onDidChangeModelLanguageConfiguration((e) => { this._lastBracketsData = []; this._updateBracketsSoon.schedule(); diff --git a/src/vs/editor/contrib/caretOperations/transpose.ts b/src/vs/editor/contrib/caretOperations/transpose.ts index 6523327b253..ee59ca9c28d 100644 --- a/src/vs/editor/contrib/caretOperations/transpose.ts +++ b/src/vs/editor/contrib/caretOperations/transpose.ts @@ -15,6 +15,7 @@ import { registerEditorAction, EditorAction, ServicesAccessor } from 'vs/editor/ import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ITextModel } from 'vs/editor/common/model'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; class TransposeLettersAction extends EditorAction { @@ -67,7 +68,8 @@ class TransposeLettersAction extends EditorAction { primary: 0, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_T - } + }, + weight: KeybindingWeight.EditorContrib } }); } diff --git a/src/vs/editor/contrib/clipboard/clipboard.ts b/src/vs/editor/contrib/clipboard/clipboard.ts index 97f62935e1f..4d0ec22210d 100644 --- a/src/vs/editor/contrib/clipboard/clipboard.ts +++ b/src/vs/editor/contrib/clipboard/clipboard.ts @@ -16,6 +16,8 @@ import { registerEditorAction, IActionOptions, EditorAction, ICommandKeybindings import { CopyOptions } from 'vs/editor/browser/controller/textAreaInput'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { MenuId } from 'vs/platform/actions/common/actions'; const CLIPBOARD_CONTEXT_MENU_GROUP = '9_cutcopypaste'; @@ -62,7 +64,8 @@ class ExecCommandCutAction extends ExecCommandAction { let kbOpts: ICommandKeybindingsOptions = { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyCode.KEY_X, - win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_X, secondary: [KeyMod.Shift | KeyCode.Delete] } + win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_X, secondary: [KeyMod.Shift | KeyCode.Delete] }, + weight: KeybindingWeight.EditorContrib }; // Do not bind cut keybindings in the browser, // since browsers do that for us and it avoids security prompts @@ -78,6 +81,12 @@ class ExecCommandCutAction extends ExecCommandAction { menuOpts: { group: CLIPBOARD_CONTEXT_MENU_GROUP, order: 1 + }, + menubarOpts: { + menuId: MenuId.MenubarEditMenu, + group: '2_ccp', + title: nls.localize({ key: 'miCut', comment: ['&& denotes a mnemonic'] }, "Cu&&t"), + order: 1 } }); } @@ -99,7 +108,8 @@ class ExecCommandCopyAction extends ExecCommandAction { let kbOpts: ICommandKeybindingsOptions = { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyCode.KEY_C, - win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_C, secondary: [KeyMod.CtrlCmd | KeyCode.Insert] } + win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_C, secondary: [KeyMod.CtrlCmd | KeyCode.Insert] }, + weight: KeybindingWeight.EditorContrib }; // Do not bind copy keybindings in the browser, // since browsers do that for us and it avoids security prompts @@ -116,6 +126,12 @@ class ExecCommandCopyAction extends ExecCommandAction { menuOpts: { group: CLIPBOARD_CONTEXT_MENU_GROUP, order: 2 + }, + menubarOpts: { + menuId: MenuId.MenubarEditMenu, + group: '2_ccp', + title: nls.localize({ key: 'miCopy', comment: ['&& denotes a mnemonic'] }, "&&Copy"), + order: 2 } }); } @@ -137,7 +153,8 @@ class ExecCommandPasteAction extends ExecCommandAction { let kbOpts: ICommandKeybindingsOptions = { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyCode.KEY_V, - win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] } + win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] }, + weight: KeybindingWeight.EditorContrib }; // Do not bind paste keybindings in the browser, // since browsers do that for us and it avoids security prompts @@ -154,6 +171,12 @@ class ExecCommandPasteAction extends ExecCommandAction { menuOpts: { group: CLIPBOARD_CONTEXT_MENU_GROUP, order: 3 + }, + menubarOpts: { + menuId: MenuId.MenubarEditMenu, + group: '2_ccp', + title: nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"), + order: 3 } }); } @@ -169,7 +192,8 @@ class ExecCommandCopyWithSyntaxHighlightingAction extends ExecCommandAction { precondition: null, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: null + primary: null, + weight: KeybindingWeight.EditorContrib } }); } diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 1d3e5175ff2..1f229bfbfbc 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -26,6 +26,7 @@ import { CodeActionModel, CodeActionsComputeEvent, SUPPORTED_CODE_ACTIONS } from import { CodeActionAutoApply, CodeActionFilter, CodeActionKind } from './codeActionTrigger'; import { CodeActionContextMenu } from './codeActionWidget'; import { LightBulbWidget } from './lightBulbWidget'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; function contextKeyForSupportedActions(kind: CodeActionKind) { return ContextKeyExpr.regex( @@ -192,12 +193,13 @@ export class QuickFixAction extends EditorAction { precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyMod.CtrlCmd | KeyCode.US_DOT + primary: KeyMod.CtrlCmd | KeyCode.US_DOT, + weight: KeybindingWeight.EditorContrib } }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { return showCodeActionsForEditorSelection(editor, nls.localize('editor.action.quickFix.noneMessage', "No code actions available")); } } @@ -250,7 +252,7 @@ export class CodeActionCommand extends EditorCommand { }); } - public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, userArg: any) { + public runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor, userArg: any) { const args = CodeActionCommandArgs.fromUser(userArg); return showCodeActionsForEditorSelection(editor, nls.localize('editor.action.quickFix.noneMessage', "No code actions available"), { kind: args.kind, includeSourceActions: true }, args.apply); } @@ -272,7 +274,8 @@ export class RefactorAction extends EditorAction { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_R, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_R - } + }, + weight: KeybindingWeight.EditorContrib }, menuOpts: { group: '1_modification', @@ -284,7 +287,7 @@ export class RefactorAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { return showCodeActionsForEditorSelection(editor, nls.localize('editor.action.refactor.noneMessage', "No refactorings available"), { kind: CodeActionKind.Refactor }, @@ -313,7 +316,7 @@ export class SourceAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { return showCodeActionsForEditorSelection(editor, nls.localize('editor.action.source.noneMessage', "No source actions available"), { kind: CodeActionKind.Source, includeSourceActions: true }, @@ -335,12 +338,13 @@ export class OrganizeImportsAction extends EditorAction { contextKeyForSupportedActions(CodeActionKind.SourceOrganizeImports)), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_O + primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_O, + weight: KeybindingWeight.EditorContrib } }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { return showCodeActionsForEditorSelection(editor, nls.localize('editor.action.organize.noneMessage', "No organize imports action available"), { kind: CodeActionKind.SourceOrganizeImports, includeSourceActions: true }, diff --git a/src/vs/editor/contrib/codelens/codelensController.ts b/src/vs/editor/contrib/codelens/codelensController.ts index be43648e661..23fde7dc577 100644 --- a/src/vs/editor/contrib/codelens/codelensController.ts +++ b/src/vs/editor/contrib/codelens/codelensController.ts @@ -5,20 +5,20 @@ 'use strict'; -import { RunOnceScheduler, CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; +import { CancelablePromise, createCancelablePromise, RunOnceScheduler } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { CodeLensProviderRegistry, ICodeLensSymbol } from 'vs/editor/common/modes'; +import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { StableEditorScrollState } from 'vs/editor/browser/core/editorState'; import * as editorBrowser from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { ICodeLensData, getCodeLensData } from './codelens'; import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions'; -import { CodeLens, CodeLensHelper } from 'vs/editor/contrib/codelens/codelensWidget'; +import * as editorCommon from 'vs/editor/common/editorCommon'; import { IModelDecorationsChangeAccessor } from 'vs/editor/common/model'; +import { CodeLensProviderRegistry, ICodeLensSymbol } from 'vs/editor/common/modes'; +import { CodeLens, CodeLensHelper } from 'vs/editor/contrib/codelens/codelensWidget'; +import { ICommandService } from 'vs/platform/commands/common/commands'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { StableEditorScrollState } from 'vs/editor/browser/core/editorState'; +import { getCodeLensData, ICodeLensData } from './codelens'; export class CodeLensContribution implements editorCommon.IEditorContribution { @@ -167,22 +167,20 @@ export class CodeLensContribution implements editorCommon.IEditorContribution { this._localToDispose.push(this._editor.onDidLayoutChange(e => { this._detectVisibleLenses.schedule(); })); - this._localToDispose.push({ - dispose: () => { - if (this._editor.getModel()) { - const scrollState = StableEditorScrollState.capture(this._editor); - this._editor.changeDecorations((changeAccessor) => { - this._editor.changeViewZones((accessor) => { - this._disposeAllLenses(changeAccessor, accessor); - }); + this._localToDispose.push(toDisposable(() => { + if (this._editor.getModel()) { + const scrollState = StableEditorScrollState.capture(this._editor); + this._editor.changeDecorations((changeAccessor) => { + this._editor.changeViewZones((accessor) => { + this._disposeAllLenses(changeAccessor, accessor); }); - scrollState.restore(this._editor); - } else { - // No accessors available - this._disposeAllLenses(null, null); - } + }); + scrollState.restore(this._editor); + } else { + // No accessors available + this._disposeAllLenses(null, null); } - }); + })); scheduler.schedule(); } diff --git a/src/vs/editor/contrib/comment/comment.ts b/src/vs/editor/contrib/comment/comment.ts index b570ff1f041..264e8527e52 100644 --- a/src/vs/editor/contrib/comment/comment.ts +++ b/src/vs/editor/contrib/comment/comment.ts @@ -12,6 +12,8 @@ import { registerEditorAction, IActionOptions, EditorAction, ServicesAccessor } import { BlockCommentCommand } from './blockCommentCommand'; import { LineCommentCommand, Type } from './lineCommentCommand'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { MenuId } from 'vs/platform/actions/common/actions'; abstract class CommentLineAction extends EditorAction { @@ -52,7 +54,14 @@ class ToggleCommentLineAction extends CommentLineAction { precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyMod.CtrlCmd | KeyCode.US_SLASH + primary: KeyMod.CtrlCmd | KeyCode.US_SLASH, + weight: KeybindingWeight.EditorContrib + }, + menubarOpts: { + menuId: MenuId.MenubarEditMenu, + group: '5_insert', + title: nls.localize({ key: 'miToggleLineComment', comment: ['&& denotes a mnemonic'] }, "&&Toggle Line Comment"), + order: 1 } }); } @@ -67,7 +76,8 @@ class AddLineCommentAction extends CommentLineAction { precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_C) + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_C), + weight: KeybindingWeight.EditorContrib } }); } @@ -82,7 +92,8 @@ class RemoveLineCommentAction extends CommentLineAction { precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_U) + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_U), + weight: KeybindingWeight.EditorContrib } }); } @@ -99,7 +110,14 @@ class BlockCommentAction extends EditorAction { kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_A, - linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_A } + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_A }, + weight: KeybindingWeight.EditorContrib + }, + menubarOpts: { + menuId: MenuId.MenubarEditMenu, + group: '5_insert', + title: nls.localize({ key: 'miToggleBlockComment', comment: ['&& denotes a mnemonic'] }, "Toggle &&Block Comment"), + order: 2 } }); } diff --git a/src/vs/editor/contrib/contextmenu/contextmenu.ts b/src/vs/editor/contrib/contextmenu/contextmenu.ts index a3467f32a75..75de2e1c6d1 100644 --- a/src/vs/editor/contrib/contextmenu/contextmenu.ts +++ b/src/vs/editor/contrib/contextmenu/contextmenu.ts @@ -20,6 +20,7 @@ import { IEditorContribution, IScrollEvent, ScrollType } from 'vs/editor/common/ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; export interface IPosition { x: number; @@ -227,7 +228,8 @@ class ShowContextMenu extends EditorAction { precondition: null, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.Shift | KeyCode.F10 + primary: KeyMod.Shift | KeyCode.F10, + weight: KeybindingWeight.EditorContrib } }); } diff --git a/src/vs/editor/contrib/cursorUndo/cursorUndo.ts b/src/vs/editor/contrib/cursorUndo/cursorUndo.ts index dec489e7d14..cd80e4025a1 100644 --- a/src/vs/editor/contrib/cursorUndo/cursorUndo.ts +++ b/src/vs/editor/contrib/cursorUndo/cursorUndo.ts @@ -12,6 +12,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; class CursorState { readonly selections: Selection[]; @@ -118,7 +119,8 @@ export class CursorUndo extends EditorAction { precondition: null, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.CtrlCmd | KeyCode.KEY_U + primary: KeyMod.CtrlCmd | KeyCode.KEY_U, + weight: KeybindingWeight.EditorContrib } }); } diff --git a/src/vs/editor/contrib/documentSymbols/media/Class_16x_darkp.svg b/src/vs/editor/contrib/documentSymbols/media/Class_16x_darkp.svg index 746e7dfc6d8..c43aad29efd 100644 --- a/src/vs/editor/contrib/documentSymbols/media/Class_16x_darkp.svg +++ b/src/vs/editor/contrib/documentSymbols/media/Class_16x_darkp.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Field_16x_darkp.svg b/src/vs/editor/contrib/documentSymbols/media/Field_16x_darkp.svg index e1b5aa5e31d..5fc48ceff0f 100644 --- a/src/vs/editor/contrib/documentSymbols/media/Field_16x_darkp.svg +++ b/src/vs/editor/contrib/documentSymbols/media/Field_16x_darkp.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/Interface_16x_darkp.svg b/src/vs/editor/contrib/documentSymbols/media/Interface_16x_darkp.svg index 0c08c8d50af..f7c2934a55c 100644 --- a/src/vs/editor/contrib/documentSymbols/media/Interface_16x_darkp.svg +++ b/src/vs/editor/contrib/documentSymbols/media/Interface_16x_darkp.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/LocalVariable_16x_vscode.svg b/src/vs/editor/contrib/documentSymbols/media/LocalVariable_16x_vscode.svg new file mode 100644 index 00000000000..e78894b6c63 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/LocalVariable_16x_vscode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/LocalVariable_16x_vscode_inverse.svg b/src/vs/editor/contrib/documentSymbols/media/LocalVariable_16x_vscode_inverse.svg new file mode 100644 index 00000000000..44a44b489d1 --- /dev/null +++ b/src/vs/editor/contrib/documentSymbols/media/LocalVariable_16x_vscode_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/editor/contrib/documentSymbols/media/symbol-icons.css b/src/vs/editor/contrib/documentSymbols/media/symbol-icons.css index 6a6a0e73920..76d67d5efcc 100644 --- a/src/vs/editor/contrib/documentSymbols/media/symbol-icons.css +++ b/src/vs/editor/contrib/documentSymbols/media/symbol-icons.css @@ -111,6 +111,15 @@ background-image: url('Class_16x_darkp.svg'); } +/* constructor */ +.monaco-workbench .symbol-icon.constructor { + background-image: url('Method_16x.svg'); +} +.vs-dark .monaco-workbench .symbol-icon.constructor, +.hc-black .monaco-workbench .symbol-icon.constructor { + background-image: url('Method_16x_darkp.svg'); +} + /* file */ .monaco-workbench .symbol-icon.file { background-image: url('Document_16x.svg'); @@ -131,11 +140,11 @@ /* variable */ .monaco-workbench .symbol-icon.variable { - background-image: url('Field_16x.svg'); + background-image: url('LocalVariable_16x_vscode.svg'); } .vs-dark .monaco-workbench .symbol-icon.variable, .hc-black .monaco-workbench .symbol-icon.variable { - background-image: url('Field_16x_darkp.svg'); + background-image: url('LocalVariable_16x_vscode_inverse.svg'); } /* array */ diff --git a/src/vs/editor/contrib/documentSymbols/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/outlineModel.ts index 28db38d0d47..38157732bfa 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineModel.ts @@ -21,10 +21,14 @@ export abstract class TreeElement { abstract id: string; abstract children: { [id: string]: TreeElement }; - abstract parent: TreeElement | any; + abstract parent: TreeElement; abstract adopt(newParent: TreeElement): TreeElement; + remove(): void { + delete this.parent.children[this.id]; + } + static findId(candidate: DocumentSymbol | string, container: TreeElement): string { // complex id-computation which contains the origin/extension, // the parent path, and some dedupe logic when names collide @@ -73,6 +77,13 @@ export abstract class TreeElement { } return res; } + + static empty(element: TreeElement): boolean { + for (const _key in element.children) { + return false; + } + return true; + } } export class OutlineElement extends TreeElement { @@ -297,7 +308,11 @@ export class OutlineModel extends TreeElement { onUnexpectedExternalError(err); return group; }).then(group => { - result._groups[id] = group; + if (!TreeElement.empty(group)) { + result._groups[id] = group; + } else { + group.remove(); + } }); }); diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree.ts b/src/vs/editor/contrib/documentSymbols/outlineTree.ts index e1181ea33cd..6737e50335b 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineTree.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineTree.ts @@ -23,7 +23,7 @@ import { MarkerSeverity } from 'vs/platform/markers/common/markers'; import { listErrorForeground, listWarningForeground } from 'vs/platform/theme/common/colorRegistry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -export enum OutlineItemCompareType { +export const enum OutlineItemCompareType { ByPosition, ByName, ByKind @@ -162,7 +162,7 @@ export class OutlineRenderer implements IRenderer { renderElement(tree: ITree, element: OutlineGroup | OutlineElement, templateId: string, template: OutlineTemplate): void { if (element instanceof OutlineElement) { - template.icon.className = `outline-element-icon symbol-icon ${symbolKindToCssClass(element.symbol.kind)}`; + template.icon.className = `outline-element-icon ${symbolKindToCssClass(element.symbol.kind)}`; template.label.set(element.symbol.name, element.score ? createMatches(element.score[1]) : undefined, localize('title.template', "{0} ({1})", element.symbol.name, OutlineRenderer._symbolKindNames[element.symbol.kind])); template.detail.innerText = element.symbol.detail || ''; this._renderMarkerInfo(element, template); diff --git a/src/vs/editor/contrib/find/findController.ts b/src/vs/editor/contrib/find/findController.ts index 73bf698e3b8..5a43011b332 100644 --- a/src/vs/editor/contrib/find/findController.ts +++ b/src/vs/editor/contrib/find/findController.ts @@ -23,8 +23,9 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { FindWidget, IFindController } from 'vs/editor/contrib/find/findWidget'; import { FindOptionsWidget } from 'vs/editor/contrib/find/findOptionsWidget'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { optional } from 'vs/platform/instantiation/common/instantiation'; +import { MenuId } from 'vs/platform/actions/common/actions'; export function getSelectionSearchString(editor: ICodeEditor): string { let selection = editor.getSelection(); @@ -386,7 +387,14 @@ export class StartFindAction extends EditorAction { precondition: null, kbOpts: { kbExpr: null, - primary: KeyMod.CtrlCmd | KeyCode.KEY_F + primary: KeyMod.CtrlCmd | KeyCode.KEY_F, + weight: KeybindingWeight.EditorContrib + }, + menubarOpts: { + menuId: MenuId.MenubarEditMenu, + group: '3_find', + title: nls.localize({ key: 'miFind', comment: ['&& denotes a mnemonic'] }, "&&Find"), + order: 1 } }); } @@ -418,7 +426,8 @@ export class StartFindWithSelectionAction extends EditorAction { primary: null, mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_E, - } + }, + weight: KeybindingWeight.EditorContrib } }); } @@ -467,7 +476,8 @@ export class NextMatchFindAction extends MatchFindAction { kbOpts: { kbExpr: EditorContextKeys.focus, primary: KeyCode.F3, - mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] } + mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] }, + weight: KeybindingWeight.EditorContrib } }); } @@ -488,7 +498,8 @@ export class PreviousMatchFindAction extends MatchFindAction { kbOpts: { kbExpr: EditorContextKeys.focus, primary: KeyMod.Shift | KeyCode.F3, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] } + mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] }, + weight: KeybindingWeight.EditorContrib } }); } @@ -533,7 +544,8 @@ export class NextSelectionMatchFindAction extends SelectionMatchFindAction { precondition: null, kbOpts: { kbExpr: EditorContextKeys.focus, - primary: KeyMod.CtrlCmd | KeyCode.F3 + primary: KeyMod.CtrlCmd | KeyCode.F3, + weight: KeybindingWeight.EditorContrib } }); } @@ -553,7 +565,8 @@ export class PreviousSelectionMatchFindAction extends SelectionMatchFindAction { precondition: null, kbOpts: { kbExpr: EditorContextKeys.focus, - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F3 + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F3, + weight: KeybindingWeight.EditorContrib } }); } @@ -574,7 +587,14 @@ export class StartFindReplaceAction extends EditorAction { kbOpts: { kbExpr: null, primary: KeyMod.CtrlCmd | KeyCode.KEY_H, - mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_F } + mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_F }, + weight: KeybindingWeight.EditorContrib + }, + menubarOpts: { + menuId: MenuId.MenubarEditMenu, + group: '3_find', + title: nls.localize({ key: 'miReplace', comment: ['&& denotes a mnemonic'] }, "&&Replace"), + order: 2 } }); } @@ -624,7 +644,7 @@ registerEditorCommand(new FindCommand({ precondition: CONTEXT_FIND_WIDGET_VISIBLE, handler: x => x.closeFindWidget(), kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(5), + weight: KeybindingWeight.EditorContrib + 5, kbExpr: EditorContextKeys.focus, primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape] @@ -636,7 +656,7 @@ registerEditorCommand(new FindCommand({ precondition: null, handler: x => x.toggleCaseSensitive(), kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(5), + weight: KeybindingWeight.EditorContrib + 5, kbExpr: EditorContextKeys.focus, primary: ToggleCaseSensitiveKeybinding.primary, mac: ToggleCaseSensitiveKeybinding.mac, @@ -650,7 +670,7 @@ registerEditorCommand(new FindCommand({ precondition: null, handler: x => x.toggleWholeWords(), kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(5), + weight: KeybindingWeight.EditorContrib + 5, kbExpr: EditorContextKeys.focus, primary: ToggleWholeWordKeybinding.primary, mac: ToggleWholeWordKeybinding.mac, @@ -664,7 +684,7 @@ registerEditorCommand(new FindCommand({ precondition: null, handler: x => x.toggleRegex(), kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(5), + weight: KeybindingWeight.EditorContrib + 5, kbExpr: EditorContextKeys.focus, primary: ToggleRegexKeybinding.primary, mac: ToggleRegexKeybinding.mac, @@ -678,7 +698,7 @@ registerEditorCommand(new FindCommand({ precondition: null, handler: x => x.toggleSearchScope(), kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(5), + weight: KeybindingWeight.EditorContrib + 5, kbExpr: EditorContextKeys.focus, primary: ToggleSearchScopeKeybinding.primary, mac: ToggleSearchScopeKeybinding.mac, @@ -692,7 +712,7 @@ registerEditorCommand(new FindCommand({ precondition: CONTEXT_FIND_WIDGET_VISIBLE, handler: x => x.replace(), kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(5), + weight: KeybindingWeight.EditorContrib + 5, kbExpr: EditorContextKeys.focus, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_1 } @@ -703,7 +723,7 @@ registerEditorCommand(new FindCommand({ precondition: CONTEXT_FIND_WIDGET_VISIBLE, handler: x => x.replaceAll(), kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(5), + weight: KeybindingWeight.EditorContrib + 5, kbExpr: EditorContextKeys.focus, primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Enter } @@ -714,7 +734,7 @@ registerEditorCommand(new FindCommand({ precondition: CONTEXT_FIND_WIDGET_VISIBLE, handler: x => x.selectAllMatches(), kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(5), + weight: KeybindingWeight.EditorContrib + 5, kbExpr: EditorContextKeys.focus, primary: KeyMod.Alt | KeyCode.Enter } diff --git a/src/vs/editor/contrib/folding/folding.ts b/src/vs/editor/contrib/folding/folding.ts index 43a305a49bc..a2a57574264 100644 --- a/src/vs/editor/contrib/folding/folding.ts +++ b/src/vs/editor/contrib/folding/folding.ts @@ -32,6 +32,7 @@ import { FoldingRangeProviderRegistry, FoldingRangeKind } from 'vs/editor/common import { SyntaxRangeProvider, ID_SYNTAX_PROVIDER } from './syntaxRangeProvider'; import { CancellationToken } from 'vs/base/common/cancellation'; import { InitializingRangeProvider, ID_INIT_PROVIDER } from 'vs/editor/contrib/folding/intializingRangeProvider'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; export const ID = 'editor.contrib.folding'; @@ -486,7 +487,8 @@ class UnfoldAction extends FoldingAction { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_CLOSE_SQUARE_BRACKET, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_CLOSE_SQUARE_BRACKET - } + }, + weight: KeybindingWeight.EditorContrib }, description: { description: 'Unfold the content in the editor', @@ -526,7 +528,8 @@ class UnFoldRecursivelyAction extends FoldingAction { precondition: null, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_CLOSE_SQUARE_BRACKET) + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_CLOSE_SQUARE_BRACKET), + weight: KeybindingWeight.EditorContrib } }); } @@ -549,7 +552,8 @@ class FoldAction extends FoldingAction { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_OPEN_SQUARE_BRACKET, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_OPEN_SQUARE_BRACKET - } + }, + weight: KeybindingWeight.EditorContrib }, description: { description: 'Fold the content in the editor', @@ -589,7 +593,8 @@ class FoldRecursivelyAction extends FoldingAction { precondition: null, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_OPEN_SQUARE_BRACKET) + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_OPEN_SQUARE_BRACKET), + weight: KeybindingWeight.EditorContrib } }); } @@ -610,7 +615,8 @@ class FoldAllBlockCommentsAction extends FoldingAction { precondition: null, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_SLASH) + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_SLASH), + weight: KeybindingWeight.EditorContrib } }); } @@ -638,7 +644,8 @@ class FoldAllRegionsAction extends FoldingAction { precondition: null, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_8) + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_8), + weight: KeybindingWeight.EditorContrib } }); } @@ -666,7 +673,8 @@ class UnfoldAllRegionsAction extends FoldingAction { precondition: null, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_9) + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_9), + weight: KeybindingWeight.EditorContrib } }); } @@ -694,7 +702,8 @@ class FoldAllAction extends FoldingAction { precondition: null, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_0) + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_0), + weight: KeybindingWeight.EditorContrib } }); } @@ -714,7 +723,8 @@ class UnfoldAllAction extends FoldingAction { precondition: null, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_J) + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_J), + weight: KeybindingWeight.EditorContrib } }); } @@ -757,7 +767,8 @@ for (let i = 1; i <= 7; i++) { precondition: null, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | (KeyCode.KEY_0 + i)) + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | (KeyCode.KEY_0 + i)), + weight: KeybindingWeight.EditorContrib } }) ); diff --git a/src/vs/editor/contrib/format/format.ts b/src/vs/editor/contrib/format/format.ts index 60b2b6e72c5..c98fefbab91 100644 --- a/src/vs/editor/contrib/format/format.ts +++ b/src/vs/editor/contrib/format/format.ts @@ -12,8 +12,9 @@ import { ITextModel } from 'vs/editor/common/model'; import { registerDefaultLanguageCommand, registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; import { DocumentFormattingEditProviderRegistry, DocumentRangeFormattingEditProviderRegistry, OnTypeFormattingEditProviderRegistry, FormattingOptions, TextEdit } from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { asWinJsPromise, first } from 'vs/base/common/async'; +import { asWinJsPromise, first2 } from 'vs/base/common/async'; import { Position } from 'vs/editor/common/core/position'; +import { CancellationToken } from 'vs/base/common/cancellation'; export class NoProviderError extends Error { @@ -26,30 +27,30 @@ export class NoProviderError extends Error { } } -export function getDocumentRangeFormattingEdits(model: ITextModel, range: Range, options: FormattingOptions): TPromise { +export function getDocumentRangeFormattingEdits(model: ITextModel, range: Range, options: FormattingOptions, token: CancellationToken): Promise { const providers = DocumentRangeFormattingEditProviderRegistry.ordered(model); if (providers.length === 0) { - return TPromise.wrapError(new NoProviderError()); + return Promise.reject(new NoProviderError()); } - return first(providers.map(provider => () => { - return asWinJsPromise(token => provider.provideDocumentRangeFormattingEdits(model, range, options, token)) + return first2(providers.map(provider => () => { + return Promise.resolve(provider.provideDocumentRangeFormattingEdits(model, range, options, token)) .then(undefined, onUnexpectedExternalError); }), result => !isFalsyOrEmpty(result)); } -export function getDocumentFormattingEdits(model: ITextModel, options: FormattingOptions): TPromise { +export function getDocumentFormattingEdits(model: ITextModel, options: FormattingOptions, token: CancellationToken): Promise { const providers = DocumentFormattingEditProviderRegistry.ordered(model); // try range formatters when no document formatter is registered if (providers.length === 0) { - return getDocumentRangeFormattingEdits(model, model.getFullModelRange(), options); + return getDocumentRangeFormattingEdits(model, model.getFullModelRange(), options, token); } - return first(providers.map(provider => () => { - return asWinJsPromise(token => provider.provideDocumentFormattingEdits(model, options, token)) + return first2(providers.map(provider => () => { + return Promise.resolve(provider.provideDocumentFormattingEdits(model, options, token)) .then(undefined, onUnexpectedExternalError); }), result => !isFalsyOrEmpty(result)); } @@ -77,7 +78,7 @@ registerLanguageCommand('_executeFormatRangeProvider', function (accessor, args) if (!model) { throw illegalArgument('resource'); } - return getDocumentRangeFormattingEdits(model, Range.lift(range), options); + return getDocumentRangeFormattingEdits(model, Range.lift(range), options, CancellationToken.None); }); registerLanguageCommand('_executeFormatDocumentProvider', function (accessor, args) { @@ -90,7 +91,7 @@ registerLanguageCommand('_executeFormatDocumentProvider', function (accessor, ar throw illegalArgument('resource'); } - return getDocumentFormattingEdits(model, options); + return getDocumentFormattingEdits(model, options, CancellationToken.None); }); registerDefaultLanguageCommand('_executeFormatOnTypeProvider', function (model, position, args) { diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index 985dd3e900e..02a454eab11 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -26,6 +26,8 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ISingleEditOperation } from 'vs/editor/common/model'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { CancellationToken } from 'vs/base/common/cancellation'; function alertFormattingEdits(edits: ISingleEditOperation[]): void { @@ -238,7 +240,7 @@ class FormatOnPaste implements editorCommon.IEditorContribution { const { tabSize, insertSpaces } = model.getOptions(); const state = new EditorState(this.editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position); - getDocumentRangeFormattingEdits(model, range, { tabSize, insertSpaces }).then(edits => { + getDocumentRangeFormattingEdits(model, range, { tabSize, insertSpaces }, CancellationToken.None).then(edits => { return this.workerService.computeMoreMinimalEdits(model.uri, edits); }).then(edits => { if (!state.validate(this.editor) || isFalsyOrEmpty(edits)) { @@ -266,7 +268,7 @@ export abstract class AbstractFormatAction extends EditorAction { const workerService = accessor.get(IEditorWorkerService); const notificationService = accessor.get(INotificationService); - const formattingPromise = this._getFormattingEdits(editor); + const formattingPromise = this._getFormattingEdits(editor, CancellationToken.None); if (!formattingPromise) { return TPromise.as(void 0); } @@ -275,7 +277,7 @@ export abstract class AbstractFormatAction extends EditorAction { const state = new EditorState(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position); // Receive formatted value from worker - return formattingPromise.then(edits => workerService.computeMoreMinimalEdits(editor.getModel().uri, edits)).then(edits => { + return TPromise.wrap(formattingPromise).then(edits => workerService.computeMoreMinimalEdits(editor.getModel().uri, edits)).then(edits => { if (!state.validate(editor) || isFalsyOrEmpty(edits)) { return; } @@ -292,7 +294,8 @@ export abstract class AbstractFormatAction extends EditorAction { }); } - protected abstract _getFormattingEdits(editor: ICodeEditor): TPromise; + protected abstract _getFormattingEdits(editor: ICodeEditor, token: CancellationToken): Promise; + protected _notifyNoProviderError(notificationService: INotificationService, language: string): void { notificationService.info(nls.localize('no.provider', "There is no formatter for '{0}'-files installed.", language)); } @@ -310,7 +313,8 @@ export class FormatDocumentAction extends AbstractFormatAction { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_F, // secondary: [KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_D)], - linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I } + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I }, + weight: KeybindingWeight.EditorContrib }, menuOpts: { when: EditorContextKeys.hasDocumentFormattingProvider, @@ -320,10 +324,10 @@ export class FormatDocumentAction extends AbstractFormatAction { }); } - protected _getFormattingEdits(editor: ICodeEditor): TPromise { + protected _getFormattingEdits(editor: ICodeEditor, token: CancellationToken): Promise { const model = editor.getModel(); const { tabSize, insertSpaces } = model.getOptions(); - return getDocumentFormattingEdits(model, { tabSize, insertSpaces }); + return getDocumentFormattingEdits(model, { tabSize, insertSpaces }, token); } protected _notifyNoProviderError(notificationService: INotificationService, language: string): void { @@ -341,7 +345,8 @@ export class FormatSelectionAction extends AbstractFormatAction { precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasNonEmptySelection), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_F) + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_F), + weight: KeybindingWeight.EditorContrib }, menuOpts: { when: ContextKeyExpr.and(EditorContextKeys.hasDocumentSelectionFormattingProvider, EditorContextKeys.hasNonEmptySelection), @@ -351,10 +356,10 @@ export class FormatSelectionAction extends AbstractFormatAction { }); } - protected _getFormattingEdits(editor: ICodeEditor): TPromise { + protected _getFormattingEdits(editor: ICodeEditor, token: CancellationToken): Promise { const model = editor.getModel(); const { tabSize, insertSpaces } = model.getOptions(); - return getDocumentRangeFormattingEdits(model, editor.getSelection(), { tabSize, insertSpaces }); + return getDocumentRangeFormattingEdits(model, editor.getSelection(), { tabSize, insertSpaces }, token); } protected _notifyNoProviderError(notificationService: INotificationService, language: string): void { @@ -376,14 +381,14 @@ CommandsRegistry.registerCommand('editor.action.format', accessor => { constructor() { super({} as IActionOptions); } - _getFormattingEdits(editor: ICodeEditor): TPromise { + _getFormattingEdits(editor: ICodeEditor, token: CancellationToken): Promise { const model = editor.getModel(); const editorSelection = editor.getSelection(); const { tabSize, insertSpaces } = model.getOptions(); return editorSelection.isEmpty() - ? getDocumentFormattingEdits(model, { tabSize, insertSpaces }) - : getDocumentRangeFormattingEdits(model, editorSelection, { tabSize, insertSpaces }); + ? getDocumentFormattingEdits(model, { tabSize, insertSpaces }, token) + : getDocumentRangeFormattingEdits(model, editorSelection, { tabSize, insertSpaces }, token); } }().run(accessor, editor); } diff --git a/src/vs/editor/contrib/goToDefinition/clickLinkGesture.ts b/src/vs/editor/contrib/goToDefinition/clickLinkGesture.ts index f8157b3bf0c..8bc694f358a 100644 --- a/src/vs/editor/contrib/goToDefinition/clickLinkGesture.ts +++ b/src/vs/editor/contrib/goToDefinition/clickLinkGesture.ts @@ -52,19 +52,20 @@ export class ClickLinkKeyboardEvent { this.hasTriggerModifier = hasModifier(source, opts.triggerModifier); } } +export type TriggerModifier = 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey'; export class ClickLinkOptions { public readonly triggerKey: KeyCode; - public readonly triggerModifier: 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey'; + public readonly triggerModifier: TriggerModifier; public readonly triggerSideBySideKey: KeyCode; - public readonly triggerSideBySideModifier: 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey'; + public readonly triggerSideBySideModifier: TriggerModifier; constructor( triggerKey: KeyCode, - triggerModifier: 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey', + triggerModifier: TriggerModifier, triggerSideBySideKey: KeyCode, - triggerSideBySideModifier: 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey' + triggerSideBySideModifier: TriggerModifier ) { this.triggerKey = triggerKey; this.triggerModifier = triggerModifier; diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinition.ts b/src/vs/editor/contrib/goToDefinition/goToDefinition.ts index eee248b2cb0..b0474016f0a 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinition.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinition.ts @@ -3,24 +3,22 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - +import { flatten, coalesce } from 'vs/base/common/arrays'; +import { asWinJsPromise } from 'vs/base/common/async'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; import { TPromise } from 'vs/base/common/winjs.base'; -import { ITextModel } from 'vs/editor/common/model'; import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; -import LanguageFeatureRegistry from 'vs/editor/common/modes/languageFeatureRegistry'; -import { DefinitionProviderRegistry, ImplementationProviderRegistry, TypeDefinitionProviderRegistry, Location, DefinitionLink } from 'vs/editor/common/modes'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { asWinJsPromise } from 'vs/base/common/async'; import { Position } from 'vs/editor/common/core/position'; -import { flatten } from 'vs/base/common/arrays'; +import { ITextModel } from 'vs/editor/common/model'; +import { DefinitionLink, DefinitionProviderRegistry, ImplementationProviderRegistry, TypeDefinitionProviderRegistry } from 'vs/editor/common/modes'; +import LanguageFeatureRegistry from 'vs/editor/common/modes/languageFeatureRegistry'; function getDefinitions( model: ITextModel, position: Position, registry: LanguageFeatureRegistry, - provide: (provider: T, model: ITextModel, position: Position, token: CancellationToken) => Location | Location[] | Thenable + provide: (provider: T, model: ITextModel, position: Position, token: CancellationToken) => DefinitionLink | DefinitionLink[] | Thenable ): TPromise { const provider = registry.ordered(model); @@ -35,7 +33,7 @@ function getDefinitions( }); return TPromise.join(promises) .then(flatten) - .then(references => references.filter(x => !!x)); + .then(references => coalesce(references)); } @@ -45,13 +43,13 @@ export function getDefinitionsAtPosition(model: ITextModel, position: Position): }); } -export function getImplementationsAtPosition(model: ITextModel, position: Position): TPromise { +export function getImplementationsAtPosition(model: ITextModel, position: Position): TPromise { return getDefinitions(model, position, ImplementationProviderRegistry, (provider, model, position, token) => { return provider.provideImplementation(model, position, token); }); } -export function getTypeDefinitionsAtPosition(model: ITextModel, position: Position): TPromise { +export function getTypeDefinitionsAtPosition(model: ITextModel, position: Position): TPromise { return getDefinitions(model, position, TypeDefinitionProviderRegistry, (provider, model, position, token) => { return provider.provideTypeDefinition(model, position, token); }); diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts index 8c15d98772c..6ca73451d74 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import * as nls from 'vs/nls'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; @@ -13,7 +11,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { Range } from 'vs/editor/common/core/range'; import { registerEditorAction, IActionOptions, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions'; -import { Location } from 'vs/editor/common/modes'; +import { DefinitionLink } from 'vs/editor/common/modes'; import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition } from './goToDefinition'; import { ReferencesController } from 'vs/editor/contrib/referenceSearch/referencesController'; import { ReferencesModel } from 'vs/editor/contrib/referenceSearch/referencesModel'; @@ -27,6 +25,8 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ITextModel, IWordAtPosition } from 'vs/editor/common/model'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { createCancelablePromise } from 'vs/base/common/async'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; export class DefinitionActionConfig { @@ -67,7 +67,7 @@ export class DefinitionAction extends EditorAction { // * remove falsy references // * find reference at the current pos let idxOfCurrent = -1; - let result: Location[] = []; + const result: DefinitionLink[] = []; for (let i = 0; i < references.length; i++) { let reference = references[i]; if (!reference || !reference.range) { @@ -112,7 +112,7 @@ export class DefinitionAction extends EditorAction { return definitionPromise; } - protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position): TPromise { + protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position): TPromise { return getDefinitionsAtPosition(model, position); } @@ -145,8 +145,8 @@ export class DefinitionAction extends EditorAction { } } - private _openReference(editor: ICodeEditor, editorService: ICodeEditorService, reference: Location, sideBySide: boolean): TPromise { - let { uri, range } = reference; + private _openReference(editor: ICodeEditor, editorService: ICodeEditorService, reference: DefinitionLink, sideBySide: boolean): TPromise { + const { uri, range } = reference; return editorService.openCodeEditor({ resource: uri, options: { @@ -193,7 +193,8 @@ export class GoToDefinitionAction extends DefinitionAction { EditorContextKeys.isInEmbeddedEditor.toNegated()), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: goToDeclarationKb + primary: goToDeclarationKb, + weight: KeybindingWeight.EditorContrib }, menuOpts: { group: 'navigation', @@ -217,7 +218,8 @@ export class OpenDefinitionToSideAction extends DefinitionAction { EditorContextKeys.isInEmbeddedEditor.toNegated()), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, goToDeclarationKb) + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, goToDeclarationKb), + weight: KeybindingWeight.EditorContrib } }); } @@ -236,7 +238,8 @@ export class PeekDefinitionAction extends DefinitionAction { kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Alt | KeyCode.F12, - linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F10 } + linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F10 }, + weight: KeybindingWeight.EditorContrib }, menuOpts: { group: 'navigation', @@ -247,7 +250,7 @@ export class PeekDefinitionAction extends DefinitionAction { } export class ImplementationAction extends DefinitionAction { - protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position): TPromise { + protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position): TPromise { return getImplementationsAtPosition(model, position); } @@ -276,7 +279,8 @@ export class GoToImplementationAction extends ImplementationAction { EditorContextKeys.isInEmbeddedEditor.toNegated()), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyMod.CtrlCmd | KeyCode.F12 + primary: KeyMod.CtrlCmd | KeyCode.F12, + weight: KeybindingWeight.EditorContrib } }); } @@ -296,14 +300,15 @@ export class PeekImplementationAction extends ImplementationAction { EditorContextKeys.isInEmbeddedEditor.toNegated()), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F12 + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F12, + weight: KeybindingWeight.EditorContrib } }); } } export class TypeDefinitionAction extends DefinitionAction { - protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position): TPromise { + protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position): TPromise { return getTypeDefinitionsAtPosition(model, position); } @@ -332,7 +337,8 @@ export class GoToTypeDefinitionAction extends TypeDefinitionAction { EditorContextKeys.isInEmbeddedEditor.toNegated()), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: 0 + primary: 0, + weight: KeybindingWeight.EditorContrib }, menuOpts: { group: 'navigation', @@ -356,7 +362,8 @@ export class PeekTypeDefinitionAction extends TypeDefinitionAction { EditorContextKeys.isInEmbeddedEditor.toNegated()), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: 0 + primary: 0, + weight: KeybindingWeight.EditorContrib } }); } @@ -369,3 +376,31 @@ registerEditorAction(GoToImplementationAction); registerEditorAction(PeekImplementationAction); registerEditorAction(GoToTypeDefinitionAction); registerEditorAction(PeekTypeDefinitionAction); + +// Go to menu +MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { + group: 'z_go_to', + command: { + id: 'editor.action.goToDeclaration', + title: nls.localize({ key: 'miGotoDefinition', comment: ['&& denotes a mnemonic'] }, "Go to &&Definition") + }, + order: 4 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { + group: 'z_go_to', + command: { + id: 'editor.action.goToTypeDefinition', + title: nls.localize({ key: 'miGotoTypeDefinition', comment: ['&& denotes a mnemonic'] }, "Go to &&Type Definition") + }, + order: 5 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { + group: 'z_go_to', + command: { + id: 'editor.action.goToImplementation', + title: nls.localize({ key: 'miGotoImplementation', comment: ['&& denotes a mnemonic'] }, "Go to &&Implementation") + }, + order: 6 +}); \ No newline at end of file diff --git a/src/vs/editor/contrib/gotoError/gotoError.ts b/src/vs/editor/contrib/gotoError/gotoError.ts index 1c56024bc20..2ccee5cba7a 100644 --- a/src/vs/editor/contrib/gotoError/gotoError.ts +++ b/src/vs/editor/contrib/gotoError/gotoError.ts @@ -19,7 +19,7 @@ import { registerEditorAction, registerEditorContribution, ServicesAccessor, IAc import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { MarkerNavigationWidget } from './gotoErrorWidget'; import { compare } from 'vs/base/common/strings'; import { binarySearch } from 'vs/base/common/arrays'; @@ -401,7 +401,8 @@ class NextMarkerInFilesAction extends MarkerNavigationAction { precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.focus, - primary: KeyCode.F8 + primary: KeyCode.F8, + weight: KeybindingWeight.EditorContrib } }); } @@ -416,7 +417,8 @@ class PrevMarkerInFilesAction extends MarkerNavigationAction { precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.focus, - primary: KeyMod.Shift | KeyCode.F8 + primary: KeyMod.Shift | KeyCode.F8, + weight: KeybindingWeight.EditorContrib } }); } @@ -437,7 +439,7 @@ registerEditorCommand(new MarkerCommand({ precondition: CONTEXT_MARKERS_NAVIGATION_VISIBLE, handler: x => x.closeMarkersNavigation(), kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(50), + weight: KeybindingWeight.EditorContrib + 50, kbExpr: EditorContextKeys.focus, primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape] diff --git a/src/vs/editor/contrib/hover/getHover.ts b/src/vs/editor/contrib/hover/getHover.ts index 1c4fabf9505..577c87d814a 100644 --- a/src/vs/editor/contrib/hover/getHover.ts +++ b/src/vs/editor/contrib/hover/getHover.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - import { coalesce } from 'vs/base/common/arrays'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; import { ITextModel } from 'vs/editor/common/model'; @@ -16,23 +14,23 @@ import { CancellationToken } from 'vs/base/common/cancellation'; export function getHover(model: ITextModel, position: Position, token: CancellationToken): Promise { const supports = HoverProviderRegistry.ordered(model); - const values: Hover[] = []; - const promises = supports.map((support, idx) => { - return Promise.resolve(support.provideHover(model, position, token)).then((result) => { - if (result) { - let hasRange = (typeof result.range !== 'undefined'); - let hasHtmlContent = typeof result.contents !== 'undefined' && result.contents && result.contents.length > 0; - if (hasRange && hasHtmlContent) { - values[idx] = result; - } - } + const promises = supports.map(support => { + return Promise.resolve(support.provideHover(model, position, token)).then(hover => { + return hover && isValid(hover) ? hover : undefined; }, err => { onUnexpectedExternalError(err); + return undefined; }); }); - return Promise.all(promises).then(() => coalesce(values)); + return Promise.all(promises).then(values => coalesce(values)); } registerDefaultLanguageCommand('_executeHoverProvider', (model, position) => getHover(model, position, CancellationToken.None)); + +function isValid(result: Hover) { + const hasRange = (typeof result.range !== 'undefined'); + const hasHtmlContent = typeof result.contents !== 'undefined' && result.contents && result.contents.length > 0; + return hasRange && hasHtmlContent; +} diff --git a/src/vs/editor/contrib/hover/hover.ts b/src/vs/editor/contrib/hover/hover.ts index 45f39711585..cfef3c6c719 100644 --- a/src/vs/editor/contrib/hover/hover.ts +++ b/src/vs/editor/contrib/hover/hover.ts @@ -26,6 +26,7 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer'; import { IEmptyContentData } from 'vs/editor/browser/controller/mouseTarget'; import { HoverStartMode } from 'vs/editor/contrib/hover/hoverOperation'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; export class ModesHoverController implements IEditorContribution { @@ -250,7 +251,8 @@ class ShowHoverAction extends EditorAction { precondition: null, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_I) + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_I), + weight: KeybindingWeight.EditorContrib } }); } diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index 26db8c908f0..071469c7a18 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -361,11 +361,11 @@ export class ModesContentHoverWidget extends ContentHoverWidget { newRange = range.setEndPosition(range.endLineNumber, range.startColumn + model.presentation.label.length); } - editorModel.pushEditOperations([], textEdits, () => []); + this._editor.executeEdits('colorpicker', textEdits); if (model.presentation.additionalTextEdits) { textEdits = [...model.presentation.additionalTextEdits]; - editorModel.pushEditOperations([], textEdits, () => []); + this._editor.executeEdits('colorpicker', textEdits); this.hide(); } this._editor.pushUndoStop(); diff --git a/src/vs/editor/contrib/inPlaceReplace/inPlaceReplace.ts b/src/vs/editor/contrib/inPlaceReplace/inPlaceReplace.ts index 3127c005bfe..eba360f0676 100644 --- a/src/vs/editor/contrib/inPlaceReplace/inPlaceReplace.ts +++ b/src/vs/editor/contrib/inPlaceReplace/inPlaceReplace.ts @@ -22,6 +22,7 @@ import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { CancelablePromise, createCancelablePromise, timeout } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; class InPlaceReplaceController implements IEditorContribution { @@ -142,7 +143,8 @@ class InPlaceReplaceUp extends EditorAction { precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_COMMA + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_COMMA, + weight: KeybindingWeight.EditorContrib } }); } @@ -166,7 +168,8 @@ class InPlaceReplaceDown extends EditorAction { precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_DOT + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_DOT, + weight: KeybindingWeight.EditorContrib } }); } diff --git a/src/vs/editor/contrib/indentation/indentation.ts b/src/vs/editor/contrib/indentation/indentation.ts index ab6c491ed74..7437bf9a56c 100644 --- a/src/vs/editor/contrib/indentation/indentation.ts +++ b/src/vs/editor/contrib/indentation/indentation.ts @@ -11,7 +11,6 @@ import { IEditorContribution, ICommand, ICursorStateComputerData, IEditOperation import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { registerEditorAction, ServicesAccessor, IActionOptions, EditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { IModelService } from 'vs/editor/common/services/modelService'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; @@ -23,6 +22,7 @@ import { TextEdit, StandardTokenType } from 'vs/editor/common/modes'; import * as IndentUtil from './indentUtils'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IndentConsts } from 'vs/editor/common/modes/supports/indentRules'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; export function shiftIndent(tabSize: number, indentation: string, count?: number): string { count = count || 1; @@ -217,7 +217,7 @@ export class ChangeIndentationSizeAction extends EditorAction { } public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise { - const quickOpenService = accessor.get(IQuickOpenService); + const quickInputService = accessor.get(IQuickInputService); const modelService = accessor.get(IModelService); let model = editor.getModel(); @@ -237,7 +237,7 @@ export class ChangeIndentationSizeAction extends EditorAction { const autoFocusIndex = Math.min(model.getOptions().tabSize - 1, 7); return TPromise.timeout(50 /* quick open is sensitive to being opened so soon after another */).then(() => - quickOpenService.pick(picks, { placeHolder: nls.localize({ key: 'selectTabWidth', comment: ['Tab corresponds to the tab key'] }, "Select Tab Size for Current File"), autoFocus: { autoFocusIndex } }).then(pick => { + quickInputService.pick(picks, { placeHolder: nls.localize({ key: 'selectTabWidth', comment: ['Tab corresponds to the tab key'] }, "Select Tab Size for Current File"), activeItem: picks[autoFocusIndex] }).then(pick => { if (pick) { model.updateOptions({ tabSize: parseInt(pick.label, 10), diff --git a/src/vs/editor/contrib/linesOperations/linesOperations.ts b/src/vs/editor/contrib/linesOperations/linesOperations.ts index 7f2c9fb66c4..6ec061f49ac 100644 --- a/src/vs/editor/contrib/linesOperations/linesOperations.ts +++ b/src/vs/editor/contrib/linesOperations/linesOperations.ts @@ -23,6 +23,8 @@ import { MoveLinesCommand } from './moveLinesCommand'; import { TypeOperations } from 'vs/editor/common/controller/cursorTypeOperations'; import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { MenuId } from 'vs/platform/actions/common/actions'; // copy lines @@ -60,7 +62,14 @@ class CopyLinesUpAction extends AbstractCopyLinesAction { kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Alt | KeyMod.Shift | KeyCode.UpArrow, - linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.UpArrow } + linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.UpArrow }, + weight: KeybindingWeight.EditorContrib + }, + menubarOpts: { + menuId: MenuId.MenubarSelectionMenu, + group: '2_line', + title: nls.localize({ key: 'miCopyLinesUp', comment: ['&& denotes a mnemonic'] }, "&&Copy Line Up"), + order: 1 } }); } @@ -76,7 +85,14 @@ class CopyLinesDownAction extends AbstractCopyLinesAction { kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Alt | KeyMod.Shift | KeyCode.DownArrow, - linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.DownArrow } + linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.DownArrow }, + weight: KeybindingWeight.EditorContrib + }, + menubarOpts: { + menuId: MenuId.MenubarSelectionMenu, + group: '2_line', + title: nls.localize({ key: 'miCopyLinesDown', comment: ['&& denotes a mnemonic'] }, "Co&&py Line Down"), + order: 2 } }); } @@ -119,7 +135,14 @@ class MoveLinesUpAction extends AbstractMoveLinesAction { kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Alt | KeyCode.UpArrow, - linux: { primary: KeyMod.Alt | KeyCode.UpArrow } + linux: { primary: KeyMod.Alt | KeyCode.UpArrow }, + weight: KeybindingWeight.EditorContrib + }, + menubarOpts: { + menuId: MenuId.MenubarSelectionMenu, + group: '2_line', + title: nls.localize({ key: 'miMoveLinesUp', comment: ['&& denotes a mnemonic'] }, "Mo&&ve Line Up"), + order: 3 } }); } @@ -135,7 +158,14 @@ class MoveLinesDownAction extends AbstractMoveLinesAction { kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Alt | KeyCode.DownArrow, - linux: { primary: KeyMod.Alt | KeyCode.DownArrow } + linux: { primary: KeyMod.Alt | KeyCode.DownArrow }, + weight: KeybindingWeight.EditorContrib + }, + menubarOpts: { + menuId: MenuId.MenubarSelectionMenu, + group: '2_line', + title: nls.localize({ key: 'miMoveLinesDown', comment: ['&& denotes a mnemonic'] }, "Move &&Line Down"), + order: 4 } }); } @@ -204,7 +234,8 @@ export class TrimTrailingWhitespaceAction extends EditorAction { precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_X) + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_X), + weight: KeybindingWeight.EditorContrib } }); } @@ -245,7 +276,8 @@ class DeleteLinesAction extends EditorAction { precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_K + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_K, + weight: KeybindingWeight.EditorContrib } }); } @@ -314,7 +346,8 @@ export class IndentLinesAction extends EditorAction { precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyMod.CtrlCmd | KeyCode.US_CLOSE_SQUARE_BRACKET + primary: KeyMod.CtrlCmd | KeyCode.US_CLOSE_SQUARE_BRACKET, + weight: KeybindingWeight.EditorContrib } }); } @@ -335,7 +368,8 @@ class OutdentLinesAction extends EditorAction { precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyMod.CtrlCmd | KeyCode.US_OPEN_SQUARE_BRACKET + primary: KeyMod.CtrlCmd | KeyCode.US_OPEN_SQUARE_BRACKET, + weight: KeybindingWeight.EditorContrib } }); } @@ -354,7 +388,8 @@ export class InsertLineBeforeAction extends EditorAction { precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter, + weight: KeybindingWeight.EditorContrib } }); } @@ -374,7 +409,8 @@ export class InsertLineAfterAction extends EditorAction { precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyMod.CtrlCmd | KeyCode.Enter + primary: KeyMod.CtrlCmd | KeyCode.Enter, + weight: KeybindingWeight.EditorContrib } }); } @@ -434,7 +470,8 @@ export class DeleteAllLeftAction extends AbstractDeleteAllToBoundaryAction { kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: null, - mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace } + mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace }, + weight: KeybindingWeight.EditorContrib } }); } @@ -502,7 +539,8 @@ export class DeleteAllRightAction extends AbstractDeleteAllToBoundaryAction { kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: null, - mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_K, secondary: [KeyMod.CtrlCmd | KeyCode.Delete] } + mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_K, secondary: [KeyMod.CtrlCmd | KeyCode.Delete] }, + weight: KeybindingWeight.EditorContrib } }); } @@ -559,7 +597,8 @@ export class JoinLinesAction extends EditorAction { kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: 0, - mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_J } + mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_J }, + weight: KeybindingWeight.EditorContrib } }); } diff --git a/src/vs/editor/contrib/message/messageController.ts b/src/vs/editor/contrib/message/messageController.ts index e80d0f6b90c..38ace15a614 100644 --- a/src/vs/editor/contrib/message/messageController.ts +++ b/src/vs/editor/contrib/message/messageController.ts @@ -19,7 +19,7 @@ import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/cont import { IPosition } from 'vs/editor/common/core/position'; import { registerThemingParticipant, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; import { inputValidationInfoBorder, inputValidationInfoBackground } from 'vs/platform/theme/common/colorRegistry'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; export class MessageController extends Disposable implements editorCommon.IEditorContribution { @@ -114,7 +114,7 @@ registerEditorCommand(new MessageCommand({ precondition: MessageController.MESSAGE_VISIBLE, handler: c => c.closeMessage(), kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(30), + weight: KeybindingWeight.EditorContrib + 30, primary: KeyCode.Escape } })); diff --git a/src/vs/editor/contrib/multicursor/multicursor.ts b/src/vs/editor/contrib/multicursor/multicursor.ts index 0517d7061a5..c93314e994a 100644 --- a/src/vs/editor/contrib/multicursor/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/multicursor.ts @@ -25,6 +25,8 @@ import { overviewRulerSelectionHighlightForeground } from 'vs/platform/theme/com import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { INewFindReplaceState, FindOptionOverride } from 'vs/editor/contrib/find/findState'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { MenuId } from 'vs/platform/actions/common/actions'; export class InsertCursorAbove extends EditorAction { @@ -40,7 +42,14 @@ export class InsertCursorAbove extends EditorAction { linux: { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.UpArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow] - } + }, + weight: KeybindingWeight.EditorContrib + }, + menubarOpts: { + menuId: MenuId.MenubarSelectionMenu, + group: '3_multi', + title: nls.localize({ key: 'miInsertCursorAbove', comment: ['&& denotes a mnemonic'] }, "&&Add Cursor Above"), + order: 2 } }); } @@ -78,7 +87,14 @@ export class InsertCursorBelow extends EditorAction { linux: { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.DownArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow] - } + }, + weight: KeybindingWeight.EditorContrib + }, + menubarOpts: { + menuId: MenuId.MenubarSelectionMenu, + group: '3_multi', + title: nls.localize({ key: 'miInsertCursorBelow', comment: ['&& denotes a mnemonic'] }, "A&&dd Cursor Below"), + order: 3 } }); } @@ -112,7 +128,14 @@ class InsertCursorAtEndOfEachLineSelected extends EditorAction { precondition: null, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_I + primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_I, + weight: KeybindingWeight.EditorContrib + }, + menubarOpts: { + menuId: MenuId.MenubarSelectionMenu, + group: '3_multi', + title: nls.localize({ key: 'miInsertCursorAtEndOfEachLineSelected', comment: ['&& denotes a mnemonic'] }, "Add C&&ursors to Line Ends"), + order: 4 } }); } @@ -522,7 +545,14 @@ export class AddSelectionToNextFindMatchAction extends MultiCursorSelectionContr precondition: null, kbOpts: { kbExpr: EditorContextKeys.focus, - primary: KeyMod.CtrlCmd | KeyCode.KEY_D + primary: KeyMod.CtrlCmd | KeyCode.KEY_D, + weight: KeybindingWeight.EditorContrib + }, + menubarOpts: { + menuId: MenuId.MenubarSelectionMenu, + group: '3_multi', + title: nls.localize({ key: 'miAddSelectionToNextFindMatch', comment: ['&& denotes a mnemonic'] }, "Add &&Next Occurrence"), + order: 5 } }); } @@ -537,7 +567,13 @@ export class AddSelectionToPreviousFindMatchAction extends MultiCursorSelectionC id: 'editor.action.addSelectionToPreviousFindMatch', label: nls.localize('addSelectionToPreviousFindMatch', "Add Selection To Previous Find Match"), alias: 'Add Selection To Previous Find Match', - precondition: null + precondition: null, + menubarOpts: { + menuId: MenuId.MenubarSelectionMenu, + group: '3_multi', + title: nls.localize({ key: 'miAddSelectionToPreviousFindMatch', comment: ['&& denotes a mnemonic'] }, "Add P&&revious Occurrence"), + order: 6 + } }); } protected _run(multiCursorController: MultiCursorSelectionController, findController: CommonFindController): void { @@ -554,7 +590,8 @@ export class MoveSelectionToNextFindMatchAction extends MultiCursorSelectionCont precondition: null, kbOpts: { kbExpr: EditorContextKeys.focus, - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_D) + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_D), + weight: KeybindingWeight.EditorContrib } }); } @@ -586,7 +623,14 @@ export class SelectHighlightsAction extends MultiCursorSelectionControllerAction precondition: null, kbOpts: { kbExpr: EditorContextKeys.focus, - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_L + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_L, + weight: KeybindingWeight.EditorContrib + }, + menubarOpts: { + menuId: MenuId.MenubarSelectionMenu, + group: '3_multi', + title: nls.localize({ key: 'miSelectHighlights', comment: ['&& denotes a mnemonic'] }, "Select All &&Occurrences"), + order: 7 } }); } @@ -604,7 +648,8 @@ export class CompatChangeAll extends MultiCursorSelectionControllerAction { precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyMod.CtrlCmd | KeyCode.F2 + primary: KeyMod.CtrlCmd | KeyCode.F2, + weight: KeybindingWeight.EditorContrib }, menuOpts: { group: '1_modification', diff --git a/src/vs/editor/contrib/parameterHints/parameterHints.ts b/src/vs/editor/contrib/parameterHints/parameterHints.ts index 92d7eb7a799..6ec4cda8857 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHints.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHints.ts @@ -15,7 +15,7 @@ import { registerEditorAction, registerEditorContribution, ServicesAccessor, Edi import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ParameterHintsWidget } from './parameterHintsWidget'; import { Context } from 'vs/editor/contrib/parameterHints/provideSignatureHelp'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; class ParameterHintsController implements IEditorContribution { @@ -68,7 +68,8 @@ export class TriggerParameterHintsAction extends EditorAction { precondition: EditorContextKeys.hasSignatureHelpProvider, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Space + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Space, + weight: KeybindingWeight.EditorContrib } }); } @@ -84,7 +85,7 @@ export class TriggerParameterHintsAction extends EditorAction { registerEditorContribution(ParameterHintsController); registerEditorAction(TriggerParameterHintsAction); -const weight = KeybindingsRegistry.WEIGHT.editorContrib(75); +const weight = KeybindingWeight.EditorContrib + 75; const ParameterHintsCommand = EditorCommand.bindToContribution(ParameterHintsController.get); diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index 9a5cfbdb383..80c27ea30e5 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -169,7 +169,7 @@ export class ParameterHintsModel extends Disposable { } private onEditorConfigurationChange(): void { - this.enabled = this.editor.getConfiguration().contribInfo.parameterHints; + this.enabled = this.editor.getConfiguration().contribInfo.parameterHints.enabled; if (!this.enabled) { this.cancel(); @@ -476,14 +476,20 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { next(): boolean { const length = this.hints.signatures.length; const last = (this.currentSignature % length) === (length - 1); + const cycle = this.editor.getConfiguration().contribInfo.parameterHints.cycle; // If there is only one signature, or we're on last signature of list - if (length < 2 || last) { + if ((length < 2 || last) && !cycle) { this.cancel(); return false; } - this.currentSignature++; + if (last && cycle) { + this.currentSignature = 0; + } else { + this.currentSignature++; + } + this.render(); return true; } @@ -491,13 +497,20 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { previous(): boolean { const length = this.hints.signatures.length; const first = this.currentSignature === 0; + const cycle = this.editor.getConfiguration().contribInfo.parameterHints.cycle; - if (length < 2 || first) { + // If there is only one signature, or we're on first signature of list + if ((length < 2 || first) && !cycle) { this.cancel(); return false; } - this.currentSignature--; + if (first && cycle) { + this.currentSignature = length - 1; + } else { + this.currentSignature--; + } + this.render(); return true; } diff --git a/src/vs/editor/contrib/referenceSearch/referenceSearch.ts b/src/vs/editor/contrib/referenceSearch/referenceSearch.ts index ff8515fdd1e..787b9d4760d 100644 --- a/src/vs/editor/contrib/referenceSearch/referenceSearch.ts +++ b/src/vs/editor/contrib/referenceSearch/referenceSearch.ts @@ -8,7 +8,7 @@ import * as nls from 'vs/nls'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { TPromise } from 'vs/base/common/winjs.base'; import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { Position, IPosition } from 'vs/editor/common/core/position'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { registerEditorAction, ServicesAccessor, EditorAction, registerEditorContribution, registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; @@ -70,7 +70,8 @@ export class ReferenceAction extends EditorAction { EditorContextKeys.isInEmbeddedEditor.toNegated()), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyMod.Shift | KeyCode.F12 + primary: KeyMod.Shift | KeyCode.F12, + weight: KeybindingWeight.EditorContrib }, menuOpts: { group: 'navigation', @@ -192,7 +193,7 @@ function withController(accessor: ServicesAccessor, fn: (controller: ReferencesC KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'goToNextReference', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50), + weight: KeybindingWeight.WorkbenchContrib + 50, primary: KeyCode.F4, when: ctxReferenceSearchVisible, handler(accessor) { @@ -204,7 +205,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'goToNextReferenceFromEmbeddedEditor', - weight: KeybindingsRegistry.WEIGHT.editorContrib(50), + weight: KeybindingWeight.EditorContrib + 50, primary: KeyCode.F4, when: PeekContext.inPeekEditor, handler(accessor) { @@ -216,7 +217,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'goToPreviousReference', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50), + weight: KeybindingWeight.WorkbenchContrib + 50, primary: KeyMod.Shift | KeyCode.F4, when: ctxReferenceSearchVisible, handler(accessor) { @@ -228,7 +229,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'goToPreviousReferenceFromEmbeddedEditor', - weight: KeybindingsRegistry.WEIGHT.editorContrib(50), + weight: KeybindingWeight.EditorContrib + 50, primary: KeyMod.Shift | KeyCode.F4, when: PeekContext.inPeekEditor, handler(accessor) { @@ -240,7 +241,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'closeReferenceSearch', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50), + weight: KeybindingWeight.WorkbenchContrib + 50, primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape], when: ContextKeyExpr.and(ctxReferenceSearchVisible, ContextKeyExpr.not('config.editor.stablePeek')), @@ -249,7 +250,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'closeReferenceSearchEditor', - weight: KeybindingsRegistry.WEIGHT.editorContrib(-101), + weight: KeybindingWeight.EditorContrib - 101, primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape], when: ContextKeyExpr.and(PeekContext.inPeekEditor, ContextKeyExpr.not('config.editor.stablePeek')), @@ -258,7 +259,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'openReferenceToSide', - weight: KeybindingsRegistry.WEIGHT.editorContrib(), + weight: KeybindingWeight.EditorContrib, primary: KeyMod.CtrlCmd | KeyCode.Enter, mac: { primary: KeyMod.WinCtrl | KeyCode.Enter diff --git a/src/vs/editor/contrib/referenceSearch/referencesController.ts b/src/vs/editor/contrib/referenceSearch/referencesController.ts index 92dcaddafe9..32b8746b768 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesController.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesController.ts @@ -9,20 +9,16 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { TPromise } from 'vs/base/common/winjs.base'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IStorageService } from 'vs/platform/storage/common/storage'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ReferencesModel } from './referencesModel'; import { ReferenceWidget, LayoutData } from './referencesWidget'; import { Range } from 'vs/editor/common/core/range'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; import { Position } from 'vs/editor/common/core/position'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { Location } from 'vs/editor/common/modes'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { CancelablePromise } from 'vs/base/common/async'; @@ -56,14 +52,10 @@ export abstract class ReferencesController implements editorCommon.IEditorContri editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService, @ICodeEditorService private readonly _editorService: ICodeEditorService, - @ITextModelService private readonly _textModelResolverService: ITextModelService, @INotificationService private readonly _notificationService: INotificationService, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, @IStorageService private readonly _storageService: IStorageService, - @IThemeService private readonly _themeService: IThemeService, @IConfigurationService private readonly _configurationService: IConfigurationService, - @optional(IEnvironmentService) private _environmentService: IEnvironmentService ) { this._editor = editor; this._referenceSearchVisible = ctxReferenceSearchVisible.bindTo(contextKeyService); @@ -106,7 +98,7 @@ export abstract class ReferencesController implements editorCommon.IEditorContri })); const storageKey = 'peekViewLayout'; const data = JSON.parse(this._storageService.get(storageKey, undefined, '{}')); - this._widget = new ReferenceWidget(this._editor, this._defaultTreeKeyboardSupport, data, this._textModelResolverService, this._contextService, this._themeService, this._instantiationService, this._environmentService); + this._widget = this._instantiationService.createInstance(ReferenceWidget, this._editor, this._defaultTreeKeyboardSupport, data); this._widget.setTitle(nls.localize('labelLoading', "Loading...")); this._widget.show(range); this._disposables.push(this._widget.onDidClose(() => { diff --git a/src/vs/editor/contrib/referenceSearch/referencesModel.ts b/src/vs/editor/contrib/referenceSearch/referencesModel.ts index 81865714ad8..d27198f8a52 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesModel.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesModel.ts @@ -6,7 +6,7 @@ import { localize } from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; -import { basename, dirname } from 'vs/base/common/paths'; +import { basename } from 'vs/base/common/paths'; import { IDisposable, dispose, IReference } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; import URI from 'vs/base/common/uri'; @@ -46,14 +46,6 @@ export class OneReference { return this._parent.uri; } - public get name(): string { - return this._parent.name; - } - - public get directory(): string { - return this._parent.directory; - } - public get range(): IRange { return this._range; } @@ -135,14 +127,6 @@ export class FileReferences implements IDisposable { return this._uri; } - public get name(): string { - return basename(this.uri.fsPath); - } - - public get directory(): string { - return dirname(this.uri.fsPath); - } - public get preview(): FilePreview { return this._preview; } diff --git a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts index e4f12351690..45fd356d9d9 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts @@ -7,7 +7,6 @@ import 'vs/css!./media/referencesWidget'; import * as nls from 'vs/nls'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { getPathLabel } from 'vs/base/common/labels'; import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable, dispose, IReference } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; @@ -21,10 +20,9 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { GestureEvent } from 'vs/base/browser/touch'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; -import { FileLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; +import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import * as tree from 'vs/base/parts/tree/browser/tree'; -import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Range, IRange } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { TextModel, ModelDecorationOptions } from 'vs/editor/common/model/textModel'; @@ -37,13 +35,15 @@ import { registerColor, activeContrastBorder, contrastBorder } from 'vs/platform import { registerThemingParticipant, ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import URI from 'vs/base/common/uri'; import { TrackedRangeStickiness, IModelDeltaDecoration } from 'vs/editor/common/model'; import { WorkbenchTree, WorkbenchTreeController } from 'vs/platform/list/browser/listService'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { Location } from 'vs/editor/common/modes'; import { ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; +import { dirname, basenameOrAuthority } from 'vs/base/common/resources'; +import { getBaseLabel } from 'vs/base/common/labels'; + class DecorationsManager implements IDisposable { @@ -294,20 +294,19 @@ class Controller extends WorkbenchTreeController { class FileReferencesTemplate { - readonly file: FileLabel; + readonly file: IconLabel; readonly badge: CountBadge; readonly dispose: () => void; constructor( container: HTMLElement, - @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, - @optional(IEnvironmentService) private _environmentService: IEnvironmentService, + @IUriDisplayService private readonly _uriDisplay: IUriDisplayService, @IThemeService themeService: IThemeService, ) { const parent = document.createElement('div'); dom.addClass(parent, 'reference-file'); container.appendChild(parent); - this.file = new FileLabel(parent, URI.parse('no:file'), this._contextService, this._environmentService); + this.file = new IconLabel(parent); this.badge = new CountBadge($('.count').appendTo(parent).getHTMLElement()); const styler = attachBadgeStyler(this.badge, themeService); @@ -319,7 +318,8 @@ class FileReferencesTemplate { } set(element: FileReferences) { - this.file.setFile(element.uri, this._contextService, this._environmentService); + let parent = dirname(element.uri); + this.file.setValue(getBaseLabel(element.uri), parent ? this._uriDisplay.getLabel(parent, true) : undefined, { title: this._uriDisplay.getLabel(element.uri) }); const len = element.children.length; this.badge.setCount(len); if (element.failure) { @@ -367,9 +367,8 @@ class Renderer implements tree.IRenderer { }; constructor( - @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, @IThemeService private readonly _themeService: IThemeService, - @optional(IEnvironmentService) private _environmentService: IEnvironmentService, + @IUriDisplayService private readonly _uriDisplay: IUriDisplayService, ) { // } @@ -389,7 +388,7 @@ class Renderer implements tree.IRenderer { renderTemplate(tree: tree.ITree, templateId: string, container: HTMLElement) { if (templateId === Renderer._ids.FileReferences) { - return new FileReferencesTemplate(container, this._contextService, this._environmentService, this._themeService); + return new FileReferencesTemplate(container, this._uriDisplay, this._themeService); } else if (templateId === Renderer._ids.OneReference) { return new OneReferenceTemplate(container); } @@ -530,11 +529,10 @@ export class ReferenceWidget extends PeekViewWidget { editor: ICodeEditor, private _defaultTreeKeyboardSupport: boolean, public layoutData: LayoutData, - private _textModelResolverService: ITextModelService, - private _contextService: IWorkspaceContextService, - themeService: IThemeService, - private _instantiationService: IInstantiationService, - private _environmentService: IEnvironmentService + @IThemeService themeService: IThemeService, + @ITextModelService private _textModelResolverService: ITextModelService, + @IInstantiationService private _instantiationService: IInstantiationService, + @IUriDisplayService private _uriDisplay: IUriDisplayService ) { super(editor, { showFrame: false, showArrow: true, isResizeable: true, isAccessible: true }); @@ -780,7 +778,7 @@ export class ReferenceWidget extends PeekViewWidget { // Update widget header if (reference.uri.scheme !== Schemas.inMemory) { - this.setTitle(reference.name, getPathLabel(reference.directory, this._environmentService, this._contextService)); + this.setTitle(basenameOrAuthority(reference.uri), this._uriDisplay.getLabel(dirname(reference.uri), false)); } else { this.setTitle(nls.localize('peekView.alternateTitle', "References")); } diff --git a/src/vs/editor/contrib/rename/rename.ts b/src/vs/editor/contrib/rename/rename.ts index d83828e5695..c5eeedbb0e5 100644 --- a/src/vs/editor/contrib/rename/rename.ts +++ b/src/vs/editor/contrib/rename/rename.ts @@ -25,7 +25,7 @@ import { alert } from 'vs/base/browser/ui/aria/aria'; import { Range } from 'vs/editor/common/core/range'; import { MessageController } from 'vs/editor/contrib/message/messageController'; import { EditorState, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import URI from 'vs/base/common/uri'; @@ -224,7 +224,8 @@ export class RenameAction extends EditorAction { precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasRenameProvider), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyCode.F2 + primary: KeyCode.F2, + weight: KeybindingWeight.EditorContrib }, menuOpts: { group: '1_modification', @@ -269,7 +270,7 @@ registerEditorCommand(new RenameCommand({ precondition: CONTEXT_RENAME_INPUT_VISIBLE, handler: x => x.acceptRenameInput(), kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(99), + weight: KeybindingWeight.EditorContrib + 99, kbExpr: EditorContextKeys.focus, primary: KeyCode.Enter } @@ -280,7 +281,7 @@ registerEditorCommand(new RenameCommand({ precondition: CONTEXT_RENAME_INPUT_VISIBLE, handler: x => x.cancelRenameInput(), kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(99), + weight: KeybindingWeight.EditorContrib + 99, kbExpr: EditorContextKeys.focus, primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape] diff --git a/src/vs/editor/contrib/smartSelect/smartSelect.ts b/src/vs/editor/contrib/smartSelect/smartSelect.ts index 7c25548d3c9..cfe14c52788 100644 --- a/src/vs/editor/contrib/smartSelect/smartSelect.ts +++ b/src/vs/editor/contrib/smartSelect/smartSelect.ts @@ -16,6 +16,8 @@ import { registerEditorAction, ServicesAccessor, IActionOptions, EditorAction, r import { TokenSelectionSupport, ILogicalSelectionEntry } from './tokenSelectionSupport'; import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { MenuId } from 'vs/platform/actions/common/actions'; // --- selection state machine @@ -173,7 +175,14 @@ class GrowSelectionAction extends AbstractSmartSelect { kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.RightArrow, - mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyMod.Shift | KeyCode.RightArrow } + mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyMod.Shift | KeyCode.RightArrow }, + weight: KeybindingWeight.EditorContrib + }, + menubarOpts: { + menuId: MenuId.MenubarSelectionMenu, + group: '1_basic', + title: nls.localize({ key: 'miSmartSelectGrow', comment: ['&& denotes a mnemonic'] }, "&&Expand Selection"), + order: 2 } }); } @@ -189,7 +198,14 @@ class ShrinkSelectionAction extends AbstractSmartSelect { kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.LeftArrow, - mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyMod.Shift | KeyCode.LeftArrow } + mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyMod.Shift | KeyCode.LeftArrow }, + weight: KeybindingWeight.EditorContrib + }, + menubarOpts: { + menuId: MenuId.MenubarSelectionMenu, + group: '1_basic', + title: nls.localize({ key: 'miSmartSelectShrink', comment: ['&& denotes a mnemonic'] }, "&&Shrink Selection"), + order: 3 } }); } diff --git a/src/vs/editor/contrib/snippet/snippetController2.ts b/src/vs/editor/contrib/snippet/snippetController2.ts index 3022a612e99..9a3a443b47a 100644 --- a/src/vs/editor/contrib/snippet/snippetController2.ts +++ b/src/vs/editor/contrib/snippet/snippetController2.ts @@ -18,7 +18,7 @@ import { Range } from 'vs/editor/common/core/range'; import { Choice } from 'vs/editor/contrib/snippet/snippetParser'; import { repeat } from 'vs/base/common/strings'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ILogService } from 'vs/platform/log/common/log'; @@ -227,7 +227,7 @@ registerEditorCommand(new CommandCtor({ precondition: ContextKeyExpr.and(SnippetController2.InSnippetMode, SnippetController2.HasNextTabstop), handler: ctrl => ctrl.next(), kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(30), + weight: KeybindingWeight.EditorContrib + 30, kbExpr: EditorContextKeys.editorTextFocus, primary: KeyCode.Tab } @@ -237,7 +237,7 @@ registerEditorCommand(new CommandCtor({ precondition: ContextKeyExpr.and(SnippetController2.InSnippetMode, SnippetController2.HasPrevTabstop), handler: ctrl => ctrl.prev(), kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(30), + weight: KeybindingWeight.EditorContrib + 30, kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyCode.Tab } @@ -247,7 +247,7 @@ registerEditorCommand(new CommandCtor({ precondition: SnippetController2.InSnippetMode, handler: ctrl => ctrl.cancel(), kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(30), + weight: KeybindingWeight.EditorContrib + 30, kbExpr: EditorContextKeys.editorTextFocus, primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape] @@ -259,7 +259,7 @@ registerEditorCommand(new CommandCtor({ precondition: SnippetController2.InSnippetMode, handler: ctrl => ctrl.finish(), // kbOpts: { - // weight: KeybindingsRegistry.WEIGHT.editorContrib(30), + // weight: KeybindingWeight.EditorContrib + 30, // kbExpr: EditorContextKeys.textFocus, // primary: KeyCode.Enter, // } diff --git a/src/vs/editor/contrib/snippet/snippetParser.ts b/src/vs/editor/contrib/snippet/snippetParser.ts index bf034b00bb3..c839cffd343 100644 --- a/src/vs/editor/contrib/snippet/snippetParser.ts +++ b/src/vs/editor/contrib/snippet/snippetParser.ts @@ -7,7 +7,7 @@ import { CharCode } from 'vs/base/common/charCode'; -export enum TokenType { +export const enum TokenType { Dollar, Colon, Comma, diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index f92c3459f73..e281a671244 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -26,7 +26,7 @@ import { Context as SuggestContext } from './suggest'; import { SuggestModel, State } from './suggestModel'; import { ICompletionItem } from './completionModel'; import { SuggestWidget, ISelectedSuggestion } from './suggestWidget'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { SuggestMemories } from 'vs/editor/contrib/suggest/suggestMemory'; class AcceptOnCharacterOracle { @@ -325,7 +325,8 @@ export class TriggerSuggestAction extends EditorAction { kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyCode.Space, - mac: { primary: KeyMod.WinCtrl | KeyCode.Space } + mac: { primary: KeyMod.WinCtrl | KeyCode.Space }, + weight: KeybindingWeight.EditorContrib } }); } @@ -344,7 +345,7 @@ export class TriggerSuggestAction extends EditorAction { registerEditorContribution(SuggestController); registerEditorAction(TriggerSuggestAction); -const weight = KeybindingsRegistry.WEIGHT.editorContrib(90); +const weight = KeybindingWeight.EditorContrib + 90; const SuggestCommand = EditorCommand.bindToContribution(SuggestController.get); diff --git a/src/vs/editor/contrib/suggest/suggestModel.ts b/src/vs/editor/contrib/suggest/suggestModel.ts index 4a427b85dfe..d3b11c702fb 100644 --- a/src/vs/editor/contrib/suggest/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/suggestModel.ts @@ -123,6 +123,9 @@ export class SuggestModel implements IDisposable { this._updateTriggerCharacters(); this.cancel(); })); + this._toDispose.push(this._editor.onDidBlurEditorText(() => { + this.cancel(); + })); this._toDispose.push(this._editor.onDidChangeConfiguration(() => { this._updateTriggerCharacters(); this._updateQuickSuggest(); diff --git a/src/vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode.ts b/src/vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode.ts index e3aa3349af1..87cb996bb1c 100644 --- a/src/vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode.ts +++ b/src/vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode.ts @@ -9,6 +9,7 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { registerEditorAction, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions'; import { TabFocus } from 'vs/editor/common/config/commonEditorConfig'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; export class ToggleTabFocusModeAction extends EditorAction { @@ -23,7 +24,8 @@ export class ToggleTabFocusModeAction extends EditorAction { kbOpts: { kbExpr: null, primary: KeyMod.CtrlCmd | KeyCode.KEY_M, - mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_M } + mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_M }, + weight: KeybindingWeight.EditorContrib } }); } diff --git a/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts index 7f21bc957a1..8c852574b12 100644 --- a/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts @@ -25,6 +25,7 @@ import { firstIndex, isFalsyOrEmpty } from 'vs/base/common/arrays'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ITextModel, TrackedRangeStickiness, OverviewRulerLane, IModelDeltaDecoration } from 'vs/editor/common/model'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; export const editorWordHighlight = registerColor('editor.wordHighlightBackground', { dark: '#575757B8', light: '#57575740', hc: null }, nls.localize('wordHighlight', 'Background color of a symbol during read-access, like reading a variable. The color must not be opaque to not hide underlying decorations.'), true); export const editorWordHighlightStrong = registerColor('editor.wordHighlightStrongBackground', { dark: '#004972B8', light: '#0e639c40', hc: null }, nls.localize('wordHighlightStrong', 'Background color of a symbol during write-access, like writing to a variable. The color must not be opaque to not hide underlying decorations.'), true); @@ -459,7 +460,8 @@ class NextWordHighlightAction extends WordHighlightNavigationAction { precondition: ctxHasWordHighlights, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyCode.F7 + primary: KeyCode.F7, + weight: KeybindingWeight.EditorContrib } }); } @@ -474,7 +476,8 @@ class PrevWordHighlightAction extends WordHighlightNavigationAction { precondition: ctxHasWordHighlights, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyMod.Shift | KeyCode.F7 + primary: KeyMod.Shift | KeyCode.F7, + weight: KeybindingWeight.EditorContrib } }); } diff --git a/src/vs/editor/contrib/wordOperations/wordOperations.ts b/src/vs/editor/contrib/wordOperations/wordOperations.ts index 528303ab72c..237cc0bde06 100644 --- a/src/vs/editor/contrib/wordOperations/wordOperations.ts +++ b/src/vs/editor/contrib/wordOperations/wordOperations.ts @@ -19,6 +19,7 @@ import { getMapForWordSeparators, WordCharacterClassifier } from 'vs/editor/comm import { CursorState } from 'vs/editor/common/controller/cursorCommon'; import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; export interface MoveWordOptions extends ICommandOptions { inSelectionMode: boolean; @@ -100,7 +101,8 @@ export class CursorWordStartLeft extends WordLeftCommand { kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyCode.LeftArrow, - mac: { primary: KeyMod.Alt | KeyCode.LeftArrow } + mac: { primary: KeyMod.Alt | KeyCode.LeftArrow }, + weight: KeybindingWeight.EditorContrib } }); } @@ -138,7 +140,8 @@ export class CursorWordStartLeftSelect extends WordLeftCommand { kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow, - mac: { primary: KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow } + mac: { primary: KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow }, + weight: KeybindingWeight.EditorContrib } }); } @@ -187,7 +190,8 @@ export class CursorWordEndRight extends WordRightCommand { kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyCode.RightArrow, - mac: { primary: KeyMod.Alt | KeyCode.RightArrow } + mac: { primary: KeyMod.Alt | KeyCode.RightArrow }, + weight: KeybindingWeight.EditorContrib } }); } @@ -225,7 +229,8 @@ export class CursorWordEndRightSelect extends WordRightCommand { kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow, - mac: { primary: KeyMod.Alt | KeyMod.Shift | KeyCode.RightArrow } + mac: { primary: KeyMod.Alt | KeyMod.Shift | KeyCode.RightArrow }, + weight: KeybindingWeight.EditorContrib } }); } @@ -330,7 +335,8 @@ export class DeleteWordLeft extends DeleteWordLeftCommand { kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyCode.Backspace, - mac: { primary: KeyMod.Alt | KeyCode.Backspace } + mac: { primary: KeyMod.Alt | KeyCode.Backspace }, + weight: KeybindingWeight.EditorContrib } }); } @@ -368,7 +374,8 @@ export class DeleteWordRight extends DeleteWordRightCommand { kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyMod.CtrlCmd | KeyCode.Delete, - mac: { primary: KeyMod.Alt | KeyCode.Delete } + mac: { primary: KeyMod.Alt | KeyCode.Delete }, + weight: KeybindingWeight.EditorContrib } }); } diff --git a/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts b/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts index 2074dd9cfc6..ee993f138a0 100644 --- a/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts +++ b/src/vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts @@ -39,7 +39,7 @@ suite('WordPartOperations', () => { test('move word part left basic', () => { withTestCodeEditor([ 'start line', - 'thisIsACamelCaseVar this_is_a_snake_case_var', + 'thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', 'end line' ], {}, (editor, _) => { editor.setPosition(new Position(3, 8)); @@ -47,6 +47,16 @@ suite('WordPartOperations', () => { [3, 5], [3, 4], [3, 1], + [2, 81], + [2, 78], + [2, 73], + [2, 70], + [2, 66], + [2, 65], + [2, 59], + [2, 54], + [2, 51], + [2, 47], [2, 46], [2, 42], [2, 37], @@ -82,7 +92,7 @@ suite('WordPartOperations', () => { test('move word part right basic', () => { withTestCodeEditor([ 'start line', - 'thisIsACamelCaseVar this_is_a_snake_case_var', + 'thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', 'end line' ], {}, (editor, _) => { editor.setPosition(new Position(1, 1)); @@ -105,6 +115,16 @@ suite('WordPartOperations', () => { [2, 38], [2, 43], [2, 46], + [2, 47], + [2, 52], + [2, 55], + [2, 60], + [2, 65], + [2, 66], + [2, 71], + [2, 73], + [2, 78], + [2, 81], [3, 1], [3, 4], [3, 5], @@ -117,93 +137,112 @@ suite('WordPartOperations', () => { const pos = editor.getPosition(); actualStops.push([pos.lineNumber, pos.column]); } - assert.deepEqual(actualStops, expectedStops); }); }); test('delete word part left basic', () => { withTestCodeEditor([ - ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var' + ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse' ], {}, (editor, _) => { const model = editor.getModel(); - editor.setPosition(new Position(1, 84)); + editor.setPosition(new Position(1, 1000)); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case', '001'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake', '002'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a', '003'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is', '004'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this', '005'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar ', '006'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar', '007'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCase', '008'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamel', '009'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsA', '010'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIs', '011'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ this', '012'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ ', '013'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */', '014'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 ', '015'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3', '015bis'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-', '016'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5', '017'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +', '018'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 ', '019'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3', '019bis'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= ', '020'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+=', '021'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a', '022'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text ', '023'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text', '024'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some ', '025'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some', '026'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just ', '027'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just', '028'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* ', '029'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /*', '030'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' ', '031'); - deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), '', '032'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixed', '001'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_IS', '002'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this', '003'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE ', '004'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE', '005'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS', '006'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS', '007'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS', '008'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var ', '009'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '010'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case', '011'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake', '012'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a', '013'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is', '014'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this', '015'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar ', '016'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar', '017'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamelCase', '018'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsACamel', '019'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIsA', '020'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ thisIs', '021'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ this', '022'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */ ', '023'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 */', '024'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3 ', '025'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-3', '025bis'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5-', '026'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +5', '027'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 +', '028'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3 ', '029'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= 3', '029bis'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+= ', '030'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a+=', '031'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text a', '032'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text ', '033'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some text', '034'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some ', '035'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just some', '036'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just ', '037'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* Just', '038'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /* ', '039'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' /*', '040'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), ' ', '041'); + deleteWordPartLeft(editor); assert.equal(model.getLineContent(1), '', '042'); }); }); test('delete word part right basic', () => { withTestCodeEditor([ - ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var' + ' /* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse' ], {}, (editor, _) => { const model = editor.getModel(); editor.setPosition(new Position(1, 1)); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '/* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '001'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '002'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '003'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '004'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '005'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '006'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '007'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '008'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '009'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '010'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '011'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '012'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '013'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '-3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '014'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '3 */ thisIsACamelCaseVar this_is_a_snake_case_var', '015'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' */ thisIsACamelCaseVar this_is_a_snake_case_var', '016'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' thisIsACamelCaseVar this_is_a_snake_case_var', '017'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'thisIsACamelCaseVar this_is_a_snake_case_var', '018'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'IsACamelCaseVar this_is_a_snake_case_var', '019'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'ACamelCaseVar this_is_a_snake_case_var', '020'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'CamelCaseVar this_is_a_snake_case_var', '021'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'CaseVar this_is_a_snake_case_var', '022'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'Var this_is_a_snake_case_var', '023'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' this_is_a_snake_case_var', '024'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'this_is_a_snake_case_var', '025'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'is_a_snake_case_var', '026'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'a_snake_case_var', '027'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'snake_case_var', '028'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'case_var', '029'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'var', '030'); - deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '', '031'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '/* Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '001'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '002'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'Just some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '003'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '004'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'some text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '005'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '006'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'text a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '007'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '008'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'a+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '009'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '+= 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '010'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' 3 +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '011'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' +5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '012'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '5-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '013'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '-3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '014'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '3 */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '015'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' */ thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '016'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '017'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'thisIsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '018'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'IsACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '019'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'ACamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '020'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'CamelCaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '021'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'CaseVar this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '022'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'Var this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '023'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '024'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'this_is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '025'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'is_a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '026'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'a_snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '027'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'snake_case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '028'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'case_var THIS_IS_CAPS_SNAKE this_ISMixedUse', '029'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'var THIS_IS_CAPS_SNAKE this_ISMixedUse', '030'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' THIS_IS_CAPS_SNAKE this_ISMixedUse', '031'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'THIS_IS_CAPS_SNAKE this_ISMixedUse', '032'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'IS_CAPS_SNAKE this_ISMixedUse', '033'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'CAPS_SNAKE this_ISMixedUse', '034'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'SNAKE this_ISMixedUse', '035'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), ' this_ISMixedUse', '036'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'this_ISMixedUse', '037'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'ISMixedUse', '038'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'MixedUse', '039'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), 'Use', '040'); + deleteWordPartRight(editor); assert.equal(model.getLineContent(1), '', '041'); }); }); }); diff --git a/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts b/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts index 28829ba7b31..fec93549d35 100644 --- a/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts +++ b/src/vs/editor/contrib/wordPartOperations/wordPartOperations.ts @@ -15,6 +15,8 @@ import { WordNavigationType, WordPartOperations } from 'vs/editor/common/control import { WordCharacterClassifier } from 'vs/editor/common/controller/wordCharacterClassifier'; import { DeleteWordCommand, MoveWordCommand } from '../wordOperations/wordOperations'; import { Position } from 'vs/editor/common/core/position'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; export class DeleteWordPartLeft extends DeleteWordCommand { constructor() { @@ -25,8 +27,9 @@ export class DeleteWordPartLeft extends DeleteWordCommand { precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Backspace, - mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Backspace } + primary: 0, + mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Backspace }, + weight: KeybindingWeight.EditorContrib } }); } @@ -49,8 +52,9 @@ export class DeleteWordPartRight extends DeleteWordCommand { precondition: EditorContextKeys.writable, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Delete, - mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Delete } + primary: 0, + mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Delete }, + weight: KeybindingWeight.EditorContrib } }); } @@ -76,31 +80,38 @@ export class CursorWordPartLeft extends WordPartLeftCommand { super({ inSelectionMode: false, wordNavigationType: WordNavigationType.WordStart, - id: 'cursorWordPartStartLeft', + id: 'cursorWordPartLeft', precondition: null, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, - mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.LeftArrow } + primary: 0, + mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.LeftArrow }, + weight: KeybindingWeight.EditorContrib } }); } } +// Register previous id for compatibility purposes +CommandsRegistry.registerCommandAlias('cursorWordPartStartLeft', 'cursorWordPartLeft'); + export class CursorWordPartLeftSelect extends WordPartLeftCommand { constructor() { super({ inSelectionMode: true, wordNavigationType: WordNavigationType.WordStart, - id: 'cursorWordPartStartLeftSelect', + id: 'cursorWordPartLeftSelect', precondition: null, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow, - mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow } + primary: 0, + mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyMod.Shift | KeyCode.LeftArrow }, + weight: KeybindingWeight.EditorContrib } }); } } +// Register previous id for compatibility purposes +CommandsRegistry.registerCommandAlias('cursorWordPartStartLeftSelect', 'cursorWordPartLeftSelect'); export class WordPartRightCommand extends MoveWordCommand { protected _move(wordSeparators: WordCharacterClassifier, model: ITextModel, position: Position, wordNavigationType: WordNavigationType): Position { @@ -116,8 +127,9 @@ export class CursorWordPartRight extends WordPartRightCommand { precondition: null, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, - mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.RightArrow } + primary: 0, + mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.RightArrow }, + weight: KeybindingWeight.EditorContrib } }); } @@ -131,8 +143,9 @@ export class CursorWordPartRightSelect extends WordPartRightCommand { precondition: null, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.Shift | KeyCode.RightArrow, - mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyMod.Shift | KeyCode.RightArrow } + primary: 0, + mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyMod.Shift | KeyCode.RightArrow }, + weight: KeybindingWeight.EditorContrib } }); } diff --git a/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts b/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts index 7f6504e4607..b7b0b203953 100644 --- a/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts +++ b/src/vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts @@ -30,7 +30,7 @@ import URI from 'vs/base/common/uri'; import { Selection } from 'vs/editor/common/core/selection'; import * as browser from 'vs/base/browser/browser'; import { IEditorConstructionOptions } from 'vs/editor/standalone/browser/standaloneCodeEditor'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; const CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE = new RawContextKey('accessibilityHelpWidgetVisible', false); @@ -342,7 +342,8 @@ class ShowAccessibilityHelpAction extends EditorAction { precondition: null, kbOpts: { kbExpr: EditorContextKeys.focus, - primary: (browser.isIE ? KeyMod.CtrlCmd | KeyCode.F1 : KeyMod.Alt | KeyCode.F1) + primary: (browser.isIE ? KeyMod.CtrlCmd | KeyCode.F1 : KeyMod.Alt | KeyCode.F1), + weight: KeybindingWeight.EditorContrib } }); } @@ -366,7 +367,7 @@ registerEditorCommand( precondition: CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE, handler: x => x.hide(), kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(100), + weight: KeybindingWeight.EditorContrib + 100, kbExpr: EditorContextKeys.focus, primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape] diff --git a/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts b/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts index 9f83d8111c9..28bf35f5a56 100644 --- a/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts +++ b/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts @@ -18,6 +18,7 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; interface ParseResult { position: Position; @@ -153,7 +154,8 @@ export class GotoLineAction extends BaseEditorQuickOpenAction { kbOpts: { kbExpr: EditorContextKeys.focus, primary: KeyMod.CtrlCmd | KeyCode.KEY_G, - mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_G } + mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_G }, + weight: KeybindingWeight.EditorContrib } }); } diff --git a/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts b/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts index e44e14c0da8..7fe59dd0453 100644 --- a/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts +++ b/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts @@ -18,6 +18,7 @@ import { registerEditorAction, ServicesAccessor } from 'vs/editor/browser/editor import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import * as browser from 'vs/base/browser/browser'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; export class EditorActionCommandEntry extends QuickOpenEntryGroup { private key: string; @@ -79,9 +80,12 @@ export class QuickCommandAction extends BaseEditorQuickOpenAction { precondition: null, kbOpts: { kbExpr: EditorContextKeys.focus, - primary: (browser.isIE ? KeyMod.Alt | KeyCode.F1 : KeyCode.F1) + primary: (browser.isIE ? KeyMod.Alt | KeyCode.F1 : KeyCode.F1), + weight: KeybindingWeight.EditorContrib }, menuOpts: { + group: 'z_commands', + order: 1 } }); } diff --git a/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts b/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts index 19eeda1c9cc..ebc954e1b47 100644 --- a/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts +++ b/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts @@ -22,6 +22,7 @@ import { registerEditorAction, ServicesAccessor } from 'vs/editor/browser/editor import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Range, IRange } from 'vs/editor/common/core/range'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; let SCOPE_PREFIX = ':'; @@ -120,7 +121,8 @@ export class QuickOutlineAction extends BaseEditorQuickOpenAction { precondition: EditorContextKeys.hasDocumentSymbolProvider, kbOpts: { kbExpr: EditorContextKeys.focus, - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_O + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_O, + weight: KeybindingWeight.EditorContrib }, menuOpts: { group: 'navigation', diff --git a/src/vs/editor/standalone/browser/referenceSearch/standaloneReferenceSearch.ts b/src/vs/editor/standalone/browser/referenceSearch/standaloneReferenceSearch.ts index e0b2289a0ef..3259edf4f88 100644 --- a/src/vs/editor/standalone/browser/referenceSearch/standaloneReferenceSearch.ts +++ b/src/vs/editor/standalone/browser/referenceSearch/standaloneReferenceSearch.ts @@ -5,16 +5,12 @@ 'use strict'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ReferencesController } from 'vs/editor/contrib/referenceSearch/referencesController'; @@ -24,28 +20,20 @@ export class StandaloneReferencesController extends ReferencesController { editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService, @ICodeEditorService editorService: ICodeEditorService, - @ITextModelService textModelResolverService: ITextModelService, @INotificationService notificationService: INotificationService, @IInstantiationService instantiationService: IInstantiationService, - @IWorkspaceContextService contextService: IWorkspaceContextService, @IStorageService storageService: IStorageService, - @IThemeService themeService: IThemeService, @IConfigurationService configurationService: IConfigurationService, - @optional(IEnvironmentService) environmentService: IEnvironmentService ) { super( true, editor, contextKeyService, editorService, - textModelResolverService, notificationService, instantiationService, - contextService, storageService, - themeService, configurationService, - environmentService ); } } diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 08f50a00e19..d20f7346b83 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -8,7 +8,7 @@ import Severity from 'vs/base/common/severity'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { IConfigurationService, IConfigurationChangeEvent, IConfigurationOverrides, IConfigurationData } from 'vs/platform/configuration/common/configuration'; -import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { ICommandService, ICommand, ICommandEvent, ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { AbstractKeybindingService } from 'vs/platform/keybinding/common/abstractKeybindingService'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; @@ -24,7 +24,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IProgressService, IProgressRunner } from 'vs/platform/progress/common/progress'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { ITextModelService, ITextModelContentProvider, ITextEditorModel } from 'vs/editor/common/services/resolverService'; -import { IDisposable, IReference, ImmortalReference, combinedDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, IReference, ImmortalReference, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; import * as dom from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeybindingsRegistry, IKeybindingItem } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -230,11 +230,9 @@ export class StandaloneCommandService implements ICommandService { public addCommand(command: ICommand): IDisposable { const { id } = command; this._dynamicCommands[id] = command; - return { - dispose: () => { - delete this._dynamicCommands[id]; - } - }; + return toDisposable(() => { + delete this._dynamicCommands[id]; + }); } public executeCommand(id: string, ...args: any[]): TPromise { @@ -289,18 +287,16 @@ export class StandaloneKeybindingService extends AbstractKeybindingService { weight2: 0 }); - toDispose.push({ - dispose: () => { - for (let i = 0; i < this._dynamicKeybindings.length; i++) { - let kb = this._dynamicKeybindings[i]; - if (kb.command === commandId) { - this._dynamicKeybindings.splice(i, 1); - this.updateResolver({ source: KeybindingSource.Default }); - return; - } + toDispose.push(toDisposable(() => { + for (let i = 0; i < this._dynamicKeybindings.length; i++) { + let kb = this._dynamicKeybindings[i]; + if (kb.command === commandId) { + this._dynamicKeybindings.splice(i, 1); + this.updateResolver({ source: KeybindingSource.Default }); + return; } } - }); + })); let commandService = this._commandService; if (commandService instanceof StandaloneCommandService) { diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index 3a1fabd9093..6018f9f47d5 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -5,7 +5,7 @@ 'use strict'; -import { Disposable, IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { TPromise } from 'vs/base/common/winjs.base'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -17,7 +17,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { StandaloneKeybindingService, applyConfigurationValues } from 'vs/editor/standalone/browser/simpleServices'; -import { IEditorContextViewService } from 'vs/editor/standalone/browser/standaloneServices'; +import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; @@ -272,11 +272,9 @@ export class StandaloneCodeEditor extends CodeEditorWidget implements IStandalon // Store it under the original id, such that trigger with the original id will work this._actions[id] = internalAction; - toDispose.push({ - dispose: () => { - delete this._actions[id]; - } - }); + toDispose.push(toDisposable(() => { + delete this._actions[id]; + })); return combinedDisposable(toDispose); } @@ -284,7 +282,7 @@ export class StandaloneCodeEditor extends CodeEditorWidget implements IStandalon export class StandaloneEditor extends StandaloneCodeEditor implements IStandaloneCodeEditor { - private _contextViewService: IEditorContextViewService; + private _contextViewService: ContextViewService; private readonly _configurationService: IConfigurationService; private _ownsModel: boolean; @@ -311,7 +309,7 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon delete options.model; super(domElement, options, instantiationService, codeEditorService, commandService, contextKeyService, keybindingService, themeService, notificationService); - this._contextViewService = contextViewService; + this._contextViewService = contextViewService; this._configurationService = configurationService; this._register(toDispose); @@ -359,7 +357,7 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon export class StandaloneDiffEditor extends DiffEditorWidget implements IStandaloneDiffEditor { - private _contextViewService: IEditorContextViewService; + private _contextViewService: ContextViewService; private readonly _configurationService: IConfigurationService; constructor( @@ -384,7 +382,7 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon super(domElement, options, editorWorkerService, contextKeyService, instantiationService, codeEditorService, themeService, notificationService); - this._contextViewService = contextViewService; + this._contextViewService = contextViewService; this._configurationService = configurationService; this._register(toDispose); diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index e4f58e5df58..d3275d83f30 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -45,11 +45,6 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IListService, ListService } from 'vs/platform/list/browser/listService'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; -export interface IEditorContextViewService extends IContextViewService { - dispose(): void; - setContainer(domNode: HTMLElement): void; -} - export interface IEditorOverrideServices { [index: string]: any; } diff --git a/src/vs/editor/test/browser/controller/textAreaState.test.ts b/src/vs/editor/test/browser/controller/textAreaState.test.ts index 492ad210207..a954c51f42e 100644 --- a/src/vs/editor/test/browser/controller/textAreaState.test.ts +++ b/src/vs/editor/test/browser/controller/textAreaState.test.ts @@ -518,6 +518,20 @@ suite('TextAreaState', () => { ); }); + test('issue #49480: Double curly braces inserted', () => { + // Characters get doubled + testDeduceInput( + new TextAreaState( + 'aa', + 2, 2, + null, null + ), + 'aaa', + 3, 3, true, true, + 'a', 0 + ); + }); + suite('PagedScreenReaderStrategy', () => { function testPagedScreenReaderStrategy(lines: string[], selection: Selection, expected: TextAreaState): void { diff --git a/src/vs/loader.js b/src/vs/loader.js index 291f0e8b6b3..d2b9f26ab38 100644 --- a/src/vs/loader.js +++ b/src/vs/loader.js @@ -212,7 +212,7 @@ var AMDLoader; return '===anonymous' + (Utilities.NEXT_ANONYMOUS_ID++) + '==='; }; Utilities.isAnonymousModule = function (id) { - return /^===anonymous/.test(id); + return Utilities.startsWith(id, '===anonymous'); }; Utilities.getHighPerformanceTimestamp = function () { if (!this.PERFORMANCE_NOW_PROBED) { @@ -811,15 +811,17 @@ var AMDLoader; errorCode: 'cachedDataRejected', path: cachedDataPath }); - NodeScriptLoader._runSoon(function () { return _this._fs.unlink(cachedDataPath, function (err) { - if (err) { - moduleManager.getConfig().getOptionsLiteral().onNodeCachedData({ - errorCode: 'unlink', - path: cachedDataPath, - detail: err - }); - } - }); }, moduleManager.getConfig().getOptionsLiteral().nodeCachedDataWriteDelay); + NodeScriptLoader._runSoon(function () { + return _this._fs.unlink(cachedDataPath, function (err) { + if (err) { + moduleManager.getConfig().getOptionsLiteral().onNodeCachedData({ + errorCode: 'unlink', + path: cachedDataPath, + detail: err + }); + } + }); + }, moduleManager.getConfig().getOptionsLiteral().nodeCachedDataWriteDelay); } else if (script.cachedDataProduced) { // data produced => tell outside world @@ -828,15 +830,17 @@ var AMDLoader; length: script.cachedData.length }); // data produced => write cache file - NodeScriptLoader._runSoon(function () { return _this._fs.writeFile(cachedDataPath, script.cachedData, function (err) { - if (err) { - moduleManager.getConfig().getOptionsLiteral().onNodeCachedData({ - errorCode: 'writeFile', - path: cachedDataPath, - detail: err - }); - } - }); }, moduleManager.getConfig().getOptionsLiteral().nodeCachedDataWriteDelay); + NodeScriptLoader._runSoon(function () { + return _this._fs.writeFile(cachedDataPath, script.cachedData, function (err) { + if (err) { + moduleManager.getConfig().getOptionsLiteral().onNodeCachedData({ + errorCode: 'writeFile', + path: cachedDataPath, + detail: err + }); + } + }); + }, moduleManager.getConfig().getOptionsLiteral().nodeCachedDataWriteDelay); } }; NodeScriptLoader._runSoon = function (callback, minTimeout) { @@ -1403,7 +1407,8 @@ var AMDLoader; this._knownModules2[moduleId] = true; var strModuleId = this._moduleIdProvider.getStrModuleId(moduleId); var paths = this._config.moduleIdToPaths(strModuleId); - if (this._env.isNode && strModuleId.indexOf('/') === -1) { + var scopedPackageRegex = /^@[^\/]+\/[^\/]+$/; // matches @scope/package-name + if (this._env.isNode && (strModuleId.indexOf('/') === -1 || scopedPackageRegex.test(strModuleId))) { paths.push('node|' + strModuleId); } var lastPathIndex = -1; @@ -1649,6 +1654,9 @@ var AMDLoader; RequireFunc.getStats = function () { return moduleManager.getLoaderEvents(); }; + RequireFunc.define = function () { + return DefineFunc.apply(null, arguments); + }; function init() { if (typeof AMDLoader.global.require !== 'undefined' || typeof require !== 'undefined') { var _nodeRequire_1 = (AMDLoader.global.require || require); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 0a9e5161b0e..0642820ecfc 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -48,26 +48,21 @@ declare namespace monaco { export type TValueCallback = (value: T | PromiseLike) => void; - export type ProgressCallback = (progress: TProgress) => void; - - export class Promise { + export class Promise { constructor( executor: ( resolve: (value: T | PromiseLike) => void, - reject: (reason: any) => void, - progress: (progress: TProgress) => void) => void, + reject: (reason: any) => void) => void, oncancel?: () => void); public then( onfulfilled?: ((value: T) => TResult1 | PromiseLike) | null, - onrejected?: ((reason: any) => TResult2 | PromiseLike) | null, - onprogress?: (progress: TProgress) => void): Promise; + onrejected?: ((reason: any) => TResult2 | PromiseLike) | null): Promise; public done( onfulfilled?: (value: T) => void, - onrejected?: (reason: any) => void, - onprogress?: (progress: TProgress) => void): void; + onrejected?: (reason: any) => void): void; public cancel(): void; @@ -2508,6 +2503,22 @@ declare namespace monaco.editor { sticky?: boolean; } + /** + * Configuration options for parameter hints + */ + export interface IEditorParameterHintOptions { + /** + * Enable parameter hints. + * Defaults to true. + */ + enabled?: boolean; + /** + * Enable cycling of parameter hints. + * Defaults to false. + */ + cycle?: boolean; + } + export interface ISuggestOptions { /** * Enable graceful matching. Defaults to true. @@ -2793,9 +2804,9 @@ declare namespace monaco.editor { */ quickSuggestionsDelay?: number; /** - * Enables parameter hints + * Parameter hint options. */ - parameterHints?: boolean; + parameterHints?: IEditorParameterHintOptions; /** * Render icons in suggestions box. * Defaults to true. @@ -3130,6 +3141,11 @@ declare namespace monaco.editor { readonly snippetsPreventQuickSuggestions: boolean; } + export interface InternalParameterHintOptions { + readonly enabled: boolean; + readonly cycle: boolean; + } + export interface EditorWrappingInfo { readonly inDiffEditor: boolean; readonly isDominatedByLongLines: boolean; @@ -3194,7 +3210,7 @@ declare namespace monaco.editor { strings: boolean; }; readonly quickSuggestionsDelay: number; - readonly parameterHints: boolean; + readonly parameterHints: InternalParameterHintOptions; readonly iconsInSuggestions: boolean; readonly formatOnType: boolean; readonly formatOnPaste: boolean; @@ -4922,7 +4938,7 @@ declare namespace monaco.languages { /** * Provide the definition of the symbol at the given position and document. */ - provideDefinition(model: editor.ITextModel, position: Position, token: CancellationToken): DefinitionLink | Thenable; + provideDefinition(model: editor.ITextModel, position: Position, token: CancellationToken): Definition | DefinitionLink[] | Thenable; } /** @@ -4933,7 +4949,7 @@ declare namespace monaco.languages { /** * Provide the implementation of the symbol at the given position and document. */ - provideImplementation(model: editor.ITextModel, position: Position, token: CancellationToken): Definition | Thenable; + provideImplementation(model: editor.ITextModel, position: Position, token: CancellationToken): Definition | DefinitionLink[] | Thenable; } /** @@ -4944,7 +4960,7 @@ declare namespace monaco.languages { /** * Provide the type definition of the symbol at the given position and document. */ - provideTypeDefinition(model: editor.ITextModel, position: Position, token: CancellationToken): Definition | Thenable; + provideTypeDefinition(model: editor.ITextModel, position: Position, token: CancellationToken): Definition | DefinitionLink[] | Thenable; } /** @@ -5166,11 +5182,11 @@ declare namespace monaco.languages { export interface FoldingRange { /** - * The zero-based start line of the range to fold. The folded area starts after the line's last character. + * The one-based start line of the range to fold. The folded area starts after the line's last character. */ start: number; /** - * The zero-based end line of the range to fold. The folded area ends with the line's last character. + * The one-based end line of the range to fold. The folded area ends with the line's last character. */ end: number; /** diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index e919252f47a..f0ba370d22d 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -93,9 +93,11 @@ export class MenuId { static readonly MenubarAppearanceMenu = new MenuId(); static readonly MenubarLayoutMenu = new MenuId(); static readonly MenubarGoMenu = new MenuId(); + static readonly MenubarSwitchEditorMenu = new MenuId(); + static readonly MenubarSwitchGroupMenu = new MenuId(); static readonly MenubarDebugMenu = new MenuId(); + static readonly MenubarNewBreakpointMenu = new MenuId(); static readonly MenubarTasksMenu = new MenuId(); - static readonly MenubarWindowMenu = new MenuId(); static readonly MenubarPreferencesMenu = new MenuId(); static readonly MenubarHelpMenu = new MenuId(); static readonly MenubarTerminalMenu = new MenuId(); diff --git a/src/vs/platform/backup/common/backup.ts b/src/vs/platform/backup/common/backup.ts index 1d77135f933..4d2595d83ef 100644 --- a/src/vs/platform/backup/common/backup.ts +++ b/src/vs/platform/backup/common/backup.ts @@ -5,11 +5,15 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import URI from 'vs/base/common/uri'; export interface IBackupWorkspacesFormat { rootWorkspaces: IWorkspaceIdentifier[]; - folderWorkspaces: string[]; + folderURIWorkspaces: string[]; emptyWorkspaces: string[]; + + // deprecated + folderWorkspaces?: string[]; // use folderURIWorkspaces instead } export const IBackupMainService = createDecorator('backupMainService'); @@ -20,10 +24,14 @@ export interface IBackupMainService { isHotExitEnabled(): boolean; getWorkspaceBackups(): IWorkspaceIdentifier[]; - getFolderBackupPaths(): string[]; + getFolderBackupPaths(): URI[]; getEmptyWindowBackupPaths(): string[]; registerWorkspaceBackupSync(workspace: IWorkspaceIdentifier, migrateFrom?: string): string; - registerFolderBackupSync(folderPath: string): string; + registerFolderBackupSync(folderUri: URI): string; registerEmptyWindowBackupSync(backupFolder?: string): string; + + unregisterWorkspaceBackupSync(workspace: IWorkspaceIdentifier): void; + unregisterFolderBackupSync(folderUri: URI): void; + unregisterEmptyWindowBackupSync(backupFolder: string): void; } \ No newline at end of file diff --git a/src/vs/platform/backup/electron-main/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts index 9201f86adb9..8c879b66d8b 100644 --- a/src/vs/platform/backup/electron-main/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -3,18 +3,23 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as arrays from 'vs/base/common/arrays'; import * as fs from 'fs'; import * as path from 'path'; import * as crypto from 'crypto'; import * as platform from 'vs/base/common/platform'; import * as extfs from 'vs/base/node/extfs'; -import { IBackupWorkspacesFormat, IBackupMainService } from 'vs/platform/backup/common/backup'; +import * as arrays from 'vs/base/common/arrays'; +import { IBackupMainService, IBackupWorkspacesFormat } from 'vs/platform/backup/common/backup'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IFilesConfiguration, HotExitConfiguration } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; -import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; + +import { IWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import URI from 'vs/base/common/uri'; +import { isEqual as areResourcesEquals, getComparisonKey, hasToIgnoreCase } from 'vs/base/common/resources'; +import { isEqual } from 'vs/base/common/paths'; +import { Schemas } from 'vs/base/common/network'; export class BackupMainService implements IBackupMainService { @@ -23,7 +28,9 @@ export class BackupMainService implements IBackupMainService { protected backupHome: string; protected workspacesJsonPath: string; - protected backups: IBackupWorkspacesFormat; + protected rootWorkspaces: IWorkspaceIdentifier[]; + protected folderWorkspaces: URI[]; + protected emptyWorkspaces: string[]; constructor( @IEnvironmentService environmentService: IEnvironmentService, @@ -43,17 +50,16 @@ export class BackupMainService implements IBackupMainService { return []; } - return this.backups.rootWorkspaces.slice(0); // return a copy + return this.rootWorkspaces.slice(0); // return a copy } - public getFolderBackupPaths(): string[] { + public getFolderBackupPaths(): URI[] { if (this.isHotExitOnExitAndWindowClose()) { // Only non-folder windows are restored on main process launch when // hot exit is configured as onExitAndWindowClose. return []; } - - return this.backups.folderWorkspaces.slice(0); // return a copy + return this.folderWorkspaces.slice(0); // return a copy } public isHotExitEnabled(): boolean { @@ -71,13 +77,16 @@ export class BackupMainService implements IBackupMainService { } public getEmptyWindowBackupPaths(): string[] { - return this.backups.emptyWorkspaces.slice(0); // return a copy + return this.emptyWorkspaces.slice(0); // return a copy } public registerWorkspaceBackupSync(workspace: IWorkspaceIdentifier, migrateFrom?: string): string { - this.pushBackupPathsSync(workspace, this.backups.rootWorkspaces); + if (!this.rootWorkspaces.some(w => w.id === workspace.id)) { + this.rootWorkspaces.push(workspace); + this.saveSync(); + } - const backupPath = path.join(this.backupHome, workspace.id); + const backupPath = this.getBackupPath(workspace.id); if (migrateFrom) { this.moveBackupFolderSync(backupPath, migrateFrom); @@ -103,10 +112,28 @@ export class BackupMainService implements IBackupMainService { } } - public registerFolderBackupSync(folderPath: string): string { - this.pushBackupPathsSync(folderPath, this.backups.folderWorkspaces); + public unregisterWorkspaceBackupSync(workspace: IWorkspaceIdentifier): void { + let index = arrays.firstIndex(this.rootWorkspaces, w => w.id === workspace.id); + if (index !== -1) { + this.rootWorkspaces.splice(index, 1); + this.saveSync(); + } + } - return path.join(this.backupHome, this.getFolderHash(folderPath)); + public registerFolderBackupSync(folderUri: URI): string { + if (!this.folderWorkspaces.some(uri => areResourcesEquals(folderUri, uri, hasToIgnoreCase(folderUri)))) { + this.folderWorkspaces.push(folderUri); + this.saveSync(); + } + return this.getBackupPath(this.getFolderHash(folderUri)); + } + + public unregisterFolderBackupSync(folderUri: URI): void { + let index = arrays.firstIndex(this.folderWorkspaces, uri => areResourcesEquals(folderUri, uri, hasToIgnoreCase(folderUri))); + if (index !== -1) { + this.folderWorkspaces.splice(index, 1); + this.saveSync(); + } } public registerEmptyWindowBackupSync(backupFolder?: string): string { @@ -115,52 +142,23 @@ export class BackupMainService implements IBackupMainService { if (!backupFolder) { backupFolder = this.getRandomEmptyWindowId(); } - - this.pushBackupPathsSync(backupFolder, this.backups.emptyWorkspaces); - - return path.join(this.backupHome, backupFolder); + if (!this.emptyWorkspaces.some(w => isEqual(w, backupFolder, !platform.isLinux))) { + this.emptyWorkspaces.push(backupFolder); + this.saveSync(); + } + return this.getBackupPath(backupFolder); } - private pushBackupPathsSync(workspaceIdentifier: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier, target: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[]): void { - if (this.indexOf(workspaceIdentifier, target) === -1) { - target.push(workspaceIdentifier); + public unregisterEmptyWindowBackupSync(backupFolder: string): void { + let index = arrays.firstIndex(this.emptyWorkspaces, w => isEqual(w, backupFolder, !platform.isLinux)); + if (index !== -1) { + this.emptyWorkspaces.splice(index, 1); this.saveSync(); } } - protected removeBackupPathSync(workspaceIdentifier: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier, target: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[]): void { - if (!target) { - return; - } - - const index = this.indexOf(workspaceIdentifier, target); - if (index === -1) { - return; - } - - target.splice(index, 1); - this.saveSync(); - } - - private indexOf(workspaceIdentifier: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier, target: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[]): number { - if (!target) { - return -1; - } - - const sanitizedWorkspaceIdentifier = this.sanitizeId(workspaceIdentifier); - - return arrays.firstIndex(target, id => this.sanitizeId(id) === sanitizedWorkspaceIdentifier); - } - - private sanitizeId(workspaceIdentifier: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): string { - if (isSingleFolderWorkspaceIdentifier(workspaceIdentifier)) { - return this.sanitizePath(workspaceIdentifier); - } - - return workspaceIdentifier.id; - } - protected loadSync(): void { + let backups: IBackupWorkspacesFormat; try { backups = JSON.parse(fs.readFileSync(this.workspacesJsonPath, 'utf8').toString()); // invalid JSON or permission issue can happen here @@ -168,117 +166,168 @@ export class BackupMainService implements IBackupMainService { backups = Object.create(null); } - // Ensure rootWorkspaces is a object[] - if (backups.rootWorkspaces) { - const rws = backups.rootWorkspaces; - if (!Array.isArray(rws) || rws.some(r => typeof r !== 'object')) { - backups.rootWorkspaces = []; - } - } else { - backups.rootWorkspaces = []; - } + // read empty worrkspace backs first + this.emptyWorkspaces = this.validateEmptyWorkspaces(backups.emptyWorkspaces); - // Ensure folderWorkspaces is a string[] - if (backups.folderWorkspaces) { - const fws = backups.folderWorkspaces; - if (!Array.isArray(fws) || fws.some(f => typeof f !== 'string')) { - backups.folderWorkspaces = []; - } - } else { - backups.folderWorkspaces = []; - } + // read workspace backups + this.rootWorkspaces = this.validateWorkspaces(backups.rootWorkspaces); - // Ensure emptyWorkspaces is a string[] - if (backups.emptyWorkspaces) { - const fws = backups.emptyWorkspaces; - if (!Array.isArray(fws) || fws.some(f => typeof f !== 'string')) { - backups.emptyWorkspaces = []; - } - } else { - backups.emptyWorkspaces = []; - } - - this.backups = this.dedupeBackups(backups); - - // Validate backup workspaces - this.validateBackupWorkspaces(backups); - } - - protected dedupeBackups(backups: IBackupWorkspacesFormat): IBackupWorkspacesFormat { - - // De-duplicate folder/workspace backups. don't worry about cleaning them up any duplicates as - // they will be removed when there are no backups. - backups.folderWorkspaces = arrays.distinct(backups.folderWorkspaces, ws => this.sanitizePath(ws)); - backups.rootWorkspaces = arrays.distinct(backups.rootWorkspaces, ws => this.sanitizePath(ws.id)); - - return backups; - } - - private validateBackupWorkspaces(backups: IBackupWorkspacesFormat): void { - const staleBackupWorkspaces: { workspaceIdentifier: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier; backupPath: string; target: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[] }[] = []; - - const workspaceAndFolders: { workspaceIdentifier: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier, target: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[] }[] = []; - workspaceAndFolders.push(...backups.rootWorkspaces.map(r => ({ workspaceIdentifier: r, target: backups.rootWorkspaces }))); - workspaceAndFolders.push(...backups.folderWorkspaces.map(f => ({ workspaceIdentifier: f, target: backups.folderWorkspaces }))); - - // Validate Workspace and Folder Backups - workspaceAndFolders.forEach(workspaceOrFolder => { - const workspaceId = workspaceOrFolder.workspaceIdentifier; - const workspacePath = isSingleFolderWorkspaceIdentifier(workspaceId) ? workspaceId : workspaceId.configPath; - const backupPath = path.join(this.backupHome, isSingleFolderWorkspaceIdentifier(workspaceId) ? this.getFolderHash(workspaceId) : workspaceId.id); - const hasBackups = this.hasBackupsSync(backupPath); - const missingWorkspace = hasBackups && !fs.existsSync(workspacePath); - - // If the workspace/folder has no backups, make sure to delete it - // If the workspace/folder has backups, but the target workspace is missing, convert backups to empty ones - if (!hasBackups || missingWorkspace) { - staleBackupWorkspaces.push({ workspaceIdentifier: workspaceId, backupPath, target: workspaceOrFolder.target }); - - if (missingWorkspace) { - this.convertToEmptyWindowBackup(backupPath); + // read folder backups + let workspaceFolders: URI[]; + try { + if (Array.isArray(backups.folderURIWorkspaces)) { + workspaceFolders = backups.folderURIWorkspaces.map(f => URI.parse(f)); + } else if (Array.isArray(backups.folderWorkspaces)) { + // migrate legacy folder paths + workspaceFolders = []; + for (const folderPath of backups.folderWorkspaces) { + const oldFolderHash = this.getLegacyFolderHash(folderPath); + const folderUri = URI.file(folderPath); + const newFolderHash = this.getFolderHash(folderUri); + if (newFolderHash !== oldFolderHash) { + this.moveBackupFolderSync(this.getBackupPath(newFolderHash), this.getBackupPath(oldFolderHash)); + } + workspaceFolders.push(folderUri); } } - }); + } catch (e) { + // ignore URI parsing exceptions + } + this.folderWorkspaces = this.validateFolders(workspaceFolders); + + // save again in case some workspaces or folders have been removed + this.saveSync(); + + } + + private getBackupPath(oldFolderHash: string): string { + return path.join(this.backupHome, oldFolderHash); + } + + private validateWorkspaces(rootWorkspaces: IWorkspaceIdentifier[]): IWorkspaceIdentifier[] { + if (!Array.isArray(rootWorkspaces)) { + return []; + } + + const seenIds: { [id: string]: boolean } = Object.create(null); + const result: IWorkspaceIdentifier[] = []; + + // Validate Workspaces + for (let workspace of rootWorkspaces) { + if (!isWorkspaceIdentifier(workspace)) { + return []; // wrong format, skip all entries + } + + if (!seenIds[workspace.id]) { + seenIds[workspace.id] = true; + + const backupPath = this.getBackupPath(workspace.id); + const hasBackups = this.hasBackupsSync(backupPath); + + // If the workspace has no backups, ignore it + if (hasBackups) { + if (fs.existsSync(workspace.configPath)) { + result.push(workspace); + } else { + // If the workspace has backups, but the target workspace is missing, convert backups to empty ones + this.convertToEmptyWindowBackup(backupPath); + } + } else { + this.deleteStaleBackup(backupPath); + } + } + } + return result; + } + + private validateFolders(folderWorkspaces: URI[]): URI[] { + if (!Array.isArray(folderWorkspaces)) { + return []; + } + + const result: URI[] = []; + const seen: { [id: string]: boolean } = Object.create(null); + + for (let folderURI of folderWorkspaces) { + const key = getComparisonKey(folderURI); + if (!seen[key]) { + seen[key] = true; + + const backupPath = this.getBackupPath(this.getFolderHash(folderURI)); + const hasBackups = this.hasBackupsSync(backupPath); + + // If the folder has no backups, ignore it + if (hasBackups) { + if (folderURI.scheme !== Schemas.file || fs.existsSync(folderURI.fsPath)) { + result.push(folderURI); + } else { + // If the folder has backups, but the target workspace is missing, convert backups to empty ones + this.convertToEmptyWindowBackup(backupPath); + } + } else { + this.deleteStaleBackup(backupPath); + } + } + } + + return result; + } + private validateEmptyWorkspaces(emptyWorkspaces: string[]): string[] { + if (!Array.isArray(emptyWorkspaces)) { + return []; + } + + const result: string[] = []; + const seen: { [id: string]: boolean } = Object.create(null); // Validate Empty Windows - backups.emptyWorkspaces.forEach(backupFolder => { - const backupPath = path.join(this.backupHome, backupFolder); - if (!this.hasBackupsSync(backupPath)) { - staleBackupWorkspaces.push({ workspaceIdentifier: backupFolder, backupPath, target: backups.emptyWorkspaces }); + for (let backupFolder of emptyWorkspaces) { + if (typeof backupFolder !== 'string') { + return []; } - }); - // Clean up stale backups - staleBackupWorkspaces.forEach(staleBackupWorkspace => { - const { backupPath, workspaceIdentifier, target } = staleBackupWorkspace; + if (!seen[backupFolder]) { + seen[backupFolder] = true; - try { + const backupPath = this.getBackupPath(backupFolder); + if (this.hasBackupsSync(backupPath)) { + result.push(backupFolder); + } else { + this.deleteStaleBackup(backupPath); + } + } + } + + return result; + } + + private deleteStaleBackup(backupPath: string) { + try { + if (fs.existsSync(backupPath)) { extfs.delSync(backupPath); - } catch (ex) { - this.logService.error(`Backup: Could not delete stale backup: ${ex.toString()}`); } - - this.removeBackupPathSync(workspaceIdentifier, target); - }); + } catch (ex) { + this.logService.error(`Backup: Could not delete stale backup: ${ex.toString()}`); + } } private convertToEmptyWindowBackup(backupPath: string): boolean { // New empty window backup - const identifier = this.getRandomEmptyWindowId(); - this.pushBackupPathsSync(identifier, this.backups.emptyWorkspaces); + let newBackupFolder = this.getRandomEmptyWindowId(); + while (this.emptyWorkspaces.some(w => isEqual(w, newBackupFolder, platform.isLinux))) { + newBackupFolder = this.getRandomEmptyWindowId(); + } // Rename backupPath to new empty window backup path - const newEmptyWindowBackupPath = path.join(this.backupHome, identifier); + const newEmptyWindowBackupPath = this.getBackupPath(newBackupFolder); try { fs.renameSync(backupPath, newEmptyWindowBackupPath); } catch (ex) { this.logService.error(`Backup: Could not rename backup folder: ${ex.toString()}`); - - this.removeBackupPathSync(identifier, this.backups.emptyWorkspaces); - return false; } + this.emptyWorkspaces.push(newBackupFolder); return true; } @@ -308,8 +357,12 @@ export class BackupMainService implements IBackupMainService { if (!fs.existsSync(this.backupHome)) { fs.mkdirSync(this.backupHome); } - - extfs.writeFileAndFlushSync(this.workspacesJsonPath, JSON.stringify(this.backups)); + const backups: IBackupWorkspacesFormat = { + rootWorkspaces: this.rootWorkspaces, + folderURIWorkspaces: this.folderWorkspaces.map(f => f.toString()), + emptyWorkspaces: this.emptyWorkspaces + }; + extfs.writeFileAndFlushSync(this.workspacesJsonPath, JSON.stringify(backups)); } catch (ex) { this.logService.error(`Backup: Could not save workspaces.json: ${ex.toString()}`); } @@ -319,11 +372,19 @@ export class BackupMainService implements IBackupMainService { return (Date.now() + Math.round(Math.random() * 1000)).toString(); } - private sanitizePath(p: string): string { - return platform.isLinux ? p : p.toLowerCase(); + protected getFolderHash(folderUri: URI): string { + let key; + if (folderUri.scheme === Schemas.file) { + // for backward compatibility, use the fspath as key + key = platform.isLinux ? folderUri.fsPath : folderUri.fsPath.toLowerCase(); + + } else { + key = hasToIgnoreCase(folderUri) ? folderUri.toString().toLowerCase() : folderUri.toString(); + } + return crypto.createHash('md5').update(key).digest('hex'); } - protected getFolderHash(folderPath: string): string { - return crypto.createHash('md5').update(this.sanitizePath(folderPath)).digest('hex'); + protected getLegacyFolderHash(folderPath: string): string { + return crypto.createHash('md5').update(platform.isLinux ? folderPath : folderPath.toLowerCase()).digest('hex'); } -} +} \ No newline at end of file diff --git a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts index ff421ddbdf7..ab249bac7dc 100644 --- a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts +++ b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts @@ -25,6 +25,11 @@ import { getRandomTestPath } from 'vs/workbench/test/workbenchTestServices'; import { Schemas } from 'vs/base/common/network'; suite('BackupMainService', () => { + + function assertEqualUris(actual: Uri[], expected: Uri[]) { + assert.deepEqual(actual.map(a => a.toString()), expected.map(a => a.toString())); + } + const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupservice'); const backupHome = path.join(parentDir, 'Backups'); const backupWorkspacesPath = path.join(backupHome, 'workspaces.json'); @@ -43,28 +48,21 @@ suite('BackupMainService', () => { this.loadSync(); } - public get backupsData(): IBackupWorkspacesFormat { - return this.backups; - } - - public removeBackupPathSync(workspaceIdentifier: string | IWorkspaceIdentifier, target: (string | IWorkspaceIdentifier)[]): void { - return super.removeBackupPathSync(workspaceIdentifier, target); - } - public loadSync(): void { super.loadSync(); } - public dedupeBackups(backups: IBackupWorkspacesFormat): IBackupWorkspacesFormat { - return super.dedupeBackups(backups); + public toBackupPath(arg: Uri | string): string { + const id = arg instanceof Uri ? super.getFolderHash(arg) : arg; + return path.join(this.backupHome, id); } - public toBackupPath(workspacePath: string): string { - return path.join(this.backupHome, super.getFolderHash(workspacePath)); + public getFolderHash(folderUri: Uri): string { + return super.getFolderHash(folderUri); } - public getFolderHash(folderPath: string): string { - return super.getFolderHash(folderPath); + public toLegacyBackupPath(folderPath: string): string { + return path.join(this.backupHome, super.getLegacyFolderHash(folderPath)); } } @@ -75,6 +73,31 @@ suite('BackupMainService', () => { }; } + async function ensureFolderExists(uri: Uri): Promise { + if (!fs.existsSync(uri.fsPath)) { + fs.mkdirSync(uri.fsPath); + } + const backupFolder = service.toBackupPath(uri); + await createBackupFolder(backupFolder); + } + + async function ensureWorkspaceExists(workspace: IWorkspaceIdentifier): Promise { + if (!fs.existsSync(workspace.configPath)) { + await pfs.writeFile(workspace.configPath, 'Hello'); + } + const backupFolder = service.toBackupPath(workspace.id); + await createBackupFolder(backupFolder); + return workspace; + } + + async function createBackupFolder(backupFolder: string): Promise { + if (!fs.existsSync(backupFolder)) { + fs.mkdirSync(backupFolder); + fs.mkdirSync(path.join(backupFolder, Schemas.file)); + await pfs.writeFile(path.join(backupFolder, Schemas.file, 'foo.txt'), 'Hello'); + } + } + function sanitizePath(p: string): string { return platform.isLinux ? p : p.toLowerCase(); } @@ -82,6 +105,8 @@ suite('BackupMainService', () => { const fooFile = Uri.file(platform.isWindows ? 'C:\\foo' : '/foo'); const barFile = Uri.file(platform.isWindows ? 'C:\\bar' : '/bar'); + const existingTestFolder1 = Uri.file(path.join(parentDir, 'folder1')); + let service: TestBackupMainService; let configService: TestConfigurationService; @@ -103,40 +128,40 @@ suite('BackupMainService', () => { this.timeout(1000 * 10); // increase timeout for this test // 1) backup workspace path does not exist - service.registerFolderBackupSync(fooFile.fsPath); - service.registerFolderBackupSync(barFile.fsPath); + service.registerFolderBackupSync(fooFile); + service.registerFolderBackupSync(barFile); service.loadSync(); - assert.deepEqual(service.getFolderBackupPaths(), []); + assertEqualUris(service.getFolderBackupPaths(), []); // 2) backup workspace path exists with empty contents within - fs.mkdirSync(service.toBackupPath(fooFile.fsPath)); - fs.mkdirSync(service.toBackupPath(barFile.fsPath)); - service.registerFolderBackupSync(fooFile.fsPath); - service.registerFolderBackupSync(barFile.fsPath); + fs.mkdirSync(service.toBackupPath(fooFile)); + fs.mkdirSync(service.toBackupPath(barFile)); + service.registerFolderBackupSync(fooFile); + service.registerFolderBackupSync(barFile); service.loadSync(); - assert.deepEqual(service.getFolderBackupPaths(), []); - assert.ok(!fs.existsSync(service.toBackupPath(fooFile.fsPath))); - assert.ok(!fs.existsSync(service.toBackupPath(barFile.fsPath))); + assertEqualUris(service.getFolderBackupPaths(), []); + assert.ok(!fs.existsSync(service.toBackupPath(fooFile))); + assert.ok(!fs.existsSync(service.toBackupPath(barFile))); // 3) backup workspace path exists with empty folders within - fs.mkdirSync(service.toBackupPath(fooFile.fsPath)); - fs.mkdirSync(service.toBackupPath(barFile.fsPath)); - fs.mkdirSync(path.join(service.toBackupPath(fooFile.fsPath), Schemas.file)); - fs.mkdirSync(path.join(service.toBackupPath(barFile.fsPath), Schemas.untitled)); - service.registerFolderBackupSync(fooFile.fsPath); - service.registerFolderBackupSync(barFile.fsPath); + fs.mkdirSync(service.toBackupPath(fooFile)); + fs.mkdirSync(service.toBackupPath(barFile)); + fs.mkdirSync(path.join(service.toBackupPath(fooFile), Schemas.file)); + fs.mkdirSync(path.join(service.toBackupPath(barFile), Schemas.untitled)); + service.registerFolderBackupSync(fooFile); + service.registerFolderBackupSync(barFile); service.loadSync(); - assert.deepEqual(service.getFolderBackupPaths(), []); - assert.ok(!fs.existsSync(service.toBackupPath(fooFile.fsPath))); - assert.ok(!fs.existsSync(service.toBackupPath(barFile.fsPath))); + assertEqualUris(service.getFolderBackupPaths(), []); + assert.ok(!fs.existsSync(service.toBackupPath(fooFile))); + assert.ok(!fs.existsSync(service.toBackupPath(barFile))); // 4) backup workspace path points to a workspace that no longer exists // so it should convert the backup worspace to an empty workspace backup - const fileBackups = path.join(service.toBackupPath(fooFile.fsPath), Schemas.file); - fs.mkdirSync(service.toBackupPath(fooFile.fsPath)); - fs.mkdirSync(service.toBackupPath(barFile.fsPath)); + const fileBackups = path.join(service.toBackupPath(fooFile), Schemas.file); + fs.mkdirSync(service.toBackupPath(fooFile)); + fs.mkdirSync(service.toBackupPath(barFile)); fs.mkdirSync(fileBackups); - service.registerFolderBackupSync(fooFile.fsPath); + service.registerFolderBackupSync(fooFile); assert.equal(service.getFolderBackupPaths().length, 1); assert.equal(service.getEmptyWindowBackupPaths().length, 0); fs.writeFileSync(path.join(fileBackups, 'backup.txt'), ''); @@ -155,32 +180,32 @@ suite('BackupMainService', () => { assert.deepEqual(service.getWorkspaceBackups(), []); // 2) backup workspace path exists with empty contents within - fs.mkdirSync(service.toBackupPath(fooFile.fsPath)); - fs.mkdirSync(service.toBackupPath(barFile.fsPath)); + fs.mkdirSync(service.toBackupPath(fooFile)); + fs.mkdirSync(service.toBackupPath(barFile)); service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath)); service.registerWorkspaceBackupSync(toWorkspace(barFile.fsPath)); service.loadSync(); assert.deepEqual(service.getWorkspaceBackups(), []); - assert.ok(!fs.existsSync(service.toBackupPath(fooFile.fsPath))); - assert.ok(!fs.existsSync(service.toBackupPath(barFile.fsPath))); + assert.ok(!fs.existsSync(service.toBackupPath(fooFile))); + assert.ok(!fs.existsSync(service.toBackupPath(barFile))); // 3) backup workspace path exists with empty folders within - fs.mkdirSync(service.toBackupPath(fooFile.fsPath)); - fs.mkdirSync(service.toBackupPath(barFile.fsPath)); - fs.mkdirSync(path.join(service.toBackupPath(fooFile.fsPath), Schemas.file)); - fs.mkdirSync(path.join(service.toBackupPath(barFile.fsPath), Schemas.untitled)); + fs.mkdirSync(service.toBackupPath(fooFile)); + fs.mkdirSync(service.toBackupPath(barFile)); + fs.mkdirSync(path.join(service.toBackupPath(fooFile), Schemas.file)); + fs.mkdirSync(path.join(service.toBackupPath(barFile), Schemas.untitled)); service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath)); service.registerWorkspaceBackupSync(toWorkspace(barFile.fsPath)); service.loadSync(); assert.deepEqual(service.getWorkspaceBackups(), []); - assert.ok(!fs.existsSync(service.toBackupPath(fooFile.fsPath))); - assert.ok(!fs.existsSync(service.toBackupPath(barFile.fsPath))); + assert.ok(!fs.existsSync(service.toBackupPath(fooFile))); + assert.ok(!fs.existsSync(service.toBackupPath(barFile))); // 4) backup workspace path points to a workspace that no longer exists // so it should convert the backup worspace to an empty workspace backup - const fileBackups = path.join(service.toBackupPath(fooFile.fsPath), Schemas.file); - fs.mkdirSync(service.toBackupPath(fooFile.fsPath)); - fs.mkdirSync(service.toBackupPath(barFile.fsPath)); + const fileBackups = path.join(service.toBackupPath(fooFile), Schemas.file); + fs.mkdirSync(service.toBackupPath(fooFile)); + fs.mkdirSync(service.toBackupPath(barFile)); fs.mkdirSync(fileBackups); service.registerWorkspaceBackupSync(toWorkspace(fooFile.fsPath)); assert.equal(service.getWorkspaceBackups().length, 1); @@ -192,10 +217,10 @@ suite('BackupMainService', () => { }); test('service supports to migrate backup data from another location', () => { - const backupPathToMigrate = service.toBackupPath(fooFile.fsPath); + const backupPathToMigrate = service.toBackupPath(fooFile); fs.mkdirSync(backupPathToMigrate); fs.writeFileSync(path.join(backupPathToMigrate, 'backup.txt'), 'Some Data'); - service.registerFolderBackupSync(backupPathToMigrate); + service.registerFolderBackupSync(Uri.file(backupPathToMigrate)); const workspaceBackupPath = service.registerWorkspaceBackupSync(toWorkspace(barFile.fsPath), backupPathToMigrate); @@ -208,15 +233,15 @@ suite('BackupMainService', () => { }); test('service backup migration makes sure to preserve existing backups', () => { - const backupPathToMigrate = service.toBackupPath(fooFile.fsPath); + const backupPathToMigrate = service.toBackupPath(fooFile); fs.mkdirSync(backupPathToMigrate); fs.writeFileSync(path.join(backupPathToMigrate, 'backup.txt'), 'Some Data'); - service.registerFolderBackupSync(backupPathToMigrate); + service.registerFolderBackupSync(Uri.file(backupPathToMigrate)); - const backupPathToPreserve = service.toBackupPath(barFile.fsPath); + const backupPathToPreserve = service.toBackupPath(barFile); fs.mkdirSync(backupPathToPreserve); fs.writeFileSync(path.join(backupPathToPreserve, 'backup.txt'), 'Some Data'); - service.registerFolderBackupSync(backupPathToPreserve); + service.registerFolderBackupSync(Uri.file(backupPathToPreserve)); const workspaceBackupPath = service.registerWorkspaceBackupSync(toWorkspace(barFile.fsPath), backupPathToMigrate); @@ -229,56 +254,102 @@ suite('BackupMainService', () => { assert.equal(1, fs.readdirSync(path.join(backupHome, emptyBackups[0])).length); }); + suite('migrate folderPath to folderURI', () => { + + test('migration makes sure to preserve existing backups', async () => { + if (platform.isLinux) { + return; // TODO:Martin #54483 fix tests + } + + let path1 = path.join(parentDir, 'folder1').toLowerCase(); + let path2 = path.join(parentDir, 'folder2').toUpperCase(); + let uri1 = Uri.file(path1); + let uri2 = Uri.file(path2); + + if (!fs.existsSync(path1)) { + fs.mkdirSync(path1); + } + if (!fs.existsSync(path2)) { + fs.mkdirSync(path2); + } + const backupFolder1 = service.toLegacyBackupPath(path1); + if (!fs.existsSync(backupFolder1)) { + fs.mkdirSync(backupFolder1); + fs.mkdirSync(path.join(backupFolder1, Schemas.file)); + await pfs.writeFile(path.join(backupFolder1, Schemas.file, 'unsaved1.txt'), 'Legacy'); + } + const backupFolder2 = service.toLegacyBackupPath(path2); + if (!fs.existsSync(backupFolder2)) { + fs.mkdirSync(backupFolder2); + fs.mkdirSync(path.join(backupFolder2, Schemas.file)); + await pfs.writeFile(path.join(backupFolder2, Schemas.file, 'unsaved2.txt'), 'Legacy'); + } + + const workspacesJson = { rootWorkspaces: [], folderWorkspaces: [path1, path2], emptyWorkspaces: [] }; + await pfs.writeFile(backupWorkspacesPath, JSON.stringify(workspacesJson)).then(() => { + service.loadSync(); + return pfs.readFile(backupWorkspacesPath, 'utf-8').then(content => { + const json = JSON.parse(content); + assert.deepEqual(json.folderURIWorkspaces, [uri1.toString(), uri2.toString()]); + const newBackupFolder1 = service.toBackupPath(uri1); + assert.ok(fs.existsSync(path.join(newBackupFolder1, Schemas.file, 'unsaved1.txt'))); + const newBackupFolder2 = service.toBackupPath(uri2); + assert.ok(fs.existsSync(path.join(newBackupFolder2, Schemas.file, 'unsaved2.txt'))); + }); + }); + }); + }); + suite('loadSync', () => { test('getFolderBackupPaths() should return [] when workspaces.json doesn\'t exist', () => { - assert.deepEqual(service.getFolderBackupPaths(), []); + assertEqualUris(service.getFolderBackupPaths(), []); }); test('getFolderBackupPaths() should return [] when workspaces.json is not properly formed JSON', () => { fs.writeFileSync(backupWorkspacesPath, ''); service.loadSync(); - assert.deepEqual(service.getFolderBackupPaths(), []); + assertEqualUris(service.getFolderBackupPaths(), []); fs.writeFileSync(backupWorkspacesPath, '{]'); service.loadSync(); - assert.deepEqual(service.getFolderBackupPaths(), []); + assertEqualUris(service.getFolderBackupPaths(), []); fs.writeFileSync(backupWorkspacesPath, 'foo'); service.loadSync(); - assert.deepEqual(service.getFolderBackupPaths(), []); + assertEqualUris(service.getFolderBackupPaths(), []); }); test('getFolderBackupPaths() should return [] when folderWorkspaces in workspaces.json is absent', () => { fs.writeFileSync(backupWorkspacesPath, '{}'); service.loadSync(); - assert.deepEqual(service.getFolderBackupPaths(), []); + assertEqualUris(service.getFolderBackupPaths(), []); }); test('getFolderBackupPaths() should return [] when folderWorkspaces in workspaces.json is not a string array', () => { fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaces":{}}'); service.loadSync(); - assert.deepEqual(service.getFolderBackupPaths(), []); + assertEqualUris(service.getFolderBackupPaths(), []); fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaces":{"foo": ["bar"]}}'); service.loadSync(); - assert.deepEqual(service.getFolderBackupPaths(), []); + assertEqualUris(service.getFolderBackupPaths(), []); fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaces":{"foo": []}}'); service.loadSync(); - assert.deepEqual(service.getFolderBackupPaths(), []); + assertEqualUris(service.getFolderBackupPaths(), []); fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaces":{"foo": "bar"}}'); service.loadSync(); - assert.deepEqual(service.getFolderBackupPaths(), []); + assertEqualUris(service.getFolderBackupPaths(), []); fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaces":"foo"}'); service.loadSync(); - assert.deepEqual(service.getFolderBackupPaths(), []); + assertEqualUris(service.getFolderBackupPaths(), []); fs.writeFileSync(backupWorkspacesPath, '{"folderWorkspaces":1}'); service.loadSync(); - assert.deepEqual(service.getFolderBackupPaths(), []); + assertEqualUris(service.getFolderBackupPaths(), []); }); test('getFolderBackupPaths() should return [] when files.hotExit = "onExitAndWindowClose"', () => { - service.registerFolderBackupSync(fooFile.fsPath.toUpperCase()); - assert.deepEqual(service.getFolderBackupPaths(), [fooFile.fsPath.toUpperCase()]); + service.registerFolderBackupSync(Uri.file(fooFile.fsPath.toUpperCase())); + assertEqualUris(service.getFolderBackupPaths(), [Uri.file(fooFile.fsPath.toUpperCase())]); configService.setUserConfiguration('files.hotExit', HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE); service.loadSync(); - assert.deepEqual(service.getFolderBackupPaths(), []); + assertEqualUris(service.getFolderBackupPaths(), []); }); test('getWorkspaceBackups() should return [] when workspaces.json doesn\'t exist', () => { @@ -379,59 +450,82 @@ suite('BackupMainService', () => { }); suite('dedupeFolderWorkspaces', () => { - test('should ignore duplicates on Windows and Mac (folder workspace)', () => { - // Skip test on Linux - if (platform.isLinux) { - return; - } + test('should ignore duplicates (folder workspace)', async () => { - const backups: IBackupWorkspacesFormat = { + await ensureFolderExists(existingTestFolder1); + + const workspacesJson: IBackupWorkspacesFormat = { rootWorkspaces: [], - folderWorkspaces: platform.isWindows ? ['c:\\FOO', 'C:\\FOO', 'c:\\foo'] : ['/FOO', '/foo'], + folderURIWorkspaces: [existingTestFolder1.toString(), existingTestFolder1.toString()], emptyWorkspaces: [] }; - - service.dedupeBackups(backups); - - assert.equal(backups.folderWorkspaces.length, 1); - if (platform.isWindows) { - assert.deepEqual(backups.folderWorkspaces, ['c:\\FOO'], 'should return the first duplicated entry'); - } else { - assert.deepEqual(backups.folderWorkspaces, ['/FOO'], 'should return the first duplicated entry'); - } + return pfs.writeFile(backupWorkspacesPath, JSON.stringify(workspacesJson)).then(() => { + service.loadSync(); + return pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => { + const json = JSON.parse(buffer); + assert.deepEqual(json.folderURIWorkspaces, [existingTestFolder1.toString()]); + }); + }); }); - test('should ignore duplicates on Windows and Mac (root workspace)', () => { - // Skip test on Linux - if (platform.isLinux) { - return; - } + test('should ignore duplicates on Windows and Mac (folder workspace)', async () => { - const backups: IBackupWorkspacesFormat = { - rootWorkspaces: platform.isWindows ? [toWorkspace('c:\\FOO'), toWorkspace('C:\\FOO'), toWorkspace('c:\\foo')] : [toWorkspace('/FOO'), toWorkspace('/foo')], - folderWorkspaces: [], + await ensureFolderExists(existingTestFolder1); + + const workspacesJson: IBackupWorkspacesFormat = { + rootWorkspaces: [], + folderURIWorkspaces: [existingTestFolder1.toString(), existingTestFolder1.toString().toLowerCase()], emptyWorkspaces: [] }; + return pfs.writeFile(backupWorkspacesPath, JSON.stringify(workspacesJson)).then(() => { + service.loadSync(); + return pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => { + const json = JSON.parse(buffer); + assert.deepEqual(json.folderURIWorkspaces, [existingTestFolder1.toString()]); + }); + }); + }); - service.dedupeBackups(backups); - - assert.equal(backups.rootWorkspaces.length, 1); - if (platform.isWindows) { - assert.deepEqual(backups.rootWorkspaces.map(r => r.configPath), ['c:\\FOO'], 'should return the first duplicated entry'); - } else { - assert.deepEqual(backups.rootWorkspaces.map(r => r.configPath), ['/FOO'], 'should return the first duplicated entry'); + test('should ignore duplicates on Windows and Mac (root workspace)', async () => { + if (platform.isLinux) { + return; // TODO:Martin #54483 fix tests } + + const workspacePath = path.join(parentDir, 'Foo.code-workspace'); + + const workspace1 = await ensureWorkspaceExists(toWorkspace(workspacePath)); + const workspace2 = await ensureWorkspaceExists(toWorkspace(workspacePath.toUpperCase())); + const workspace3 = await ensureWorkspaceExists(toWorkspace(workspacePath.toLowerCase())); + + const workspacesJson: IBackupWorkspacesFormat = { + rootWorkspaces: [workspace1, workspace2, workspace3], + folderURIWorkspaces: [], + emptyWorkspaces: [] + }; + return pfs.writeFile(backupWorkspacesPath, JSON.stringify(workspacesJson)).then(() => { + service.loadSync(); + return pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => { + const json = JSON.parse(buffer); + assert.equal(json.rootWorkspaces.length, platform.isLinux ? 3 : 1); + if (platform.isLinux) { + assert.deepEqual(json.rootWorkspaces.map(r => r.configPath), [workspacePath, workspacePath.toUpperCase(), workspacePath.toLowerCase()]); + } else { + assert.deepEqual(json.rootWorkspaces.map(r => r.configPath), [workspacePath], 'should return the first duplicated entry'); + } + }); + }); + }); }); suite('registerWindowForBackups', () => { test('should persist paths to workspaces.json (folder workspace)', () => { - service.registerFolderBackupSync(fooFile.fsPath); - service.registerFolderBackupSync(barFile.fsPath); - assert.deepEqual(service.getFolderBackupPaths(), [fooFile.fsPath, barFile.fsPath]); + service.registerFolderBackupSync(fooFile); + service.registerFolderBackupSync(barFile); + assertEqualUris(service.getFolderBackupPaths(), [fooFile, barFile]); return pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => { const json = JSON.parse(buffer); - assert.deepEqual(json.folderWorkspaces, [fooFile.fsPath, barFile.fsPath]); + assert.deepEqual(json.folderURIWorkspaces, [fooFile.toString(), barFile.toString()]); }); }); @@ -455,11 +549,11 @@ suite('BackupMainService', () => { }); test('should always store the workspace path in workspaces.json using the case given, regardless of whether the file system is case-sensitive (folder workspace)', () => { - service.registerFolderBackupSync(fooFile.fsPath.toUpperCase()); - assert.deepEqual(service.getFolderBackupPaths(), [fooFile.fsPath.toUpperCase()]); + service.registerFolderBackupSync(Uri.file(fooFile.fsPath.toUpperCase())); + assertEqualUris(service.getFolderBackupPaths(), [Uri.file(fooFile.fsPath.toUpperCase())]); return pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => { const json = JSON.parse(buffer); - assert.deepEqual(json.folderWorkspaces, [fooFile.fsPath.toUpperCase()]); + assert.deepEqual(json.folderURIWorkspaces, [Uri.file(fooFile.fsPath.toUpperCase()).toString()]); }); }); @@ -475,16 +569,16 @@ suite('BackupMainService', () => { suite('removeBackupPathSync', () => { test('should remove folder workspaces from workspaces.json (folder workspace)', () => { - service.registerFolderBackupSync(fooFile.fsPath); - service.registerFolderBackupSync(barFile.fsPath); - service.removeBackupPathSync(fooFile.fsPath, service.backupsData.folderWorkspaces); + service.registerFolderBackupSync(fooFile); + service.registerFolderBackupSync(barFile); + service.unregisterFolderBackupSync(fooFile); return pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => { const json = JSON.parse(buffer); - assert.deepEqual(json.folderWorkspaces, [barFile.fsPath]); - service.removeBackupPathSync(barFile.fsPath, service.backupsData.folderWorkspaces); + assert.deepEqual(json.folderURIWorkspaces, [barFile.toString()]); + service.unregisterFolderBackupSync(barFile); return pfs.readFile(backupWorkspacesPath, 'utf-8').then(content => { const json2 = JSON.parse(content); - assert.deepEqual(json2.folderWorkspaces, []); + assert.deepEqual(json2.folderURIWorkspaces, []); }); }); }); @@ -494,11 +588,11 @@ suite('BackupMainService', () => { service.registerWorkspaceBackupSync(ws1); const ws2 = toWorkspace(barFile.fsPath); service.registerWorkspaceBackupSync(ws2); - service.removeBackupPathSync(ws1, service.backupsData.rootWorkspaces); + service.unregisterWorkspaceBackupSync(ws1); return pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => { const json = JSON.parse(buffer); assert.deepEqual(json.rootWorkspaces.map(r => r.configPath), [barFile.fsPath]); - service.removeBackupPathSync(ws2, service.backupsData.rootWorkspaces); + service.unregisterWorkspaceBackupSync(ws2); return pfs.readFile(backupWorkspacesPath, 'utf-8').then(content => { const json2 = JSON.parse(content); assert.deepEqual(json2.rootWorkspaces, []); @@ -509,11 +603,11 @@ suite('BackupMainService', () => { test('should remove empty workspaces from workspaces.json', () => { service.registerEmptyWindowBackupSync('foo'); service.registerEmptyWindowBackupSync('bar'); - service.removeBackupPathSync('foo', service.backupsData.emptyWorkspaces); + service.unregisterEmptyWindowBackupSync('foo'); return pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => { const json = JSON.parse(buffer); assert.deepEqual(json.emptyWorkspaces, ['bar']); - service.removeBackupPathSync('bar', service.backupsData.emptyWorkspaces); + service.unregisterEmptyWindowBackupSync('bar'); return pfs.readFile(backupWorkspacesPath, 'utf-8').then(content => { const json2 = JSON.parse(content); assert.deepEqual(json2.emptyWorkspaces, []); @@ -521,23 +615,24 @@ suite('BackupMainService', () => { }); }); - test('should fail gracefully when removing a path that doesn\'t exist', () => { - const workspacesJson: IBackupWorkspacesFormat = { rootWorkspaces: [], folderWorkspaces: [fooFile.fsPath], emptyWorkspaces: [] }; + test('should fail gracefully when removing a path that doesn\'t exist', async () => { + + await ensureFolderExists(existingTestFolder1); // make sure backup folder exists, so the folder is not removed on loadSync + + const workspacesJson: IBackupWorkspacesFormat = { rootWorkspaces: [], folderURIWorkspaces: [existingTestFolder1.toString()], emptyWorkspaces: [] }; return pfs.writeFile(backupWorkspacesPath, JSON.stringify(workspacesJson)).then(() => { - service.removeBackupPathSync(barFile.fsPath, service.backupsData.folderWorkspaces); - service.removeBackupPathSync('test', service.backupsData.emptyWorkspaces); + service.loadSync(); + service.unregisterFolderBackupSync(barFile); + service.unregisterEmptyWindowBackupSync('test'); return pfs.readFile(backupWorkspacesPath, 'utf-8').then(content => { const json = JSON.parse(content); - assert.deepEqual(json.folderWorkspaces, [fooFile.fsPath]); + assert.deepEqual(json.folderURIWorkspaces, [existingTestFolder1.toString()]); }); }); }); }); suite('getWorkspaceHash', () => { - test('should perform an md5 hash on the path', () => { - assert.equal(service.getFolderHash('/foo'), '1effb2475fcfba4f9e8b8a1dbc8f3caf'); - }); test('should ignore case on Windows and Mac', () => { // Skip test on Linux @@ -546,19 +641,19 @@ suite('BackupMainService', () => { } if (platform.isMacintosh) { - assert.equal(service.getFolderHash('/foo'), service.getFolderHash('/FOO')); + assert.equal(service.getFolderHash(Uri.file('/foo')), service.getFolderHash(Uri.file('/FOO'))); } if (platform.isWindows) { - assert.equal(service.getFolderHash('c:\\foo'), service.getFolderHash('C:\\FOO')); + assert.equal(service.getFolderHash(Uri.file('c:\\foo')), service.getFolderHash(Uri.file('C:\\FOO'))); } }); }); suite('mixed path casing', () => { test('should handle case insensitive paths properly (registerWindowForBackupsSync) (folder workspace)', () => { - service.registerFolderBackupSync(fooFile.fsPath); - service.registerFolderBackupSync(fooFile.fsPath.toUpperCase()); + service.registerFolderBackupSync(fooFile); + service.registerFolderBackupSync(Uri.file(fooFile.fsPath.toUpperCase())); if (platform.isLinux) { assert.equal(service.getFolderBackupPaths().length, 2); @@ -581,13 +676,13 @@ suite('BackupMainService', () => { test('should handle case insensitive paths properly (removeBackupPathSync) (folder workspace)', () => { // same case - service.registerFolderBackupSync(fooFile.fsPath); - service.removeBackupPathSync(fooFile.fsPath, service.backupsData.folderWorkspaces); + service.registerFolderBackupSync(fooFile); + service.unregisterFolderBackupSync(fooFile); assert.equal(service.getFolderBackupPaths().length, 0); // mixed case - service.registerFolderBackupSync(fooFile.fsPath); - service.removeBackupPathSync(fooFile.fsPath.toUpperCase(), service.backupsData.folderWorkspaces); + service.registerFolderBackupSync(fooFile); + service.unregisterFolderBackupSync(Uri.file(fooFile.fsPath.toUpperCase())); if (platform.isLinux) { assert.equal(service.getFolderBackupPaths().length, 1); diff --git a/src/vs/platform/commands/common/commands.ts b/src/vs/platform/commands/common/commands.ts index 7d52c3ecaa0..b912a022dfc 100644 --- a/src/vs/platform/commands/common/commands.ts +++ b/src/vs/platform/commands/common/commands.ts @@ -5,7 +5,7 @@ 'use strict'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { TypeConstraint, validateConstraints } from 'vs/base/common/types'; import { ServicesAccessor, createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event } from 'vs/base/common/event'; @@ -46,6 +46,7 @@ export interface ICommandHandlerDescription { export interface ICommandRegistry { registerCommand(id: string, command: ICommandHandler): IDisposable; registerCommand(command: ICommand): IDisposable; + registerCommandAlias(oldId: string, newId: string): IDisposable; getCommand(id: string): ICommand; getCommands(): ICommandsMap; } @@ -91,14 +92,18 @@ export const CommandsRegistry: ICommandRegistry = new class implements ICommandR let removeFn = commands.unshift(idOrCommand); - return { - dispose: () => { - removeFn(); - if (this._commands.get(id).isEmpty()) { - this._commands.delete(id); - } + return toDisposable(() => { + removeFn(); + if (this._commands.get(id).isEmpty()) { + this._commands.delete(id); } - }; + }); + } + + registerCommandAlias(oldId: string, newId: string): IDisposable { + return CommandsRegistry.registerCommand(oldId, (accessor, ...args) => { + accessor.get(ICommandService).executeCommand(newId, ...args); + }); } getCommand(id: string): ICommand { diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index ef37ffe80d5..0c63a065226 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -78,6 +78,7 @@ export interface IConfigurationPropertySchema extends IJSONSchema { scope?: ConfigurationScope; notMultiRootAdopted?: boolean; included?: boolean; + tags?: string[]; } export interface IConfigurationNode { diff --git a/src/vs/platform/contextview/browser/contextMenuHandler.ts b/src/vs/platform/contextview/browser/contextMenuHandler.ts index 05e1e4439dd..6a39c62dbc0 100644 --- a/src/vs/platform/contextview/browser/contextMenuHandler.ts +++ b/src/vs/platform/contextview/browser/contextMenuHandler.ts @@ -82,7 +82,7 @@ export class ContextMenuHandler { menu.onDidCancel(() => this.contextViewService.hideContextView(true), null, menuDisposables); menu.onDidBlur(() => this.contextViewService.hideContextView(true), null, menuDisposables); - menu.focus(); + menu.focus(!!delegate.autoSelectFirstItem); return combinedDisposable([...menuDisposables, menu]); }, diff --git a/src/vs/platform/driver/electron-browser/driver.ts b/src/vs/platform/driver/electron-browser/driver.ts index 4dbaaff6c95..6b4d876f49d 100644 --- a/src/vs/platform/driver/electron-browser/driver.ts +++ b/src/vs/platform/driver/electron-browser/driver.ts @@ -207,7 +207,7 @@ class WindowDriver implements IWindowDriver { return TPromise.wrapError(new Error('Xterm not found')); } - xterm._core.send(text); + xterm._core.handler(text); return TPromise.as(null); } diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 99792a19f00..4fc2805267b 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -8,6 +8,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' export interface ParsedArgs { [arg: string]: any; _: string[]; + 'folder-uri'?: string | string[]; _urls?: string[]; help?: boolean; version?: boolean; @@ -29,7 +30,6 @@ export interface ParsedArgs { verbose?: boolean; log?: string; logExtensionHostCommunication?: boolean; - 'disable-extensions'?: boolean; 'extensions-dir'?: string; extensionDevelopmentPath?: string; extensionTestsPath?: string; @@ -38,6 +38,8 @@ export interface ParsedArgs { debugId?: string; debugSearch?: string; debugBrkSearch?: string; + 'disable-extensions'?: boolean; + 'disable-extension'?: string | string[]; 'list-extensions'?: boolean; 'show-versions'?: boolean; 'install-extension'?: string | string[]; @@ -100,7 +102,7 @@ export interface IEnvironmentService { workspacesHome: string; isExtensionDevelopment: boolean; - disableExtensions: boolean; + disableExtensions: boolean | string[]; extensionsPath: string; extensionDevelopmentPath: string; extensionTestsPath: string; @@ -117,6 +119,7 @@ export interface IEnvironmentService { performance: boolean; // logging + log: string; logsPath: string; verbose: boolean; diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 3a8b2891705..30cfd3ddd91 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -18,9 +18,11 @@ const options: minimist.Opts = { 'locale', 'user-data-dir', 'extensions-dir', + 'folder-uri', 'extensionDevelopmentPath', 'extensionTestsPath', 'install-extension', + 'disable-extension', 'uninstall-extension', 'debugId', 'debugPluginHost', @@ -143,6 +145,7 @@ export function parseArgs(args: string[]): ParsedArgs { const optionsHelp: { [name: string]: string; } = { '-d, --diff ': localize('diff', "Compare two files with each other."), + '--folder-uri ': localize('folder uri', "Opens a window with given folder uri(s)"), '-a, --add

': localize('add', "Add folder(s) to the last active window."), '-g, --goto ': localize('goto', "Open a file at the path on the specified line and character position."), '-n, --new-window': localize('newWindow', "Force to open a new window."), @@ -170,6 +173,7 @@ const troubleshootingHelp: { [name: string]: string; } = { '-p, --performance': localize('performance', "Start with the 'Developer: Startup Performance' command enabled."), '--prof-startup': localize('prof-startup', "Run CPU profiler during startup"), '--disable-extensions': localize('disableExtensions', "Disable all installed extensions."), + '--disable-extension ': localize('disableExtension', "Disable an extension."), '--inspect-extensions': localize('inspect-extensions', "Allow debugging and profiling of extensions. Check the developer tools for the connection URI."), '--inspect-brk-extensions': localize('inspect-brk-extensions', "Allow debugging and profiling of extensions with the extension host being paused after start. Check the developer tools for the connection URI."), '--disable-gpu': localize('disableGPU', "Disable GPU hardware acceleration."), diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index 06e2171dfb5..2224b6afd17 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -8,12 +8,12 @@ import * as crypto from 'crypto'; import * as paths from 'vs/base/node/paths'; import * as os from 'os'; import * as path from 'path'; -import URI from 'vs/base/common/uri'; import { memoize } from 'vs/base/common/decorators'; import pkg from 'vs/platform/node/package'; import product from 'vs/platform/node/product'; import { toLocalISOString } from 'vs/base/common/date'; import { isWindows, isLinux } from 'vs/base/common/platform'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; // Read this before there's any chance it is overwritten // Related to https://github.com/Microsoft/vscode/issues/30624 @@ -77,7 +77,7 @@ export class EnvironmentService implements IEnvironmentService { get args(): ParsedArgs { return this._args; } @memoize - get appRoot(): string { return path.dirname(URI.parse(require.toUrl('')).fsPath); } + get appRoot(): string { return path.dirname(getPathFromAmdModule(require, '')); } get execPath(): string { return this._execPath; } @@ -153,7 +153,21 @@ export class EnvironmentService implements IEnvironmentService { @memoize get extensionTestsPath(): string { return this._args.extensionTestsPath ? path.normalize(this._args.extensionTestsPath) : this._args.extensionTestsPath; } - get disableExtensions(): boolean { return this._args['disable-extensions']; } + get disableExtensions(): boolean | string[] { + if (this._args['disable-extensions']) { + return true; + } + const disableExtensions: string | string[] = this._args['disable-extension']; + if (disableExtensions) { + if (typeof disableExtensions === 'string') { + return [disableExtensions]; + } + if (Array.isArray(disableExtensions) && disableExtensions.length > 0) { + return disableExtensions; + } + } + return false; + } get skipGettingStarted(): boolean { return this._args['skip-getting-started']; } @@ -169,6 +183,7 @@ export class EnvironmentService implements IEnvironmentService { get isBuilt(): boolean { return !process.env['VSCODE_DEV']; } get verbose(): boolean { return this._args.verbose; } + get log(): string { return this._args.log; } get wait(): boolean { return this._args.wait; } get logExtensionHostCommunication(): boolean { return this._args.logExtensionHostCommunication; } diff --git a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts index 28c6ce6805f..f56f3aabbbf 100644 --- a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts +++ b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts @@ -8,7 +8,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IExtensionManagementService, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, ILocalExtension, isIExtensionIdentifier, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { getIdFromLocalExtensionId, areSameExtensions, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { getIdFromLocalExtensionId, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -29,7 +29,7 @@ export class ExtensionEnablementService implements IExtensionEnablementService { @IStorageService private storageService: IStorageService, @IWorkspaceContextService private contextService: IWorkspaceContextService, @IEnvironmentService private environmentService: IEnvironmentService, - @IExtensionManagementService extensionManagementService: IExtensionManagementService + @IExtensionManagementService private extensionManagementService: IExtensionManagementService ) { extensionManagementService.onDidUninstallExtension(this._onDidUninstallExtension, this, this.disposables); } @@ -38,7 +38,11 @@ export class ExtensionEnablementService implements IExtensionEnablementService { return this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY; } - getDisabledExtensions(): TPromise { + get allUserExtensionsDisabled(): boolean { + return this.environmentService.disableExtensions === true; + } + + async getDisabledExtensions(): Promise { let result = this._getDisabledExtensions(StorageScope.GLOBAL); @@ -54,14 +58,25 @@ export class ExtensionEnablementService implements IExtensionEnablementService { } } - return TPromise.as(result); + if (this.environmentService.disableExtensions) { + const allInstalledExtensions = await this.extensionManagementService.getInstalled(); + for (const installedExtension of allInstalledExtensions) { + if (this._isExtensionDisabledInEnvironment(installedExtension)) { + if (!result.some(r => areSameExtensions(r, installedExtension.galleryIdentifier))) { + result.push(installedExtension.galleryIdentifier); + } + } + } + } + + return result; } getEnablementState(extension: ILocalExtension): EnablementState { - if (this.environmentService.disableExtensions && extension.type === LocalExtensionType.User) { + if (this._isExtensionDisabledInEnvironment(extension)) { return EnablementState.Disabled; } - const identifier = this._getIdentifier(extension); + const identifier = extension.galleryIdentifier; if (this.hasWorkspace) { if (this._getEnabledExtensions(StorageScope.WORKSPACE).filter(e => areSameExtensions(e, identifier))[0]) { return EnablementState.WorkspaceEnabled; @@ -95,7 +110,7 @@ export class ExtensionEnablementService implements IExtensionEnablementService { if (!this.canChangeEnablement(arg)) { return TPromise.wrap(false); } - identifier = this._getIdentifier(arg); + identifier = arg.galleryIdentifier; } const workspace = newState === EnablementState.WorkspaceDisabled || newState === EnablementState.WorkspaceEnabled; @@ -134,6 +149,17 @@ export class ExtensionEnablementService implements IExtensionEnablementService { return enablementState === EnablementState.WorkspaceEnabled || enablementState === EnablementState.Enabled; } + private _isExtensionDisabledInEnvironment(extension: ILocalExtension): boolean { + if (this.allUserExtensionsDisabled) { + return extension.type === LocalExtensionType.User; + } + const disabledExtensions = this.environmentService.disableExtensions; + if (Array.isArray(disabledExtensions)) { + return disabledExtensions.some(id => areSameExtensions({ id }, extension.galleryIdentifier)); + } + return false; + } + private _getEnablementState(identifier: IExtensionIdentifier): EnablementState { if (this.hasWorkspace) { if (this._getEnabledExtensions(StorageScope.WORKSPACE).filter(e => areSameExtensions(e, identifier))[0]) { @@ -150,10 +176,6 @@ export class ExtensionEnablementService implements IExtensionEnablementService { return EnablementState.Enabled; } - private _getIdentifier(extension: ILocalExtension): IExtensionIdentifier { - return { id: getGalleryExtensionIdFromLocal(extension), uuid: extension.identifier.uuid }; - } - private _enableExtension(identifier: IExtensionIdentifier): void { this._removeFromDisabledExtensions(identifier, StorageScope.WORKSPACE); this._removeFromEnabledExtensions(identifier, StorageScope.WORKSPACE); diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index eaa3ec6ed6f..e30d7e8787e 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -343,6 +343,8 @@ export const IExtensionEnablementService = createDecorator; + getDisabledExtensions(): Promise; /** * Returns the enablement state for the given extension diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 5cc1c5b5bb7..fca1b8fcc1e 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -40,8 +40,9 @@ import { ExtensionsLifecycle } from 'vs/platform/extensionManagement/node/extens import { toErrorMessage } from 'vs/base/common/errorMessage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { isEngineValid } from 'vs/platform/extensions/node/extensionValidator'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; -const SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions')); +const SystemExtensionsRoot = path.normalize(path.join(getPathFromAmdModule(require, ''), '..', 'extensions')); const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem'; const ERROR_SCANNING_USER_EXTENSIONS = 'scanningUser'; const INSTALL_ERROR_UNSET_UNINSTALLED = 'unsetUninstalled'; @@ -250,7 +251,7 @@ export class ExtensionManagementService extends Disposable implements IExtension const existingExtension = installed.filter(i => areSameExtensions(i.galleryIdentifier, extension.identifier))[0]; operation = existingExtension ? InstallOperation.Update : InstallOperation.Install; return this.downloadInstallableExtension(extension, operation) - .then(installableExtension => this.installExtension(installableExtension)) + .then(installableExtension => this.installExtension(installableExtension).then(local => always(pfs.rimraf(installableExtension.zipPath), () => null).then(() => local))) .then(local => this.installDependenciesAndPackExtensions(local, existingExtension) .then(() => local, error => this.uninstall(local, true).then(() => TPromise.wrapError(error), () => TPromise.wrapError(error)))); }) @@ -331,7 +332,7 @@ export class ExtensionManagementService extends Disposable implements IExtension return this.galleryService.download(extension, operation) .then( zipPath => { - this.logService.info('Downloaded extension:', extension.name); + this.logService.info('Downloaded extension:', extension.name, zipPath); return validateLocalExtension(zipPath) .then( manifest => ({ zipPath, id: getLocalExtensionIdFromManifest(manifest), metadata }), @@ -538,12 +539,20 @@ export class ExtensionManagementService extends Disposable implements IExtension private checkForDependenciesAndUninstall(extension: ILocalExtension, installed: ILocalExtension[], force: boolean): TPromise { return this.preUninstallExtension(extension) .then(() => { - if (force) { - return this.uninstallExtensionAsPack(extension, installed); + const packedExtensions = this.getAllPackExtensionsToUninstall(extension, installed); + if (packedExtensions.length) { + return this.uninstallExtensions(extension, packedExtensions, installed); + } + const dependencies = this.getDependenciesToUninstall(extension, installed); + if (dependencies.length) { + if (force) { + return this.uninstallExtensions(extension, dependencies, installed); + } else { + return this.promptForDependenciesAndUninstall(extension, dependencies, installed); + } + } else { + return this.uninstallExtensions(extension, [], installed); } - const hasInstalledExtensionPack = extension.manifest.extensionPack && extension.manifest.extensionPack.length && installed.some(i => extension.manifest.extensionPack.some(dep => areSameExtensions({ id: dep }, i.galleryIdentifier))); - const hasDependencies = extension.manifest.extensionDependencies && extension.manifest.extensionDependencies.length > 0; - return hasInstalledExtensionPack || hasDependencies ? this.promptForPackAndUninstall(extension, installed) : this.uninstallExtensions(extension, [], installed); }) .then(() => this.postUninstallExtension(extension), error => { @@ -552,17 +561,17 @@ export class ExtensionManagementService extends Disposable implements IExtension }); } - private promptForPackAndUninstall(extension: ILocalExtension, installed: ILocalExtension[]): TPromise { - const message = nls.localize('uninstallExtensionPackConfirmation', "Would you like to uninstall '{0}' only or as a pack?", extension.manifest.displayName || extension.manifest.name); + private promptForDependenciesAndUninstall(extension: ILocalExtension, dependencies: ILocalExtension[], installed: ILocalExtension[]): TPromise { + const message = nls.localize('uninstallDependeciesConfirmation', "Also uninstall the dependencies of the extension '{0}'?", extension.manifest.displayName || extension.manifest.name); const buttons = [ - nls.localize('uninstallPack', "Uninstall Extension Pack"), - nls.localize('uninstallOnly', "Uninstall Extension Only"), + nls.localize('yes', "Yes"), + nls.localize('no', "No"), nls.localize('cancel', "Cancel") ]; return this.dialogService.show(Severity.Info, message, buttons, { cancelId: 2 }) .then(value => { if (value === 0) { - return this.uninstallExtensionAsPack(extension, installed); + return this.uninstallExtensions(extension, dependencies, installed); } if (value === 1) { return this.uninstallExtensions(extension, [], installed); @@ -572,16 +581,6 @@ export class ExtensionManagementService extends Disposable implements IExtension }, error => TPromise.wrapError(errors.canceled())); } - private uninstallExtensionAsPack(extension: ILocalExtension, installed: ILocalExtension[]): TPromise { - const extensionsToUninstall = this.getDependenciesToUninstall(extension, installed); - for (const packExtensionToUninstall of this.getAllPackExtensionsToUninstall(extension, installed)) { - if (extensionsToUninstall.indexOf(packExtensionToUninstall) === -1) { - extensionsToUninstall.push(packExtensionToUninstall); - } - } - return this.uninstallExtensions(extension, extensionsToUninstall, installed); - } - private uninstallExtensions(extension: ILocalExtension, otherExtensionsToUninstall: ILocalExtension[], installed: ILocalExtension[]): TPromise { const dependents = this.getDependents(extension, installed); if (dependents.length) { diff --git a/src/vs/platform/extensionManagement/test/common/extensionEnablementService.test.ts b/src/vs/platform/extensionManagement/test/common/extensionEnablementService.test.ts index 717a17dd3b0..655d4b05b50 100644 --- a/src/vs/platform/extensionManagement/test/common/extensionEnablementService.test.ts +++ b/src/vs/platform/extensionManagement/test/common/extensionEnablementService.test.ts @@ -35,10 +35,11 @@ export class TestExtensionEnablementService extends ExtensionEnablementService { constructor(instantiationService: TestInstantiationService) { super(storageService(instantiationService), instantiationService.get(IWorkspaceContextService), instantiationService.get(IEnvironmentService) || instantiationService.stub(IEnvironmentService, {} as IEnvironmentService), - instantiationService.get(IExtensionManagementService) || instantiationService.stub(IExtensionManagementService, { onDidUninstallExtension: new Emitter() })); + instantiationService.get(IExtensionManagementService) || instantiationService.stub(IExtensionManagementService, + { onDidUninstallExtension: new Emitter().event } as IExtensionManagementService)); } - public reset(): TPromise { + public async reset(): Promise { return this.getDisabledExtensions().then(extensions => extensions.forEach(d => this.setEnablement(aLocalExtension(d.id), EnablementState.Enabled))); } } @@ -52,7 +53,7 @@ suite('ExtensionEnablementService Test', () => { setup(() => { instantiationService = new TestInstantiationService(); - instantiationService.stub(IExtensionManagementService, { onDidUninstallExtension: didUninstallEvent.event }); + instantiationService.stub(IExtensionManagementService, { onDidUninstallExtension: didUninstallEvent.event, getInstalled: () => TPromise.as([]) } as IExtensionManagementService); testObject = new TestExtensionEnablementService(instantiationService); }); @@ -331,6 +332,12 @@ suite('ExtensionEnablementService Test', () => { assert.equal(testObject.canChangeEnablement(aLocalExtension('pub.a')), false); }); + test('test canChangeEnablement return false when the extension is disabled in environment', () => { + instantiationService.stub(IEnvironmentService, { disableExtensions: ['pub.a'] } as IEnvironmentService); + testObject = new TestExtensionEnablementService(instantiationService); + assert.equal(testObject.canChangeEnablement(aLocalExtension('pub.a')), false); + }); + test('test canChangeEnablement return true for system extensions when extensions are disabled in environment', () => { instantiationService.stub(IEnvironmentService, { disableExtensions: true } as IEnvironmentService); testObject = new TestExtensionEnablementService(instantiationService); @@ -338,12 +345,32 @@ suite('ExtensionEnablementService Test', () => { extension.type = LocalExtensionType.System; assert.equal(testObject.canChangeEnablement(extension), true); }); + + test('test canChangeEnablement return false for system extensions when extension is disabled in environment', () => { + instantiationService.stub(IEnvironmentService, { disableExtensions: ['pub.a'] } as IEnvironmentService); + testObject = new TestExtensionEnablementService(instantiationService); + const extension = aLocalExtension('pub.a'); + extension.type = LocalExtensionType.System; + assert.equal(testObject.canChangeEnablement(extension), true); + }); + + test('test getDisabledExtensions include extensions disabled in enviroment', () => { + instantiationService.stub(IEnvironmentService, { disableExtensions: ['pub.a'] } as IEnvironmentService); + instantiationService.stub(IExtensionManagementService, { onDidUninstallExtension: didUninstallEvent.event, getInstalled: () => TPromise.as([aLocalExtension('pub.a'), aLocalExtension('pub.b')]) } as IExtensionManagementService); + testObject = new TestExtensionEnablementService(instantiationService); + return testObject.getDisabledExtensions() + .then(actual => { + assert.equal(actual.length, 1); + assert.equal(actual[0].id, 'pub.a'); + }); + }); }); function aLocalExtension(id: string, contributes?: IExtensionContributions): ILocalExtension { const [publisher, name] = id.split('.'); return Object.create({ identifier: { id }, + galleryIdentifier: { id, uuid: void 0 }, manifest: { name, publisher, diff --git a/src/vs/platform/history/common/history.ts b/src/vs/platform/history/common/history.ts index 1ce03f870fc..f09d7fd44ba 100644 --- a/src/vs/platform/history/common/history.ts +++ b/src/vs/platform/history/common/history.ts @@ -24,7 +24,7 @@ export interface IHistoryMainService { addRecentlyOpened(workspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[], files: string[]): void; getRecentlyOpened(currentWorkspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier, currentFiles?: IPath[]): IRecentlyOpened; - removeFromRecentlyOpened(paths: string[]): void; + removeFromRecentlyOpened(paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string)[]): void; clearRecentlyOpened(): void; updateWindowsJumpList(): void; diff --git a/src/vs/platform/history/electron-main/historyMainService.ts b/src/vs/platform/history/electron-main/historyMainService.ts index 9b7309f7c55..e98b36915e1 100644 --- a/src/vs/platform/history/electron-main/historyMainService.ts +++ b/src/vs/platform/history/electron-main/historyMainService.ts @@ -5,22 +5,34 @@ 'use strict'; -import * as path from 'path'; import * as nls from 'vs/nls'; import * as arrays from 'vs/base/common/arrays'; import { trim } from 'vs/base/common/strings'; import { IStateService } from 'vs/platform/state/common/state'; import { app } from 'electron'; import { ILogService } from 'vs/platform/log/common/log'; -import { getPathLabel, getBaseLabel } from 'vs/base/common/labels'; +import { getBaseLabel } from 'vs/base/common/labels'; import { IPath } from 'vs/platform/windows/common/windows'; import { Event as CommonEvent, Emitter } from 'vs/base/common/event'; import { isWindows, isMacintosh, isLinux } from 'vs/base/common/platform'; -import { IWorkspaceIdentifier, IWorkspacesMainService, getWorkspaceLabel, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IWorkspaceSavedEvent } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, IWorkspacesMainService, getWorkspaceLabel, IWorkspaceSavedEvent, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IHistoryMainService, IRecentlyOpened } from 'vs/platform/history/common/history'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { isEqual } from 'vs/base/common/paths'; import { RunOnceScheduler } from 'vs/base/common/async'; +import { getComparisonKey, isEqual as areResourcesEqual, hasToIgnoreCase, dirname } from 'vs/base/common/resources'; +import URI, { UriComponents } from 'vs/base/common/uri'; +import { Schemas } from 'vs/base/common/network'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; + +interface ISerializedRecentlyOpened { + workspaces2: (IWorkspaceIdentifier | string)[]; // IWorkspaceIdentifier or URI.toString() + files: string[]; +} + +interface ILegacySerializedRecentlyOpened { + workspaces: (IWorkspaceIdentifier | string | UriComponents)[]; // legacy (UriComponents was also supported for a few insider builds) +} export class HistoryMainService implements IHistoryMainService { @@ -40,7 +52,8 @@ export class HistoryMainService implements IHistoryMainService { @IStateService private stateService: IStateService, @ILogService private logService: ILogService, @IWorkspacesMainService private workspacesMainService: IWorkspacesMainService, - @IEnvironmentService private environmentService: IEnvironmentService + @IEnvironmentService private environmentService: IEnvironmentService, + @IUriDisplayService private uriDisplayService: IUriDisplayService ) { this.macOSRecentDocumentsUpdater = new RunOnceScheduler(() => this.updateMacOSRecentDocuments(), 800); @@ -104,21 +117,37 @@ export class HistoryMainService implements IHistoryMainService { } } - removeFromRecentlyOpened(pathsToRemove: string[]): void { + removeFromRecentlyOpened(pathsToRemove: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string)[]): void { const mru = this.getRecentlyOpened(); let update = false; pathsToRemove.forEach((pathToRemove => { // Remove workspace - let index = arrays.firstIndex(mru.workspaces, workspace => isEqual(isSingleFolderWorkspaceIdentifier(workspace) ? workspace : workspace.configPath, pathToRemove, !isLinux /* ignorecase */)); + let index = arrays.firstIndex(mru.workspaces, workspace => { + if (isWorkspaceIdentifier(pathToRemove)) { + return isWorkspaceIdentifier(workspace) && isEqual(pathToRemove.configPath, workspace.configPath, !isLinux /* ignorecase */); + } + if (isSingleFolderWorkspaceIdentifier(pathToRemove)) { + return isSingleFolderWorkspaceIdentifier(workspace) && areResourcesEqual(pathToRemove, workspace, hasToIgnoreCase(pathToRemove)); + } + if (typeof pathToRemove === 'string') { + if (isSingleFolderWorkspaceIdentifier(workspace)) { + return workspace.scheme === Schemas.file && areResourcesEqual(URI.file(pathToRemove), workspace, hasToIgnoreCase(workspace)); + } + if (isWorkspaceIdentifier(workspace)) { + return isEqual(pathToRemove, workspace.configPath, !isLinux /* ignorecase */); + } + } + return false; + }); if (index >= 0) { mru.workspaces.splice(index, 1); update = true; } // Remove file - index = arrays.firstIndex(mru.files, file => isEqual(file, pathToRemove, !isLinux /* ignorecase */)); + index = arrays.firstIndex(mru.files, file => typeof pathToRemove === 'string' && isEqual(file, pathToRemove, !isLinux /* ignorecase */)); if (index >= 0) { mru.files.splice(index, 1); update = true; @@ -153,9 +182,10 @@ export class HistoryMainService implements IHistoryMainService { let maxEntries = HistoryMainService.MAX_MACOS_DOCK_RECENT_ENTRIES; // Take up to maxEntries/2 workspaces - for (let i = 0; i < mru.workspaces.length && i < HistoryMainService.MAX_MACOS_DOCK_RECENT_ENTRIES / 2; i++) { - const workspace = mru.workspaces[i]; - app.addRecentDocument(isSingleFolderWorkspaceIdentifier(workspace) ? workspace : workspace.configPath); + const workspaces = mru.workspaces.filter(w => !(isSingleFolderWorkspaceIdentifier(w) && w.scheme !== Schemas.file)); + for (let i = 0; i < workspaces.length && i < HistoryMainService.MAX_MACOS_DOCK_RECENT_ENTRIES / 2; i++) { + const workspace = workspaces[i]; + app.addRecentDocument(isSingleFolderWorkspaceIdentifier(workspace) ? workspace.scheme === Schemas.file ? workspace.fsPath : workspace.toString() : workspace.configPath); maxEntries--; } @@ -179,7 +209,7 @@ export class HistoryMainService implements IHistoryMainService { let files: string[]; // Get from storage - const storedRecents = this.stateService.getItem(HistoryMainService.recentlyOpenedStorageKey); + const storedRecents = this.getRecentlyOpenedFromStorage(); if (storedRecents) { workspaces = storedRecents.workspaces || []; files = storedRecents.files || []; @@ -210,14 +240,62 @@ export class HistoryMainService implements IHistoryMainService { private distinctFn(workspaceOrFile: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string): string { if (isSingleFolderWorkspaceIdentifier(workspaceOrFile)) { + return getComparisonKey(workspaceOrFile); + } + if (typeof workspaceOrFile === 'string') { return isLinux ? workspaceOrFile : workspaceOrFile.toLowerCase(); } return workspaceOrFile.id; } + private getRecentlyOpenedFromStorage(): IRecentlyOpened { + const storedRecents = this.stateService.getItem(HistoryMainService.recentlyOpenedStorageKey); + const result: IRecentlyOpened = { workspaces: [], files: [] }; + if (storedRecents) { + if (Array.isArray(storedRecents.workspaces2)) { + for (const workspace of storedRecents.workspaces2) { + if (isWorkspaceIdentifier(workspace)) { + result.workspaces.push(workspace); + } else if (typeof workspace === 'string') { + result.workspaces.push(URI.parse(workspace)); + } + } + } else if (Array.isArray(storedRecents.workspaces)) { + // TODO legacy support can be removed at some point (6 month?) + // format of 1.25 and before + for (const workspace of storedRecents.workspaces) { + if (typeof workspace === 'string') { + result.workspaces.push(URI.file(workspace)); + } else if (isWorkspaceIdentifier(workspace)) { + result.workspaces.push(workspace); + } else if (workspace && typeof workspace.path === 'string' && typeof workspace.scheme === 'string') { + // added by 1.26-insiders + result.workspaces.push(URI.revive(workspace)); + } + } + } + if (Array.isArray(storedRecents.files)) { + for (const file of storedRecents.files) { + if (typeof file === 'string') { + result.files.push(file); + } + } + } + } + return result; + } + private saveRecentlyOpened(recent: IRecentlyOpened): void { - this.stateService.setItem(HistoryMainService.recentlyOpenedStorageKey, recent); + const serialized: ISerializedRecentlyOpened = { workspaces2: [], files: recent.files }; + for (const workspace of recent.workspaces) { + if (isSingleFolderWorkspaceIdentifier(workspace)) { + serialized.workspaces2.push(workspace.toString()); + } else { + serialized.workspaces2.push(workspace); + } + } + this.stateService.setItem(HistoryMainService.recentlyOpenedStorageKey, serialized); } updateWindowsJumpList(): void { @@ -257,15 +335,26 @@ export class HistoryMainService implements IHistoryMainService { type: 'custom', name: nls.localize('recentFolders', "Recent Workspaces"), items: this.getRecentlyOpened().workspaces.slice(0, 7 /* limit number of entries here */).map(workspace => { - const title = isSingleFolderWorkspaceIdentifier(workspace) ? getBaseLabel(workspace) : getWorkspaceLabel(workspace, this.environmentService); - const description = isSingleFolderWorkspaceIdentifier(workspace) ? nls.localize('folderDesc', "{0} {1}", getBaseLabel(workspace), getPathLabel(path.dirname(workspace), this.environmentService)) : nls.localize('codeWorkspace', "Code Workspace"); + const title = getWorkspaceLabel(workspace, this.environmentService, this.uriDisplayService); + const description = isSingleFolderWorkspaceIdentifier(workspace) ? nls.localize('folderDesc', "{0} {1}", getBaseLabel(workspace), this.uriDisplayService.getLabel(dirname(workspace))) : nls.localize('codeWorkspace', "Code Workspace"); + let args; + // use quotes to support paths with whitespaces + if (isSingleFolderWorkspaceIdentifier(workspace)) { + if (workspace.scheme === Schemas.file) { + args = `"${workspace.fsPath}"`; + } else { + args = `--folderUri "${workspace.path}"`; + } + } else { + args = `"${workspace.configPath}"`; + } return { type: 'task', title, description, program: process.execPath, - args: `"${isSingleFolderWorkspaceIdentifier(workspace) ? workspace : workspace.configPath}"`, // open folder (use quotes to support paths with whitespaces) + args, iconPath: 'explorer.exe', // simulate folder icon iconIndex: 0 }; diff --git a/src/vs/platform/keybinding/common/abstractKeybindingService.ts b/src/vs/platform/keybinding/common/abstractKeybindingService.ts index 075cb92fe87..4c6c989f169 100644 --- a/src/vs/platform/keybinding/common/abstractKeybindingService.ts +++ b/src/vs/platform/keybinding/common/abstractKeybindingService.ts @@ -5,7 +5,7 @@ 'use strict'; import * as nls from 'vs/nls'; -import { ResolvedKeybinding, Keybinding } from 'vs/base/common/keyCodes'; +import { ResolvedKeybinding, Keybinding, KeyCode } from 'vs/base/common/keyCodes'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { KeybindingResolver, IResolveResult } from 'vs/platform/keybinding/common/keybindingResolver'; @@ -204,4 +204,18 @@ export abstract class AbstractKeybindingService extends Disposable implements IK return shouldPreventDefault; } + + mightProducePrintableCharacter(event: IKeyboardEvent): boolean { + if (event.ctrlKey || event.metaKey) { + // ignore ctrl/cmd-combination but not shift/alt-combinatios + return false; + } + // weak check for certain ranges. this is properly implemented in a subclass + // with access to the KeyboardMapperFactory. + if ((event.keyCode >= KeyCode.KEY_A && event.keyCode <= KeyCode.KEY_Z) + || (event.keyCode >= KeyCode.KEY_0 && event.keyCode <= KeyCode.KEY_9)) { + return true; + } + return false; + } } diff --git a/src/vs/platform/keybinding/common/keybinding.ts b/src/vs/platform/keybinding/common/keybinding.ts index c491ec7a8d0..7110738c198 100644 --- a/src/vs/platform/keybinding/common/keybinding.ts +++ b/src/vs/platform/keybinding/common/keybinding.ts @@ -77,5 +77,11 @@ export interface IKeybindingService { getKeybindings(): ResolvedKeybindingItem[]; customKeybindingsCount(): number; + + /** + * Will the given key event produce a character that's rendered on screen, e.g. in a + * text box. *Note* that the results of this function can be incorrect. + */ + mightProducePrintableCharacter(event: IKeyboardEvent): boolean; } diff --git a/src/vs/platform/keybinding/common/keybindingsRegistry.ts b/src/vs/platform/keybinding/common/keybindingsRegistry.ts index 39113558334..3d581b4e64f 100644 --- a/src/vs/platform/keybinding/common/keybindingsRegistry.ts +++ b/src/vs/platform/keybinding/common/keybindingsRegistry.ts @@ -57,6 +57,14 @@ export const enum KeybindingRuleSource { Extension = 1 } +export const enum KeybindingWeight { + EditorCore = 0, + EditorContrib = 100, + WorkbenchContrib = 200, + BuiltinExtension = 300, + ExternalExtension = 400 +} + export interface ICommandAndKeybindingRule extends IKeybindingRule { handler: ICommandHandler; description?: ICommandHandlerDescription; @@ -67,14 +75,6 @@ export interface IKeybindingsRegistry { registerKeybindingRule2(rule: IKeybindingRule2, source?: KeybindingRuleSource): void; registerCommandAndKeybindingRule(desc: ICommandAndKeybindingRule, source?: KeybindingRuleSource): void; getDefaultKeybindings(): IKeybindingItem[]; - - WEIGHT: { - editorCore(importance?: number): number; - editorContrib(importance?: number): number; - workbenchContrib(importance?: number): number; - builtinExtension(importance?: number): number; - externalExtension(importance?: number): number; - }; } class KeybindingsRegistryImpl implements IKeybindingsRegistry { @@ -82,24 +82,6 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry { private _keybindings: IKeybindingItem[]; private _keybindingsSorted: boolean; - public WEIGHT = { - editorCore: (importance: number = 0): number => { - return 0 + importance; - }, - editorContrib: (importance: number = 0): number => { - return 100 + importance; - }, - workbenchContrib: (importance: number = 0): number => { - return 200 + importance; - }, - builtinExtension: (importance: number = 0): number => { - return 300 + importance; - }, - externalExtension: (importance: number = 0): number => { - return 400 + importance; - } - }; - constructor() { this._keybindings = []; this._keybindingsSorted = true; diff --git a/src/vs/platform/keybinding/test/common/keybindingLabels.test.ts b/src/vs/platform/keybinding/test/common/keybindingLabels.test.ts index 19f285354cc..25dc83e6d4d 100644 --- a/src/vs/platform/keybinding/test/common/keybindingLabels.test.ts +++ b/src/vs/platform/keybinding/test/common/keybindingLabels.test.ts @@ -55,24 +55,24 @@ suite('KeybindingLabels', () => { assertUSLabel(OperatingSystem.Linux, KeyMod.CtrlCmd | KeyCode.KEY_A, 'Ctrl+A'); assertUSLabel(OperatingSystem.Linux, KeyMod.Shift | KeyCode.KEY_A, 'Shift+A'); assertUSLabel(OperatingSystem.Linux, KeyMod.Alt | KeyCode.KEY_A, 'Alt+A'); - assertUSLabel(OperatingSystem.Linux, KeyMod.WinCtrl | KeyCode.KEY_A, 'Windows+A'); + assertUSLabel(OperatingSystem.Linux, KeyMod.WinCtrl | KeyCode.KEY_A, 'Super+A'); // two modifiers assertUSLabel(OperatingSystem.Linux, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_A, 'Ctrl+Shift+A'); assertUSLabel(OperatingSystem.Linux, KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_A, 'Ctrl+Alt+A'); - assertUSLabel(OperatingSystem.Linux, KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_A, 'Ctrl+Windows+A'); + assertUSLabel(OperatingSystem.Linux, KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_A, 'Ctrl+Super+A'); assertUSLabel(OperatingSystem.Linux, KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_A, 'Shift+Alt+A'); - assertUSLabel(OperatingSystem.Linux, KeyMod.Shift | KeyMod.WinCtrl | KeyCode.KEY_A, 'Shift+Windows+A'); - assertUSLabel(OperatingSystem.Linux, KeyMod.Alt | KeyMod.WinCtrl | KeyCode.KEY_A, 'Alt+Windows+A'); + assertUSLabel(OperatingSystem.Linux, KeyMod.Shift | KeyMod.WinCtrl | KeyCode.KEY_A, 'Shift+Super+A'); + assertUSLabel(OperatingSystem.Linux, KeyMod.Alt | KeyMod.WinCtrl | KeyCode.KEY_A, 'Alt+Super+A'); // three modifiers assertUSLabel(OperatingSystem.Linux, KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_A, 'Ctrl+Shift+Alt+A'); - assertUSLabel(OperatingSystem.Linux, KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.WinCtrl | KeyCode.KEY_A, 'Ctrl+Shift+Windows+A'); - assertUSLabel(OperatingSystem.Linux, KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.KEY_A, 'Ctrl+Alt+Windows+A'); - assertUSLabel(OperatingSystem.Linux, KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.KEY_A, 'Shift+Alt+Windows+A'); + assertUSLabel(OperatingSystem.Linux, KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.WinCtrl | KeyCode.KEY_A, 'Ctrl+Shift+Super+A'); + assertUSLabel(OperatingSystem.Linux, KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.KEY_A, 'Ctrl+Alt+Super+A'); + assertUSLabel(OperatingSystem.Linux, KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.KEY_A, 'Shift+Alt+Super+A'); // four modifiers - assertUSLabel(OperatingSystem.Linux, KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.KEY_A, 'Ctrl+Shift+Alt+Windows+A'); + assertUSLabel(OperatingSystem.Linux, KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.KEY_A, 'Ctrl+Shift+Alt+Super+A'); // chord assertUSLabel(OperatingSystem.Linux, KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_A, KeyMod.CtrlCmd | KeyCode.KEY_B), 'Ctrl+A Ctrl+B'); @@ -122,7 +122,7 @@ suite('KeybindingLabels', () => { } assertAriaLabel(OperatingSystem.Windows, KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.KEY_A, 'Control+Shift+Alt+Windows+A'); - assertAriaLabel(OperatingSystem.Linux, KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.KEY_A, 'Control+Shift+Alt+Windows+A'); + assertAriaLabel(OperatingSystem.Linux, KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.KEY_A, 'Control+Shift+Alt+Super+A'); assertAriaLabel(OperatingSystem.Macintosh, KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.KEY_A, 'Control+Shift+Alt+Command+A'); }); diff --git a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts index 8a44c7bd8db..a8b00dfb030 100644 --- a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts +++ b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts @@ -124,4 +124,8 @@ export class MockKeybindingService implements IKeybindingService { dispatchEvent(e: IKeyboardEvent, target: IContextKeyServiceTarget): boolean { return false; } + + mightProducePrintableCharacter(e: IKeyboardEvent): boolean { + return false; + } } diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index ebd78c911f1..8df85fe9180 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -31,6 +31,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { onUnexpectedError, canceled } from 'vs/base/common/errors'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; export type ListWidget = List | PagedList | ITree; @@ -102,11 +103,6 @@ export const WorkbenchListMultiSelection = new RawContextKey('listMulti function createScopedContextKeyService(contextKeyService: IContextKeyService, widget: ListWidget): IContextKeyService { const result = contextKeyService.createScoped(widget.getHTMLElement()); - - if (widget instanceof List || widget instanceof PagedList) { - WorkbenchListSupportsMultiSelectContextKey.bindTo(result); - } - RawWorkbenchListFocusContextKey.bindTo(result); return result; } @@ -233,6 +229,10 @@ export class WorkbenchList extends List { ); this.contextKeyService = createScopedContextKeyService(contextKeyService, this); + + const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService); + listSupportsMultiSelect.set(!(options.multipleSelectionSupport === false)); + this.listHasSelectionOrFocus = WorkbenchListHasSelectionOrFocus.bindTo(this.contextKeyService); this.listDoubleSelection = WorkbenchListDoubleSelection.bindTo(this.contextKeyService); this.listMultiSelection = WorkbenchListMultiSelection.bindTo(this.contextKeyService); @@ -305,6 +305,9 @@ export class WorkbenchPagedList extends PagedList { this.contextKeyService = createScopedContextKeyService(contextKeyService, this); + const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService); + listSupportsMultiSelect.set(!(options.multipleSelectionSupport === false)); + this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); this.disposables.push(combinedDisposable([ @@ -371,6 +374,9 @@ export class WorkbenchTree extends Tree { this.disposables = []; this.contextKeyService = createScopedContextKeyService(contextKeyService, this); + + WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService); + this.listHasSelectionOrFocus = WorkbenchListHasSelectionOrFocus.bindTo(this.contextKeyService); this.listDoubleSelection = WorkbenchListDoubleSelection.bindTo(this.contextKeyService); this.listMultiSelection = WorkbenchListMultiSelection.bindTo(this.contextKeyService); @@ -573,7 +579,8 @@ export class HighlightingTreeController extends WorkbenchTreeController { constructor( options: IControllerOptions, private readonly onType: () => any, - @IConfigurationService configurationService: IConfigurationService + @IConfigurationService configurationService: IConfigurationService, + @IKeybindingService private readonly _keybindingService: IKeybindingService, ) { super(options, configurationService); } @@ -586,16 +593,7 @@ export class HighlightingTreeController extends WorkbenchTreeController { if (this.upKeyBindingDispatcher.has(event.keyCode)) { return false; } - if (event.ctrlKey || event.metaKey) { - // ignore ctrl/cmd-combination but not shift/alt-combinatios - return false; - } - // crazy -> during keydown focus moves to the input box - // and because of that the keyup event is handled by the - // input field - if (event.keyCode >= KeyCode.KEY_A && event.keyCode <= KeyCode.KEY_Z) { - // todo@joh this is much weaker than using the KeyboardMapperFactory - // but due to layering-challanges that's not available here... + if (this._keybindingService.mightProducePrintableCharacter(event)) { this.onType(); return true; } @@ -649,19 +647,23 @@ export class HighlightingWorkbenchTree extends WorkbenchTree { this.input.onDidChange(this.updateHighlights, this, this.disposables); this.disposables.push(attachInputBoxStyler(this.input, themeService)); this.disposables.push(this.input); - this.disposables.push(addStandardDisposableListener(this.input.inputElement, 'keyup', event => { + this.disposables.push(addStandardDisposableListener(this.input.inputElement, 'keydown', event => { //todo@joh make this command/context-key based - if (event.keyCode === KeyCode.DownArrow) { - this.focusNext(); - this.domFocus(); - } else if (event.keyCode === KeyCode.UpArrow) { - this.focusPrevious(); - this.domFocus(); - } else if (event.keyCode === KeyCode.Enter) { - this.setSelection(this.getSelection()); - } else if (event.keyCode === KeyCode.Escape) { - this.input.value = ''; - this.domFocus(); + switch (event.keyCode) { + case KeyCode.DownArrow: + case KeyCode.Tab: + this.domFocus(); + event.preventDefault(); + break; + case KeyCode.Enter: + this.setSelection(this.getSelection()); + event.preventDefault(); + break; + case KeyCode.Escape: + this.input.value = ''; + this.domFocus(); + event.preventDefault(); + break; } })); } @@ -694,26 +696,26 @@ export class HighlightingWorkbenchTree extends WorkbenchTree { private updateHighlights(pattern: string): void { // remember old selection - let defaultSelection: any[]; + let defaultSelection: any[] = []; if (!this.lastSelection && pattern) { this.lastSelection = this.getSelection(); - defaultSelection = []; } else if (this.lastSelection && !pattern) { defaultSelection = this.lastSelection; this.lastSelection = []; } - let topElement = this.renderer.updateHighlights(this, pattern); - if (topElement && pattern) { - this.reveal(topElement).then(_ => { - this.setSelection([topElement], this); - this.setFocus(topElement, this); - return this.refresh(); - }, onUnexpectedError); - } else { - this.setSelection(defaultSelection, this); - this.refresh().then(undefined, onUnexpectedError); - } + const topElement = this.renderer.updateHighlights(this, pattern); + + this.refresh().then(() => { + if (topElement && pattern) { + this.reveal(topElement, .5).then(_ => { + this.setSelection([topElement], this); + this.setFocus(topElement, this); + }); + } else { + this.setSelection(defaultSelection, this); + } + }, onUnexpectedError); } } @@ -739,20 +741,16 @@ configurationRegistry.registerConfiguration({ '- `ctrlCmd` refers to a value the setting can take and should not be localized.', '- `Control` and `Command` refer to the modifier keys Ctrl or Cmd on the keyboard and can be localized.' ] - }, "The modifier to be used to add an item in trees and lists to a multi-selection with the mouse (for example in the explorer, open editors and scm view). `ctrlCmd` maps to `Control` on Windows and Linux and to `Command` on macOS. The 'Open to Side' mouse gestures - if supported - will adapt such that they do not conflict with the multiselect modifier.") + }, "The modifier to be used to add an item in trees and lists to a multi-selection with the mouse (for example in the explorer, open editors and scm view). The 'Open to Side' mouse gestures - if supported - will adapt such that they do not conflict with the multiselect modifier.") }, [openModeSettingKey]: { 'type': 'string', 'enum': ['singleClick', 'doubleClick'], - 'enumDescriptions': [ - localize('openMode.singleClick', "Opens items on mouse single click."), - localize('openMode.doubleClick', "Open items on mouse double click.") - ], 'default': 'singleClick', 'description': localize({ key: 'openModeModifier', comment: ['`singleClick` and `doubleClick` refers to a value the setting can take and should not be localized.'] - }, "Controls how to open items in trees and lists using the mouse (if supported). Set to `singleClick` to open items with a single mouse click and `doubleClick` to only open via mouse double click. For parents with children in trees, this setting will control if a single click expands the parent or a double click. Note that some trees and lists might choose to ignore this setting if it is not applicable. ") + }, "Controls how to open items in trees and lists using the mouse (if supported). For parents with children in trees, this setting will control if a single click expands the parent or a double click. Note that some trees and lists might choose to ignore this setting if it is not applicable. ") }, [horizontalScrollingKey]: { 'type': 'boolean', diff --git a/src/vs/platform/markers/test/common/markerService.test.ts b/src/vs/platform/markers/test/common/markerService.test.ts index 06495c239e4..25cb2e933a2 100644 --- a/src/vs/platform/markers/test/common/markerService.test.ts +++ b/src/vs/platform/markers/test/common/markerService.test.ts @@ -58,16 +58,16 @@ suite('Marker Service', () => { test('changeOne override', () => { let service = new markerService.MarkerService(); - service.changeOne('far', URI.parse('/path/only.cs'), [randomMarkerData()]); + service.changeOne('far', URI.parse('file:///path/only.cs'), [randomMarkerData()]); assert.equal(service.read().length, 1); assert.equal(service.read({ owner: 'far' }).length, 1); - service.changeOne('boo', URI.parse('/path/only.cs'), [randomMarkerData()]); + service.changeOne('boo', URI.parse('file:///path/only.cs'), [randomMarkerData()]); assert.equal(service.read().length, 2); assert.equal(service.read({ owner: 'far' }).length, 1); assert.equal(service.read({ owner: 'boo' }).length, 1); - service.changeOne('far', URI.parse('/path/only.cs'), [randomMarkerData(), randomMarkerData()]); + service.changeOne('far', URI.parse('file:///path/only.cs'), [randomMarkerData(), randomMarkerData()]); assert.equal(service.read({ owner: 'far' }).length, 2); assert.equal(service.read({ owner: 'boo' }).length, 1); @@ -76,13 +76,13 @@ suite('Marker Service', () => { test('changeOne/All clears', () => { let service = new markerService.MarkerService(); - service.changeOne('far', URI.parse('/path/only.cs'), [randomMarkerData()]); - service.changeOne('boo', URI.parse('/path/only.cs'), [randomMarkerData()]); + service.changeOne('far', URI.parse('file:///path/only.cs'), [randomMarkerData()]); + service.changeOne('boo', URI.parse('file:///path/only.cs'), [randomMarkerData()]); assert.equal(service.read({ owner: 'far' }).length, 1); assert.equal(service.read({ owner: 'boo' }).length, 1); assert.equal(service.read().length, 2); - service.changeOne('far', URI.parse('/path/only.cs'), []); + service.changeOne('far', URI.parse('file:///path/only.cs'), []); assert.equal(service.read({ owner: 'far' }).length, 0); assert.equal(service.read({ owner: 'boo' }).length, 1); assert.equal(service.read().length, 1); diff --git a/src/vs/platform/menubar/common/menubar.ts b/src/vs/platform/menubar/common/menubar.ts index 5fcc3ecec23..3e58c3e4c9b 100644 --- a/src/vs/platform/menubar/common/menubar.ts +++ b/src/vs/platform/menubar/common/menubar.ts @@ -13,7 +13,7 @@ export const IMenubarService = createDecorator('menubarService' export interface IMenubarService { _serviceBrand: any; - updateMenubar(windowId: number, menus: IMenubarData): TPromise; + updateMenubar(windowId: number, menus: IMenubarData, additionalKeybindings?: Array): TPromise; } export interface IMenubarData { @@ -23,7 +23,13 @@ export interface IMenubarData { } export interface IMenubarMenu { - items: Array; + items: Array; +} + +export interface IMenubarKeybinding { + id: string; + label: string; + isNative: boolean; } export interface IMenubarMenuItemAction { @@ -31,8 +37,29 @@ export interface IMenubarMenuItemAction { label: string; checked: boolean; enabled: boolean; + keybinding?: IMenubarKeybinding; +} + +export interface IMenubarMenuItemSubmenu { + id: string; + label: string; + submenu: IMenubarMenu; } export interface IMenubarMenuItemSeparator { id: 'vscode.menubar.separator'; +} + +export type MenubarMenuItem = IMenubarMenuItemAction | IMenubarMenuItemSubmenu | IMenubarMenuItemSeparator; + +export function isMenubarMenuItemSubmenu(menuItem: MenubarMenuItem): menuItem is IMenubarMenuItemSubmenu { + return (menuItem).submenu !== undefined; +} + +export function isMenubarMenuItemAction(menuItem: MenubarMenuItem): menuItem is IMenubarMenuItemAction { + return (menuItem).checked !== undefined || (menuItem).enabled !== undefined; +} + +export function isMenubarMenuItemSeparator(menuItem: MenubarMenuItem): menuItem is IMenubarMenuItemSeparator { + return (menuItem).id === 'vscode.menubar.separator'; } \ No newline at end of file diff --git a/src/vs/platform/menubar/common/menubarIpc.ts b/src/vs/platform/menubar/common/menubarIpc.ts index 13699176ef5..25a28a13845 100644 --- a/src/vs/platform/menubar/common/menubarIpc.ts +++ b/src/vs/platform/menubar/common/menubarIpc.ts @@ -6,7 +6,7 @@ 'use strict'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IMenubarService, IMenubarData } from 'vs/platform/menubar/common/menubar'; +import { IMenubarService, IMenubarData, IMenubarKeybinding } from 'vs/platform/menubar/common/menubar'; import { Event } from 'vs/base/common/event'; export interface IMenubarChannel extends IChannel { @@ -24,7 +24,7 @@ export class MenubarChannel implements IMenubarChannel { call(command: string, arg?: any): TPromise { switch (command) { - case 'updateMenubar': return this.service.updateMenubar(arg[0], arg[1]); + case 'updateMenubar': return this.service.updateMenubar(arg[0], arg[1], arg[2]); } return undefined; } @@ -36,7 +36,7 @@ export class MenubarChannelClient implements IMenubarService { constructor(private channel: IMenubarChannel) { } - updateMenubar(windowId: number, menus: IMenubarData): TPromise { - return this.channel.call('updateMenubar', [windowId, menus]); + updateMenubar(windowId: number, menus: IMenubarData, additionalKeybindings?: Array): TPromise { + return this.channel.call('updateMenubar', [windowId, menus, additionalKeybindings]); } } \ No newline at end of file diff --git a/src/vs/platform/menubar/electron-main/menubarService.ts b/src/vs/platform/menubar/electron-main/menubarService.ts index d4a846baf26..7d7997c6812 100644 --- a/src/vs/platform/menubar/electron-main/menubarService.ts +++ b/src/vs/platform/menubar/electron-main/menubarService.ts @@ -5,7 +5,7 @@ 'use strict'; -import { IMenubarService, IMenubarData } from 'vs/platform/menubar/common/menubar'; +import { IMenubarService, IMenubarData, IMenubarKeybinding } from 'vs/platform/menubar/common/menubar'; import { Menubar } from 'vs/code/electron-main/menubar'; import { ILogService } from 'vs/platform/log/common/log'; import { TPromise } from 'vs/base/common/winjs.base'; @@ -22,17 +22,16 @@ export class MenubarService implements IMenubarService { @ILogService private logService: ILogService ) { // Install Menu - // TODO@sbatten: Remove if block if (isMacintosh && isWindows) { this._menubar = this.instantiationService.createInstance(Menubar); } } - updateMenubar(windowId: number, menus: IMenubarData): TPromise { + updateMenubar(windowId: number, menus: IMenubarData, additionalKeybindings?: Array): TPromise { this.logService.trace('menubarService#updateMenubar', windowId); if (this._menubar) { - this._menubar.updateMenu(menus, windowId); + this._menubar.updateMenu(menus, windowId, additionalKeybindings); } return TPromise.as(null); diff --git a/src/vs/platform/node/package.ts b/src/vs/platform/node/package.ts index fff85d911f2..93c32bc7117 100644 --- a/src/vs/platform/node/package.ts +++ b/src/vs/platform/node/package.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as path from 'path'; -import uri from 'vs/base/common/uri'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; export interface IPackageConfiguration { name: string; version: string; } -const rootPath = path.dirname(uri.parse(require.toUrl('')).fsPath); +const rootPath = path.dirname(getPathFromAmdModule(require, '')); const packageJsonPath = path.join(rootPath, 'package.json'); -export default require.__$__nodeRequire(packageJsonPath) as IPackageConfiguration; \ No newline at end of file +export default require.__$__nodeRequire(packageJsonPath) as IPackageConfiguration; diff --git a/src/vs/platform/node/product.ts b/src/vs/platform/node/product.ts index b4ed6f55eab..dbb84fd473f 100644 --- a/src/vs/platform/node/product.ts +++ b/src/vs/platform/node/product.ts @@ -4,12 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import * as path from 'path'; -import uri from 'vs/base/common/uri'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; export interface IProductConfiguration { nameShort: string; nameLong: string; applicationName: string; + win32AppId: string; + win32x64AppId: string; + win32UserAppId: string; + win32x64UserAppId: string; win32AppUserModelId: string; win32MutexName: string; darwinBundleIdentifier: string; @@ -86,7 +90,7 @@ export interface ISurveyData { userProbability: number; } -const rootPath = path.dirname(uri.parse(require.toUrl('')).fsPath); +const rootPath = path.dirname(getPathFromAmdModule(require, '')); const productJsonPath = path.join(rootPath, 'product.json'); const product = require.__$__nodeRequire(productJsonPath) as IProductConfiguration; diff --git a/src/vs/platform/quickinput/common/quickInput.ts b/src/vs/platform/quickinput/common/quickInput.ts index 0ec12af97a1..8b88046261d 100644 --- a/src/vs/platform/quickinput/common/quickInput.ts +++ b/src/vs/platform/quickinput/common/quickInput.ts @@ -16,6 +16,8 @@ export interface IQuickPickItem { label: string; description?: string; detail?: string; + iconClasses?: string[]; + buttons?: IQuickInputButton[]; picked?: boolean; } @@ -50,7 +52,23 @@ export interface IPickOptions { */ canPickMany?: boolean; + /** + * enables quick navigate in the picker to open an element without typing + */ + quickNavigate?: IQuickNavigateConfiguration; + + /** + * a context key to set when this picker is active + */ + contextKey?: string; + + /** + * an optional property for the item to focus initially. + */ + activeItem?: TPromise | T; + onDidFocus?: (entry: T) => void; + onDidTriggerItemButton?: (context: IQuickPickItemButtonContext) => void; } export interface IInputOptions { @@ -98,6 +116,8 @@ export interface IQuickInput { enabled: boolean; + contextKey: string | undefined; + busy: boolean; ignoreFocusOut: boolean; @@ -125,6 +145,8 @@ export interface IQuickPick extends IQuickInput { readonly onDidTriggerButton: Event; + readonly onDidTriggerItemButton: Event>; + items: ReadonlyArray; canSelectMany: boolean; @@ -133,6 +155,8 @@ export interface IQuickPick extends IQuickInput { matchOnDetail: boolean; + quickNavigate: IQuickNavigateConfiguration | undefined; + activeItems: ReadonlyArray; readonly onDidChangeActive: Event; @@ -166,12 +190,24 @@ export interface IInputBox extends IQuickInput { } export interface IQuickInputButton { - iconPath: { dark: URI; light?: URI; }; - tooltip?: string | undefined; + iconPath?: { dark: URI; light?: URI; }; + iconClass?: string; + tooltip?: string; +} + +export interface IQuickPickItemButtonEvent { + button: IQuickInputButton; + item: T; +} + +export interface IQuickPickItemButtonContext extends IQuickPickItemButtonEvent { + removeItem(): void; } export const IQuickInputService = createDecorator('quickInputService'); +export type Omit = Pick>; + export interface IQuickInputService { _serviceBrand: any; @@ -179,7 +215,9 @@ export interface IQuickInputService { /** * Opens the quick input box for selecting items and returns a promise with the user selected item(s) if any. */ - pick>(picks: TPromise, options?: O, token?: CancellationToken): TPromise; + pick(picks: TPromise | T[], options?: IPickOptions & { canPickMany: true }, token?: CancellationToken): TPromise; + pick(picks: TPromise | T[], options?: IPickOptions & { canPickMany: false }, token?: CancellationToken): TPromise; + pick(picks: TPromise | T[], options?: Omit, 'canPickMany'>, token?: CancellationToken): TPromise; /** * Opens the quick input box for text input and returns a promise with the user typed value if any. diff --git a/src/vs/platform/request/node/request.ts b/src/vs/platform/request/node/request.ts index b8d501fa2b5..878a5c4a334 100644 --- a/src/vs/platform/request/node/request.ts +++ b/src/vs/platform/request/node/request.ts @@ -37,12 +37,12 @@ Registry.as(Extensions.Configuration) 'http.proxy': { type: 'string', pattern: '^https?://([^:]*(:[^@]*)?@)?([^:]+)(:\\d+)?/?$|^$', - description: localize('proxy', "The proxy setting to use. If not set will be taken from the http_proxy and https_proxy environment variables") + description: localize('proxy', "The proxy setting to use. If not set will be taken from the http_proxy and https_proxy environment variables.") }, 'http.proxyStrictSSL': { type: 'boolean', default: true, - description: localize('strictSSL', "Whether the proxy server certificate should be verified against the list of supplied CAs.") + description: localize('strictSSL', "Controls whether the proxy server certificate should be verified against the list of supplied CAs.") }, 'http.proxyAuthorization': { type: ['null', 'string'], diff --git a/src/vs/platform/search/common/search.ts b/src/vs/platform/search/common/search.ts index 91ad26c83c2..fff587381ba 100644 --- a/src/vs/platform/search/common/search.ts +++ b/src/vs/platform/search/common/search.ts @@ -10,7 +10,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import * as objects from 'vs/base/common/objects'; import * as paths from 'vs/base/common/paths'; import uri, { UriComponents } from 'vs/base/common/uri'; -import { PPromise, TPromise } from 'vs/base/common/winjs.base'; +import { TPromise } from 'vs/base/common/winjs.base'; import { IFilesConfiguration } from 'vs/platform/files/common/files'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -24,10 +24,10 @@ export const ISearchService = createDecorator('searchService'); */ export interface ISearchService { _serviceBrand: any; - search(query: ISearchQuery): PPromise; + search(query: ISearchQuery, onProgress?: (result: ISearchProgressItem) => void): TPromise; extendQuery(query: ISearchQuery): void; clearCache(cacheKey: string): TPromise; - registerSearchResultProvider(scheme: string, provider: ISearchResultProvider): IDisposable; + registerSearchResultProvider(scheme: string, type: SearchProviderType, provider: ISearchResultProvider): IDisposable; } export interface ISearchHistoryValues { @@ -45,8 +45,17 @@ export interface ISearchHistoryService { save(history: ISearchHistoryValues): void; } +/** + * TODO@roblou - split text from file search entirely, or share code in a more natural way. + */ +export enum SearchProviderType { + file, + fileIndex, + text +} + export interface ISearchResultProvider { - search(query: ISearchQuery): PPromise; + search(query: ISearchQuery, onProgress?: (p: ISearchProgressItem) => void): TPromise; clearCache(cacheKey: string): TPromise; } diff --git a/src/vs/platform/state/node/stateService.ts b/src/vs/platform/state/node/stateService.ts index 77d9c406d30..5238541eac1 100644 --- a/src/vs/platform/state/node/stateService.ts +++ b/src/vs/platform/state/node/stateService.ts @@ -6,7 +6,7 @@ 'use strict'; import * as path from 'path'; -import * as fs from 'original-fs'; +import * as fs from 'fs'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { writeFileAndFlushSync } from 'vs/base/node/extfs'; import { isUndefined, isUndefinedOrNull } from 'vs/base/common/types'; diff --git a/src/vs/platform/telemetry/common/telemetryService.ts b/src/vs/platform/telemetry/common/telemetryService.ts index dba27cde4b1..72b7d856930 100644 --- a/src/vs/platform/telemetry/common/telemetryService.ts +++ b/src/vs/platform/telemetry/common/telemetryService.ts @@ -21,7 +21,6 @@ export interface ITelemetryServiceConfig { appender: ITelemetryAppender; commonProperties?: TPromise<{ [name: string]: any }>; piiPaths?: string[]; - userOptIn?: boolean; } export class TelemetryService implements ITelemetryService { @@ -46,7 +45,7 @@ export class TelemetryService implements ITelemetryService { this._appender = config.appender; this._commonProperties = config.commonProperties || TPromise.as({}); this._piiPaths = config.piiPaths || []; - this._userOptIn = typeof config.userOptIn === 'undefined' ? true : config.userOptIn; + this._userOptIn = true; // static cleanup pattern for: `file:///DANGEROUS/PATH/resources/app/Useful/Information` this._cleanupPatterns = [/file:\/\/\/.*?\/resources\/app\//gi]; @@ -167,8 +166,9 @@ Registry.as(Extensions.Configuration).registerConfigurat 'properties': { 'telemetry.enableTelemetry': { 'type': 'boolean', - 'description': localize('telemetry.enableTelemetry', "Enable usage data and errors to be sent to Microsoft."), - 'default': true + 'description': localize('telemetry.enableTelemetry', "Enable usage data and errors to be sent to a Microsoft online service."), + 'default': true, + 'tags': ['usesOnlineServices'] } } }); \ No newline at end of file diff --git a/src/vs/platform/telemetry/common/telemetryUtils.ts b/src/vs/platform/telemetry/common/telemetryUtils.ts index a68ff28367b..83a4326ca5e 100644 --- a/src/vs/platform/telemetry/common/telemetryUtils.ts +++ b/src/vs/platform/telemetry/common/telemetryUtils.ts @@ -113,7 +113,8 @@ const configurationValueWhitelist = [ 'editor.multiCursorModifier', 'editor.quickSuggestions', 'editor.quickSuggestionsDelay', - 'editor.parameterHints', + 'editor.parameterHints.enabled', + 'editor.parameterHints.cycle', 'editor.autoClosingBrackets', 'editor.autoIndent', 'editor.formatOnType', diff --git a/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts b/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts index 83ff1f5b7a8..8cd91f0803b 100644 --- a/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts +++ b/src/vs/platform/telemetry/test/electron-browser/telemetryService.test.ts @@ -14,8 +14,6 @@ import * as Errors from 'vs/base/common/errors'; import * as sinon from 'sinon'; import { getConfigurationValue } from 'vs/platform/configuration/common/configuration'; -const optInStatusEventName: string = 'optInStatus'; - class TestTelemetryAppender implements ITelemetryAppender { public events: any[]; @@ -719,29 +717,9 @@ suite('TelemetryService', () => { } })); - test('Telemetry Service respects user opt-in settings', sinon.test(function () { + test('Telemetry Service sends events when enableTelemetry is on', sinon.test(function () { let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ userOptIn: false, appender: testAppender }, undefined); - - return service.publicLog('testEvent').then(() => { - assert.equal(testAppender.getEventsCount(), 0); - service.dispose(); - }); - })); - - test('Telemetry Service does not sent optInStatus when user opted out', sinon.test(function () { - let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ userOptIn: false, appender: testAppender }, undefined); - - return service.publicLog(optInStatusEventName, { optIn: false }).then(() => { - assert.equal(testAppender.getEventsCount(), 0); - service.dispose(); - }); - })); - - test('Telemetry Service sends events when enableTelemetry is on even user optin is on', sinon.test(function () { - let testAppender = new TestTelemetryAppender(); - let service = new TelemetryService({ userOptIn: true, appender: testAppender }, undefined); + let service = new TelemetryService({ appender: testAppender }, undefined); return service.publicLog('testEvent').then(() => { assert.equal(testAppender.getEventsCount(), 1); diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 6b799c0333f..8d984bd6f21 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -149,9 +149,9 @@ export function getColorRegistry(): IColorRegistry { // ----- base colors -export const foreground = registerColor('foreground', { dark: '#CCCCCC', light: '#6C6C6C', hc: '#FFFFFF' }, nls.localize('foreground', "Overall foreground color. This color is only used if not overridden by a component.")); +export const foreground = registerColor('foreground', { dark: '#CCCCCC', light: '#616161', hc: '#FFFFFF' }, nls.localize('foreground', "Overall foreground color. This color is only used if not overridden by a component.")); export const errorForeground = registerColor('errorForeground', { dark: '#F48771', light: '#A1260D', hc: '#F48771' }, nls.localize('errorForeground', "Overall foreground color for error messages. This color is only used if not overridden by a component.")); -export const descriptionForeground = registerColor('descriptionForeground', { light: transparent(foreground, 0.7), dark: transparent(foreground, 0.7), hc: transparent(foreground, 0.7) }, nls.localize('descriptionForeground', "Foreground color for description text providing additional information, for example for a label.")); +export const descriptionForeground = registerColor('descriptionForeground', { light: '#717171', dark: transparent(foreground, 0.7), hc: transparent(foreground, 0.7) }, nls.localize('descriptionForeground', "Foreground color for description text providing additional information, for example for a label.")); export const focusBorder = registerColor('focusBorder', { dark: Color.fromHex('#0E639C').transparent(0.6), light: Color.fromHex('#007ACC').transparent(0.4), hc: '#F38518' }, nls.localize('focusBorder', "Overall border color for focused elements. This color is only used if not overridden by a component.")); @@ -163,8 +163,8 @@ export const selectionBackground = registerColor('selection.background', { light // ------ text colors export const textSeparatorForeground = registerColor('textSeparator.foreground', { light: '#0000002e', dark: '#ffffff2e', hc: Color.black }, nls.localize('textSeparatorForeground', "Color for text separators.")); -export const textLinkForeground = registerColor('textLink.foreground', { light: '#4080D0', dark: '#4080D0', hc: '#4080D0' }, nls.localize('textLinkForeground', "Foreground color for links in text.")); -export const textLinkActiveForeground = registerColor('textLink.activeForeground', { light: '#4080D0', dark: '#4080D0', hc: '#4080D0' }, nls.localize('textLinkActiveForeground', "Foreground color for links in text when clicked on and on mouse hover.")); +export const textLinkForeground = registerColor('textLink.foreground', { light: '#006AB1', dark: '#3794FF', hc: '#3794FF' }, nls.localize('textLinkForeground', "Foreground color for links in text.")); +export const textLinkActiveForeground = registerColor('textLink.activeForeground', { light: '#006AB1', dark: '#3794FF', hc: '#3794FF' }, nls.localize('textLinkActiveForeground', "Foreground color for links in text when clicked on and on mouse hover.")); export const textPreformatForeground = registerColor('textPreformat.foreground', { light: '#A31515', dark: '#D7BA7D', hc: '#D7BA7D' }, nls.localize('textPreformatForeground', "Foreground color for preformatted text segments.")); export const textBlockQuoteBackground = registerColor('textBlockQuote.background', { light: '#7f7f7f1a', dark: '#7f7f7f1a', hc: null }, nls.localize('textBlockQuoteBackground', "Background color for block quotes in text.")); export const textBlockQuoteBorder = registerColor('textBlockQuote.border', { light: '#007acc80', dark: '#007acc80', hc: Color.white }, nls.localize('textBlockQuoteBorder', "Border color for block quotes in text.")); @@ -177,7 +177,7 @@ export const inputBackground = registerColor('input.background', { dark: '#3C3C3 export const inputForeground = registerColor('input.foreground', { dark: foreground, light: foreground, hc: foreground }, nls.localize('inputBoxForeground', "Input box foreground.")); export const inputBorder = registerColor('input.border', { dark: null, light: null, hc: contrastBorder }, nls.localize('inputBoxBorder', "Input box border.")); export const inputActiveOptionBorder = registerColor('inputOption.activeBorder', { dark: '#007ACC', light: '#007ACC', hc: activeContrastBorder }, nls.localize('inputBoxActiveOptionBorder', "Border color of activated options in input fields.")); -export const inputPlaceholderForeground = registerColor('input.placeholderForeground', { dark: null, light: null, hc: null }, nls.localize('inputPlaceholderForeground', "Input box foreground color for placeholder text.")); +export const inputPlaceholderForeground = registerColor('input.placeholderForeground', { light: transparent(foreground, 0.5), dark: transparent(foreground, 0.5), hc: transparent(foreground, 0.7) }, nls.localize('inputPlaceholderForeground', "Input box foreground color for placeholder text.")); export const inputValidationInfoBackground = registerColor('inputValidation.infoBackground', { dark: '#063B49', light: '#D6ECF2', hc: Color.black }, nls.localize('inputValidationInfoBackground', "Input validation background color for information severity.")); export const inputValidationInfoBorder = registerColor('inputValidation.infoBorder', { dark: '#007acc', light: '#007acc', hc: contrastBorder }, nls.localize('inputValidationInfoBorder', "Input validation border color for information severity.")); @@ -191,30 +191,30 @@ export const selectListBackground = registerColor('dropdown.listBackground', { d export const selectForeground = registerColor('dropdown.foreground', { dark: '#F0F0F0', light: null, hc: Color.white }, nls.localize('dropdownForeground', "Dropdown foreground.")); export const selectBorder = registerColor('dropdown.border', { dark: selectBackground, light: '#CECECE', hc: contrastBorder }, nls.localize('dropdownBorder', "Dropdown border.")); -export const listFocusBackground = registerColor('list.focusBackground', { dark: '#073655', light: '#DCEBFC', hc: null }, nls.localize('listFocusBackground', "List/Tree background color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); +export const listFocusBackground = registerColor('list.focusBackground', { dark: '#062F4A', light: '#DFF0FF', hc: null }, nls.localize('listFocusBackground', "List/Tree background color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listFocusForeground = registerColor('list.focusForeground', { dark: null, light: null, hc: null }, nls.localize('listFocusForeground', "List/Tree foreground color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); -export const listActiveSelectionBackground = registerColor('list.activeSelectionBackground', { dark: '#094771', light: '#3399FF', hc: null }, nls.localize('listActiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); +export const listActiveSelectionBackground = registerColor('list.activeSelectionBackground', { dark: '#094771', light: '#2477CE', hc: null }, nls.localize('listActiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listActiveSelectionForeground = registerColor('list.activeSelectionForeground', { dark: Color.white, light: Color.white, hc: null }, nls.localize('listActiveSelectionForeground', "List/Tree foreground color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); -export const listInactiveSelectionBackground = registerColor('list.inactiveSelectionBackground', { dark: '#3F3F46', light: '#CCCEDB', hc: null }, nls.localize('listInactiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); +export const listInactiveSelectionBackground = registerColor('list.inactiveSelectionBackground', { dark: '#37373D', light: '#dddfea', hc: null }, nls.localize('listInactiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveSelectionForeground = registerColor('list.inactiveSelectionForeground', { dark: null, light: null, hc: null }, nls.localize('listInactiveSelectionForeground', "List/Tree foreground color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); -export const listInactiveFocusBackground = registerColor('list.inactiveFocusBackground', { dark: '#313135', light: '#d8dae6', hc: null }, nls.localize('listInactiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); +export const listInactiveFocusBackground = registerColor('list.inactiveFocusBackground', { dark: '#313135', light: '#d8dae6', hc: null }, nls.localize('listInactiveFocusBackground', "List/Tree background color for the focused item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); export const listHoverBackground = registerColor('list.hoverBackground', { dark: '#2A2D2E', light: '#F0F0F0', hc: null }, nls.localize('listHoverBackground', "List/Tree background when hovering over items using the mouse.")); export const listHoverForeground = registerColor('list.hoverForeground', { dark: null, light: null, hc: null }, nls.localize('listHoverForeground', "List/Tree foreground when hovering over items using the mouse.")); export const listDropBackground = registerColor('list.dropBackground', { dark: listFocusBackground, light: listFocusBackground, hc: null }, nls.localize('listDropBackground', "List/Tree drag and drop background when moving items around using the mouse.")); export const listHighlightForeground = registerColor('list.highlightForeground', { dark: '#0097fb', light: '#007acc', hc: focusBorder }, nls.localize('highlight', 'List/Tree foreground color of the match highlights when searching inside the list/tree.')); export const listInvalidItemForeground = registerColor('list.invalidItemForeground', { dark: '#B89500', light: '#B89500', hc: '#B89500' }, nls.localize('invalidItemForeground', 'List/Tree foreground color for invalid items, for example an unresolved root in explorer.')); -export const listErrorForeground = registerColor('list.errorForeground', { dark: '#ea4646', light: '#d60a0a', hc: null }, nls.localize('listErrorForeground', 'Foreground color of list items containing errors.')); +export const listErrorForeground = registerColor('list.errorForeground', { dark: '#F88070', light: '#B01011', hc: null }, nls.localize('listErrorForeground', 'Foreground color of list items containing errors.')); export const listWarningForeground = registerColor('list.warningForeground', { dark: '#4d9e4d', light: '#117711', hc: null }, nls.localize('listWarningForeground', 'Foreground color of list items containing warnings.')); -export const pickerGroupForeground = registerColor('pickerGroup.foreground', { dark: Color.fromHex('#0097FB').transparent(0.6), light: Color.fromHex('#007ACC').transparent(0.6), hc: Color.white }, nls.localize('pickerGroupForeground', "Quick picker color for grouping labels.")); +export const pickerGroupForeground = registerColor('pickerGroup.foreground', { dark: '#3794FF', light: '#006AB1', hc: Color.white }, nls.localize('pickerGroupForeground', "Quick picker color for grouping labels.")); export const pickerGroupBorder = registerColor('pickerGroup.border', { dark: '#3F3F46', light: '#CCCEDB', hc: Color.white }, nls.localize('pickerGroupBorder', "Quick picker color for grouping borders.")); export const buttonForeground = registerColor('button.foreground', { dark: Color.white, light: Color.white, hc: Color.white }, nls.localize('buttonForeground', "Button foreground color.")); export const buttonBackground = registerColor('button.background', { dark: '#0E639C', light: '#007ACC', hc: null }, nls.localize('buttonBackground', "Button background color.")); export const buttonHoverBackground = registerColor('button.hoverBackground', { dark: lighten(buttonBackground, 0.2), light: darken(buttonBackground, 0.2), hc: null }, nls.localize('buttonHoverBackground', "Button background color when hovering.")); -export const badgeBackground = registerColor('badge.background', { dark: '#4D4D4D', light: '#BEBEBE', hc: Color.black }, nls.localize('badgeBackground', "Badge background color. Badges are small information labels, e.g. for search results count.")); -export const badgeForeground = registerColor('badge.foreground', { dark: Color.white, light: Color.white, hc: Color.white }, nls.localize('badgeForeground', "Badge foreground color. Badges are small information labels, e.g. for search results count.")); +export const badgeBackground = registerColor('badge.background', { dark: '#4D4D4D', light: '#C4C4C4', hc: Color.black }, nls.localize('badgeBackground', "Badge background color. Badges are small information labels, e.g. for search results count.")); +export const badgeForeground = registerColor('badge.foreground', { dark: Color.white, light: '#333', hc: Color.white }, nls.localize('badgeForeground', "Badge foreground color. Badges are small information labels, e.g. for search results count.")); export const scrollbarShadow = registerColor('scrollbar.shadow', { dark: '#000000', light: '#DDDDDD', hc: null }, nls.localize('scrollbarShadow', "Scrollbar shadow to indicate that the view is scrolled.")); export const scrollbarSliderBackground = registerColor('scrollbarSlider.background', { dark: Color.fromHex('#797979').transparent(0.4), light: Color.fromHex('#646464').transparent(0.4), hc: transparent(contrastBorder, 0.6) }, nls.localize('scrollbarSliderBackground', "Scrollbar slider background color.")); @@ -223,10 +223,10 @@ export const scrollbarSliderActiveBackground = registerColor('scrollbarSlider.ac export const progressBarBackground = registerColor('progressBar.background', { dark: Color.fromHex('#0E70C0'), light: Color.fromHex('#0E70C0'), hc: contrastBorder }, nls.localize('progressBarBackground', "Background color of the progress bar that can show for long running operations.")); -export const breadcrumbsForeground = registerColor('breadcrumb.breadcrumbsForeground', { light: Color.fromHex('#6C6C6C').transparent(.7), dark: Color.fromHex('#CCCCCC').transparent(.7), hc: Color.white.transparent(.7) }, nls.localize('breadcrumbsFocusForeground', "Color of focused breadcrumb items.")); -export const breadcrumbsFocusForeground = registerColor('breadcrumb.breadcrumbsFocusForeground', { light: '#6C6C6C', dark: '#CCCCCC', hc: Color.white }, nls.localize('breadcrumbsFocusForeground', "Color of focused breadcrumb items.")); -export const breadcrumbsActiveSelectionForeground = registerColor('breadcrumb.breadcrumbsActiveSelectionForeground', { light: '#6C6C6C', dark: '#CCCCCC', hc: Color.white }, nls.localize('breadcrumbsSelectedForegound', "Color of selected breadcrumb items.")); -export const breadcrumbsActiveSelectionBackground = registerColor('breadcrumb.breadcrumbsActiveSelectionBackground', { light: '#F3F3F3', dark: '#252526', hc: Color.black }, nls.localize('breadcrumbsSelectedBackground', "Background color of selected breadcrumb items.")); +export const breadcrumbsForeground = registerColor('breadcrumb.foreground', { light: Color.fromHex('#6C6C6C').transparent(.7), dark: Color.fromHex('#CCCCCC').transparent(.7), hc: Color.white.transparent(.7) }, nls.localize('breadcrumbsFocusForeground', "Color of focused breadcrumb items.")); +export const breadcrumbsFocusForeground = registerColor('breadcrumb.focusForeground', { light: '#6C6C6C', dark: '#CCCCCC', hc: Color.white }, nls.localize('breadcrumbsFocusForeground', "Color of focused breadcrumb items.")); +export const breadcrumbsActiveSelectionForeground = registerColor('breadcrumb.activeSelectionForeground', { light: '#6C6C6C', dark: '#CCCCCC', hc: Color.white }, nls.localize('breadcrumbsSelectedForegound', "Color of selected breadcrumb items.")); +export const breadcrumbsPickerBackground = registerColor('breadcrumbPicker.background', { light: '#ECECEC', dark: '#252526', hc: Color.black }, nls.localize('breadcrumbsSelectedBackground', "Background color of breadcrumb item picker.")); /** * Editor background color. diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index c8921a0a5e3..ff6542c0a9a 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -6,7 +6,7 @@ 'use strict'; import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; -import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, lighten, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionBackground, breadcrumbsActiveSelectionForeground } from 'vs/platform/theme/common/colorRegistry'; +import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground } from 'vs/platform/theme/common/colorRegistry'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; @@ -177,7 +177,7 @@ export function attachQuickOpenStyler(widget: IThemable, themeService: IThemeSer inputValidationErrorBackground: (style && style.inputValidationErrorBackground) || inputValidationErrorBackground, listFocusBackground: (style && style.listFocusBackground) || listFocusBackground, listFocusForeground: (style && style.listFocusForeground) || listFocusForeground, - listActiveSelectionBackground: (style && style.listActiveSelectionBackground) || lighten(listActiveSelectionBackground, 0.1), + listActiveSelectionBackground: (style && style.listActiveSelectionBackground) || listActiveSelectionBackground, listActiveSelectionForeground: (style && style.listActiveSelectionForeground) || listActiveSelectionForeground, listFocusAndSelectionBackground: style && style.listFocusAndSelectionBackground || listActiveSelectionBackground, listFocusAndSelectionForeground: (style && style.listFocusAndSelectionForeground) || listActiveSelectionForeground, @@ -219,7 +219,7 @@ export function attachListStyler(widget: IThemable, themeService: IThemeService, export const defaultListStyles: IColorMapping = { listFocusBackground: listFocusBackground, listFocusForeground: listFocusForeground, - listActiveSelectionBackground: lighten(listActiveSelectionBackground, 0.1), + listActiveSelectionBackground: listActiveSelectionBackground, listActiveSelectionForeground: listActiveSelectionForeground, listFocusAndSelectionBackground: listActiveSelectionBackground, listFocusAndSelectionForeground: listActiveSelectionForeground, @@ -266,20 +266,16 @@ export function attachStylerCallback(themeService: IThemeService, colors: { [nam export interface IBreadcrumbsWidgetStyleOverrides extends IStyleOverrides { breadcrumbsBackground?: ColorIdentifier; breadcrumbsForeground?: ColorIdentifier; - breadcrumbsHoverBackground?: ColorIdentifier; breadcrumbsHoverForeground?: ColorIdentifier; breadcrumbsFocusForeground?: ColorIdentifier; - breadcrumbsFocusAndSelectionBackground?: ColorIdentifier; breadcrumbsFocusAndSelectionForeground?: ColorIdentifier; } export const defaultBreadcrumbsStyles = { breadcrumbsBackground: editorBackground, breadcrumbsForeground: breadcrumbsForeground, - breadcrumbsHoverBackground: editorBackground, breadcrumbsHoverForeground: breadcrumbsFocusForeground, breadcrumbsFocusForeground: breadcrumbsFocusForeground, - breadcrumbsFocusAndSelectionBackground: breadcrumbsActiveSelectionBackground, breadcrumbsFocusAndSelectionForeground: breadcrumbsActiveSelectionForeground, }; diff --git a/src/vs/platform/theme/common/themeService.ts b/src/vs/platform/theme/common/themeService.ts index c58b79603bc..7c1e2f9bae6 100644 --- a/src/vs/platform/theme/common/themeService.ts +++ b/src/vs/platform/theme/common/themeService.ts @@ -6,7 +6,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Color } from 'vs/base/common/color'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/platform/registry/common/platform'; import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; import { Event, Emitter } from 'vs/base/common/event'; @@ -111,12 +111,10 @@ class ThemingRegistry implements IThemingRegistry { public onThemeChange(participant: IThemingParticipant): IDisposable { this.themingParticipants.push(participant); this.onThemingParticipantAddedEmitter.fire(participant); - return { - dispose: () => { - const idx = this.themingParticipants.indexOf(participant); - this.themingParticipants.splice(idx, 1); - } - }; + return toDisposable(() => { + const idx = this.themingParticipants.indexOf(participant); + this.themingParticipants.splice(idx, 1); + }); } public get onThemingParticipantAdded(): Event { diff --git a/src/vs/platform/theme/test/electron-browser/colorRegistry.releaseTest.ts b/src/vs/platform/theme/test/electron-browser/colorRegistry.releaseTest.ts index 39858929cb5..e5658bcaf38 100644 --- a/src/vs/platform/theme/test/electron-browser/colorRegistry.releaseTest.ts +++ b/src/vs/platform/theme/test/electron-browser/colorRegistry.releaseTest.ts @@ -18,6 +18,7 @@ import { request, asText } from 'vs/base/node/request'; import * as pfs from 'vs/base/node/pfs'; import * as path from 'path'; import * as assert from 'assert'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; interface ColorInfo { @@ -103,7 +104,7 @@ function getDescription(color: ColorContribution) { } async function getColorsFromExtension(): Promise<{ [id: string]: string }> { - let extPath = require.toUrl('../../../../../../extensions'); + let extPath = getPathFromAmdModule(require, '../../../../../../extensions'); let extFolders = await pfs.readDirsInDir(extPath); let result: { [id: string]: string } = Object.create(null); for (let folder of extFolders) { @@ -127,4 +128,4 @@ async function getColorsFromExtension(): Promise<{ [id: string]: string }> { } return result; -} \ No newline at end of file +} diff --git a/src/vs/platform/update/electron-main/updateService.win32.ts b/src/vs/platform/update/electron-main/updateService.win32.ts index 53b4f047dc6..6f5e750aafa 100644 --- a/src/vs/platform/update/electron-main/updateService.win32.ts +++ b/src/vs/platform/update/electron-main/updateService.win32.ts @@ -5,7 +5,7 @@ 'use strict'; -import * as fs from 'original-fs'; +import * as fs from 'fs'; import * as path from 'path'; import * as pfs from 'vs/base/node/pfs'; import { memoize } from 'vs/base/common/decorators'; @@ -79,10 +79,15 @@ export class Win32UpdateService extends AbstractUpdateService { if (getUpdateType() === UpdateType.Setup) { /* __GDPR__ - "update:win32SetupTarget" : { - "target" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ + "update:win32SetupTarget" : { + "target" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + /* __GDPR__ + "update:winSetupTarget" : { + "target" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ telemetryService.publicLog('update:win32SetupTarget', { target: product.target }); } } diff --git a/src/vs/platform/update/node/update.config.contribution.ts b/src/vs/platform/update/node/update.config.contribution.ts index 86ad52731dc..7ad60bdfc4f 100644 --- a/src/vs/platform/update/node/update.config.contribution.ts +++ b/src/vs/platform/update/node/update.config.contribution.ts @@ -21,18 +21,21 @@ configurationRegistry.registerConfiguration({ 'enum': ['none', 'default'], 'default': 'default', 'scope': ConfigurationScope.APPLICATION, - 'description': nls.localize('updateChannel', "Configure whether you receive automatic updates from an update channel. Requires a restart after change.") + 'description': nls.localize('updateChannel', "Configure whether you receive automatic updates from an update channel. Requires a restart after change. The updates are fetched from an online service."), + 'tags': ['usesOnlineServices'] }, 'update.enableWindowsBackgroundUpdates': { 'type': 'boolean', 'default': true, 'scope': ConfigurationScope.APPLICATION, - 'description': nls.localize('enableWindowsBackgroundUpdates', "Enables Windows background updates.") + 'description': nls.localize('enableWindowsBackgroundUpdates', "Enables Windows background updates. The updates are fetched from an online service."), + 'tags': ['usesOnlineServices'] }, 'update.showReleaseNotes': { 'type': 'boolean', 'default': true, - 'description': nls.localize('showReleaseNotes', "Show Release Notes after an update.") + 'description': nls.localize('showReleaseNotes', "Show Release Notes after an update. The Release Notes are fetched from an online service."), + 'tags': ['usesOnlineServices'] } } }); diff --git a/src/vs/platform/uriDisplay/common/uriDisplay.ts b/src/vs/platform/uriDisplay/common/uriDisplay.ts new file mode 100644 index 00000000000..486aeb0d77e --- /dev/null +++ b/src/vs/platform/uriDisplay/common/uriDisplay.ts @@ -0,0 +1,120 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import URI from 'vs/base/common/uri'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { Event, Emitter } from 'vs/base/common/event'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { isEqual, basenameOrAuthority } from 'vs/base/common/resources'; +import { isLinux, isWindows } from 'vs/base/common/platform'; +import { tildify } from 'vs/base/common/labels'; +import { ltrim } from 'vs/base/common/strings'; + +export interface IUriDisplayService { + _serviceBrand: any; + getLabel(resource: URI, relative?: boolean): string; + registerFormater(schema: string, formater: UriDisplayRules): IDisposable; + onDidRegisterFormater: Event<{ scheme: string, formater: UriDisplayRules }>; +} + +export interface UriDisplayRules { + label: string; // myLabel:/${path} + separator: '/' | '\\' | ''; + tildify?: boolean; + normalizeDriveLetter?: boolean; +} + +const URI_DISPLAY_SERVICE_ID = 'uriDisplay'; +const sepRegexp = /\//g; +const labelMatchingRegexp = /\$\{scheme\}|\$\{authority\}|\$\{path\}/g; + +function hasDriveLetter(path: string): boolean { + return isWindows && path && path[2] === ':'; +} + +export class UriDisplayService implements IUriDisplayService { + _serviceBrand: any; + + private readonly formaters = new Map(); + private readonly _onDidRegisterFormater = new Emitter<{ scheme: string, formater: UriDisplayRules }>(); + + constructor( + @IEnvironmentService private environmentService: IEnvironmentService, + @IWorkspaceContextService private contextService: IWorkspaceContextService + ) { } + + + get onDidRegisterFormater(): Event<{ scheme: string, formater: UriDisplayRules }> { + return this._onDidRegisterFormater.event; + } + + getLabel(resource: URI, relative: boolean): string { + if (!resource) { + return undefined; + } + const formater = this.formaters.get(resource.scheme); + if (!formater) { + return resource.path; + } + + if (relative) { + const baseResource = this.contextService && this.contextService.getWorkspaceFolder(resource); + if (baseResource) { + let relativeLabel: string; + if (isEqual(baseResource.uri, resource, !isLinux)) { + relativeLabel = ''; // no label if resources are identical + } else { + const baseResourceLabel = this.formatUri(baseResource.uri, formater); + relativeLabel = ltrim(this.formatUri(resource, formater).substring(baseResourceLabel.length), formater.separator); + } + + const hasMultipleRoots = this.contextService.getWorkspace().folders.length > 1; + if (hasMultipleRoots) { + const rootName = (baseResource && baseResource.name) ? baseResource.name : basenameOrAuthority(baseResource.uri); + relativeLabel = relativeLabel ? (rootName + ' • ' + relativeLabel) : rootName; // always show root basename if there are multiple + } + + return relativeLabel; + } + } + + return this.formatUri(resource, formater); + } + + registerFormater(scheme: string, formater: UriDisplayRules): IDisposable { + this.formaters.set(scheme, formater); + this._onDidRegisterFormater.fire({ scheme, formater }); + + return { + dispose: () => this.formaters.delete(scheme) + }; + } + + private formatUri(resource: URI, formater: UriDisplayRules): string { + let label = formater.label.replace(labelMatchingRegexp, match => { + switch (match) { + case '${scheme}': return resource.scheme; + case '${authority}': return resource.authority; + case '${path}': return resource.path; + default: return ''; + } + }); + + // convert \c:\something => C:\something + if (formater.normalizeDriveLetter && hasDriveLetter(label)) { + label = label.charAt(1).toUpperCase() + label.substr(2); + } + + if (formater.tildify) { + label = tildify(label, this.environmentService.userHome); + } + + return label.replace(sepRegexp, formater.separator); + } +} + +export const IUriDisplayService = createDecorator(URI_DISPLAY_SERVICE_ID); diff --git a/src/vs/platform/uriDisplay/electron-browser/uriDisplay.contribution.ts b/src/vs/platform/uriDisplay/electron-browser/uriDisplay.contribution.ts new file mode 100644 index 00000000000..9637712a2ec --- /dev/null +++ b/src/vs/platform/uriDisplay/electron-browser/uriDisplay.contribution.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 { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; +import { ipcRenderer as ipc } from 'electron'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; + +/** + * Uri display registration needs to be shared from renderer to main. + * Since there will be another instance of the uri display service running on main. + */ +class UriDisplayRegistrationContribution implements IWorkbenchContribution { + + constructor(@IUriDisplayService uriDisplayService: IUriDisplayService) { + uriDisplayService.onDidRegisterFormater(data => { + ipc.send('vscode:uriDisplayRegisterFormater', data); + }); + } +} + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(UriDisplayRegistrationContribution, LifecyclePhase.Starting); diff --git a/src/vs/platform/uriDisplay/test/uriDisplay.test.ts b/src/vs/platform/uriDisplay/test/uriDisplay.test.ts new file mode 100644 index 00000000000..a54cc2318b4 --- /dev/null +++ b/src/vs/platform/uriDisplay/test/uriDisplay.test.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { IUriDisplayService, UriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; +import { TestEnvironmentService, TestContextService } from 'vs/workbench/test/workbenchTestServices'; +import { Schemas } from 'vs/base/common/network'; +import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; +import URI from 'vs/base/common/uri'; +import { nativeSep } from 'vs/base/common/paths'; +import { isWindows } from 'vs/base/common/platform'; + +suite('URI Display', () => { + + let uriDisplayService: IUriDisplayService; + + setup(() => { + uriDisplayService = new UriDisplayService(TestEnvironmentService, new TestContextService()); + }); + + test('file scheme', function () { + uriDisplayService.registerFormater(Schemas.file, { + label: '${path}', + separator: nativeSep, + tildify: !isWindows, + normalizeDriveLetter: isWindows + }); + + const uri1 = TestWorkspace.folders[0].uri.with({ path: TestWorkspace.folders[0].uri.path.concat('/a/b/c/d') }); + assert.equal(uriDisplayService.getLabel(uri1, true), isWindows ? 'a\\b\\c\\d' : 'a/b/c/d'); + assert.equal(uriDisplayService.getLabel(uri1, false), isWindows ? 'C:\\testWorkspace\\a\\b\\c\\d' : '/testWorkspace/a/b/c/d'); + + const uri2 = URI.file('c:\\1/2/3'); + assert.equal(uriDisplayService.getLabel(uri2, false), isWindows ? 'C:\\1\\2\\3' : '/c:\\1/2/3'); + }); + + test('custom scheme', function () { + uriDisplayService.registerFormater(Schemas.vscode, { + label: 'LABEL/${path}/${authority}/END', + separator: '/', + tildify: true, + normalizeDriveLetter: true + }); + + const uri1 = URI.parse('vscode://microsoft.com/1/2/3/4/5'); + assert.equal(uriDisplayService.getLabel(uri1, false), 'LABEL//1/2/3/4/5/microsoft.com/END'); + }); +}); diff --git a/src/vs/platform/url/common/urlIpc.ts b/src/vs/platform/url/common/urlIpc.ts index 84fdc5c2e65..5cc4e919f95 100644 --- a/src/vs/platform/url/common/urlIpc.ts +++ b/src/vs/platform/url/common/urlIpc.ts @@ -39,7 +39,7 @@ export class URLServiceChannelClient implements IURLService { constructor(private channel: IChannel) { } - open(url: URI): TPromise { + open(url: URI): TPromise { return this.channel.call('open', url.toJSON()); } diff --git a/src/vs/platform/widget/browser/contextScopedHistoryWidget.ts b/src/vs/platform/widget/browser/contextScopedHistoryWidget.ts index c98eea4b962..013485d5e42 100644 --- a/src/vs/platform/widget/browser/contextScopedHistoryWidget.ts +++ b/src/vs/platform/widget/browser/contextScopedHistoryWidget.ts @@ -10,7 +10,7 @@ import { FindInput, IFindInputOptions } from 'vs/base/browser/ui/findinput/findI import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; import { IContextScopedWidget, getContextScopedWidget, createWidgetScopedContextKeyService, bindContextScopedWidget } from 'vs/platform/widget/common/contextScopedWidget'; import { IHistoryNavigationWidget } from 'vs/base/browser/history'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; export const HistoryNavigationWidgetContext = 'historyNavigationWidget'; @@ -53,7 +53,7 @@ export class ContextScopedFindInput extends FindInput { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'history.showPrevious', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(new ContextKeyDefinedExpr(HistoryNavigationWidgetContext), new ContextKeyEqualsExpr(HistoryNavigationEnablementContext, true)), primary: KeyCode.UpArrow, secondary: [KeyMod.Alt | KeyCode.UpArrow], @@ -65,7 +65,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'history.showNext', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: new ContextKeyAndExpr([new ContextKeyDefinedExpr(HistoryNavigationWidgetContext), new ContextKeyEqualsExpr(HistoryNavigationEnablementContext, true)]), primary: KeyCode.DownArrow, secondary: [KeyMod.Alt | KeyCode.DownArrow], diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 6964c04bf3e..0a289de0b97 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -11,12 +11,13 @@ import { Event, latch, anyEvent } from 'vs/base/common/event'; import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; -import { IWorkspaceIdentifier, IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IRecentlyOpened } from 'vs/platform/history/common/history'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { PerformanceEntry } from 'vs/base/common/performance'; import { LogLevel } from 'vs/platform/log/common/log'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import URI, { UriComponents } from 'vs/base/common/uri'; export const IWindowsService = createDecorator('windowsService'); @@ -126,7 +127,7 @@ export interface IWindowsService { toggleFullScreen(windowId: number): TPromise; setRepresentedFilename(windowId: number, fileName: string): TPromise; addRecentlyOpened(files: string[]): TPromise; - removeFromRecentlyOpened(paths: string[]): TPromise; + removeFromRecentlyOpened(paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string)[]): TPromise; clearRecentlyOpened(): TPromise; getRecentlyOpened(windowId: number): TPromise; focusWindow(windowId: number): TPromise; @@ -142,6 +143,7 @@ export interface IWindowsService { relaunch(options: { addArgs?: string[], removeArgs?: string[] }): TPromise; // macOS Native Tabs + newWindowTab(): TPromise; showPreviousWindowTab(): TPromise; showNextWindowTab(): TPromise; moveWindowTabToNewWindow(): TPromise; @@ -156,10 +158,10 @@ export interface IWindowsService { toggleSharedProcess(): TPromise; // Global methods - openWindow(windowId: number, paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean; }): TPromise; + openWindow(windowId: number, paths: URI[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs }): TPromise; openNewWindow(): TPromise; showWindow(windowId: number): TPromise; - getWindows(): TPromise<{ id: number; workspace?: IWorkspaceIdentifier; folderPath?: string; title: string; filename?: string; }[]>; + getWindows(): TPromise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]>; getWindowCount(): TPromise; log(severity: string, ...messages: string[]): TPromise; showItemInFolder(path: string): TPromise; @@ -172,7 +174,6 @@ export interface IWindowsService { // TODO: this is a bit backwards startCrashReporter(config: CrashReporterStartOptions): TPromise; - openAccessibilityOptions(): TPromise; openAboutDialog(): TPromise; } @@ -209,7 +210,7 @@ export interface IWindowService { getRecentlyOpened(): TPromise; focusWindow(): TPromise; closeWindow(): TPromise; - openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean; }): TPromise; + openWindow(paths: URI[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs }): TPromise; isFocused(): TPromise; setDocumentEdited(flag: boolean): TPromise; isMaximized(): TPromise; @@ -317,7 +318,7 @@ export interface IOpenFileRequest { } export interface IAddFoldersRequest { - foldersToAdd: IPath[]; + foldersToAdd: UriComponents[]; } export interface IWindowConfiguration extends ParsedArgs, IOpenFileRequest { @@ -335,7 +336,7 @@ export interface IWindowConfiguration extends ParsedArgs, IOpenFileRequest { backupPath?: string; workspace?: IWorkspaceIdentifier; - folderPath?: string; + folderUri?: ISingleFolderWorkspaceIdentifier; zoomLevel?: number; fullscreen?: boolean; diff --git a/src/vs/platform/windows/common/windowsIpc.ts b/src/vs/platform/windows/common/windowsIpc.ts index 3fde497f017..09a3958cc3b 100644 --- a/src/vs/platform/windows/common/windowsIpc.ts +++ b/src/vs/platform/windows/common/windowsIpc.ts @@ -9,7 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { Event, buffer } from 'vs/base/common/event'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IWindowsService, INativeOpenDialogOptions, IEnterWorkspaceResult, CrashReporterStartOptions, IMessageBoxResult, MessageBoxOptions, SaveDialogOptions, OpenDialogOptions, IDevToolsOptions } from 'vs/platform/windows/common/windows'; -import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IRecentlyOpened } from 'vs/platform/history/common/history'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import URI from 'vs/base/common/uri'; @@ -32,6 +32,7 @@ export interface IWindowsChannel extends IChannel { call(command: 'showSaveDialog', arg: [number, SaveDialogOptions]): TPromise; call(command: 'showOpenDialog', arg: [number, OpenDialogOptions]): TPromise; call(command: 'reloadWindow', arg: [number, ParsedArgs]): TPromise; + call(command: 'openDevTools', arg: [number, IDevToolsOptions]): TPromise; call(command: 'toggleDevTools', arg: number): TPromise; call(command: 'closeWorkspace', arg: number): TPromise; call(command: 'enterWorkspace', arg: [number, string]): TPromise; @@ -40,14 +41,15 @@ export interface IWindowsChannel extends IChannel { call(command: 'toggleFullScreen', arg: number): TPromise; call(command: 'setRepresentedFilename', arg: [number, string]): TPromise; call(command: 'addRecentlyOpened', arg: string[]): TPromise; - call(command: 'removeFromRecentlyOpened', arg: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[]): TPromise; + call(command: 'removeFromRecentlyOpened', arg: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string)[]): TPromise; call(command: 'clearRecentlyOpened'): TPromise; call(command: 'getRecentlyOpened', arg: number): TPromise; - call(command: 'showPreviousWindowTab', arg: number): TPromise; - call(command: 'showNextWindowTab', arg: number): TPromise; - call(command: 'moveWindowTabToNewWindow', arg: number): TPromise; - call(command: 'mergeAllWindowTabs', arg: number): TPromise; - call(command: 'toggleWindowTabsBar', arg: number): TPromise; + call(command: 'newWindowTab'): TPromise; + call(command: 'showPreviousWindowTab'): TPromise; + call(command: 'showNextWindowTab'): TPromise; + call(command: 'moveWindowTabToNewWindow'): TPromise; + call(command: 'mergeAllWindowTabs'): TPromise; + call(command: 'toggleWindowTabsBar'): TPromise; call(command: 'updateTouchBar', arg: [number, ISerializableCommandAction[][]]): TPromise; call(command: 'focusWindow', arg: number): TPromise; call(command: 'closeWindow', arg: number): TPromise; @@ -59,12 +61,12 @@ export interface IWindowsChannel extends IChannel { call(command: 'onWindowTitleDoubleClick', arg: number): TPromise; call(command: 'setDocumentEdited', arg: [number, boolean]): TPromise; call(command: 'quit'): TPromise; - call(command: 'openWindow', arg: [number, string[], { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean }]): TPromise; + call(command: 'openWindow', arg: [number, URI[], { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs }]): TPromise; call(command: 'openNewWindow'): TPromise; call(command: 'showWindow', arg: number): TPromise; - call(command: 'getWindows'): TPromise<{ id: number; workspace?: IWorkspaceIdentifier; folderPath?: string; title: string; filename?: string; }[]>; + call(command: 'getWindows'): TPromise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]>; call(command: 'getWindowCount'): TPromise; - call(command: 'relaunch', arg: { addArgs?: string[], removeArgs?: string[] }): TPromise; + call(command: 'relaunch', arg: [{ addArgs?: string[], removeArgs?: string[] }]): TPromise; call(command: 'whenSharedProcessReady'): TPromise; call(command: 'toggleSharedProcess'): TPromise; call(command: 'log', arg: [string, string[]]): TPromise; @@ -72,9 +74,7 @@ export interface IWindowsChannel extends IChannel { call(command: 'getActiveWindowId'): TPromise; call(command: 'openExternal', arg: string): TPromise; call(command: 'startCrashReporter', arg: CrashReporterStartOptions): TPromise; - call(command: 'openAccessibilityOptions'): TPromise; call(command: 'openAboutDialog'): TPromise; - call(command: string, arg?: any): TPromise; } export class WindowsChannel implements IWindowsChannel { @@ -140,8 +140,15 @@ export class WindowsChannel implements IWindowsChannel { case 'toggleFullScreen': return this.service.toggleFullScreen(arg); case 'setRepresentedFilename': return this.service.setRepresentedFilename(arg[0], arg[1]); case 'addRecentlyOpened': return this.service.addRecentlyOpened(arg); - case 'removeFromRecentlyOpened': return this.service.removeFromRecentlyOpened(arg); + case 'removeFromRecentlyOpened': { + let paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string)[] = arg; + if (Array.isArray(paths)) { + paths = paths.map(path => isWorkspaceIdentifier(path) || typeof path === 'string' ? path : URI.revive(path)); + } + return this.service.removeFromRecentlyOpened(paths); + } case 'clearRecentlyOpened': return this.service.clearRecentlyOpened(); + case 'newWindowTab': return this.service.newWindowTab(); case 'showPreviousWindowTab': return this.service.showPreviousWindowTab(); case 'showNextWindowTab': return this.service.showNextWindowTab(); case 'moveWindowTabToNewWindow': return this.service.moveWindowTabToNewWindow(); @@ -158,7 +165,7 @@ export class WindowsChannel implements IWindowsChannel { case 'minimizeWindow': return this.service.minimizeWindow(arg); case 'onWindowTitleDoubleClick': return this.service.onWindowTitleDoubleClick(arg); case 'setDocumentEdited': return this.service.setDocumentEdited(arg[0], arg[1]); - case 'openWindow': return this.service.openWindow(arg[0], arg[1], arg[2]); + case 'openWindow': return this.service.openWindow(arg[0], arg[1] ? (arg[1]).map(r => URI.revive(r)) : arg[1], arg[2]); case 'openNewWindow': return this.service.openNewWindow(); case 'showWindow': return this.service.showWindow(arg); case 'getWindows': return this.service.getWindows(); @@ -172,7 +179,6 @@ export class WindowsChannel implements IWindowsChannel { case 'getActiveWindowId': return this.service.getActiveWindowId(); case 'openExternal': return this.service.openExternal(arg); case 'startCrashReporter': return this.service.startCrashReporter(arg); - case 'openAccessibilityOptions': return this.service.openAccessibilityOptions(); case 'openAboutDialog': return this.service.openAboutDialog(); } return undefined; @@ -260,7 +266,7 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('addRecentlyOpened', files); } - removeFromRecentlyOpened(paths: string[]): TPromise { + removeFromRecentlyOpened(paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string)[]): TPromise { return this.channel.call('removeFromRecentlyOpened', paths); } @@ -269,7 +275,15 @@ export class WindowsChannelClient implements IWindowsService { } getRecentlyOpened(windowId: number): TPromise { - return this.channel.call('getRecentlyOpened', windowId); + return this.channel.call('getRecentlyOpened', windowId) + .then(recentlyOpened => { + recentlyOpened.workspaces = recentlyOpened.workspaces.map(workspace => isWorkspaceIdentifier(workspace) ? workspace : URI.revive(workspace)); + return recentlyOpened; + }); + } + + newWindowTab(): TPromise { + return this.channel.call('newWindowTab'); } showPreviousWindowTab(): TPromise { @@ -344,7 +358,7 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('toggleSharedProcess'); } - openWindow(windowId: number, paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean }): TPromise { + openWindow(windowId: number, paths: URI[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs }): TPromise { return this.channel.call('openWindow', [windowId, paths, options]); } @@ -356,8 +370,8 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('showWindow', windowId); } - getWindows(): TPromise<{ id: number; workspace?: IWorkspaceIdentifier; folderPath?: string; title: string; filename?: string; }[]> { - return this.channel.call('getWindows'); + getWindows(): TPromise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]> { + return this.channel.call('getWindows').then(result => { result.forEach(win => win.folderUri = win.folderUri ? URI.revive(win.folderUri) : win.folderUri); return result; }); } getWindowCount(): TPromise { @@ -388,10 +402,6 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('updateTouchBar', [windowId, items]); } - openAccessibilityOptions(): TPromise { - return this.channel.call('openAccessibilityOptions'); - } - openAboutDialog(): TPromise { return this.channel.call('openAboutDialog'); } diff --git a/src/vs/platform/windows/electron-browser/windowService.ts b/src/vs/platform/windows/electron-browser/windowService.ts index 837aee8581d..c32c299263e 100644 --- a/src/vs/platform/windows/electron-browser/windowService.ts +++ b/src/vs/platform/windows/electron-browser/windowService.ts @@ -12,6 +12,7 @@ import { IRecentlyOpened } from 'vs/platform/history/common/history'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; +import URI from 'vs/base/common/uri'; export class WindowService implements IWindowService { @@ -93,7 +94,7 @@ export class WindowService implements IWindowService { return this.windowsService.saveAndEnterWorkspace(this.windowId, path); } - openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean; }): TPromise { + openWindow(paths: URI[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs }): TPromise { return this.windowsService.openWindow(this.windowId, paths, options); } diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 5c014f82886..20b7711912c 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -13,6 +13,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { IProcessEnvironment } from 'vs/base/common/platform'; import { IWorkspaceIdentifier, IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; +import URI from 'vs/base/common/uri'; export interface IWindowState { width?: number; @@ -35,7 +36,7 @@ export interface ICodeWindow { win: Electron.BrowserWindow; config: IWindowConfiguration; - openedFolderPath: string; + openedFolderUri: URI; openedWorkspace: IWorkspaceIdentifier; backupPath: string; @@ -47,6 +48,8 @@ export interface ICodeWindow { readyState: ReadyState; ready(): TPromise; + addTabbedWindow(window: ICodeWindow): void; + load(config: IWindowConfiguration, isReload?: boolean, disableExtensions?: boolean): void; reload(configuration?: IWindowConfiguration, cli?: ParsedArgs): void; @@ -109,6 +112,7 @@ export interface IWindowsMainService { getLastActiveWindow(): ICodeWindow; waitForWindowCloseOrLoad(windowId: number): TPromise; openNewWindow(context: OpenContext): ICodeWindow[]; + openNewTabbedWindow(context: OpenContext): ICodeWindow[]; sendToFocused(channel: string, ...args: any[]): void; sendToAll(channel: string, payload: any, windowIdsToIgnore?: number[]): void; getFocusedWindow(): ICodeWindow; @@ -123,9 +127,10 @@ export interface IOpenConfiguration { contextWindowId?: number; cli: ParsedArgs; userEnv?: IProcessEnvironment; - pathsToOpen?: string[]; + urisToOpen?: URI[]; preferNewWindow?: boolean; forceNewWindow?: boolean; + forceNewTabbedWindow?: boolean; forceReuseWindow?: boolean; forceEmpty?: boolean; diffMode?: boolean; diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index c9e45bedb10..25960e94c67 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -13,13 +13,13 @@ import URI from 'vs/base/common/uri'; import product from 'vs/platform/node/product'; import { IWindowsService, OpenContext, INativeOpenDialogOptions, IEnterWorkspaceResult, IMessageBoxResult, IDevToolsOptions } from 'vs/platform/windows/common/windows'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; -import { shell, crashReporter, app, Menu, clipboard, BrowserWindow } from 'electron'; +import { shell, crashReporter, app, Menu, clipboard } from 'electron'; import { Event, fromNodeEventEmitter, mapEvent, filterEvent, anyEvent, latch } from 'vs/base/common/event'; import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; import { IWindowsMainService, ISharedProcess } from 'vs/platform/windows/electron-main/windows'; import { IHistoryMainService, IRecentlyOpened } from 'vs/platform/history/common/history'; -import { IWorkspaceIdentifier, IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { Schemas } from 'vs/base/common/network'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; @@ -233,7 +233,7 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable return TPromise.as(null); } - removeFromRecentlyOpened(paths: string[]): TPromise { + removeFromRecentlyOpened(paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string)[]): TPromise { this.logService.trace('windowsService#removeFromRecentlyOpened'); this.historyService.removeFromRecentlyOpened(paths); @@ -252,12 +252,20 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable const codeWindow = this.windowsMainService.getWindowById(windowId); if (codeWindow) { - return TPromise.as(this.historyService.getRecentlyOpened(codeWindow.config.workspace || codeWindow.config.folderPath, codeWindow.config.filesToOpen)); + return TPromise.as(this.historyService.getRecentlyOpened(codeWindow.config.workspace || codeWindow.config.folderUri, codeWindow.config.filesToOpen)); } return TPromise.as(this.historyService.getRecentlyOpened()); } + newWindowTab(): TPromise { + this.logService.trace('windowsService#newWindowTab'); + + this.windowsMainService.openNewTabbedWindow(OpenContext.API); + + return TPromise.as(void 0); + } + showPreviousWindowTab(): TPromise { this.logService.trace('windowsService#showPreviousWindowTab'); Menu.sendActionToFirstResponder('selectPreviousTab:'); @@ -392,7 +400,7 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable return TPromise.as(null); } - openWindow(windowId: number, paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean }): TPromise { + openWindow(windowId: number, paths: URI[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs }): TPromise { this.logService.trace('windowsService#openWindow'); if (!paths || !paths.length) { return TPromise.as(null); @@ -401,8 +409,8 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable this.windowsMainService.open({ context: OpenContext.API, contextWindowId: windowId, - cli: this.environmentService.args, - pathsToOpen: paths, + urisToOpen: paths, + cli: options && options.args ? { ...this.environmentService.args, ...options.args } : this.environmentService.args, forceNewWindow: options && options.forceNewWindow, forceReuseWindow: options && options.forceReuseWindow, forceOpenWorkspaceAsFile: options && options.forceOpenWorkspaceAsFile @@ -413,7 +421,9 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable openNewWindow(): TPromise { this.logService.trace('windowsService#openNewWindow'); + this.windowsMainService.openNewWindow(OpenContext.API); + return TPromise.as(null); } @@ -428,10 +438,10 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable return TPromise.as(null); } - getWindows(): TPromise<{ id: number; workspace?: IWorkspaceIdentifier; folderPath?: string; title: string; filename?: string; }[]> { + getWindows(): TPromise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]> { this.logService.trace('windowsService#getWindows'); const windows = this.windowsMainService.getWindows(); - const result = windows.map(w => ({ id: w.id, workspace: w.openedWorkspace, openedFolderPath: w.openedFolderPath, title: w.win.getTitle(), filename: w.getRepresentedFilename() })); + const result = windows.map(w => ({ id: w.id, workspace: w.openedWorkspace, folderUri: w.openedFolderUri, title: w.win.getTitle(), filename: w.getRepresentedFilename() })); return TPromise.as(result); } @@ -491,29 +501,6 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable return TPromise.as(null); } - openAccessibilityOptions(): TPromise { - this.logService.trace('windowsService#openAccessibilityOptions'); - - const win = new BrowserWindow({ - alwaysOnTop: true, - skipTaskbar: true, - resizable: false, - width: 450, - height: 300, - show: true, - title: nls.localize('accessibilityOptionsWindowTitle', "Accessibility Options"), - webPreferences: { - disableBlinkFeatures: 'Auxclick' - } - }); - - win.setMenuBarVisibility(false); - - win.loadURL('chrome://accessibility'); - - return TPromise.as(null); - } - openAboutDialog(): TPromise { this.logService.trace('windowsService#openAboutDialog'); const lastActiveWindow = this.windowsMainService.getFocusedWindow() || this.windowsMainService.getLastActiveWindow(); @@ -563,9 +550,9 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable private openFileForURI(uri: URI): TPromise { const cli = assign(Object.create(null), this.environmentService.args, { goto: true }); - const pathsToOpen = [uri.fsPath]; + const urisToOpen = [uri]; - this.windowsMainService.open({ context: OpenContext.API, cli, pathsToOpen }); + this.windowsMainService.open({ context: OpenContext.API, cli, urisToOpen }); return TPromise.wrap(true); } diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index 2263c848f17..35d10686eb3 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -10,7 +10,7 @@ import * as resources from 'vs/base/common/resources'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { TernarySearchTree } from 'vs/base/common/map'; import { Event } from 'vs/base/common/event'; -import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, IStoredWorkspaceFolder, isRawFileWorkspaceFolder, isRawUriWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, IStoredWorkspaceFolder, isRawFileWorkspaceFolder, isRawUriWorkspaceFolder, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { coalesce, distinct } from 'vs/base/common/arrays'; import { isLinux } from 'vs/base/common/platform'; @@ -77,6 +77,15 @@ export interface IWorkspaceContextService { isInsideWorkspace(resource: URI): boolean; } +export namespace IWorkspace { + export function isIWorkspace(thing: any): thing is IWorkspace { + return thing && typeof thing === 'object' + && typeof (thing as IWorkspace).id === 'string' + && typeof (thing as IWorkspace).name === 'string' + && Array.isArray((thing as IWorkspace).folders); + } +} + export interface IWorkspace { /** @@ -118,6 +127,15 @@ export interface IWorkspaceFolderData { readonly index: number; } +export namespace IWorkspaceFolder { + export function isIWorkspaceFolder(thing: any): thing is IWorkspaceFolder { + return thing && typeof thing === 'object' + && URI.isUri((thing as IWorkspaceFolder).uri) + && typeof (thing as IWorkspaceFolder).name === 'string' + && typeof (thing as IWorkspaceFolder).toResource === 'function'; + } +} + export interface IWorkspaceFolder extends IWorkspaceFolderData { /** @@ -216,7 +234,7 @@ export class WorkspaceFolder implements IWorkspaceFolder { } toResource(relativePath: string): URI { - return this.uri.with({ path: paths.join(this.uri.path, relativePath) }); + return resources.joinPath(this.uri, relativePath); } toJSON(): IWorkspaceFolderData { @@ -260,7 +278,7 @@ function toUri(path: string, relativeTo: URI): URI { return URI.file(path); } if (relativeTo) { - return relativeTo.with({ path: paths.join(relativeTo.path, path) }); + return resources.joinPath(relativeTo, path); } } return null; diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index dea71450314..3c3f6fe8c40 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -13,9 +13,11 @@ import { basename, dirname, join } from 'vs/base/common/paths'; import { isLinux } from 'vs/base/common/platform'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { Event } from 'vs/base/common/event'; -import { tildify, getPathLabel } from 'vs/base/common/labels'; +import { getBaseLabel } from 'vs/base/common/labels'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import URI from 'vs/base/common/uri'; +import { Schemas } from 'vs/base/common/network'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; export const IWorkspacesMainService = createDecorator('workspacesMainService'); export const IWorkspacesService = createDecorator('workspacesService'); @@ -27,7 +29,7 @@ export const UNTITLED_WORKSPACE_NAME = 'workspace.json'; /** * A single folder workspace identifier is just the path to the folder. */ -export type ISingleFolderWorkspaceIdentifier = string; +export type ISingleFolderWorkspaceIdentifier = URI; export interface IWorkspaceIdentifier { id: string; @@ -111,11 +113,17 @@ export interface IWorkspacesService { createWorkspace(folders?: IWorkspaceFolderCreationData[]): TPromise; } -export function getWorkspaceLabel(workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier), environmentService: IEnvironmentService, options?: { verbose: boolean }): string { +export function getWorkspaceLabel(workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier), environmentService: IEnvironmentService, uriDisplayService: IUriDisplayService, options?: { verbose: boolean }): string { // Workspace: Single Folder if (isSingleFolderWorkspaceIdentifier(workspace)) { - return tildify(workspace, environmentService.userHome); + // Folder on disk + if (workspace.scheme === Schemas.file) { + return options && options.verbose ? uriDisplayService.getLabel(workspace) : getBaseLabel(workspace); + } + + // Remote folder + return options && options.verbose ? uriDisplayService.getLabel(workspace) : `${getBaseLabel(workspace)} (${workspace.scheme})`; } // Workspace: Untitled @@ -127,14 +135,14 @@ export function getWorkspaceLabel(workspace: (IWorkspaceIdentifier | ISingleFold const filename = basename(workspace.configPath); const workspaceName = filename.substr(0, filename.length - WORKSPACE_EXTENSION.length - 1); if (options && options.verbose) { - return localize('workspaceNameVerbose', "{0} (Workspace)", getPathLabel(join(dirname(workspace.configPath), workspaceName), environmentService)); + return localize('workspaceNameVerbose', "{0} (Workspace)", uriDisplayService.getLabel(URI.file(join(dirname(workspace.configPath), workspaceName)))); } return localize('workspaceName', "{0} (Workspace)", workspaceName); } export function isSingleFolderWorkspaceIdentifier(obj: any): obj is ISingleFolderWorkspaceIdentifier { - return typeof obj === 'string'; + return obj instanceof URI; } export function isWorkspaceIdentifier(obj: any): obj is IWorkspaceIdentifier { diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 27a09386ff7..71081b8f94a 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -2156,6 +2156,41 @@ declare module 'vscode' { resolveCodeLens?(codeLens: CodeLens, token: CancellationToken): ProviderResult; } + /** + * Information about where a symbol is defined. + * + * Provides additional metadata over normal [location](#Location) definitions, including the range of + * the defining symbol + */ + export interface DefinitionLink { + /** + * Span of the symbol being defined in the source file. + * + * Used as the underlined span for mouse definition hover. Defaults to the word range at + * the definition position. + */ + originSelectionRange?: Range; + + /** + * The resource identifier of the definition. + */ + targetUri: Uri; + + /** + * The full range of the definition. + * + * For a class definition for example, this would be the entire body of the class definition. + */ + targetRange: Range; + + /** + * The span of the symbol definition. + * + * For a class definition, this would be the class name itself in the class definition. + */ + targetSelectionRange?: Range; + } + /** * The definition of a symbol represented as one or many [locations](#Location). * For most programming languages there is only one location at which a symbol is @@ -2179,7 +2214,7 @@ declare module 'vscode' { * @return A definition or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined` or `null`. */ - provideDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + provideDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; } /** @@ -2197,7 +2232,7 @@ declare module 'vscode' { * @return A definition or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined` or `null`. */ - provideImplementation(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + provideImplementation(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; } /** @@ -2215,7 +2250,7 @@ declare module 'vscode' { * @return A definition or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined` or `null`. */ - provideTypeDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + provideTypeDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; } /** @@ -2672,13 +2707,15 @@ declare module 'vscode' { } /** - * A workspace edit represents textual and files changes for + * A workspace edit is a collection of textual and files changes for * multiple resources and documents. + * + * Use the [applyEdit](#workspace.applyEdit)-function to apply a workspace edit. */ export class WorkspaceEdit { /** - * The number of affected resources. + * The number of affected resources of textual or resource changes. */ readonly size: number; @@ -2709,7 +2746,8 @@ declare module 'vscode' { delete(uri: Uri, range: Range): void; /** - * Check if this edit affects the given resource. + * Check if a text edit for a resource exists. + * * @param uri A resource identifier. * @return `true` if the given resource will be touched by this edit. */ @@ -2731,6 +2769,33 @@ declare module 'vscode' { */ get(uri: Uri): TextEdit[]; + /** + * Create a regular file. + * + * @param uri Uri of the new file.. + * @param options Defines if an existing file should be overwritten or be + * ignored. When overwrite and ignoreIfExists are both set overwrite wins. + */ + createFile(uri: Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean }): void; + + /** + * Delete a file or folder. + * + * @param uri The uri of the file that is to be deleted. + */ + deleteFile(uri: Uri, options?: { recursive?: boolean, ignoreIfNotExists?: boolean }): void; + + /** + * Rename a file or folder. + * + * @param oldUri The existing file. + * @param newUri The new location. + * @param options Defines if existing files should be overwritten or be + * ignored. When overwrite and ignoreIfExists are both set overwrite wins. + */ + renameFile(oldUri: Uri, newUri: Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean }): void; + + /** * Get all text edits grouped by resource. * @@ -3487,6 +3552,7 @@ declare module 'vscode' { * [Region](#FoldingRangeKind.Region). The kind is used to categorize folding ranges and used by commands * like 'Fold all comments'. See * [FoldingRangeKind](#FoldingRangeKind) for an enumeration of all kinds. + * If not set, the range is originated from a syntax element. */ kind?: FoldingRangeKind; @@ -3501,7 +3567,10 @@ declare module 'vscode' { } /** - * An enumeration of all folding range kinds. The kind is used to categorize folding ranges. + * An enumeration of specific folding range kinds. The kind is an optional field of a [FoldingRange](#FoldingRange) + * and is used to distinguish specific folding ranges such as ranges originated from comments. The kind is used by commands like + * `Fold all comments` or `Fold all regions`. + * If the kind is not set on the range, the range originated from a syntax element other than comments, imports or region markers. */ export enum FoldingRangeKind { /** @@ -3513,7 +3582,7 @@ declare module 'vscode' { */ Imports = 2, /** - * Kind for folding range representing regions (for example a folding range marked by `#region` and `#endregion`). + * Kind for folding range representing regions originating from folding markers like `#region` and `#endregion`. */ Region = 3 } @@ -4557,22 +4626,22 @@ declare module 'vscode' { /** * The clean task group; */ - public static Clean: TaskGroup; + static Clean: TaskGroup; /** * The build task group; */ - public static Build: TaskGroup; + static Build: TaskGroup; /** * The rebuild all task group; */ - public static Rebuild: TaskGroup; + static Rebuild: TaskGroup; /** * The test all task group; */ - public static Test: TaskGroup; + static Test: TaskGroup; private constructor(id: string, label: string); } @@ -5465,6 +5534,11 @@ declare module 'vscode' { */ title: string; + /** + * Icon for the panel shown in UI. + */ + iconPath?: Uri | { light: Uri; dark: Uri }; + /** * Webview belonging to the panel. */ @@ -5478,8 +5552,6 @@ declare module 'vscode' { /** * Editor position of the panel. This property is only set if the webview is in * one of the editor view columns. - * - * @deprecated */ readonly viewColumn?: ViewColumn; @@ -6086,6 +6158,29 @@ declare module 'vscode' { */ export function showInputBox(options?: InputBoxOptions, token?: CancellationToken): Thenable; + /** + * Creates a [QuickPick](#QuickPick) to let the user pick an item from a list + * of items of type T. + * + * Note that in many cases the more convenient [window.showQuickPick](#window.showQuickPick) + * is easier to use. [window.createQuickPick](#window.createQuickPick) should be used + * when [window.showQuickPick](#window.showQuickPick) does not offer the required flexibility. + * + * @return A new [QuickPick](#QuickPick). + */ + export function createQuickPick(): QuickPick; + + /** + * Creates a [InputBox](#InputBox) to let the user enter some text input. + * + * Note that in many cases the more convenient [window.showInputBox](#window.showInputBox) + * is easier to use. [window.createInputBox](#window.createInputBox) should be used + * when [window.showInputBox](#window.showInputBox) does not offer the required flexibility. + * + * @return A new [InputBox](#InputBox). + */ + export function createInputBox(): InputBox; + /** * Create a new [output channel](#OutputChannel) with the given name. * @@ -6550,6 +6645,269 @@ declare module 'vscode' { cancellable?: boolean; } + /** + * A light-weight user input UI that is intially not visible. After + * configuring it through its properties the extension can make it + * visible by calling [QuickInput.show](#QuickInput.show). + * + * There are several reasons why this UI might have to be hidden and + * the extension will be notified through [QuickInput.onDidHide](#QuickInput.onDidHide). + * (Examples include: an explict call to [QuickInput.hide](#QuickInput.hide), + * the user pressing Esc, some other input UI opening, etc.) + * + * A user pressing Enter or some other gesture implying acceptance + * of the current state does not automatically hide this UI component. + * It is up to the extension to decide whether to accept the user's input + * and if the UI should indeed be hidden through a call to [QuickInput.hide](#QuickInput.hide). + * + * When the extension no longer needs this input UI, it should + * [QuickInput.dispose](#QuickInput.dispose) it to allow for freeing up + * any resources associated with it. + * + * See [QuickPick](#QuickPick) and [InputBox](#InputBox) for concrete UIs. + */ + export interface QuickInput { + + /** + * An optional title. + */ + title: string | undefined; + + /** + * An optional current step count. + */ + step: number | undefined; + + /** + * An optional total step count. + */ + totalSteps: number | undefined; + + /** + * If the UI should allow for user input. Defaults to true. + * + * Change this to false, e.g., while validating user input or + * loading data for the next step in user input. + */ + enabled: boolean; + + /** + * If the UI should show a progress indicator. Defaults to false. + * + * Change this to true, e.g., while loading more data or validating + * user input. + */ + busy: boolean; + + /** + * If the UI should stay open even when loosing UI focus. Defaults to false. + */ + ignoreFocusOut: boolean; + + /** + * Makes the input UI visible in its current configuration. Any other input + * UI will first fire an [QuickInput.onDidHide](#QuickInput.onDidHide) event. + */ + show(): void; + + /** + * Hides this input UI. This will also fire an [QuickInput.onDidHide](#QuickInput.onDidHide) + * event. + */ + hide(): void; + + /** + * An event signaling when this input UI is hidden. + * + * There are several reasons why this UI might have to be hidden and + * the extension will be notified through [QuickInput.onDidHide](#QuickInput.onDidHide). + * (Examples include: an explict call to [QuickInput.hide](#QuickInput.hide), + * the user pressing Esc, some other input UI opening, etc.) + */ + onDidHide: Event; + + /** + * Dispose of this input UI and any associated resources. If it is still + * visible, it is first hidden. After this call the input UI is no longer + * functional and no additional methods or properties on it should be + * accessed. Instead a new input UI should be created. + */ + dispose(): void; + } + + /** + * A concrete [QuickInput](#QuickInput) to let the user pick an item from a + * list of items of type T. The items can be filtered through a filter text field and + * there is an option [canSelectMany](#QuickPick.canSelectMany) to allow for + * selecting multiple items. + * + * Note that in many cases the more convenient [window.showQuickPick](#window.showQuickPick) + * is easier to use. [window.createQuickPick](#window.createQuickPick) should be used + * when [window.showQuickPick](#window.showQuickPick) does not offer the required flexibility. + */ + export interface QuickPick extends QuickInput { + + /** + * Current value of the filter text. + */ + value: string; + + /** + * Optional placeholder in the filter text. + */ + placeholder: string | undefined; + + /** + * An event signaling when the value of the filter text has changed. + */ + readonly onDidChangeValue: Event; + + /** + * An event signaling when the user indicated acceptance of the selected item(s). + */ + readonly onDidAccept: Event; + + /** + * Buttons for actions in the UI. + */ + buttons: ReadonlyArray; + + /** + * An event signaling when a button was triggered. + */ + readonly onDidTriggerButton: Event; + + /** + * Items to pick from. + */ + items: ReadonlyArray; + + /** + * If multiple items can be selected at the same time. Defaults to false. + */ + canSelectMany: boolean; + + /** + * If the filter text should also be matched against the description of the items. Defaults to false. + */ + matchOnDescription: boolean; + + /** + * If the filter text should also be matched against the detail of the items. Defaults to false. + */ + matchOnDetail: boolean; + + /** + * Active items. This can be read and updated by the extension. + */ + activeItems: ReadonlyArray; + + /** + * An event signaling when the active items have changed. + */ + readonly onDidChangeActive: Event; + + /** + * Selected items. This can be read and updated by the extension. + */ + selectedItems: ReadonlyArray; + + /** + * An event signaling when the selected items have changed. + */ + readonly onDidChangeSelection: Event; + } + + /** + * A concrete [QuickInput](#QuickInput) to let the user input a text value. + * + * Note that in many cases the more convenient [window.showInputBox](#window.showInputBox) + * is easier to use. [window.createInputBox](#window.createInputBox) should be used + * when [window.showInputBox](#window.showInputBox) does not offer the required flexibility. + */ + export interface InputBox extends QuickInput { + + /** + * Current input value. + */ + value: string; + + /** + * Optional placeholder in the filter text. + */ + placeholder: string | undefined; + + /** + * If the input value should be hidden. Defaults to false. + */ + password: boolean; + + /** + * An event signaling when the value has changed. + */ + readonly onDidChangeValue: Event; + + /** + * An event signaling when the user indicated acceptance of the input value. + */ + readonly onDidAccept: Event; + + /** + * Buttons for actions in the UI. + */ + buttons: ReadonlyArray; + + /** + * An event signaling when a button was triggered. + */ + readonly onDidTriggerButton: Event; + + /** + * An optional prompt text providing some ask or explanation to the user. + */ + prompt: string | undefined; + + /** + * An optional validation message indicating a problem with the current input value. + */ + validationMessage: string | undefined; + } + + /** + * Button for an action in a [QuickPick](#QuickPick) or [InputBox](#InputBox). + */ + export interface QuickInputButton { + + /** + * Icon for the button. + */ + readonly iconPath: Uri | { light: Uri; dark: Uri } | ThemeIcon; + + /** + * An optional tooltip. + */ + readonly tooltip?: string | undefined; + } + + /** + * Predefined buttons for [QuickPick](#QuickPick) and [InputBox](#InputBox). + */ + export class QuickInputButtons { + + /** + * A back button for [QuickPick](#QuickPick) and [InputBox](#InputBox). + * + * When a navigation 'back' button is needed this one should be used for consistency. + * It comes with a predefined icon, tooltip and location. + */ + static readonly Back: QuickInputButton; + + /** + * @hidden + */ + private constructor(); + } + /** * An event describing an individual change in the text of a [document](#TextDocument). */ @@ -6854,12 +7212,17 @@ declare module 'vscode' { export function saveAll(includeUntitled?: boolean): Thenable; /** - * Make changes to one or many resources as defined by the given + * Make changes to one or many resources or create, delete, and rename resources as defined by the given * [workspace edit](#WorkspaceEdit). * - * When applying a workspace edit, the editor implements an 'all-or-nothing'-strategy, - * that means failure to load one document or make changes to one document will cause - * the edit to be rejected. + * All changes of a workspace edit are applied in the same order in which they have been added. If + * multiple textual inserts are made at the same position, these strings appear in the resulting text + * in the order the 'inserts' were made. Invalid sequences like 'delete file a' -> 'insert text in file a' + * cause failure of the operation. + * + * When applying a workspace edit that consists only of text edits an 'all-or-nothing'-strategy is used. + * A workspace edit with resource creations or deletions aborts the operation, e.g. consective edits will + * not be attempted, when a single edit fails. * * @param edit A workspace edit. * @return A thenable that resolves when the edit could be applied. @@ -6992,13 +7355,13 @@ declare module 'vscode' { export const onDidChangeConfiguration: Event; /** - * Register a task provider. + * ~~Register a task provider.~~ + * + * @deprecated Use the corresponding function on the `tasks` namespace instead * * @param type The task kind type this provider is registered for. * @param provider A task provider. * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - * - * @deprecated Use the corresponding function on the `tasks` namespace instead */ export function registerTaskProvider(type: string, provider: TaskProvider): Disposable; diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index f899c196ba9..03f3b993f60 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -70,24 +70,24 @@ declare module 'vscode' { * Whether external files that exclude files, like .gitignore, should be respected. * See the vscode setting `"search.useIgnoreFiles"`. */ - useIgnoreFiles?: boolean; + useIgnoreFiles: boolean; /** * Whether symlinks should be followed while searching. * See the vscode setting `"search.followSymlinks"`. */ - followSymlinks?: boolean; - - /** - * The maximum number of results to be returned. - */ - maxResults?: number; + followSymlinks: boolean; } /** * Options that apply to text search. */ export interface TextSearchOptions extends SearchOptions { + /** + * The maximum number of results to be returned. + */ + maxResults: number; + /** * TODO@roblou - total length? # of context lines? leading and trailing # of chars? */ @@ -113,19 +113,22 @@ declare module 'vscode' { * The search pattern to match against file paths. */ pattern: string; - - /** - * `cacheKey` has the same value when `provideFileSearchResults` is invoked multiple times during a single quickopen session. - * Providers can optionally use this to cache results at the beginning of a quickopen session and filter results as the user types. - * It will have a different value for each folder searched. - */ - cacheKey?: string; } /** * Options that apply to file search. */ - export interface FileSearchOptions extends SearchOptions { } + export interface FileSearchOptions extends SearchOptions { + /** + * The maximum number of results to be returned. + */ + maxResults: number; + } + + /** + * Options that apply to requesting the file index. + */ + export interface FileIndexOptions extends SearchOptions { } export interface TextSearchResultPreview { /** @@ -161,9 +164,37 @@ declare module 'vscode' { } /** - * A SearchProvider provides search results for files or text in files. It can be invoked by quickopen, the search viewlet, and other extensions. + * A FileIndexProvider provides a list of files in the given folder. VS Code will filter that list for searching with quickopen or from other extensions. + * + * A FileIndexProvider is the simpler of two ways to implement file search in VS Code. Use a FileIndexProvider if you are able to provide a listing of all files + * in a folder, and want VS Code to filter them according to the user's search query. + * + * The FileIndexProvider will be invoked once when quickopen is opened, and VS Code will filter the returned list. It will also be invoked when + * `workspace.findFiles` is called. + * + * If a [`FileSearchProvider`](#FileSearchProvider) is registered for the scheme, that provider will be used instead. */ - export interface SearchProvider { + export interface FileIndexProvider { + /** + * Provide the set of files in the folder. + * @param options A set of options to consider while searching. + * @param token A cancellation token. + */ + provideFileIndex(options: FileIndexOptions, token: CancellationToken): Thenable; + } + + /** + * A FileSearchProvider provides search results for files in the given folder that match a query string. It can be invoked by quickopen or other extensions. + * + * A FileSearchProvider is the more powerful of two ways to implement file search in VS Code. Use a FileSearchProvider if you wish to search within a folder for + * all files that match the user's query. + * + * The FileSearchProvider will be invoked on every keypress in quickopen. When `workspace.findFiles` is called, it will be invoked with an empty query string, + * and in that case, every file in the folder should be returned. + * + * @see [FileIndexProvider](#FileIndexProvider) + */ + export interface FileSearchProvider { /** * Provide the set of files that match a certain file path pattern. * @param query The parameters for this query. @@ -171,14 +202,13 @@ declare module 'vscode' { * @param progress A progress callback that must be invoked for all results. * @param token A cancellation token. */ - provideFileSearchResults?(query: FileSearchQuery, options: FileSearchOptions, progress: Progress, token: CancellationToken): Thenable; - - /** - * Optional - if the provider makes use of `query.cacheKey`, it can implement this method which is invoked when the cache can be cleared. - * @param cacheKey The same key that was passed as `query.cacheKey`. - */ - clearCache?(cacheKey: string): void; + provideFileSearchResults(query: FileSearchQuery, options: FileSearchOptions, token: CancellationToken): Thenable; + } + /** + * A TextSearchProvider provides search results for text results inside files in the workspace. + */ + export interface TextSearchProvider { /** * Provide results that match the given text pattern. * @param query The parameters for this query. @@ -186,7 +216,7 @@ declare module 'vscode' { * @param progress A progress callback that must be invoked for all results. * @param token A cancellation token. */ - provideTextSearchResults?(query: TextSearchQuery, options: TextSearchOptions, progress: Progress, token: CancellationToken): Thenable; + provideTextSearchResults(query: TextSearchQuery, options: TextSearchOptions, progress: Progress, token: CancellationToken): Thenable; } /** @@ -232,6 +262,22 @@ declare module 'vscode' { } export namespace workspace { + /** + * DEPRECATED + */ + export function registerSearchProvider(): Disposable; + + /** + * Register a file index provider. + * + * Only one provider can be registered per scheme. + * + * @param scheme The provider will be invoked for workspace folders that have this file scheme. + * @param provider The provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerFileIndexProvider(scheme: string, provider: FileIndexProvider): Disposable; + /** * Register a search provider. * @@ -241,8 +287,18 @@ declare module 'vscode' { * @param provider The provider. * @return A [disposable](#Disposable) that unregisters this provider when being disposed. */ - export function registerSearchProvider(scheme: string, provider: SearchProvider): Disposable; + export function registerFileSearchProvider(scheme: string, provider: FileSearchProvider): Disposable; + /** + * Register a text search provider. + * + * Only one provider can be registered per scheme. + * + * @param scheme The provider will be invoked for workspace folders that have this file scheme. + * @param provider The provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerTextSearchProvider(scheme: string, provider: TextSearchProvider): Disposable; /** * Search text in files across all [workspace folders](#workspace.workspaceFolders) in the workspace. @@ -306,9 +362,9 @@ declare module 'vscode' { priority?: number; title?: string; bubble?: boolean; - abbreviation?: string; + abbreviation?: string; // letter, not optional color?: ThemeColor; - source?: string; + source?: string; // hacky... we should remove it and use equality under the hood } export interface SourceControlResourceDecorations { @@ -467,6 +523,23 @@ declare module 'vscode' { //#endregion + //#region Joao: SCM selected provider + + export interface SourceControl { + + /** + * Whether the source control is selected. + */ + readonly selected: boolean; + + /** + * An event signaling when the selection state changes. + */ + readonly onDidChangeSelection: Event; + } + + //#endregion + //#region Comments /** * Comments provider related APIs are still in early stages, they may be changed significantly during our API experiments. @@ -697,346 +770,6 @@ declare module 'vscode' { //#endregion - //#region QuickInput API - - export namespace window { - - /** - * A back button for [QuickPick](#QuickPick) and [InputBox](#InputBox). - * - * When a navigation 'back' button is needed this one should be used for consistency. - * It comes with a predefined icon, tooltip and location. - */ - export const quickInputBackButton: QuickInputButton; - - /** - * Creates a [QuickPick](#QuickPick) to let the user pick an item from a list - * of items of type T. - * - * Note that in many cases the more convenient [window.showQuickPick](#window.showQuickPick) - * is easier to use. [window.createQuickPick](#window.createQuickPick) should be used - * when [window.showQuickPick](#window.showQuickPick) does not offer the required flexibility. - * - * @return A new [QuickPick](#QuickPick). - */ - export function createQuickPick(): QuickPick; - - /** - * Creates a [InputBox](#InputBox) to let the user enter some text input. - * - * Note that in many cases the more convenient [window.showInputBox](#window.showInputBox) - * is easier to use. [window.createInputBox](#window.createInputBox) should be used - * when [window.showInputBox](#window.showInputBox) does not offer the required flexibility. - * - * @return A new [InputBox](#InputBox). - */ - export function createInputBox(): InputBox; - } - - /** - * A light-weight user input UI that is intially not visible. After - * configuring it through its properties the extension can make it - * visible by calling [QuickInput.show](#QuickInput.show). - * - * There are several reasons why this UI might have to be hidden and - * the extension will be notified through [QuickInput.onDidHide](#QuickInput.onDidHide). - * (Examples include: an explict call to [QuickInput.hide](#QuickInput.hide), - * the user pressing Esc, some other input UI opening, etc.) - * - * A user pressing Enter or some other gesture implying acceptance - * of the current state does not automatically hide this UI component. - * It is up to the extension to decide whether to accept the user's input - * and if the UI should indeed be hidden through a call to [QuickInput.hide](#QuickInput.hide). - * - * When the extension no longer needs this input UI, it should - * [QuickInput.dispose](#QuickInput.dispose) it to allow for freeing up - * any resources associated with it. - * - * See [QuickPick](#QuickPick) and [InputBox](#InputBox) for concrete UIs. - */ - export interface QuickInput { - - /** - * An optional title. - */ - title: string | undefined; - - /** - * An optional current step count. - */ - step: number | undefined; - - /** - * An optional total step count. - */ - totalSteps: number | undefined; - - /** - * If the UI should allow for user input. Defaults to true. - * - * Change this to false, e.g., while validating user input or - * loading data for the next step in user input. - */ - enabled: boolean; - - /** - * If the UI should show a progress indicator. Defaults to false. - * - * Change this to true, e.g., while loading more data or validating - * user input. - */ - busy: boolean; - - /** - * If the UI should stay open even when loosing UI focus. Defaults to false. - */ - ignoreFocusOut: boolean; - - /** - * Makes the input UI visible in its current configuration. Any other input - * UI will first fire an [QuickInput.onDidHide](#QuickInput.onDidHide) event. - */ - show(): void; - - /** - * Hides this input UI. This will also fire an [QuickInput.onDidHide](#QuickInput.onDidHide) - * event. - */ - hide(): void; - - /** - * An event signaling when this input UI is hidden. - * - * There are several reasons why this UI might have to be hidden and - * the extension will be notified through [QuickInput.onDidHide](#QuickInput.onDidHide). - * (Examples include: an explict call to [QuickInput.hide](#QuickInput.hide), - * the user pressing Esc, some other input UI opening, etc.) - */ - onDidHide: Event; - - /** - * Dispose of this input UI and any associated resources. If it is still - * visible, it is first hidden. After this call the input UI is no longer - * functional and no additional methods or properties on it should be - * accessed. Instead a new input UI should be created. - */ - dispose(): void; - } - - /** - * A concrete [QuickInput](#QuickInput) to let the user pick an item from a - * list of items of type T. The items can be filtered through a filter text field and - * there is an option [canSelectMany](#QuickPick.canSelectMany) to allow for - * selecting multiple items. - * - * Note that in many cases the more convenient [window.showQuickPick](#window.showQuickPick) - * is easier to use. [window.createQuickPick](#window.createQuickPick) should be used - * when [window.showQuickPick](#window.showQuickPick) does not offer the required flexibility. - */ - export interface QuickPick extends QuickInput { - - /** - * Current value of the filter text. - */ - value: string; - - /** - * Optional placeholder in the filter text. - */ - placeholder: string | undefined; - - /** - * An event signaling when the value of the filter text has changed. - */ - readonly onDidChangeValue: Event; - - /** - * An event signaling when the user indicated acceptance of the selected item(s). - */ - readonly onDidAccept: Event; - - /** - * Buttons for actions in the UI. - */ - buttons: ReadonlyArray; - - /** - * An event signaling when a button was triggered. - */ - readonly onDidTriggerButton: Event; - - /** - * Items to pick from. - */ - items: ReadonlyArray; - - /** - * If multiple items can be selected at the same time. Defaults to false. - */ - canSelectMany: boolean; - - /** - * If the filter text should also be matched against the description of the items. Defaults to false. - */ - matchOnDescription: boolean; - - /** - * If the filter text should also be matched against the detail of the items. Defaults to false. - */ - matchOnDetail: boolean; - - /** - * Active items. This can be read and updated by the extension. - */ - activeItems: ReadonlyArray; - - /** - * An event signaling when the active items have changed. - */ - readonly onDidChangeActive: Event; - - /** - * Selected items. This can be read and updated by the extension. - */ - selectedItems: ReadonlyArray; - - /** - * An event signaling when the selected items have changed. - */ - readonly onDidChangeSelection: Event; - } - - /** - * A concrete [QuickInput](#QuickInput) to let the user input a text value. - * - * Note that in many cases the more convenient [window.showInputBox](#window.showInputBox) - * is easier to use. [window.createInputBox](#window.createInputBox) should be used - * when [window.showInputBox](#window.showInputBox) does not offer the required flexibility. - */ - export interface InputBox extends QuickInput { - - /** - * Current input value. - */ - value: string; - - /** - * Optional placeholder in the filter text. - */ - placeholder: string | undefined; - - /** - * If the input value should be hidden. Defaults to false. - */ - password: boolean; - - /** - * An event signaling when the value has changed. - */ - readonly onDidChangeValue: Event; - - /** - * An event signaling when the user indicated acceptance of the input value. - */ - readonly onDidAccept: Event; - - /** - * Buttons for actions in the UI. - */ - buttons: ReadonlyArray; - - /** - * An event signaling when a button was triggered. - */ - readonly onDidTriggerButton: Event; - - /** - * An optional prompt text providing some ask or explanation to the user. - */ - prompt: string | undefined; - - /** - * An optional validation message indicating a problem with the current input value. - */ - validationMessage: string | undefined; - } - - /** - * Button for an action in a [QuickPick](#QuickPick) or [InputBox](#InputBox). - */ - export interface QuickInputButton { - - /** - * Icon for the button. - */ - readonly iconPath: string | Uri | { light: string | Uri; dark: string | Uri } | ThemeIcon; - - /** - * An optional tooltip. - */ - readonly tooltip?: string | undefined; - } - - //#endregion - - //#region joh: https://github.com/Microsoft/vscode/issues/10659 - - /** - * A workspace edit is a collection of textual and files changes for - * multiple resources and documents. Use the [applyEdit](#workspace.applyEdit)-function - * to apply a workspace edit. Note that all changes are applied in the same order in which - * they have been added and that invalid sequences like 'delete file a' -> 'insert text in - * file a' causes failure of the operation. - */ - export interface WorkspaceEdit { - - /** - * The number of affected resources of textual or resource changes. - */ - readonly size: number; - - /** - * Create a regular file. - * - * @param uri Uri of the new file.. - * @param options Defines if an existing file should be overwritten or be ignored. - */ - createFile(uri: Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean }): void; - - /** - * Delete a file or folder. - * - * @param uri The uri of the file that is to be deleted. - */ - deleteFile(uri: Uri, options?: { recursive?: boolean, ignoreIfNotExists?: boolean }): void; - - /** - * Rename a file or folder. - * - * @param oldUri The existing file. - * @param newUri The new location. - * @param options Defines if existing files should be overwritten. - */ - renameFile(oldUri: Uri, newUri: Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean }): void; - } - - export namespace workspace { - /** - * Make changes to one or many resources as defined by the given - * [workspace edit](#WorkspaceEdit). - * - * The editor implements an 'all-or-nothing'-strategy and that means failure to modify, - * delete, rename, or create one file will abort the operation. In that case, the thenable returned - * by this function resolves to `false`. - * - * @param edit A workspace edit. - * @return A thenable that resolves when the edit could be applied. - */ - export function applyEdit(edit: WorkspaceEdit): Thenable; - } - - //#endregion - //#region mjbvz,joh: https://github.com/Microsoft/vscode/issues/43768 export interface FileRenameEvent { readonly oldUri: Uri; @@ -1054,47 +787,4 @@ declare module 'vscode' { export const onDidRenameFile: Event; } //#endregion - - //#region Matt: Deinition range - - /** - * Information about where a symbol is defined. - * - * Provides additional metadata over normal [location](#Location) definitions, including the range of - * the defining symbol - */ - export interface DefinitionLink { - /** - * Span of the symbol being defined in the source file. - * - * Used as the underlined span for mouse definition hover. Defaults to the word range at - * the definition position. - */ - origin?: Range; - - /** - * The resource identifier of the definition. - */ - uri: Uri; - - /** - * The full range of the definition. - * - * For a class definition for example, this would be the entire body of the class definition. - */ - range: Range; - - /** - * The span of the symbol definition. - * - * For a class definition, this would be the class name itself in the class definition. - */ - selectionRange?: Range; - } - - export interface DefinitionProvider { - provideDefinition2?(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; - } - - //#endregion } diff --git a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts index 29bf1a401c4..334c62d7251 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts @@ -5,7 +5,7 @@ 'use strict'; import { Emitter, Event } from 'vs/base/common/event'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { FileWriteOptions, FileSystemProviderCapabilities, IFileChange, IFileService, IFileSystemProvider, IStat, IWatchOptions, FileType, FileOverwriteOptions, FileDeleteOptions } from 'vs/platform/files/common/files'; @@ -71,11 +71,9 @@ class RemoteFileSystemProvider implements IFileSystemProvider { watch(resource: URI, opts: IWatchOptions) { const session = Math.random(); this._proxy.$watch(this._handle, session, resource, opts); - return { - dispose: () => { - this._proxy.$unwatch(this._handle, session); - } - }; + return toDisposable(() => { + this._proxy.$unwatch(this._handle, session); + }); } $onFileSystemChange(changes: IFileChangeDto[]): void { @@ -94,36 +92,36 @@ class RemoteFileSystemProvider implements IFileSystemProvider { }); } - readFile(resource: URI): TPromise { + readFile(resource: URI): TPromise { return this._proxy.$readFile(this._handle, resource).then(encoded => { return Buffer.from(encoded, 'base64'); }); } - writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): TPromise { + writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): TPromise { let encoded = Buffer.isBuffer(content) ? content.toString('base64') : Buffer.from(content.buffer, content.byteOffset, content.byteLength).toString('base64'); return this._proxy.$writeFile(this._handle, resource, encoded, opts); } - delete(resource: URI, opts: FileDeleteOptions): TPromise { + delete(resource: URI, opts: FileDeleteOptions): TPromise { return this._proxy.$delete(this._handle, resource, opts); } - mkdir(resource: URI): TPromise { + mkdir(resource: URI): TPromise { return this._proxy.$mkdir(this._handle, resource); } - readdir(resource: URI): TPromise<[string, FileType][], any> { + readdir(resource: URI): TPromise<[string, FileType][]> { return this._proxy.$readdir(this._handle, resource); } - rename(resource: URI, target: URI, opts: FileOverwriteOptions): TPromise { + rename(resource: URI, target: URI, opts: FileOverwriteOptions): TPromise { return this._proxy.$rename(this._handle, resource, target, opts); } - copy(resource: URI, target: URI, opts: FileOverwriteOptions): TPromise { + copy(resource: URI, target: URI, opts: FileOverwriteOptions): TPromise { return this._proxy.$copy(this._handle, resource, target, opts); } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts index 7ecf37c7311..baa55c39fa6 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts @@ -162,7 +162,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha $registerImplementationSupport(handle: number, selector: ISerializedDocumentFilter[]): void { this._registrations[handle] = modes.ImplementationProviderRegistry.register(typeConverters.LanguageSelector.from(selector), { provideImplementation: (model, position, token): Thenable => { - return wireCancellationToken(token, this._proxy.$provideImplementation(handle, model.uri, position)).then(MainThreadLanguageFeatures._reviveLocationDto); + return wireCancellationToken(token, this._proxy.$provideImplementation(handle, model.uri, position)).then(MainThreadLanguageFeatures._reviveDefinitionLinkDto); } }); } @@ -170,7 +170,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha $registerTypeDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void { this._registrations[handle] = modes.TypeDefinitionProviderRegistry.register(typeConverters.LanguageSelector.from(selector), { provideTypeDefinition: (model, position, token): Thenable => { - return wireCancellationToken(token, this._proxy.$provideTypeDefinition(handle, model.uri, position)).then(MainThreadLanguageFeatures._reviveLocationDto); + return wireCancellationToken(token, this._proxy.$provideTypeDefinition(handle, model.uri, position)).then(MainThreadLanguageFeatures._reviveDefinitionLinkDto); } }); } diff --git a/src/vs/workbench/api/electron-browser/mainThreadSCM.ts b/src/vs/workbench/api/electron-browser/mainThreadSCM.ts index 8ee8daaee7a..a6c6f1caa69 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSCM.ts @@ -7,7 +7,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import URI, { UriComponents } from 'vs/base/common/uri'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Event, Emitter, debounceEvent } from 'vs/base/common/event'; import { assign } from 'vs/base/common/objects'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGroup, ISCMResourceDecorations, IInputValidation } from 'vs/workbench/services/scm/common/scm'; @@ -270,6 +270,9 @@ export class MainThreadSCM implements MainThreadSCMShape { @ISCMService private scmService: ISCMService ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostSCM); + + debounceEvent(scmService.onDidChangeSelectedRepositories, (_, e) => e, 100) + (this.onDidChangeSelectedRepositories, this, this._disposables); } dispose(): void { @@ -417,4 +420,12 @@ export class MainThreadSCM implements MainThreadSCMShape { repository.input.validateInput = () => TPromise.as(undefined); } } + + private onDidChangeSelectedRepositories(repositories: ISCMRepository[]): void { + const handles = repositories + .filter(r => r.provider instanceof MainThreadSCMProvider) + .map(r => (r.provider as MainThreadSCMProvider).handle); + + this._proxy.$setSelectedSourceControls(handles); + } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts index 9091dc3412a..aa936863ad3 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts @@ -37,6 +37,7 @@ import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction'; import { ICodeActionsOnSaveOptions } from 'vs/editor/common/config/editorOptions'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; export interface ISaveParticipantParticipant extends ISaveParticipant { // progressMessage: string; @@ -215,16 +216,21 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant { const timeout = this._configurationService.getValue('editor.formatOnSaveTimeout', { overrideIdentifier: model.getLanguageIdentifier().language, resource: editorModel.getResource() }); return new Promise((resolve, reject) => { - setTimeout(() => reject(localize('timeout.formatOnSave', "Aborted format on save after {0}ms", timeout)), timeout); - getDocumentFormattingEdits(model, { tabSize, insertSpaces }) - .then(edits => this._editorWorkerService.computeMoreMinimalEdits(model.uri, edits)) - .then(resolve, err => { - if (!(err instanceof Error) || err.name !== NoProviderError.Name) { - reject(err); - } else { - resolve(); - } - }); + let source = new CancellationTokenSource(); + let request = getDocumentFormattingEdits(model, { tabSize, insertSpaces }, source.token); + + setTimeout(() => { + reject(localize('timeout.formatOnSave', "Aborted format on save after {0}ms", timeout)); + source.cancel(); + }, timeout); + + request.then(edits => this._editorWorkerService.computeMoreMinimalEdits(model.uri, edits)).then(resolve, err => { + if (!(err instanceof Error) || err.name !== NoProviderError.Name) { + reject(err); + } else { + resolve(); + } + }); }).then(edits => { if (!isFalsyOrEmpty(edits) && versionNow === model.getVersionId()) { diff --git a/src/vs/workbench/api/electron-browser/mainThreadSearch.ts b/src/vs/workbench/api/electron-browser/mainThreadSearch.ts index 7e3224c1de2..9028249c12b 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSearch.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSearch.ts @@ -5,14 +5,14 @@ 'use strict'; import { isFalsyOrEmpty } from 'vs/base/common/arrays'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { values } from 'vs/base/common/map'; import URI, { UriComponents } from 'vs/base/common/uri'; -import { PPromise, TPromise } from 'vs/base/common/winjs.base'; -import { IFileMatch, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, QueryType, IRawFileMatch2, ISearchCompleteStats } from 'vs/platform/search/common/search'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IFileMatch, IRawFileMatch2, ISearchComplete, ISearchCompleteStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, QueryType, SearchProviderType } from 'vs/platform/search/common/search'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { ExtHostContext, ExtHostSearchShape, IExtHostContext, MainContext, MainThreadSearchShape } from '../node/extHost.protocol'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @extHostNamedCustomer(MainContext.MainThreadSearch) export class MainThreadSearch implements MainThreadSearchShape { @@ -33,8 +33,16 @@ export class MainThreadSearch implements MainThreadSearchShape { this._searchProvider.clear(); } - $registerSearchProvider(handle: number, scheme: string): void { - this._searchProvider.set(handle, new RemoteSearchProvider(this._searchService, scheme, handle, this._proxy)); + $registerTextSearchProvider(handle: number, scheme: string): void { + this._searchProvider.set(handle, new RemoteSearchProvider(this._searchService, SearchProviderType.text, scheme, handle, this._proxy)); + } + + $registerFileSearchProvider(handle: number, scheme: string): void { + this._searchProvider.set(handle, new RemoteSearchProvider(this._searchService, SearchProviderType.file, scheme, handle, this._proxy)); + } + + $registerFileIndexProvider(handle: number, scheme: string): void { + this._searchProvider.set(handle, new RemoteSearchProvider(this._searchService, SearchProviderType.fileIndex, scheme, handle, this._proxy)); } $unregisterProvider(handle: number): void { @@ -86,38 +94,29 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable { constructor( searchService: ISearchService, + type: SearchProviderType, private readonly _scheme: string, private readonly _handle: number, private readonly _proxy: ExtHostSearchShape ) { - this._registrations = [searchService.registerSearchResultProvider(this._scheme, this)]; + this._registrations = [searchService.registerSearchResultProvider(this._scheme, type, this)]; } dispose(): void { dispose(this._registrations); } - search(query: ISearchQuery): PPromise { + search(query: ISearchQuery, onProgress?: (p: ISearchProgressItem) => void): TPromise { if (isFalsyOrEmpty(query.folderQueries)) { - return PPromise.as(undefined); + return TPromise.as(undefined); } - const folderQueriesForScheme = query.folderQueries.filter(fq => fq.folder.scheme === this._scheme); - if (!folderQueriesForScheme.length) { - return TPromise.wrap(null); - } - - query = { - ...query, - folderQueries: folderQueriesForScheme - }; - let outer: TPromise; - return new PPromise((resolve, reject, report) => { + return new TPromise((resolve, reject) => { - const search = new SearchOperation(report); + const search = new SearchOperation(onProgress); this._searches.set(search.id, search); outer = query.type === QueryType.File @@ -139,7 +138,7 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable { } clearCache(cacheKey: string): TPromise { - return this._proxy.$clearCache(this._handle, cacheKey); + return this._proxy.$clearCache(cacheKey); } handleFindMatch(session: number, dataOrUri: (UriComponents | IRawFileMatch2)[]): void { diff --git a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts index ccf129ce117..c25e31ae085 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts @@ -16,6 +16,8 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape private _proxy: ExtHostTerminalServiceShape; private _toDispose: IDisposable[] = []; private _terminalProcesses: { [id: number]: ITerminalProcessExtHostProxy } = {}; + private _terminalOnDidWriteDataListeners: { [id: number]: IDisposable } = {}; + private _terminalOnDidAcceptInputListeners: { [id: number]: IDisposable } = {}; constructor( extHostContext: IExtHostContext, @@ -114,9 +116,18 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape public $terminalRendererRegisterOnInputListener(terminalId: number): void { const terminalInstance = this.terminalService.getInstanceFromId(terminalId); - if (terminalInstance) { - terminalInstance.addDisposable(terminalInstance.onRendererInput(data => this._onTerminalRendererInput(terminalId, data))); + if (!terminalInstance) { + return; } + + // Listener already registered + if (this._terminalOnDidAcceptInputListeners.hasOwnProperty(terminalId)) { + return; + } + + // Register + this._terminalOnDidAcceptInputListeners[terminalId] = terminalInstance.onRendererInput(data => this._onTerminalRendererInput(terminalId, data)); + terminalInstance.addDisposable(this._terminalOnDidAcceptInputListeners[terminalId]); } public $sendText(terminalId: number, text: string, addNewLine: boolean): void { @@ -128,11 +139,20 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape public $registerOnDataListener(terminalId: number): void { const terminalInstance = this.terminalService.getInstanceFromId(terminalId); - if (terminalInstance) { - terminalInstance.addDisposable(terminalInstance.onData(data => { - this._onTerminalData(terminalId, data); - })); + if (!terminalInstance) { + return; } + + // Listener already registered + if (this._terminalOnDidWriteDataListeners[terminalId]) { + return; + } + + // Register + this._terminalOnDidWriteDataListeners[terminalId] = terminalInstance.onData(data => { + this._onTerminalData(terminalId, data); + }); + terminalInstance.addDisposable(this._terminalOnDidWriteDataListeners[terminalId]); } private _onActiveTerminalChanged(terminalId: number | undefined): void { diff --git a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts index 8def9ec323a..e671f1e86b3 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWebview.ts @@ -2,23 +2,23 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import * as map from 'vs/base/common/map'; import URI, { UriComponents } from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { localize } from 'vs/nls'; -import { EditorViewColumn, viewColumnToEditorGroup, editorGroupToViewColumn } from 'vs/workbench/api/shared/editor'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle } from 'vs/workbench/api/node/extHost.protocol'; +import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions } from 'vs/workbench/api/node/extHost.protocol'; +import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/shared/editor'; import { WebviewEditor } from 'vs/workbench/parts/webview/electron-browser/webviewEditor'; import { WebviewEditorInput } from 'vs/workbench/parts/webview/electron-browser/webviewEditorInput'; -import { IWebviewEditorService, WebviewInputOptions, WebviewReviver, ICreateWebViewShowOptions } from 'vs/workbench/parts/webview/electron-browser/webviewEditorService'; +import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions, WebviewReviver } from 'vs/workbench/parts/webview/electron-browser/webviewEditorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; -import { extHostNamedCustomer } from './extHostCustomers'; import * as vscode from 'vscode'; +import { extHostNamedCustomer } from './extHostCustomers'; @extHostNamedCustomer(MainContext.MainThreadWebviews) export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviver { @@ -96,6 +96,11 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv webview.setName(value); } + public $setIconPath(handle: WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents } | undefined): void { + const webview = this.getWebview(handle); + webview.iconPath = reviveWebviewIcon(value); + } + public $setHtml(handle: WebviewPanelHandle, value: string): void { const webview = this.getWebview(handle); webview.html = value; @@ -106,15 +111,15 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv webview.setOptions(reviveWebviewOptions(options)); } - public $reveal(handle: WebviewPanelHandle, viewColumn: EditorViewColumn | null, preserveFocus: boolean): void { + public $reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void { const webview = this.getWebview(handle); if (webview.isDisposed()) { return; } - const targetGroup = this._editorGroupService.getGroup(viewColumnToEditorGroup(this._editorGroupService, viewColumn)); + const targetGroup = this._editorGroupService.getGroup(viewColumnToEditorGroup(this._editorGroupService, showOptions.viewColumn)); - this._webviewService.revealWebview(webview, targetGroup || this._editorGroupService.activeGroup, preserveFocus); + this._webviewService.revealWebview(webview, targetGroup || this._editorGroupService.activeGroup, showOptions.preserveFocus); } public $postMessage(handle: WebviewPanelHandle, message: any): TPromise { @@ -185,9 +190,12 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv onDidClickLink: uri => this.onDidClickLink(handle, uri), onMessage: message => this._proxy.$onMessage(handle, message), onDispose: () => { + const cleanUp = () => { + this._webviews.delete(handle); + }; this._proxy.$onDidDisposeWebviewPanel(handle).then( - () => this._webviews.delete(handle), - () => this._webviews.delete(handle)); + cleanUp, + cleanUp); } }; } @@ -297,3 +305,16 @@ function reviveWebviewOptions(options: WebviewInputOptions): WebviewInputOptions localResourceRoots: Array.isArray(options.localResourceRoots) ? options.localResourceRoots.map(URI.revive) : undefined }; } + +function reviveWebviewIcon( + value: { light: UriComponents, dark: UriComponents } | undefined +): { light: URI, dark: URI } | undefined { + if (!value) { + return undefined; + } + + return { + light: URI.revive(value.light), + dark: URI.revive(value.dark) + }; +} \ No newline at end of file diff --git a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts index 2a3635b457d..e797ba2ba0b 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts @@ -11,7 +11,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IFileMatch, IFolderQuery, IPatternInfo, IQueryOptions, ISearchConfiguration, ISearchQuery, ISearchService, QueryType } from 'vs/platform/search/common/search'; +import { IFolderQuery, IPatternInfo, IQueryOptions, ISearchConfiguration, ISearchQuery, ISearchService, QueryType, ISearchProgressItem } from 'vs/platform/search/common/search'; import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; @@ -20,6 +20,9 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; import { ExtHostContext, ExtHostWorkspaceShape, IExtHostContext, MainContext, MainThreadWorkspaceShape } from '../node/extHost.protocol'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { IWindowService } from 'vs/platform/windows/common/windows'; @extHostNamedCustomer(MainContext.MainThreadWorkspace) export class MainThreadWorkspace implements MainThreadWorkspaceShape { @@ -162,35 +165,36 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { return search; } - $startTextSearch(pattern: IPatternInfo, options: IQueryOptions, requestId: number): TPromise { + $startTextSearch(pattern: IPatternInfo, options: IQueryOptions, requestId: number): TPromise { const workspace = this._contextService.getWorkspace(); const folders = workspace.folders.map(folder => folder.uri); const queryBuilder = this._instantiationService.createInstance(QueryBuilder); const query = queryBuilder.text(pattern, folders, options); - return new TPromise((resolve, reject) => { - const search = this._searchService.search(query).then( - () => { - delete this._activeSearches[requestId]; - resolve(null); - }, - err => { - delete this._activeSearches[requestId]; - if (!isPromiseCanceledError(err)) { - reject(TPromise.wrapError(err)); - } + const onProgress = (p: ISearchProgressItem) => { + if (p.lineMatches) { + this._proxy.$handleTextSearchResult(p, requestId); + } + }; - return undefined; - }, - p => { - if (p.lineMatches) { - this._proxy.$handleTextSearchResult(p, requestId); - } - }); + const search = this._searchService.search(query, onProgress).then( + () => { + delete this._activeSearches[requestId]; + return null; + }, + err => { + delete this._activeSearches[requestId]; + if (!isPromiseCanceledError(err)) { + return TPromise.wrapError(err); + } - this._activeSearches[requestId] = search; - }); + return undefined; + }); + + this._activeSearches[requestId] = search; + + return search; } $cancelSearch(requestId: number): Thenable { @@ -212,8 +216,18 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { } } -CommandsRegistry.registerCommand('_workbench.enterWorkspace', function (accessor: ServicesAccessor, workspace: URI) { +CommandsRegistry.registerCommand('_workbench.enterWorkspace', async function (accessor: ServicesAccessor, workspace: URI, disableExtensions: string[]) { const workspaceEditingService = accessor.get(IWorkspaceEditingService); + const extensionService = accessor.get(IExtensionService); + const windowService = accessor.get(IWindowService); + + if (disableExtensions && disableExtensions.length) { + const runningExtensions = await extensionService.getExtensions(); + // If requested extension to disable is running, then reload window with given workspace + if (disableExtensions && runningExtensions.some(runningExtension => disableExtensions.some(id => areSameExtensions({ id }, { id: runningExtension.id })))) { + return windowService.openWindow([URI.file(workspace.fsPath)], { args: { _: [], 'disable-extension': disableExtensions } }); + } + } return workspaceEditingService.enterWorkspace(workspace.fsPath); }); \ No newline at end of file diff --git a/src/vs/workbench/api/node/apiCommands.ts b/src/vs/workbench/api/node/apiCommands.ts index 049f955bb8b..d04d6b2334c 100644 --- a/src/vs/workbench/api/node/apiCommands.ts +++ b/src/vs/workbench/api/node/apiCommands.ts @@ -5,6 +5,7 @@ 'use strict'; import URI from 'vs/base/common/uri'; +import { isMalformedFileUri } from 'vs/base/common/resources'; import * as vscode from 'vscode'; import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters'; import { CommandsRegistry, ICommandService, ICommandHandler } from 'vs/platform/commands/common/commands'; @@ -48,8 +49,14 @@ export class OpenFolderAPICommand { if (!uri) { return executor.executeCommand('_files.pickFolderAndOpen', forceNewWindow); } + let correctedUri = isMalformedFileUri(uri); + if (correctedUri) { + // workaround for #55916 and #55891, will be removed in 1.28 + console.warn(`'vscode.openFolder' command invoked with an invalid URI (file:// scheme missing): '${uri}'. Converted to a 'file://' URI: ${correctedUri}`); + uri = correctedUri; + } - return executor.executeCommand('_files.windowOpen', [uri.fsPath], forceNewWindow); + return executor.executeCommand('_files.windowOpen', [uri], forceNewWindow); } } CommandsRegistry.registerCommand(OpenFolderAPICommand.ID, adjustHandler(OpenFolderAPICommand.execute)); diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 41d84006f0f..bc631fe2d4d 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -110,11 +110,11 @@ export function createApiFactory( const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostLogService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadTextEditors))); const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors)); const extHostCommands = rpcProtocol.set(ExtHostContext.ExtHostCommands, new ExtHostCommands(rpcProtocol, extHostHeapService, extHostLogService)); - const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands)); + const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService)); rpcProtocol.set(ExtHostContext.ExtHostWorkspace, extHostWorkspace); rpcProtocol.set(ExtHostContext.ExtHostConfiguration, extHostConfiguration); const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol)); - const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, schemeTransformer, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics)); + const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, schemeTransformer, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics, extHostLogService)); const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures)); const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostDocumentsAndEditors)); const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands)); @@ -262,7 +262,7 @@ export function createApiFactory( return score(typeConverters.LanguageSelector.from(selector), document.uri, document.languageId, true); }, registerCodeActionsProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable { - return extHostLanguageFeatures.registerCodeActionProvider(checkSelector(selector), provider, metadata); + return extHostLanguageFeatures.registerCodeActionProvider(checkSelector(selector), provider, extension, metadata); }, registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable { return extHostLanguageFeatures.registerCodeLensProvider(checkSelector(selector), provider); @@ -425,7 +425,7 @@ export function createApiFactory( return extHostOutputService.createOutputChannel(name); }, createWebviewPanel(viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, options: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel { - return extHostWebviews.createWebview(viewType, title, showOptions, options, extension.extensionLocation); + return extHostWebviews.createWebview(extension.extensionLocation, viewType, title, showOptions, options); }, createTerminal(nameOrOptions: vscode.TerminalOptions | string, shellPath?: string, shellArgs?: string[]): vscode.Terminal { if (typeof nameOrOptions === 'object') { @@ -455,17 +455,12 @@ export function createApiFactory( registerUriHandler(handler: vscode.UriHandler) { return extHostUrls.registerUriHandler(extension.id, handler); }, - get quickInputBackButton() { - return proposedApiFunction(extension, (): vscode.QuickInputButton => { - return extHostQuickOpen.backButton; - })(); - }, - createQuickPick: proposedApiFunction(extension, (): vscode.QuickPick => { + createQuickPick(): vscode.QuickPick { return extHostQuickOpen.createQuickPick(extension.id); - }), - createInputBox: proposedApiFunction(extension, (): vscode.InputBox => { + }, + createInputBox(): vscode.InputBox { return extHostQuickOpen.createInputBox(extension.id); - }), + }, }; // namespace: workspace @@ -582,8 +577,18 @@ export function createApiFactory( registerFileSystemProvider(scheme, provider, options) { return extHostFileSystem.registerFileSystemProvider(scheme, provider, options); }, - registerSearchProvider: proposedApiFunction(extension, (scheme, provider) => { - return extHostSearch.registerSearchProvider(scheme, provider); + registerFileSearchProvider: proposedApiFunction(extension, (scheme, provider) => { + return extHostSearch.registerFileSearchProvider(scheme, provider); + }), + registerSearchProvider: proposedApiFunction(extension, () => { + // Temp for live share in Insiders + return { dispose: () => { } }; + }), + registerTextSearchProvider: proposedApiFunction(extension, (scheme, provider) => { + return extHostSearch.registerTextSearchProvider(scheme, provider); + }), + registerFileIndexProvider: proposedApiFunction(extension, (scheme, provider) => { + return extHostSearch.registerFileIndexProvider(scheme, provider); }), registerDocumentCommentProvider: proposedApiFunction(extension, (provider: vscode.DocumentCommentProvider) => { return exthostCommentProviders.registerDocumentCommentProvider(provider); @@ -722,6 +727,7 @@ export function createApiFactory( OverviewRulerLane: OverviewRulerLane, ParameterInformation: extHostTypes.ParameterInformation, Position: extHostTypes.Position, + QuickInputButtons: extHostTypes.QuickInputButtons, Range: extHostTypes.Range, Selection: extHostTypes.Selection, SignatureHelp: extHostTypes.SignatureHelp, diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index c65fd9cf161..0afcb94f377 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -46,7 +46,6 @@ export interface IEnvironment { isExtensionDevelopmentDebug: boolean; appRoot: string; appSettingsHome: string; - disableExtensions: boolean; extensionDevelopmentPath: string; extensionTestsPath: string; } @@ -426,11 +425,17 @@ export interface MainThreadTelemetryShape extends IDisposable { export type WebviewPanelHandle = string; +export interface WebviewPanelShowOptions { + readonly viewColumn?: EditorViewColumn; + readonly preserveFocus?: boolean; +} + export interface MainThreadWebviewsShape extends IDisposable { - $createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, viewOptions: { viewColumn: EditorViewColumn, preserveFocus: boolean }, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionLocation: UriComponents): void; + $createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionLocation: UriComponents): void; $disposeWebview(handle: WebviewPanelHandle): void; - $reveal(handle: WebviewPanelHandle, viewColumn: EditorViewColumn | null, preserveFocus: boolean): void; + $reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void; $setTitle(handle: WebviewPanelHandle, value: string): void; + $setIconPath(handle: WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents } | undefined): void; $setHtml(handle: WebviewPanelHandle, value: string): void; $setOptions(handle: WebviewPanelHandle, options: vscode.WebviewOptions): void; $postMessage(handle: WebviewPanelHandle, value: any): Thenable; @@ -481,7 +486,9 @@ export interface MainThreadFileSystemShape extends IDisposable { } export interface MainThreadSearchShape extends IDisposable { - $registerSearchProvider(handle: number, scheme: string): void; + $registerFileSearchProvider(handle: number, scheme: string): void; + $registerTextSearchProvider(handle: number, scheme: string): void; + $registerFileIndexProvider(handle: number, scheme: string): void; $unregisterProvider(handle: number): void; $handleFileMatch(handle: number, session: number, data: UriComponents[]): void; $handleTextMatch(handle: number, session: number, data: IRawFileMatch2[]): void; @@ -682,7 +689,7 @@ export interface ExtHostFileSystemShape { export interface ExtHostSearchShape { $provideFileSearchResults(handle: number, session: number, query: IRawSearchQuery): TPromise; - $clearCache(handle: number, cacheKey: string): TPromise; + $clearCache(cacheKey: string): TPromise; $provideTextSearchResults(handle: number, session: number, pattern: IPatternInfo, query: IRawSearchQuery): TPromise; } @@ -812,8 +819,8 @@ export interface ExtHostLanguageFeaturesShape { $provideCodeLenses(handle: number, resource: UriComponents): TPromise; $resolveCodeLens(handle: number, resource: UriComponents, symbol: modes.ICodeLensSymbol): TPromise; $provideDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise; - $provideImplementation(handle: number, resource: UriComponents, position: IPosition): TPromise; - $provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise; + $provideImplementation(handle: number, resource: UriComponents, position: IPosition): TPromise; + $provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise; $provideHover(handle: number, resource: UriComponents, position: IPosition): TPromise; $provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition): TPromise; $provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext): TPromise; @@ -875,6 +882,7 @@ export interface ExtHostSCMShape { $onInputBoxValueChange(sourceControlHandle: number, value: string): TPromise; $executeResourceCommand(sourceControlHandle: number, groupHandle: number, handle: number): TPromise; $validateInput(sourceControlHandle: number, value: string, cursorPosition: number): TPromise<[string, number] | undefined>; + $setSelectedSourceControls(selectedSourceControlHandles: number[]): TPromise; } export interface ExtHostTaskShape { diff --git a/src/vs/workbench/api/node/extHostFileSystem.ts b/src/vs/workbench/api/node/extHostFileSystem.ts index 1b1d65efcea..4c29149c845 100644 --- a/src/vs/workbench/api/node/extHostFileSystem.ts +++ b/src/vs/workbench/api/node/extHostFileSystem.ts @@ -9,7 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { MainContext, IMainContext, ExtHostFileSystemShape, MainThreadFileSystemShape, IFileChangeDto } from './extHost.protocol'; import * as vscode from 'vscode'; import * as files from 'vs/platform/files/common/files'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { asWinJsPromise } from 'vs/base/common/async'; import { values } from 'vs/base/common/map'; import { Range, FileChangeType } from 'vs/workbench/api/node/extHostTypes'; @@ -132,15 +132,13 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { this._proxy.$onFileSystemChange(handle, mapped); }); - return { - dispose: () => { - subscription.dispose(); - this._linkProvider.delete(scheme); - this._usedSchemes.delete(scheme); - this._fsProvider.delete(handle); - this._proxy.$unregisterProvider(handle); - } - }; + return toDisposable(() => { + subscription.dispose(); + this._linkProvider.delete(scheme); + this._usedSchemes.delete(scheme); + this._fsProvider.delete(handle); + this._proxy.$unregisterProvider(handle); + }); } private static _asIStat(stat: vscode.FileStat): files.IStat { @@ -148,11 +146,11 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { return { type, ctime, mtime, size }; } - $stat(handle: number, resource: UriComponents): TPromise { + $stat(handle: number, resource: UriComponents): TPromise { return asWinJsPromise(() => this._fsProvider.get(handle).stat(URI.revive(resource))).then(ExtHostFileSystem._asIStat); } - $readdir(handle: number, resource: UriComponents): TPromise<[string, files.FileType][], any> { + $readdir(handle: number, resource: UriComponents): TPromise<[string, files.FileType][]> { return asWinJsPromise(() => this._fsProvider.get(handle).readDirectory(URI.revive(resource))); } @@ -164,23 +162,23 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { }); } - $writeFile(handle: number, resource: UriComponents, base64Content: string, opts: files.FileWriteOptions): TPromise { + $writeFile(handle: number, resource: UriComponents, base64Content: string, opts: files.FileWriteOptions): TPromise { return asWinJsPromise(() => this._fsProvider.get(handle).writeFile(URI.revive(resource), Buffer.from(base64Content, 'base64'), opts)); } - $delete(handle: number, resource: UriComponents, opts: files.FileDeleteOptions): TPromise { + $delete(handle: number, resource: UriComponents, opts: files.FileDeleteOptions): TPromise { return asWinJsPromise(() => this._fsProvider.get(handle).delete(URI.revive(resource), opts)); } - $rename(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): TPromise { + $rename(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): TPromise { return asWinJsPromise(() => this._fsProvider.get(handle).rename(URI.revive(oldUri), URI.revive(newUri), opts)); } - $copy(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): TPromise { + $copy(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): TPromise { return asWinJsPromise(() => this._fsProvider.get(handle).copy(URI.revive(oldUri), URI.revive(newUri), opts)); } - $mkdir(handle: number, resource: UriComponents): TPromise { + $mkdir(handle: number, resource: UriComponents): TPromise { return asWinJsPromise(() => this._fsProvider.get(handle).createDirectory(URI.revive(resource))); } diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index f562cb6d6fe..1742acf2378 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -25,6 +25,7 @@ import { isFalsyOrEmpty } from 'vs/base/common/arrays'; import { isObject } from 'vs/base/common/types'; import { ISelection, Selection } from 'vs/editor/common/core/selection'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; +import { ILogService } from 'vs/platform/log/common/log'; // --- adapter @@ -143,6 +144,15 @@ class CodeLensAdapter { } } +function convertToDefinitionLinks(value: vscode.Definition): modes.DefinitionLink[] { + if (Array.isArray(value)) { + return (value as (vscode.DefinitionLink | vscode.Location)[]).map(typeConvert.DefinitionLink.from); + } else if (value) { + return [typeConvert.DefinitionLink.from(value)]; + } + return undefined; +} + class DefinitionAdapter { constructor( @@ -153,29 +163,7 @@ class DefinitionAdapter { provideDefinition(resource: URI, position: IPosition): TPromise { let doc = this._documents.getDocumentData(resource).document; let pos = typeConvert.Position.to(position); - - return asWinJsPromise(token => this._provider.provideDefinition2 ? this._provider.provideDefinition2(doc, pos, token) : this._provider.provideDefinition(doc, pos, token)).then((value): modes.DefinitionLink[] => { - if (Array.isArray(value)) { - return (value as (vscode.DefinitionLink | vscode.Location)[]).map(x => DefinitionAdapter.convertDefinitionLink(x)); - } else if (value) { - return [DefinitionAdapter.convertDefinitionLink(value)]; - } - return undefined; - }); - } - - private static convertDefinitionLink(value: vscode.Location | vscode.DefinitionLink): modes.DefinitionLink { - const definitionLink = value; - return { - origin: definitionLink.origin - ? typeConvert.Range.from(definitionLink.origin) - : undefined, - uri: value.uri, - range: typeConvert.Range.from(value.range), - selectionRange: definitionLink.selectionRange - ? typeConvert.Range.from(definitionLink.selectionRange) - : undefined, - }; + return asWinJsPromise(token => this._provider.provideDefinition(doc, pos, token)).then(convertToDefinitionLinks); } } @@ -186,17 +174,10 @@ class ImplementationAdapter { private readonly _provider: vscode.ImplementationProvider ) { } - provideImplementation(resource: URI, position: IPosition): TPromise { + provideImplementation(resource: URI, position: IPosition): TPromise { let doc = this._documents.getDocumentData(resource).document; let pos = typeConvert.Position.to(position); - return asWinJsPromise(token => this._provider.provideImplementation(doc, pos, token)).then(value => { - if (Array.isArray(value)) { - return value.map(typeConvert.location.from); - } else if (value) { - return typeConvert.location.from(value); - } - return undefined; - }); + return asWinJsPromise(token => this._provider.provideImplementation(doc, pos, token)).then(convertToDefinitionLinks); } } @@ -207,17 +188,10 @@ class TypeDefinitionAdapter { private readonly _provider: vscode.TypeDefinitionProvider ) { } - provideTypeDefinition(resource: URI, position: IPosition): TPromise { + provideTypeDefinition(resource: URI, position: IPosition): TPromise { const doc = this._documents.getDocumentData(resource).document; const pos = typeConvert.Position.to(position); - return asWinJsPromise(token => this._provider.provideTypeDefinition(doc, pos, token)).then(value => { - if (Array.isArray(value)) { - return value.map(typeConvert.location.from); - } else if (value) { - return typeConvert.location.from(value); - } - return undefined; - }); + return asWinJsPromise(token => this._provider.provideTypeDefinition(doc, pos, token)).then(convertToDefinitionLinks); } } @@ -300,7 +274,9 @@ class CodeActionAdapter { private readonly _documents: ExtHostDocuments, private readonly _commands: CommandsConverter, private readonly _diagnostics: ExtHostDiagnostics, - private readonly _provider: vscode.CodeActionProvider + private readonly _provider: vscode.CodeActionProvider, + private readonly _logService: ILogService, + private readonly _extensionId: string ) { } provideCodeActions(resource: URI, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext): TPromise { @@ -341,6 +317,14 @@ class CodeActionAdapter { command: this._commands.toInternal(candidate), }); } else { + if (codeActionContext.only) { + if (!candidate.kind) { + this._logService.warn(`${this._extensionId} - Code actions of kind '${codeActionContext.only.value} 'requested but returned code action does not have a 'kind'. Code action will be dropped. Please set 'CodeAction.kind'.`); + } else if (!codeActionContext.only.contains(candidate.kind)) { + this._logService.warn(`${this._extensionId} -Code actions of kind '${codeActionContext.only.value} 'requested but returned code action is of kind '${candidate.kind.value}'. Code action will be dropped. Please check 'CodeActionContext.only' to only return requested code actions.`); + } + } + // new school: convert code action result.push({ title: candidate.title, @@ -865,6 +849,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { private _heapService: ExtHostHeapService; private _diagnostics: ExtHostDiagnostics; private _adapter = new Map(); + private readonly _logService: ILogService; constructor( mainContext: IMainContext, @@ -872,7 +857,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { documents: ExtHostDocuments, commands: ExtHostCommands, heapMonitor: ExtHostHeapService, - diagnostics: ExtHostDiagnostics + diagnostics: ExtHostDiagnostics, + logService: ILogService ) { this._schemeTransformer = schemeTransformer; this._proxy = mainContext.getProxy(MainContext.MainThreadLanguageFeatures); @@ -880,6 +866,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { this._commands = commands; this._heapService = heapMonitor; this._diagnostics = diagnostics; + this._logService = logService; } private _transformDocumentSelector(selector: vscode.DocumentSelector): ISerializedDocumentFilter[] { @@ -999,7 +986,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._createDisposable(handle); } - $provideImplementation(handle: number, resource: UriComponents, position: IPosition): TPromise { + $provideImplementation(handle: number, resource: UriComponents, position: IPosition): TPromise { return this._withAdapter(handle, ImplementationAdapter, adapter => adapter.provideImplementation(URI.revive(resource), position)); } @@ -1009,7 +996,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._createDisposable(handle); } - $provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise { + $provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise { return this._withAdapter(handle, TypeDefinitionAdapter, adapter => adapter.provideTypeDefinition(URI.revive(resource), position)); } @@ -1051,8 +1038,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { // --- quick fix - registerCodeActionProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable { - const handle = this._addNewAdapter(new CodeActionAdapter(this._documents, this._commands.converter, this._diagnostics, provider)); + registerCodeActionProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, extension?: IExtensionDescription, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable { + const handle = this._addNewAdapter(new CodeActionAdapter(this._documents, this._commands.converter, this._diagnostics, provider, this._logService, extension ? extension.id : '')); this._proxy.$registerQuickFixSupport(handle, this._transformDocumentSelector(selector), metadata && metadata.providedCodeActionKinds ? metadata.providedCodeActionKinds.map(kind => kind.value) : undefined); return this._createDisposable(handle); } diff --git a/src/vs/workbench/api/node/extHostQuickOpen.ts b/src/vs/workbench/api/node/extHostQuickOpen.ts index 43c5f784178..29928df44f9 100644 --- a/src/vs/workbench/api/node/extHostQuickOpen.ts +++ b/src/vs/workbench/api/node/extHostQuickOpen.ts @@ -14,9 +14,7 @@ import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; import { InputBox, InputBoxOptions, QuickInput, QuickInputButton, QuickPick, QuickPickItem, QuickPickOptions, WorkspaceFolder, WorkspaceFolderPickOptions } from 'vscode'; import { ExtHostQuickOpenShape, IMainContext, MainContext, MainThreadQuickOpenShape, TransferQuickPickItems, TransferQuickInput, TransferQuickInputButton } from './extHost.protocol'; import URI from 'vs/base/common/uri'; -import { ThemeIcon } from 'vs/workbench/api/node/extHostTypes'; - -const backButton: QuickInputButton = { iconPath: 'back.svg' }; +import { ThemeIcon, QuickInputButtons } from 'vs/workbench/api/node/extHostTypes'; export type Item = string | QuickPickItem; @@ -153,8 +151,6 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { // ---- QuickInput - backButton = backButton; - createQuickPick(extensionId: string): QuickPick { const session = new ExtHostQuickPick(this._proxy, extensionId, () => this._sessions.delete(session._id)); this._sessions.set(session._id, session); @@ -328,14 +324,14 @@ class ExtHostQuickInput implements QuickInput { this._buttons = buttons.slice(); this._handlesToButtons.clear(); buttons.forEach((button, i) => { - const handle = button === backButton ? -1 : i; + const handle = button === QuickInputButtons.Back ? -1 : i; this._handlesToButtons.set(handle, button); }); this.update({ buttons: buttons.map((button, i) => ({ iconPath: getIconUris(button.iconPath), tooltip: button.tooltip, - handle: button === backButton ? -1 : i, + handle: button === QuickInputButtons.Back ? -1 : i, })) }); } diff --git a/src/vs/workbench/api/node/extHostSCM.ts b/src/vs/workbench/api/node/extHostSCM.ts index eeafdc4a416..2ce26e50530 100644 --- a/src/vs/workbench/api/node/extHostSCM.ts +++ b/src/vs/workbench/api/node/extHostSCM.ts @@ -395,6 +395,15 @@ class ExtHostSourceControl implements vscode.SourceControl { this._proxy.$updateSourceControl(this.handle, { statusBarCommands: internal }); } + private _selected: boolean = false; + + get selected(): boolean { + return this._selected; + } + + private _onDidChangeSelection = new Emitter(); + readonly onDidChangeSelection = this._onDidChangeSelection.event; + private handle: number = ExtHostSourceControl._handlePool++; constructor( @@ -454,6 +463,11 @@ class ExtHostSourceControl implements vscode.SourceControl { return this._groups.get(handle); } + setSelectionState(selected: boolean): void { + this._selected = selected; + this._onDidChangeSelection.fire(selected); + } + dispose(): void { this._groups.forEach(group => group.dispose()); this._proxy.$unregisterSourceControl(this.handle); @@ -471,6 +485,8 @@ export class ExtHostSCM implements ExtHostSCMShape { private _onDidChangeActiveProvider = new Emitter(); get onDidChangeActiveProvider(): Event { return this._onDidChangeActiveProvider.event; } + private _selectedSourceControlHandles = new Set(); + constructor( mainContext: IMainContext, private _commands: ExtHostCommands, @@ -607,4 +623,41 @@ export class ExtHostSCM implements ExtHostSCMShape { return TPromise.as<[string, number]>([result.message, result.type]); }); } + + $setSelectedSourceControls(selectedSourceControlHandles: number[]): TPromise { + this.logService.trace('ExtHostSCM#$setSelectedSourceControls', selectedSourceControlHandles); + + const set = new Set(); + + for (const handle of selectedSourceControlHandles) { + set.add(handle); + } + + set.forEach(handle => { + if (!this._selectedSourceControlHandles.has(handle)) { + const sourceControl = this._sourceControls.get(handle); + + if (!sourceControl) { + return; + } + + sourceControl.setSelectionState(true); + } + }); + + this._selectedSourceControlHandles.forEach(handle => { + if (!set.has(handle)) { + const sourceControl = this._sourceControls.get(handle); + + if (!sourceControl) { + return; + } + + sourceControl.setSelectionState(false); + } + }); + + this._selectedSourceControlHandles = set; + return TPromise.as(null); + } } diff --git a/src/vs/workbench/api/node/extHostSearch.fileIndex.ts b/src/vs/workbench/api/node/extHostSearch.fileIndex.ts new file mode 100644 index 00000000000..90b8a578f1c --- /dev/null +++ b/src/vs/workbench/api/node/extHostSearch.fileIndex.ts @@ -0,0 +1,659 @@ +/*--------------------------------------------------------------------------------------------- + * 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 path from 'path'; +import * as arrays from 'vs/base/common/arrays'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; +import * as glob from 'vs/base/common/glob'; +import * as resources from 'vs/base/common/resources'; +import * as strings from 'vs/base/common/strings'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { compareItemsByScore, IItemAccessor, prepareQuery, ScorerCache } from 'vs/base/parts/quickopen/common/quickOpenScorer'; +import { IFileMatch, IFolderQuery, IRawSearchQuery, ISearchCompleteStats, ISearchQuery } from 'vs/platform/search/common/search'; +import * as vscode from 'vscode'; + +export interface IInternalFileMatch { + base: URI; + original?: URI; + relativePath?: string; // Not present for extraFiles or absolute path matches + basename: string; + size?: number; +} + +/** + * Computes the patterns that the provider handles. Discards sibling clauses and 'false' patterns + */ +export function resolvePatternsForProvider(globalPattern: glob.IExpression, folderPattern: glob.IExpression): string[] { + const merged = { + ...(globalPattern || {}), + ...(folderPattern || {}) + }; + + return Object.keys(merged) + .filter(key => { + const value = merged[key]; + return typeof value === 'boolean' && value; + }); +} + +export class QueryGlobTester { + + private _excludeExpression: glob.IExpression; + private _parsedExcludeExpression: glob.ParsedExpression; + + private _parsedIncludeExpression: glob.ParsedExpression; + + constructor(config: ISearchQuery, folderQuery: IFolderQuery) { + this._excludeExpression = { + ...(config.excludePattern || {}), + ...(folderQuery.excludePattern || {}) + }; + this._parsedExcludeExpression = glob.parse(this._excludeExpression); + + // Empty includeExpression means include nothing, so no {} shortcuts + let includeExpression: glob.IExpression = config.includePattern; + if (folderQuery.includePattern) { + if (includeExpression) { + includeExpression = { + ...includeExpression, + ...folderQuery.includePattern + }; + } else { + includeExpression = folderQuery.includePattern; + } + } + + if (includeExpression) { + this._parsedIncludeExpression = glob.parse(includeExpression); + } + } + + /** + * Guaranteed sync - siblingsFn should not return a promise. + */ + public includedInQuerySync(testPath: string, basename?: string, hasSibling?: (name: string) => boolean): boolean { + if (this._parsedExcludeExpression && this._parsedExcludeExpression(testPath, basename, hasSibling)) { + return false; + } + + if (this._parsedIncludeExpression && !this._parsedIncludeExpression(testPath, basename, hasSibling)) { + return false; + } + + return true; + } + + /** + * Guaranteed async. + */ + public includedInQuery(testPath: string, basename?: string, hasSibling?: (name: string) => boolean | TPromise): TPromise { + const excludeP = this._parsedExcludeExpression ? + TPromise.as(this._parsedExcludeExpression(testPath, basename, hasSibling)).then(result => !!result) : + TPromise.wrap(false); + + return excludeP.then(excluded => { + if (excluded) { + return false; + } + + return this._parsedIncludeExpression ? + TPromise.as(this._parsedIncludeExpression(testPath, basename, hasSibling)).then(result => !!result) : + TPromise.wrap(true); + }).then(included => { + return included; + }); + } + + public hasSiblingExcludeClauses(): boolean { + return hasSiblingClauses(this._excludeExpression); + } +} + +function hasSiblingClauses(pattern: glob.IExpression): boolean { + for (let key in pattern) { + if (typeof pattern[key] !== 'boolean') { + return true; + } + } + + return false; +} + +export interface IDirectoryEntry { + base: URI; + relativePath: string; + basename: string; +} + +export interface IDirectoryTree { + rootEntries: IDirectoryEntry[]; + pathToEntries: { [relativePath: string]: IDirectoryEntry[] }; +} + +// ??? +interface IInternalSearchComplete { + limitHit: boolean; + results: IInternalFileMatch[]; +} + +export class FileIndexSearchEngine { + private filePattern: string; + private normalizedFilePatternLowercase: string; + private includePattern: glob.ParsedExpression; + private maxResults: number; + private exists: boolean; + private isLimitHit: boolean; + private resultCount: number; + private isCanceled: boolean; + + private activeCancellationTokens: Set; + + private globalExcludePattern: glob.ParsedExpression; + + constructor(private config: ISearchQuery, private provider: vscode.FileIndexProvider) { + this.filePattern = config.filePattern; + this.includePattern = config.includePattern && glob.parse(config.includePattern); + this.maxResults = config.maxResults || null; + this.exists = config.exists; + this.resultCount = 0; + this.isLimitHit = false; + this.activeCancellationTokens = new Set(); + + if (this.filePattern) { + this.normalizedFilePatternLowercase = strings.stripWildcards(this.filePattern).toLowerCase(); + } + + this.globalExcludePattern = config.excludePattern && glob.parse(config.excludePattern); + } + + public cancel(): void { + this.isCanceled = true; + this.activeCancellationTokens.forEach(t => t.cancel()); + this.activeCancellationTokens = new Set(); + } + + public search(_onResult: (match: IInternalFileMatch) => void): TPromise<{ isLimitHit: boolean }> { + if (this.config.folderQueries.length !== 1) { + throw new Error('Searches just one folder'); + } + + const folderQuery = this.config.folderQueries[0]; + + return new TPromise<{ isLimitHit: boolean }>((resolve, reject) => { + const onResult = (match: IInternalFileMatch) => { + this.resultCount++; + _onResult(match); + }; + + if (this.isCanceled) { + return resolve({ isLimitHit: this.isLimitHit }); + } + + // For each extra file + if (this.config.extraFileResources) { + this.config.extraFileResources + .forEach(extraFile => { + const extraFileStr = extraFile.toString(); // ? + const basename = path.basename(extraFileStr); + if (this.globalExcludePattern && this.globalExcludePattern(extraFileStr, basename)) { + return; // excluded + } + + // File: Check for match on file pattern and include pattern + this.matchFile(onResult, { base: extraFile, basename }); + }); + } + + return this.searchInFolder(folderQuery, _onResult) + .then(() => { + resolve({ isLimitHit: this.isLimitHit }); + }, (errs: Error[]) => { + const errMsg = errs + .map(err => toErrorMessage(err)) + .filter(msg => !!msg)[0]; + + reject(new Error(errMsg)); + }); + }); + } + + private searchInFolder(fq: IFolderQuery, onResult: (match: IInternalFileMatch) => void): TPromise { + let cancellation = new CancellationTokenSource(); + return new TPromise((resolve, reject) => { + const options = this.getSearchOptionsForFolder(fq); + const tree = this.initDirectoryTree(); + + const queryTester = new QueryGlobTester(this.config, fq); + const noSiblingsClauses = !queryTester.hasSiblingExcludeClauses(); + + const onProviderResult = (uri: URI) => { + if (this.isCanceled) { + return; + } + + // TODO@rob - ??? + const relativePath = path.relative(fq.folder.path, uri.path); + if (noSiblingsClauses) { + const basename = path.basename(uri.path); + this.matchFile(onResult, { base: fq.folder, relativePath, basename, original: uri }); + + return; + } + + // TODO: Optimize siblings clauses with ripgrep here. + this.addDirectoryEntries(tree, fq.folder, relativePath, onResult); + }; + + new TPromise(resolve => process.nextTick(resolve)) + .then(() => { + this.activeCancellationTokens.add(cancellation); + return this.provider.provideFileIndex(options, cancellation.token); + }) + .then(results => { + this.activeCancellationTokens.delete(cancellation); + if (this.isCanceled) { + return null; + } + + results.forEach(onProviderResult); + + this.matchDirectoryTree(tree, queryTester, onResult); + return null; + }).then( + () => { + cancellation.dispose(); + resolve(undefined); + }, + err => { + cancellation.dispose(); + reject(err); + }); + }); + } + + private getSearchOptionsForFolder(fq: IFolderQuery): vscode.FileIndexOptions { + const includes = resolvePatternsForProvider(this.config.includePattern, fq.includePattern); + const excludes = resolvePatternsForProvider(this.config.excludePattern, fq.excludePattern); + + return { + folder: fq.folder, + excludes, + includes, + useIgnoreFiles: !this.config.disregardIgnoreFiles, + followSymlinks: !this.config.ignoreSymlinks + }; + } + + private initDirectoryTree(): IDirectoryTree { + const tree: IDirectoryTree = { + rootEntries: [], + pathToEntries: Object.create(null) + }; + tree.pathToEntries['.'] = tree.rootEntries; + return tree; + } + + private addDirectoryEntries({ pathToEntries }: IDirectoryTree, base: URI, relativeFile: string, onResult: (result: IInternalFileMatch) => void) { + // Support relative paths to files from a root resource (ignores excludes) + if (relativeFile === this.filePattern) { + const basename = path.basename(this.filePattern); + this.matchFile(onResult, { base: base, relativePath: this.filePattern, basename }); + } + + function add(relativePath: string) { + const basename = path.basename(relativePath); + const dirname = path.dirname(relativePath); + let entries = pathToEntries[dirname]; + if (!entries) { + entries = pathToEntries[dirname] = []; + add(dirname); + } + entries.push({ + base, + relativePath, + basename + }); + } + + add(relativeFile); + } + + private matchDirectoryTree({ rootEntries, pathToEntries }: IDirectoryTree, queryTester: QueryGlobTester, onResult: (result: IInternalFileMatch) => void) { + const self = this; + const filePattern = this.filePattern; + function matchDirectory(entries: IDirectoryEntry[]) { + // self.directoriesWalked++; + for (let i = 0, n = entries.length; i < n; i++) { + const entry = entries[i]; + const { relativePath, basename } = entry; + + // Check exclude pattern + // If the user searches for the exact file name, we adjust the glob matching + // to ignore filtering by siblings because the user seems to know what she + // is searching for and we want to include the result in that case anyway + const hasSibling = glob.hasSiblingFn(() => entries.map(entry => entry.basename)); + if (!queryTester.includedInQuerySync(relativePath, basename, filePattern !== basename ? hasSibling : undefined)) { + continue; + } + + const sub = pathToEntries[relativePath]; + if (sub) { + matchDirectory(sub); + } else { + // self.filesWalked++; + if (relativePath === filePattern) { + continue; // ignore file if its path matches with the file pattern because that is already matched above + } + + self.matchFile(onResult, entry); + } + + if (self.isLimitHit) { + break; + } + } + } + matchDirectory(rootEntries); + } + + private matchFile(onResult: (result: IInternalFileMatch) => void, candidate: IInternalFileMatch): void { + if (this.isFilePatternMatch(candidate.relativePath) && (!this.includePattern || this.includePattern(candidate.relativePath, candidate.basename))) { + if (this.exists || (this.maxResults && this.resultCount >= this.maxResults)) { + this.isLimitHit = true; + this.cancel(); + } + + if (!this.isLimitHit) { + onResult(candidate); + } + } + } + + private isFilePatternMatch(path: string): boolean { + // Check for search pattern + if (this.filePattern) { + if (this.filePattern === '*') { + return true; // support the all-matching wildcard + } + + return strings.fuzzyContains(path, this.normalizedFilePatternLowercase); + } + + // No patterns means we match all + return true; + } +} + +export class FileIndexSearchManager { + + private static readonly BATCH_SIZE = 512; + + private caches: { [cacheKey: string]: Cache; } = Object.create(null); + + private readonly folderCacheKeys = new Map>(); + + public fileSearch(config: ISearchQuery, provider: vscode.FileIndexProvider, onBatch: (matches: IFileMatch[]) => void): TPromise { + if (config.sortByScore) { + let sortedSearch = this.trySortedSearchFromCache(config); + if (!sortedSearch) { + const engineConfig = config.maxResults ? + { + ...config, + ...{ maxResults: null } + } : + config; + + const engine = new FileIndexSearchEngine(engineConfig, provider); + sortedSearch = this.doSortedSearch(engine, config); + } + + return new TPromise((c, e) => { + sortedSearch.then(complete => { + this.sendAsBatches(complete.results, onBatch, FileIndexSearchManager.BATCH_SIZE); + c(complete); + }, e); + }, () => { + sortedSearch.cancel(); + }); + } + + const engine = new FileIndexSearchEngine(config, provider); + return this.doSearch(engine) + .then(complete => { + this.sendAsBatches(complete.results, onBatch, FileIndexSearchManager.BATCH_SIZE); + return { + limitHit: complete.limitHit + }; + }); + } + + private getFolderCacheKey(config: ISearchQuery): string { + const uri = config.folderQueries[0].folder.toString(); + const folderCacheKey = config.cacheKey && `${uri}_${config.cacheKey}`; + if (!this.folderCacheKeys.get(config.cacheKey)) { + this.folderCacheKeys.set(config.cacheKey, new Set()); + } + + this.folderCacheKeys.get(config.cacheKey).add(folderCacheKey); + + return folderCacheKey; + } + + private rawMatchToSearchItem(match: IInternalFileMatch): IFileMatch { + return { + resource: match.original || resources.joinPath(match.base, match.relativePath) + }; + } + + private doSortedSearch(engine: FileIndexSearchEngine, config: ISearchQuery): TPromise { + let searchPromise: TPromise; + let allResultsPromise = new TPromise((c, e) => { + searchPromise = this.doSearch(engine).then(c, e); + }, () => { + searchPromise.cancel(); + }); + + const folderCacheKey = this.getFolderCacheKey(config); + let cache: Cache; + if (folderCacheKey) { + cache = this.getOrCreateCache(folderCacheKey); + cache.resultsToSearchCache[config.filePattern] = allResultsPromise; + allResultsPromise.then(null, err => { + delete cache.resultsToSearchCache[config.filePattern]; + }); + allResultsPromise = this.preventCancellation(allResultsPromise); + } + + let chained: TPromise; + return new TPromise((c, e) => { + chained = allResultsPromise.then(complete => { + const scorerCache: ScorerCache = cache ? cache.scorerCache : Object.create(null); + return this.sortResults(config, complete.results, scorerCache) + .then(sortedResults => { + + c({ + limitHit: complete.limitHit || typeof config.maxResults === 'number' && complete.results.length > config.maxResults, // ?? + results: sortedResults + }); + }); + }, e); + }, () => { + chained.cancel(); + }); + } + + private getOrCreateCache(cacheKey: string): Cache { + const existing = this.caches[cacheKey]; + if (existing) { + return existing; + } + return this.caches[cacheKey] = new Cache(); + } + + private trySortedSearchFromCache(config: ISearchQuery): TPromise { + const folderCacheKey = this.getFolderCacheKey(config); + const cache = folderCacheKey && this.caches[folderCacheKey]; + if (!cache) { + return undefined; + } + + const cached = this.getResultsFromCache(cache, config.filePattern); + if (cached) { + let chained: TPromise; + return new TPromise((c, e) => { + chained = cached.then(complete => { + return this.sortResults(config, complete.results, cache.scorerCache) + .then(sortedResults => { + c({ + limitHit: complete.limitHit || typeof config.maxResults === 'number' && complete.results.length > config.maxResults, + results: sortedResults + }); + }); + }, e); + }, () => { + chained.cancel(); + }); + } + return undefined; + } + + private sortResults(config: IRawSearchQuery, results: IInternalFileMatch[], scorerCache: ScorerCache): TPromise { + // we use the same compare function that is used later when showing the results using fuzzy scoring + // this is very important because we are also limiting the number of results by config.maxResults + // and as such we want the top items to be included in this result set if the number of items + // exceeds config.maxResults. + const query = prepareQuery(config.filePattern); + const compare = (matchA: IInternalFileMatch, matchB: IInternalFileMatch) => compareItemsByScore(matchA, matchB, query, true, FileMatchItemAccessor, scorerCache); + + return arrays.topAsync(results, compare, config.maxResults, 10000); + } + + private sendAsBatches(rawMatches: IInternalFileMatch[], onBatch: (batch: IFileMatch[]) => void, batchSize: number) { + const serializedMatches = rawMatches.map(rawMatch => this.rawMatchToSearchItem(rawMatch)); + if (batchSize && batchSize > 0) { + for (let i = 0; i < serializedMatches.length; i += batchSize) { + onBatch(serializedMatches.slice(i, i + batchSize)); + } + } else { + onBatch(serializedMatches); + } + } + + private getResultsFromCache(cache: Cache, searchValue: string): TPromise { + if (path.isAbsolute(searchValue)) { + return null; // bypass cache if user looks up an absolute path where matching goes directly on disk + } + + // Find cache entries by prefix of search value + const hasPathSep = searchValue.indexOf(path.sep) >= 0; + let cached: TPromise; + for (let previousSearch in cache.resultsToSearchCache) { + + // If we narrow down, we might be able to reuse the cached results + if (strings.startsWith(searchValue, previousSearch)) { + if (hasPathSep && previousSearch.indexOf(path.sep) < 0) { + continue; // since a path character widens the search for potential more matches, require it in previous search too + } + + const c = cache.resultsToSearchCache[previousSearch]; + cached = this.preventCancellation(c); + break; + } + } + + if (!cached) { + return null; + } + + return new TPromise((c, e) => { + cached.then(complete => { + // Pattern match on results + let results: IInternalFileMatch[] = []; + const normalizedSearchValueLowercase = strings.stripWildcards(searchValue).toLowerCase(); + for (let i = 0; i < complete.results.length; i++) { + let entry = complete.results[i]; + + // Check if this entry is a match for the search value + if (!strings.fuzzyContains(entry.relativePath, normalizedSearchValueLowercase)) { + continue; + } + + results.push(entry); + } + + c({ + limitHit: complete.limitHit, + results + }); + }, e); + }, () => { + cached.cancel(); + }); + } + + private doSearch(engine: FileIndexSearchEngine): TPromise { + const results: IInternalFileMatch[] = []; + const onResult = match => results.push(match); + return new TPromise((c, e) => { + engine.search(onResult).then(result => { + c({ + limitHit: result.isLimitHit, + results + }); + }, e); + }, () => { + engine.cancel(); + }); + } + + public clearCache(cacheKey: string): TPromise { + if (!this.folderCacheKeys.has(cacheKey)) { + return TPromise.wrap(undefined); + } + + const expandedKeys = this.folderCacheKeys.get(cacheKey); + expandedKeys.forEach(key => delete this.caches[key]); + + this.folderCacheKeys.delete(cacheKey); + + return TPromise.as(undefined); + } + + private preventCancellation(promise: TPromise): TPromise { + return new TPromise((c, e) => { + // Allow for piled up cancellations to come through first. + process.nextTick(() => { + promise.then(c, e); + }); + }, () => { + // Do not propagate. + }); + } +} + +class Cache { + + public resultsToSearchCache: { [searchValue: string]: TPromise; } = Object.create(null); + + public scorerCache: ScorerCache = Object.create(null); +} + +const FileMatchItemAccessor = new class implements IItemAccessor { + + public getItemLabel(match: IInternalFileMatch): string { + return match.basename; // e.g. myFile.txt + } + + public getItemDescription(match: IInternalFileMatch): string { + return match.relativePath.substr(0, match.relativePath.length - match.basename.length - 1); // e.g. some/path/to/file + } + + public getItemPath(match: IInternalFileMatch): string { + return match.relativePath; // e.g. some/path/to/file/myFile.txt + } +}; diff --git a/src/vs/workbench/api/node/extHostSearch.ts b/src/vs/workbench/api/node/extHostSearch.ts index 7dca897dd92..47b7f055eee 100644 --- a/src/vs/workbench/api/node/extHostSearch.ts +++ b/src/vs/workbench/api/node/extHostSearch.ts @@ -10,11 +10,13 @@ import { toErrorMessage } from 'vs/base/common/errorMessage'; import * as glob from 'vs/base/common/glob'; import * as resources from 'vs/base/common/resources'; import URI, { UriComponents } from 'vs/base/common/uri'; -import { PPromise, TPromise } from 'vs/base/common/winjs.base'; +import { TPromise } from 'vs/base/common/winjs.base'; import * as extfs from 'vs/base/node/extfs'; import { IFileMatch, IFolderQuery, IPatternInfo, IRawSearchQuery, ISearchCompleteStats, ISearchQuery } from 'vs/platform/search/common/search'; import * as vscode from 'vscode'; import { ExtHostSearchShape, IMainContext, MainContext, MainThreadSearchShape } from './extHost.protocol'; +import { toDisposable } from 'vs/base/common/lifecycle'; +import { IInternalFileMatch, QueryGlobTester, resolvePatternsForProvider, IDirectoryTree, IDirectoryEntry, FileIndexSearchManager } from 'vs/workbench/api/node/extHostSearch.fileIndex'; export interface ISchemeTransformer { transformOutgoing(scheme: string): string; @@ -23,14 +25,18 @@ export interface ISchemeTransformer { export class ExtHostSearch implements ExtHostSearchShape { private readonly _proxy: MainThreadSearchShape; - private readonly _searchProvider = new Map(); + private readonly _fileSearchProvider = new Map(); + private readonly _textSearchProvider = new Map(); + private readonly _fileIndexProvider = new Map(); private _handlePool: number = 0; private _fileSearchManager: FileSearchManager; + private _fileIndexSearchManager: FileIndexSearchManager; constructor(mainContext: IMainContext, private _schemeTransformer: ISchemeTransformer, private _extfs = extfs) { this._proxy = mainContext.getProxy(MainContext.MainThreadSearch); this._fileSearchManager = new FileSearchManager(); + this._fileIndexSearchManager = new FileIndexSearchManager(); } private _transformScheme(scheme: string): string { @@ -40,76 +46,73 @@ export class ExtHostSearch implements ExtHostSearchShape { return scheme; } - registerSearchProvider(scheme: string, provider: vscode.SearchProvider) { + registerFileSearchProvider(scheme: string, provider: vscode.FileSearchProvider) { const handle = this._handlePool++; - this._searchProvider.set(handle, provider); - this._proxy.$registerSearchProvider(handle, this._transformScheme(scheme)); - return { - dispose: () => { - this._searchProvider.delete(handle); - this._proxy.$unregisterProvider(handle); - } - }; + this._fileSearchProvider.set(handle, provider); + this._proxy.$registerFileSearchProvider(handle, this._transformScheme(scheme)); + return toDisposable(() => { + this._fileSearchProvider.delete(handle); + this._proxy.$unregisterProvider(handle); + }); + } + + registerTextSearchProvider(scheme: string, provider: vscode.TextSearchProvider) { + const handle = this._handlePool++; + this._textSearchProvider.set(handle, provider); + this._proxy.$registerTextSearchProvider(handle, this._transformScheme(scheme)); + return toDisposable(() => { + this._textSearchProvider.delete(handle); + this._proxy.$unregisterProvider(handle); + }); + } + + registerFileIndexProvider(scheme: string, provider: vscode.FileIndexProvider) { + const handle = this._handlePool++; + this._fileIndexProvider.set(handle, provider); + this._proxy.$registerFileIndexProvider(handle, this._transformScheme(scheme)); + return toDisposable(() => { + this._fileSearchProvider.delete(handle); + this._proxy.$unregisterProvider(handle); // TODO@roblou - unregisterFileIndexProvider + }); } $provideFileSearchResults(handle: number, session: number, rawQuery: IRawSearchQuery): TPromise { - const provider = this._searchProvider.get(handle); - if (!provider.provideFileSearchResults) { - return TPromise.as(undefined); - } - + const provider = this._fileSearchProvider.get(handle); const query = reviveQuery(rawQuery); - return this._fileSearchManager.fileSearch(query, provider).then( - null, - null, - progress => { - this._proxy.$handleFileMatch(handle, session, progress.map(p => p.resource)); + if (provider) { + return this._fileSearchManager.fileSearch(query, provider, batch => { + this._proxy.$handleFileMatch(handle, session, batch.map(p => p.resource)); }); + } else { + const indexProvider = this._fileIndexProvider.get(handle); + if (indexProvider) { + return this._fileIndexSearchManager.fileSearch(query, indexProvider, batch => { + this._proxy.$handleFileMatch(handle, session, batch.map(p => p.resource)); + }); + } else { + throw new Error('something went wrong'); + } + } } - $clearCache(handle: number, cacheKey: string): TPromise { - const provider = this._searchProvider.get(handle); - if (!provider.clearCache) { - return TPromise.as(undefined); - } - - return TPromise.as( - this._fileSearchManager.clearCache(cacheKey, provider)); + $clearCache(cacheKey: string): TPromise { + // Actually called once per provider. + // Only relevant to file index search. + return this._fileIndexSearchManager.clearCache(cacheKey); } $provideTextSearchResults(handle: number, session: number, pattern: IPatternInfo, rawQuery: IRawSearchQuery): TPromise { - const provider = this._searchProvider.get(handle); + const provider = this._textSearchProvider.get(handle); if (!provider.provideTextSearchResults) { return TPromise.as(undefined); } const query = reviveQuery(rawQuery); const engine = new TextSearchEngine(pattern, query, provider, this._extfs); - return engine.search().then( - null, - null, - progress => { - this._proxy.$handleTextMatch(handle, session, progress); - }); + return engine.search(progress => this._proxy.$handleTextMatch(handle, session, progress)); } } -/** - * Computes the patterns that the provider handles. Discards sibling clauses and 'false' patterns - */ -function resolvePatternsForProvider(globalPattern: glob.IExpression, folderPattern: glob.IExpression): string[] { - const merged = { - ...(globalPattern || {}), - ...(folderPattern || {}) - }; - - return Object.keys(merged) - .filter(key => { - const value = merged[key]; - return typeof value === 'boolean' && value; - }); -} - function reviveQuery(rawQuery: IRawSearchQuery): ISearchQuery { return { ...rawQuery, @@ -262,107 +265,6 @@ class BatchedCollector { } } -interface IDirectoryEntry { - base: URI; - relativePath: string; - basename: string; -} - -interface IDirectoryTree { - rootEntries: IDirectoryEntry[]; - pathToEntries: { [relativePath: string]: IDirectoryEntry[] }; -} - -interface IInternalFileMatch { - base: URI; - relativePath?: string; // Not present for extraFiles or absolute path matches - basename: string; - size?: number; -} - -class QueryGlobTester { - - private _excludeExpression: glob.IExpression; - private _parsedExcludeExpression: glob.ParsedExpression; - - private _parsedIncludeExpression: glob.ParsedExpression; - - constructor(config: ISearchQuery, folderQuery: IFolderQuery) { - this._excludeExpression = { - ...(config.excludePattern || {}), - ...(folderQuery.excludePattern || {}) - }; - this._parsedExcludeExpression = glob.parse(this._excludeExpression); - - // Empty includeExpression means include nothing, so no {} shortcuts - let includeExpression: glob.IExpression = config.includePattern; - if (folderQuery.includePattern) { - if (includeExpression) { - includeExpression = { - ...includeExpression, - ...folderQuery.includePattern - }; - } else { - includeExpression = folderQuery.includePattern; - } - } - - if (includeExpression) { - this._parsedIncludeExpression = glob.parse(includeExpression); - } - } - - /** - * Guaranteed sync - siblingsFn should not return a promise. - */ - public includedInQuerySync(testPath: string, basename?: string, hasSibling?: (name: string) => boolean): boolean { - if (this._parsedExcludeExpression && this._parsedExcludeExpression(testPath, basename, hasSibling)) { - return false; - } - - if (this._parsedIncludeExpression && !this._parsedIncludeExpression(testPath, basename, hasSibling)) { - return false; - } - - return true; - } - - /** - * Guaranteed async. - */ - public includedInQuery(testPath: string, basename?: string, hasSibling?: (name: string) => boolean | TPromise): TPromise { - const excludeP = this._parsedExcludeExpression ? - TPromise.as(this._parsedExcludeExpression(testPath, basename, hasSibling)).then(result => !!result) : - TPromise.wrap(false); - - return excludeP.then(excluded => { - if (excluded) { - return false; - } - - return this._parsedIncludeExpression ? - TPromise.as(this._parsedIncludeExpression(testPath, basename, hasSibling)).then(result => !!result) : - TPromise.wrap(true); - }).then(included => { - return included; - }); - } - - public hasSiblingExcludeClauses(): boolean { - return hasSiblingClauses(this._excludeExpression); - } -} - -function hasSiblingClauses(pattern: glob.IExpression): boolean { - for (let key in pattern) { - if (typeof pattern[key] !== 'boolean') { - return true; - } - } - - return false; -} - class TextSearchEngine { private activeCancellationTokens = new Set(); @@ -372,7 +274,7 @@ class TextSearchEngine { private resultCount = 0; private isCanceled: boolean; - constructor(private pattern: IPatternInfo, private config: ISearchQuery, private provider: vscode.SearchProvider, private _extfs: typeof extfs) { + constructor(private pattern: IPatternInfo, private config: ISearchQuery, private provider: vscode.TextSearchProvider, private _extfs: typeof extfs) { } public cancel(): void { @@ -381,11 +283,11 @@ class TextSearchEngine { this.activeCancellationTokens = new Set(); } - public search(): PPromise<{ limitHit: boolean }, IFileMatch[]> { + public search(onProgress: (matches: IFileMatch[]) => void): TPromise<{ limitHit: boolean }> { const folderQueries = this.config.folderQueries; - return new PPromise<{ limitHit: boolean }, IFileMatch[]>((resolve, reject, _onResult) => { - this.collector = new TextSearchResultsCollector(_onResult); + return new TPromise<{ limitHit: boolean }>((resolve, reject) => { + this.collector = new TextSearchResultsCollector(onProgress); const onResult = (match: vscode.TextSearchResult, folderIdx: number) => { if (this.isCanceled) { @@ -404,8 +306,8 @@ class TextSearchEngine { }; // For each root folder - PPromise.join(folderQueries.map((fq, i) => { - return this.searchInFolder(fq).then(null, null, r => onResult(r, i)); + TPromise.join(folderQueries.map((fq, i) => { + return this.searchInFolder(fq, r => onResult(r, i)); })).then(() => { this.collector.flush(); resolve({ limitHit: this.isLimitHit }); @@ -419,9 +321,9 @@ class TextSearchEngine { }); } - private searchInFolder(folderQuery: IFolderQuery): PPromise { + private searchInFolder(folderQuery: IFolderQuery, onResult: (result: vscode.TextSearchResult) => void): TPromise { let cancellation = new CancellationTokenSource(); - return new PPromise((resolve, reject, onResult) => { + return new TPromise((resolve, reject) => { const queryTester = new QueryGlobTester(this.config, folderQuery); const testingPs = []; @@ -515,7 +417,7 @@ class FileSearchEngine { private globalExcludePattern: glob.ParsedExpression; - constructor(private config: ISearchQuery, private provider: vscode.SearchProvider) { + constructor(private config: ISearchQuery, private provider: vscode.FileSearchProvider) { this.filePattern = config.filePattern; this.includePattern = config.includePattern && glob.parse(config.includePattern); this.maxResults = config.maxResults || null; @@ -533,10 +435,10 @@ class FileSearchEngine { this.activeCancellationTokens = new Set(); } - public search(): PPromise { + public search(_onResult: (match: IInternalFileMatch) => void): TPromise { const folderQueries = this.config.folderQueries; - return new PPromise((resolve, reject, _onResult) => { + return new TPromise((resolve, reject) => { const onResult = (match: IInternalFileMatch) => { this.resultCount++; _onResult(match); @@ -544,7 +446,7 @@ class FileSearchEngine { // Support that the file pattern is a full path to a file that exists if (this.isCanceled) { - return resolve({ limitHit: this.isLimitHit, cacheKeys: [] }); + return resolve({ limitHit: this.isLimitHit }); } // For each extra file @@ -563,10 +465,10 @@ class FileSearchEngine { } // For each root folder - PPromise.join(folderQueries.map(fq => { - return this.searchInFolder(fq).then(null, null, onResult); - })).then(cacheKeys => { - resolve({ limitHit: this.isLimitHit, cacheKeys }); + TPromise.join(folderQueries.map(fq => { + return this.searchInFolder(fq, onResult); + })).then(() => { + resolve({ limitHit: this.isLimitHit }); }, (errs: Error[]) => { const errMsg = errs .map(err => toErrorMessage(err)) @@ -577,50 +479,47 @@ class FileSearchEngine { }); } - private searchInFolder(fq: IFolderQuery): PPromise { + private searchInFolder(fq: IFolderQuery, onResult: (match: IInternalFileMatch) => void): TPromise { let cancellation = new CancellationTokenSource(); - return new PPromise((resolve, reject, onResult) => { + return new TPromise((resolve, reject) => { const options = this.getSearchOptionsForFolder(fq); const tree = this.initDirectoryTree(); const queryTester = new QueryGlobTester(this.config, fq); const noSiblingsClauses = !queryTester.hasSiblingExcludeClauses(); - const onProviderResult = (result: URI) => { - if (this.isCanceled) { - return; - } - - const relativePath = path.relative(fq.folder.fsPath, result.fsPath); - - if (noSiblingsClauses) { - const basename = path.basename(result.fsPath); - this.matchFile(onResult, { base: fq.folder, relativePath, basename }); - - return; - } - - // TODO: Optimize siblings clauses with ripgrep here. - this.addDirectoryEntries(tree, fq.folder, relativePath, onResult); - }; - - let folderCacheKey: string; new TPromise(_resolve => process.nextTick(_resolve)) .then(() => { this.activeCancellationTokens.add(cancellation); - folderCacheKey = this.config.cacheKey && (this.config.cacheKey + '_' + fq.folder.fsPath); - return this.provider.provideFileSearchResults( { - pattern: this.config.filePattern || '', - cacheKey: folderCacheKey + pattern: this.config.filePattern || '' }, options, - { report: onProviderResult }, cancellation.token); }) - .then(() => { + .then(results => { + if (this.isCanceled) { + return; + } + + if (results) { + results.forEach(result => { + const relativePath = path.relative(fq.folder.fsPath, result.fsPath); + + if (noSiblingsClauses) { + const basename = path.basename(result.fsPath); + this.matchFile(onResult, { base: fq.folder, relativePath, basename }); + + return; + } + + // TODO: Optimize siblings clauses with ripgrep here. + this.addDirectoryEntries(tree, fq.folder, relativePath, onResult); + }); + } + this.activeCancellationTokens.delete(cancellation); if (this.isCanceled) { return null; @@ -631,7 +530,7 @@ class FileSearchEngine { }).then( () => { cancellation.dispose(); - resolve(folderCacheKey); + resolve(null); }, err => { cancellation.dispose(); @@ -740,34 +639,28 @@ class FileSearchEngine { interface IInternalSearchComplete { limitHit: boolean; - cacheKeys: string[]; } class FileSearchManager { private static readonly BATCH_SIZE = 512; - private readonly expandedCacheKeys = new Map(); - - fileSearch(config: ISearchQuery, provider: vscode.SearchProvider): PPromise { - let searchP: PPromise; - return new PPromise((c, e, p) => { + fileSearch(config: ISearchQuery, provider: vscode.FileSearchProvider, onBatch: (matches: IFileMatch[]) => void): TPromise { + let searchP: TPromise; + return new TPromise((c, e) => { const engine = new FileSearchEngine(config, provider); - searchP = this.doSearch(engine, FileSearchManager.BATCH_SIZE).then( - result => { - if (config.cacheKey) { - this.expandedCacheKeys.set(config.cacheKey, result.cacheKeys); - } + const onInternalResult = (batch: IInternalFileMatch[]) => { + onBatch(batch.map(m => this.rawMatchToSearchItem(m))); + }; + searchP = this.doSearch(engine, FileSearchManager.BATCH_SIZE, onInternalResult).then( + result => { c({ limitHit: result.limitHit }); }, - e, - progress => { - p(progress.map(m => this.rawMatchToSearchItem(m))); - }); + e); }, () => { if (searchP) { searchP.cancel(); @@ -775,44 +668,44 @@ class FileSearchManager { }); } - clearCache(cacheKey: string, provider: vscode.SearchProvider): void { - if (!this.expandedCacheKeys.has(cacheKey)) { - return; - } - - this.expandedCacheKeys.get(cacheKey).forEach(key => provider.clearCache(key)); - this.expandedCacheKeys.delete(cacheKey); - } - private rawMatchToSearchItem(match: IInternalFileMatch): IFileMatch { - return { - resource: resources.joinPath(match.base, match.relativePath) - }; + if (match.relativePath) { + return { + resource: resources.joinPath(match.base, match.relativePath) + }; + } else { + // extraFileResources + return { + resource: match.base + }; + } } - private doSearch(engine: FileSearchEngine, batchSize: number): PPromise { - return new PPromise((c, e, p) => { + private doSearch(engine: FileSearchEngine, batchSize: number, onResultBatch: (matches: IInternalFileMatch[]) => void): TPromise { + return new TPromise((c, e) => { + const _onResult = match => { + if (match) { + batch.push(match); + if (batchSize > 0 && batch.length >= batchSize) { + onResultBatch(batch); + batch = []; + } + } + }; + let batch: IInternalFileMatch[] = []; - engine.search().then(result => { + engine.search(_onResult).then(result => { if (batch.length) { - p(batch); + onResultBatch(batch); } c(result); }, error => { if (batch.length) { - p(batch); + onResultBatch(batch); } e(error); - }, match => { - if (match) { - batch.push(match); - if (batchSize > 0 && batch.length >= batchSize) { - p(batch); - batch = []; - } - } }); }, () => { engine.cancel(); diff --git a/src/vs/workbench/api/node/extHostTextEditor.ts b/src/vs/workbench/api/node/extHostTextEditor.ts index 0d9d7c2d9a7..eb8afcf4c06 100644 --- a/src/vs/workbench/api/node/extHostTextEditor.ts +++ b/src/vs/workbench/api/node/extHostTextEditor.ts @@ -497,6 +497,11 @@ export class ExtHostTextEditor implements vscode.TextEditor { private _applyEdit(editBuilder: TextEditorEdit): TPromise { let editData = editBuilder.finalize(); + // return when there is nothing to do + if (editData.edits.length === 0 && !editData.setEndOfLine) { + return TPromise.wrap(true); + } + // check that the edits are not overlapping (i.e. illegal) let editRanges = editData.edits.map(edit => edit.range); diff --git a/src/vs/workbench/api/node/extHostTreeViews.ts b/src/vs/workbench/api/node/extHostTreeViews.ts index a63e56f9972..811fc4b464b 100644 --- a/src/vs/workbench/api/node/extHostTreeViews.ts +++ b/src/vs/workbench/api/node/extHostTreeViews.ts @@ -18,6 +18,7 @@ import { asWinJsPromise } from 'vs/base/common/async'; import { TreeItemCollapsibleState, ThemeIcon } from 'vs/workbench/api/node/extHostTypes'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { equals } from 'vs/base/common/arrays'; +import { ILogService } from 'vs/platform/log/common/log'; type TreeItemHandle = string; @@ -27,7 +28,8 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { constructor( private _proxy: MainThreadTreeViewsShape, - private commands: ExtHostCommands + private commands: ExtHostCommands, + private logService: ILogService ) { commands.registerArgumentProcessor({ processArgument: arg => { @@ -99,7 +101,7 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { } private createExtHostTreeViewer(id: string, dataProvider: vscode.TreeDataProvider): ExtHostTreeView { - const treeView = new ExtHostTreeView(id, dataProvider, this._proxy, this.commands.converter); + const treeView = new ExtHostTreeView(id, dataProvider, this._proxy, this.commands.converter, this.logService); this.treeViews.set(id, treeView); return treeView; } @@ -145,7 +147,7 @@ class ExtHostTreeView extends Disposable { private refreshPromise: TPromise = TPromise.as(null); - constructor(private viewId: string, private dataProvider: vscode.TreeDataProvider, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter) { + constructor(private viewId: string, private dataProvider: vscode.TreeDataProvider, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter, private logService: ILogService) { super(); this.proxy.$registerTreeViewDataProvider(viewId); if (this.dataProvider.onDidChangeTreeData) { @@ -192,7 +194,7 @@ class ExtHostTreeView extends Disposable { return this.refreshPromise .then(() => this.resolveUnknownParentChain(element)) .then(parentChain => this.resolveTreeNode(element, parentChain[parentChain.length - 1]) - .then(treeNode => this.proxy.$reveal(this.viewId, treeNode.item, parentChain.map(p => p.item), { select, focus }))); + .then(treeNode => this.proxy.$reveal(this.viewId, treeNode.item, parentChain.map(p => p.item), { select, focus })), error => this.logService.error(error)); } setExpanded(treeItemHandle: TreeItemHandle, expanded: boolean): void { diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index bf942604411..ae449946df0 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -413,6 +413,23 @@ export const location = { } }; +export namespace DefinitionLink { + export function from(value: vscode.Location | vscode.DefinitionLink): modes.DefinitionLink { + const definitionLink = value; + const location = value; + return { + origin: definitionLink.originSelectionRange + ? Range.from(definitionLink.originSelectionRange) + : undefined, + uri: definitionLink.targetUri ? definitionLink.targetUri : location.uri, + range: Range.from(definitionLink.targetRange ? definitionLink.targetRange : location.range), + selectionRange: definitionLink.targetSelectionRange + ? Range.from(definitionLink.targetSelectionRange) + : undefined, + }; + } +} + export namespace Hover { export function from(hover: vscode.Hover): modes.Hover { return { diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 4bf2cd070ba..7e7572fe66c 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -1956,3 +1956,10 @@ export enum CommentThreadCollapsibleState { */ Expanded = 1 } + +export class QuickInputButtons { + + static readonly Back: vscode.QuickInputButton = { iconPath: 'back.svg' }; + + private constructor() { } +} diff --git a/src/vs/workbench/api/node/extHostWebview.ts b/src/vs/workbench/api/node/extHostWebview.ts index 0ad6b97fa30..89302f77670 100644 --- a/src/vs/workbench/api/node/extHostWebview.ts +++ b/src/vs/workbench/api/node/extHostWebview.ts @@ -3,14 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { MainContext, MainThreadWebviewsShape, IMainContext, ExtHostWebviewsShape, WebviewPanelHandle, WebviewPanelViewState } from './extHost.protocol'; -import * as vscode from 'vscode'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; +import URI from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters'; import { EditorViewColumn } from 'vs/workbench/api/shared/editor'; -import { TPromise } from 'vs/base/common/winjs.base'; +import * as vscode from 'vscode'; +import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState } from './extHost.protocol'; import { Disposable } from './extHostTypes'; -import URI from 'vs/base/common/uri'; + +type IconPath = URI | { light: URI, dark: URI }; export class ExtHostWebview implements vscode.Webview { private readonly _handle: WebviewPanelHandle; @@ -78,6 +80,7 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel { private readonly _proxy: MainThreadWebviewsShape; private readonly _viewType: string; private _title: string; + private _iconPath: IconPath; private readonly _options: vscode.WebviewPanelOptions; private readonly _webview: ExtHostWebview; @@ -150,6 +153,20 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel { } } + get iconPath(): IconPath | undefined { + this.assertNotDisposed(); + return this._iconPath; + } + + set iconPath(value: IconPath | undefined) { + this.assertNotDisposed(); + if (this._iconPath !== value) { + this._iconPath = value; + + this._proxy.$setIconPath(this._handle, URI.isUri(value) ? { light: value, dark: value } : value); + } + } + get options() { return this._options; } @@ -191,9 +208,10 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel { public reveal(viewColumn?: vscode.ViewColumn, preserveFocus?: boolean): void { this.assertNotDisposed(); - this._proxy.$reveal(this._handle, - viewColumn ? typeConverters.ViewColumn.from(viewColumn) : undefined, - !!preserveFocus); + this._proxy.$reveal(this._handle, { + viewColumn: viewColumn ? typeConverters.ViewColumn.from(viewColumn) : undefined, + preserveFocus: !!preserveFocus + }); } private assertNotDisposed() { @@ -206,8 +224,11 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel { export class ExtHostWebviews implements ExtHostWebviewsShape { private static webviewHandlePool = 1; - private readonly _proxy: MainThreadWebviewsShape; + private static newHandle(): WebviewPanelHandle { + return ExtHostWebviews.webviewHandlePool++ + ''; + } + private readonly _proxy: MainThreadWebviewsShape; private readonly _webviewPanels = new Map(); private readonly _serializers = new Map(); @@ -217,22 +238,20 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { this._proxy = mainContext.getProxy(MainContext.MainThreadWebviews); } - createWebview( + public createWebview( + extensionLocation: URI, viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, - options: (vscode.WebviewPanelOptions & vscode.WebviewOptions) | undefined, - extensionLocation: URI + options: (vscode.WebviewPanelOptions & vscode.WebviewOptions) = {}, ): vscode.WebviewPanel { - options = options || {}; - const viewColumn = typeof showOptions === 'object' ? showOptions.viewColumn : showOptions; const webviewShowOptions = { viewColumn: typeConverters.ViewColumn.from(viewColumn), preserveFocus: typeof showOptions === 'object' && !!showOptions.preserveFocus }; - const handle = ExtHostWebviews.webviewHandlePool++ + ''; + const handle = ExtHostWebviews.newHandle(); this._proxy.$createWebviewPanel(handle, viewType, title, webviewShowOptions, options, extensionLocation); const webview = new ExtHostWebview(handle, this._proxy, options); @@ -241,7 +260,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { return panel; } - registerWebviewPanelSerializer( + public registerWebviewPanelSerializer( viewType: string, serializer: vscode.WebviewPanelSerializer ): vscode.Disposable { @@ -258,14 +277,20 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { }); } - $onMessage(handle: WebviewPanelHandle, message: any): void { + public $onMessage( + handle: WebviewPanelHandle, + message: any + ): void { const panel = this.getWebviewPanel(handle); if (panel) { panel.webview._onMessageEmitter.fire(message); } } - $onDidChangeWebviewPanelViewState(handle: WebviewPanelHandle, newState: WebviewPanelViewState): void { + public $onDidChangeWebviewPanelViewState( + handle: WebviewPanelHandle, + newState: WebviewPanelViewState + ): void { const panel = this.getWebviewPanel(handle); if (panel) { const viewColumn = typeConverters.ViewColumn.to(newState.position); diff --git a/src/vs/workbench/api/node/extHostWorkspace.ts b/src/vs/workbench/api/node/extHostWorkspace.ts index 2afd565ed8a..56e9d91c15c 100644 --- a/src/vs/workbench/api/node/extHostWorkspace.ts +++ b/src/vs/workbench/api/node/extHostWorkspace.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { posix, relative, join } from 'path'; +import { relative, join } from 'path'; import { delta as arrayDelta } from 'vs/base/common/arrays'; import { Emitter, Event } from 'vs/base/common/event'; import { TernarySearchTree } from 'vs/base/common/map'; import { normalize } from 'vs/base/common/paths'; import { isLinux } from 'vs/base/common/platform'; -import { basenameOrAuthority, isEqual } from 'vs/base/common/resources'; +import { basenameOrAuthority, isEqual, dirname } from 'vs/base/common/resources'; import { compare } from 'vs/base/common/strings'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; @@ -124,7 +124,7 @@ class ExtHostWorkspaceImpl extends Workspace { getWorkspaceFolder(uri: URI, resolveParent?: boolean): vscode.WorkspaceFolder { if (resolveParent && this._structure.get(uri.toString())) { // `uri` is a workspace folder so we check for its parent - uri = uri.with({ path: posix.dirname(uri.path) }); + uri = dirname(uri); } return this._structure.findSubstr(uri.toString()); } @@ -268,6 +268,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { if (folders.length === 0) { return undefined; } + // #54483 @Joh Why are we still using fsPath? return folders[0].uri.fsPath; } @@ -394,7 +395,13 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { excludePattern: options.exclude && globPatternToString(options.exclude) }; + let isCanceled = false; + this._activeSearchCallbacks[requestId] = p => { + if (isCanceled) { + return; + } + p.lineMatches.forEach(lineMatch => { lineMatch.offsetAndLengths.forEach(offsetAndLength => { const range = new Range(lineMatch.lineNumber, offsetAndLength[0], lineMatch.lineNumber, offsetAndLength[0] + offsetAndLength[1]); @@ -408,7 +415,10 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape { }; if (token) { - token.onCancellationRequested(() => this._proxy.$cancelSearch(requestId)); + token.onCancellationRequested(() => { + isCanceled = true; + this._proxy.$cancelSearch(requestId); + }); } return this._proxy.$startTextSearch(query, queryOptions, requestId).then( diff --git a/src/vs/workbench/browser/actions/toggleActivityBarVisibility.ts b/src/vs/workbench/browser/actions/toggleActivityBarVisibility.ts index bb784e89472..9f504717d73 100644 --- a/src/vs/workbench/browser/actions/toggleActivityBarVisibility.ts +++ b/src/vs/workbench/browser/actions/toggleActivityBarVisibility.ts @@ -8,7 +8,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; @@ -40,4 +40,13 @@ export class ToggleActivityBarVisibilityAction extends Action { } const registry = Registry.as(Extensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, ToggleActivityBarVisibilityAction.LABEL), 'View: Toggle Activity Bar Visibility', nls.localize('view', "View")); \ No newline at end of file +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, ToggleActivityBarVisibilityAction.LABEL), 'View: Toggle Activity Bar Visibility', nls.localize('view', "View")); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '2_workbench_layout', + command: { + id: ToggleActivityBarVisibilityAction.ID, + title: nls.localize({ key: 'miToggleActivityBar', comment: ['&& denotes a mnemonic'] }, "Toggle &&Activity Bar") + }, + order: 4 +}); diff --git a/src/vs/workbench/browser/actions/toggleCenteredLayout.ts b/src/vs/workbench/browser/actions/toggleCenteredLayout.ts index 2ab19d4ca4d..4be449f24ac 100644 --- a/src/vs/workbench/browser/actions/toggleCenteredLayout.ts +++ b/src/vs/workbench/browser/actions/toggleCenteredLayout.ts @@ -7,7 +7,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { IPartService } from 'vs/workbench/services/part/common/partService'; @@ -34,3 +34,21 @@ class ToggleCenteredLayout extends Action { const registry = Registry.as(Extensions.WorkbenchActions); registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCenteredLayout, ToggleCenteredLayout.ID, ToggleCenteredLayout.LABEL), 'View: Toggle Centered Layout', nls.localize('view', "View")); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '1_toggle_view', + command: { + id: ToggleCenteredLayout.ID, + title: nls.localize('miToggleCenteredLayout', "Toggle Centered Layout") + }, + order: 3 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '2_layouts', + command: { + id: 'workbench.action.editorLayoutCentered', + title: nls.localize({ key: 'miCenteredEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Centered") + }, + order: 2 +}); diff --git a/src/vs/workbench/browser/actions/toggleEditorLayout.ts b/src/vs/workbench/browser/actions/toggleEditorLayout.ts index 8f553a8fa22..32b9cc1c921 100644 --- a/src/vs/workbench/browser/actions/toggleEditorLayout.ts +++ b/src/vs/workbench/browser/actions/toggleEditorLayout.ts @@ -9,7 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; @@ -73,4 +73,13 @@ CommandsRegistry.registerCommand('_workbench.editor.setGroupOrientation', functi const registry = Registry.as(Extensions.WorkbenchActions); const group = nls.localize('view', "View"); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEditorLayoutAction, ToggleEditorLayoutAction.ID, ToggleEditorLayoutAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_0, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_0 } }), 'View: Flip Editor Group Layout', group); \ No newline at end of file +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEditorLayoutAction, ToggleEditorLayoutAction.ID, ToggleEditorLayoutAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_0, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_0 } }), 'View: Flip Editor Group Layout', group); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: 'z_flip', + command: { + id: ToggleEditorLayoutAction.ID, + title: nls.localize({ key: 'miToggleEditorLayout', comment: ['&& denotes a mnemonic'] }, "Flip &&Layout") + }, + order: 1 +}); diff --git a/src/vs/workbench/browser/actions/toggleSidebarPosition.ts b/src/vs/workbench/browser/actions/toggleSidebarPosition.ts index 3de25a53e7e..3c64a5a1c6d 100644 --- a/src/vs/workbench/browser/actions/toggleSidebarPosition.ts +++ b/src/vs/workbench/browser/actions/toggleSidebarPosition.ts @@ -8,7 +8,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { IPartService, Position } from 'vs/workbench/services/part/common/partService'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; @@ -41,3 +41,12 @@ export class ToggleSidebarPositionAction extends Action { const registry = Registry.as(Extensions.WorkbenchActions); registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSidebarPositionAction, ToggleSidebarPositionAction.ID, ToggleSidebarPositionAction.LABEL), 'View: Toggle Side Bar Position', nls.localize('view', "View")); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '2_workbench_layout', + command: { + id: ToggleSidebarPositionAction.ID, + title: nls.localize({ key: 'miMoveSidebarLeftRight', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Left/Right") + }, + order: 2 +}); diff --git a/src/vs/workbench/browser/actions/toggleSidebarVisibility.ts b/src/vs/workbench/browser/actions/toggleSidebarVisibility.ts index 95f4d8a21e8..e69151cac2b 100644 --- a/src/vs/workbench/browser/actions/toggleSidebarVisibility.ts +++ b/src/vs/workbench/browser/actions/toggleSidebarVisibility.ts @@ -8,7 +8,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; @@ -35,4 +35,13 @@ export class ToggleSidebarVisibilityAction extends Action { } const registry = Registry.as(Extensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSidebarVisibilityAction, ToggleSidebarVisibilityAction.ID, ToggleSidebarVisibilityAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_B }), 'View: Toggle Side Bar Visibility', nls.localize('view', "View")); \ No newline at end of file +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSidebarVisibilityAction, ToggleSidebarVisibilityAction.ID, ToggleSidebarVisibilityAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_B }), 'View: Toggle Side Bar Visibility', nls.localize('view', "View")); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '2_workbench_layout', + command: { + id: ToggleSidebarVisibilityAction.ID, + title: nls.localize({ key: 'miToggleSidebar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Side Bar") + }, + order: 1 +}); diff --git a/src/vs/workbench/browser/actions/toggleStatusbarVisibility.ts b/src/vs/workbench/browser/actions/toggleStatusbarVisibility.ts index 2fe83369bb8..2ec27e5f765 100644 --- a/src/vs/workbench/browser/actions/toggleStatusbarVisibility.ts +++ b/src/vs/workbench/browser/actions/toggleStatusbarVisibility.ts @@ -8,7 +8,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; @@ -40,4 +40,13 @@ export class ToggleStatusbarVisibilityAction extends Action { } const registry = Registry.as(Extensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleStatusbarVisibilityAction, ToggleStatusbarVisibilityAction.ID, ToggleStatusbarVisibilityAction.LABEL), 'View: Toggle Status Bar Visibility', nls.localize('view', "View")); \ No newline at end of file +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleStatusbarVisibilityAction, ToggleStatusbarVisibilityAction.ID, ToggleStatusbarVisibilityAction.LABEL), 'View: Toggle Status Bar Visibility', nls.localize('view', "View")); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '2_workbench_layout', + command: { + id: ToggleStatusbarVisibilityAction.ID, + title: nls.localize({ key: 'miToggleStatusbar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Status Bar") + }, + order: 3 +}); diff --git a/src/vs/workbench/browser/actions/toggleZenMode.ts b/src/vs/workbench/browser/actions/toggleZenMode.ts index 955a69799ce..a13035026da 100644 --- a/src/vs/workbench/browser/actions/toggleZenMode.ts +++ b/src/vs/workbench/browser/actions/toggleZenMode.ts @@ -8,7 +8,7 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { IPartService } from 'vs/workbench/services/part/common/partService'; @@ -33,4 +33,13 @@ class ToggleZenMode extends Action { } const registry = Registry.as(Extensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleZenMode, ToggleZenMode.ID, ToggleZenMode.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_Z) }), 'View: Toggle Zen Mode', nls.localize('view', "View")); \ No newline at end of file +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleZenMode, ToggleZenMode.ID, ToggleZenMode.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_Z) }), 'View: Toggle Zen Mode', nls.localize('view', "View")); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '1_toggle_view', + command: { + id: ToggleZenMode.ID, + title: nls.localize('miToggleZenMode', "Toggle Zen Mode") + }, + order: 2 +}); diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index bf7b0adc209..985be405ccf 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -19,6 +19,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { ICommandService } from 'vs/platform/commands/common/commands'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL, PICK_WORKSPACE_FOLDER_COMMAND_ID, defaultWorkspacePath, defaultFilePath, defaultFolderPath } from 'vs/workbench/browser/actions/workspaceCommands'; +import URI from 'vs/base/common/uri'; export class OpenFileAction extends Action { @@ -239,7 +240,7 @@ export class DuplicateWorkspaceInNewWindowAction extends Action { return this.workspacesService.createWorkspace(folders).then(newWorkspace => { return this.workspaceEditingService.copyWorkspaceSettings(newWorkspace).then(() => { - return this.windowService.openWindow([newWorkspace.configPath], { forceNewWindow: true }); + return this.windowService.openWindow([URI.file(newWorkspace.configPath)], { forceNewWindow: true }); }); }); } diff --git a/src/vs/workbench/browser/actions/workspaceCommands.ts b/src/vs/workbench/browser/actions/workspaceCommands.ts index a473438cb5a..955717730e0 100644 --- a/src/vs/workbench/browser/actions/workspaceCommands.ts +++ b/src/vs/workbench/browser/actions/workspaceCommands.ts @@ -14,15 +14,19 @@ import URI from 'vs/base/common/uri'; import * as resources from 'vs/base/common/resources'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { dirname } from 'vs/base/common/paths'; -import { IQuickOpenService, IFilePickOpenEntry, IPickOptions } from 'vs/platform/quickOpen/common/quickOpen'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { mnemonicButtonLabel, getPathLabel } from 'vs/base/common/labels'; +import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { FileKind, isParent } from 'vs/platform/files/common/files'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { isLinux } from 'vs/base/common/platform'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; +import { IQuickInputService, IPickOptions, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; +import { getIconClasses } from 'vs/workbench/browser/labels'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; export const ADD_ROOT_FOLDER_COMMAND_ID = 'addRootFolder'; export const ADD_ROOT_FOLDER_LABEL = nls.localize('addFolderToWorkspace', "Add Folder to Workspace..."); @@ -157,10 +161,12 @@ CommandsRegistry.registerCommand({ } }); -CommandsRegistry.registerCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, function (accessor, args?: [IPickOptions, CancellationToken]) { +CommandsRegistry.registerCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, function (accessor, args?: [IPickOptions, CancellationToken]) { + const quickInputService = accessor.get(IQuickInputService); + const uriDisplayService = accessor.get(IUriDisplayService); const contextService = accessor.get(IWorkspaceContextService); - const quickOpenService = accessor.get(IQuickOpenService); - const environmentService = accessor.get(IEnvironmentService); + const modelService = accessor.get(IModelService); + const modeService = accessor.get(IModeService); const folders = contextService.getWorkspace().folders; if (!folders.length) { @@ -170,14 +176,13 @@ CommandsRegistry.registerCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, function (acc const folderPicks = folders.map(folder => { return { label: folder.name, - description: getPathLabel(resources.dirname(folder.uri), environmentService, contextService), + description: uriDisplayService.getLabel(resources.dirname(folder.uri), true), folder, - resource: folder.uri, - fileKind: FileKind.ROOT_FOLDER - } as IFilePickOpenEntry; + iconClasses: getIconClasses(modelService, modeService, folder.uri, FileKind.ROOT_FOLDER) + } as IQuickPickItem; }); - let options: IPickOptions; + let options: IPickOptions; if (args) { options = args[0]; } @@ -186,8 +191,8 @@ CommandsRegistry.registerCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, function (acc options = Object.create(null); } - if (!options.autoFocus) { - options.autoFocus = { autoFocusFirstEntry: true }; + if (!options.activeItem) { + options.activeItem = folderPicks[0]; } if (!options.placeHolder) { @@ -207,7 +212,7 @@ CommandsRegistry.registerCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID, function (acc token = CancellationToken.None; } - return quickOpenService.pick(folderPicks, options, token).then(pick => { + return quickInputService.pick(folderPicks, options, token).then(pick => { if (!pick) { return void 0; } diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index 1eff3ded937..dd879f9d831 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -35,6 +35,7 @@ import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/co import { Disposable } from 'vs/base/common/lifecycle'; import { addDisposableListener, EventType } from 'vs/base/browser/dom'; import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; export interface IDraggedResource { resource: URI; @@ -165,7 +166,8 @@ export class ResourcesDropHandler { @IBackupFileService private backupFileService: IBackupFileService, @IUntitledEditorService private untitledEditorService: IUntitledEditorService, @IEditorService private editorService: IEditorService, - @IConfigurationService private configurationService: IConfigurationService + @IConfigurationService private configurationService: IConfigurationService, + @IUriDisplayService private uriDisplayService: IUriDisplayService ) { } @@ -187,7 +189,7 @@ export class ResourcesDropHandler { // Add external ones to recently open list unless dropped resource is a workspace const filesToAddToHistory = untitledOrFileResources.filter(d => d.isExternal && d.resource.scheme === Schemas.file).map(d => d.resource); if (filesToAddToHistory.length) { - this.windowsService.addRecentlyOpened(filesToAddToHistory.map(resource => resource.fsPath)); + this.windowsService.addRecentlyOpened(filesToAddToHistory.map(resource => this.uriDisplayService.getLabel(resource))); } const editors: IResourceEditor[] = untitledOrFileResources.map(untitledOrFileResource => ({ @@ -290,16 +292,16 @@ export class ResourcesDropHandler { // Pass focus to window this.windowService.focusWindow(); - let workspacesToOpen: TPromise; + let workspacesToOpen: TPromise; // Open in separate windows if we drop workspaces or just one folder if (workspaces.length > 0 || folders.length === 1) { - workspacesToOpen = TPromise.as([...workspaces, ...folders].map(resources => resources.fsPath)); + workspacesToOpen = TPromise.as([...workspaces, ...folders].map(resources => resources)); } // Multiple folders: Create new workspace with folders and open else if (folders.length > 1) { - workspacesToOpen = this.workspacesService.createWorkspace(folders.map(folder => ({ uri: folder }))).then(workspace => [workspace.configPath]); + workspacesToOpen = this.workspacesService.createWorkspace(folders.map(folder => ({ uri: folder }))).then(workspace => [URI.file(workspace.configPath)]); } // Open diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 3f6da3e94dc..bf04e217fa7 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -11,12 +11,10 @@ import { IconLabel, IIconLabelValueOptions, IIconLabelCreationOptions } from 'vs import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IModeService } from 'vs/editor/common/services/modeService'; import { toResource, IEditorInput } from 'vs/workbench/common/editor'; -import { getPathLabel, IWorkspaceFolderProvider } from 'vs/base/common/labels'; import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IDecorationsService, IResourceDecorationChangeEvent, IDecorationData } from 'vs/workbench/services/decorations/browser/decorations'; import { Schemas } from 'vs/base/common/network'; @@ -25,6 +23,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { Event, Emitter } from 'vs/base/common/event'; import { DataUri } from 'vs/workbench/common/resources'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; export interface IResourceLabel { name: string; @@ -52,13 +51,12 @@ export class ResourceLabel extends IconLabel { container: HTMLElement, options: IIconLabelCreationOptions, @IExtensionService private extensionService: IExtensionService, - @IWorkspaceContextService protected contextService: IWorkspaceContextService, @IConfigurationService private configurationService: IConfigurationService, @IModeService private modeService: IModeService, @IModelService private modelService: IModelService, - @IEnvironmentService protected environmentService: IEnvironmentService, @IDecorationsService protected decorationsService: IDecorationsService, - @IThemeService private themeService: IThemeService + @IThemeService private themeService: IThemeService, + @IUriDisplayService protected uriDisplayService: IUriDisplayService ) { super(container, options); @@ -183,6 +181,7 @@ export class ResourceLabel extends IconLabel { title: '', italic: this.options && this.options.italic, matches: this.options && this.options.matches, + extraClasses: [] }; const resource = this.label.resource; @@ -192,18 +191,18 @@ export class ResourceLabel extends IconLabel { iconLabelOptions.title = this.options.title; } else if (resource && resource.scheme !== Schemas.data /* do not accidentally inline Data URIs */) { if (!this.computedPathLabel) { - const rootProvider = resource.scheme !== Schemas.file ? this.contextService : undefined; - this.computedPathLabel = getPathLabel(resource, this.environmentService, rootProvider); + this.computedPathLabel = this.uriDisplayService.getLabel(resource); } iconLabelOptions.title = this.computedPathLabel; } - if (!this.computedIconClasses) { - this.computedIconClasses = getIconClasses(this.modelService, this.modeService, resource, this.options && this.options.fileKind); + if (this.options && !this.options.hideIcon) { + if (!this.computedIconClasses) { + this.computedIconClasses = getIconClasses(this.modelService, this.modeService, resource, this.options && this.options.fileKind); + } + iconLabelOptions.extraClasses = this.computedIconClasses.slice(0); } - - iconLabelOptions.extraClasses = this.computedIconClasses.slice(0); if (this.options && this.options.extraClasses) { iconLabelOptions.extraClasses.push(...this.options.extraClasses); } @@ -260,7 +259,6 @@ export class EditorLabel extends ResourceLabel { export interface IFileLabelOptions extends IResourceLabelOptions { hideLabel?: boolean; hidePath?: boolean; - root?: uri; } export class FileLabel extends ResourceLabel { @@ -269,16 +267,16 @@ export class FileLabel extends ResourceLabel { container: HTMLElement, options: IIconLabelCreationOptions, @IExtensionService extensionService: IExtensionService, - @IWorkspaceContextService contextService: IWorkspaceContextService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, @IConfigurationService configurationService: IConfigurationService, @IModeService modeService: IModeService, @IModelService modelService: IModelService, - @IEnvironmentService environmentService: IEnvironmentService, @IDecorationsService decorationsService: IDecorationsService, @IThemeService themeService: IThemeService, @IUntitledEditorService private untitledEditorService: IUntitledEditorService, + @IUriDisplayService uriDisplayService: IUriDisplayService ) { - super(container, options, extensionService, contextService, configurationService, modeService, modelService, environmentService, decorationsService, themeService); + super(container, options, extensionService, configurationService, modeService, modelService, decorationsService, themeService, uriDisplayService); } setFile(resource: uri, options?: IFileLabelOptions): void { @@ -300,17 +298,7 @@ export class FileLabel extends ResourceLabel { let description: string; const hidePath = (options && options.hidePath) || (resource.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(resource)); if (!hidePath) { - let rootProvider: IWorkspaceFolderProvider; - if (options && options.root) { - rootProvider = { - getWorkspaceFolder(): { uri } { return { uri: options.root }; }, - getWorkspace(): { folders: { uri: uri }[]; } { return { folders: [{ uri: options.root }] }; }, - }; - } else { - rootProvider = this.contextService; - } - - description = getPathLabel(resources.dirname(resource), this.environmentService, rootProvider); + description = this.uriDisplayService.getLabel(resources.dirname(resource), true); } this.setLabel({ resource, name, description }, options); diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 01fe21c6a4c..5089f6ff8af 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -14,7 +14,6 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { Disposable } from 'vs/base/common/lifecycle'; -import { getZoomFactor } from 'vs/base/browser/browser'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { isMacintosh } from 'vs/base/common/platform'; import { memoize } from 'vs/base/common/decorators'; @@ -28,7 +27,7 @@ import { ActivitybarPart } from 'vs/workbench/browser/parts/activitybar/activity import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart'; import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; import { StatusbarPart } from 'vs/workbench/browser/parts/statusbar/statusbarPart'; -import { MenubarPart } from 'vs/workbench/browser/parts/menubar/menubarPart'; +import { getZoomFactor } from 'vs/base/browser/browser'; const TITLE_BAR_HEIGHT = isMacintosh ? 22 : 30; const STATUS_BAR_HEIGHT = 22; @@ -65,8 +64,6 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr private _sidebarWidth: number; private sidebarHeight: number; private titlebarHeight: number; - private menubarHeight: number; - private headingHeight: number; private statusbarHeight: number; private panelSizeBeforeMaximized: number; private panelMaximized: boolean; @@ -79,7 +76,6 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr private workbenchContainer: HTMLElement, private parts: { titlebar: TitlebarPart, - menubar: MenubarPart, activitybar: ActivitybarPart, editor: EditorPart, sidebar: SidebarPart, @@ -224,9 +220,6 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr titlebar: { height: TITLE_BAR_HEIGHT }, - menubar: { - height: TITLE_BAR_HEIGHT - }, activitybar: { width: ACTIVITY_BAR_WIDTH }, @@ -319,7 +312,7 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr if (newSashHeight + HIDE_PANEL_HEIGHT_THRESHOLD < this.partLayoutInfo.panel.minHeight) { let dragCompensation = this.partLayoutInfo.panel.minHeight - HIDE_PANEL_HEIGHT_THRESHOLD; promise = this.partService.setPanelHidden(true); - startY = Math.min(this.sidebarHeight - this.statusbarHeight - this.headingHeight, e.currentY + dragCompensation); + startY = Math.min(this.sidebarHeight - this.statusbarHeight - this.titlebarHeight, e.currentY + dragCompensation); this.panelHeight = startPanelHeight; // when restoring panel, restore to the panel height we started from } @@ -420,12 +413,12 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr const isActivityBarHidden = !this.partService.isVisible(Parts.ACTIVITYBAR_PART); const isTitlebarHidden = !this.partService.isVisible(Parts.TITLEBAR_PART); - const isMenubarHidden = !this.partService.isVisible(Parts.MENUBAR_PART); const isPanelHidden = !this.partService.isVisible(Parts.PANEL_PART); const isStatusbarHidden = !this.partService.isVisible(Parts.STATUSBAR_PART); const isSidebarHidden = !this.partService.isVisible(Parts.SIDEBAR_PART); const sidebarPosition = this.partService.getSideBarPosition(); const panelPosition = this.partService.getPanelPosition(); + const menubarVisibility = this.partService.getMenubarVisibility(); // Sidebar if (this.sidebarWidth === -1) { @@ -433,11 +426,9 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr } this.statusbarHeight = isStatusbarHidden ? 0 : this.partLayoutInfo.statusbar.height; - this.titlebarHeight = isTitlebarHidden ? 0 : this.partLayoutInfo.titlebar.height / getZoomFactor(); // adjust for zoom prevention - this.menubarHeight = isMenubarHidden ? 0 : this.partLayoutInfo.menubar.height / getZoomFactor(); // adjust for zoom prevention - this.headingHeight = Math.max(this.menubarHeight, this.titlebarHeight); + this.titlebarHeight = isTitlebarHidden ? 0 : this.partLayoutInfo.titlebar.height / (!menubarVisibility || menubarVisibility === 'hidden' ? getZoomFactor() : 1); // adjust for zoom prevention - this.sidebarHeight = this.workbenchSize.height - this.statusbarHeight - this.headingHeight; + this.sidebarHeight = this.workbenchSize.height - this.statusbarHeight - this.titlebarHeight; let sidebarSize = new Dimension(this.sidebarWidth, this.sidebarHeight); // Activity Bar @@ -574,14 +565,6 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr show(titleContainer); } - // Menubar - const menubarContainer = this.parts.menubar.getContainer(); - if (isMenubarHidden) { - hide(menubarContainer); - } else { - show(menubarContainer); - } - // Editor Part and Panel part const editorContainer = this.parts.editor.getContainer(); const panelContainer = this.parts.panel.getContainer(); @@ -590,19 +573,19 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr if (panelPosition === Position.BOTTOM) { if (sidebarPosition === Position.LEFT) { - position(editorContainer, this.headingHeight, 0, this.statusbarHeight + panelDimension.height, sidebarSize.width + activityBarSize.width); - position(panelContainer, editorSize.height + this.headingHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width); + position(editorContainer, this.titlebarHeight, 0, this.statusbarHeight + panelDimension.height, sidebarSize.width + activityBarSize.width); + position(panelContainer, editorSize.height + this.titlebarHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width); } else { - position(editorContainer, this.headingHeight, sidebarSize.width, this.statusbarHeight + panelDimension.height, 0); - position(panelContainer, editorSize.height + this.headingHeight, sidebarSize.width, this.statusbarHeight, 0); + position(editorContainer, this.titlebarHeight, sidebarSize.width, this.statusbarHeight + panelDimension.height, 0); + position(panelContainer, editorSize.height + this.titlebarHeight, sidebarSize.width, this.statusbarHeight, 0); } } else { if (sidebarPosition === Position.LEFT) { - position(editorContainer, this.headingHeight, panelDimension.width, this.statusbarHeight, sidebarSize.width + activityBarSize.width); - position(panelContainer, this.headingHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width + editorSize.width); + position(editorContainer, this.titlebarHeight, panelDimension.width, this.statusbarHeight, sidebarSize.width + activityBarSize.width); + position(panelContainer, this.titlebarHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width + editorSize.width); } else { - position(editorContainer, this.headingHeight, sidebarSize.width + activityBarSize.width + panelWidth, this.statusbarHeight, 0); - position(panelContainer, this.headingHeight, sidebarSize.width + activityBarSize.width, this.statusbarHeight, editorSize.width); + position(editorContainer, this.titlebarHeight, sidebarSize.width + activityBarSize.width + panelWidth, this.statusbarHeight, 0); + position(panelContainer, this.titlebarHeight, sidebarSize.width + activityBarSize.width, this.statusbarHeight, editorSize.width); } } @@ -611,10 +594,10 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr size(activitybarContainer, null, activityBarSize.height); if (sidebarPosition === Position.LEFT) { this.parts.activitybar.getContainer().style.right = ''; - position(activitybarContainer, this.headingHeight, null, 0, 0); + position(activitybarContainer, this.titlebarHeight, null, 0, 0); } else { this.parts.activitybar.getContainer().style.left = ''; - position(activitybarContainer, this.headingHeight, 0, 0, null); + position(activitybarContainer, this.titlebarHeight, 0, 0, null); } if (isActivityBarHidden) { hide(activitybarContainer); @@ -627,9 +610,9 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr size(sidebarContainer, sidebarSize.width, sidebarSize.height); const editorAndPanelWidth = editorSize.width + (panelPosition === Position.RIGHT ? panelWidth : 0); if (sidebarPosition === Position.LEFT) { - position(sidebarContainer, this.headingHeight, editorAndPanelWidth, this.statusbarHeight, activityBarSize.width); + position(sidebarContainer, this.titlebarHeight, editorAndPanelWidth, this.statusbarHeight, activityBarSize.width); } else { - position(sidebarContainer, this.headingHeight, activityBarSize.width, this.statusbarHeight, editorAndPanelWidth); + position(sidebarContainer, this.titlebarHeight, activityBarSize.width, this.statusbarHeight, editorAndPanelWidth); } // Statusbar Part @@ -665,7 +648,6 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr // Propagate to Part Layouts this.parts.titlebar.layout(new Dimension(this.workbenchSize.width, this.titlebarHeight)); - this.parts.menubar.layout(new Dimension(this.workbenchSize.width, this.menubarHeight)); this.parts.editor.layout(new Dimension(editorSize.width, editorSize.height)); this.parts.sidebar.layout(sidebarSize); this.parts.panel.layout(panelDimension); @@ -676,7 +658,7 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr } getVerticalSashTop(sash: Sash): number { - return this.headingHeight; + return this.titlebarHeight; } getVerticalSashLeft(sash: Sash): number { @@ -703,7 +685,7 @@ export class WorkbenchLayout extends Disposable implements IVerticalSashLayoutPr getHorizontalSashTop(sash: Sash): number { const offset = 2; // Horizontal sash should be a bit lower than the editor area, thus add 2px #5524 - return offset + (this.partService.isVisible(Parts.PANEL_PART) ? this.sidebarHeight - this.panelHeight + this.headingHeight : this.sidebarHeight + this.headingHeight); + return offset + (this.partService.isVisible(Parts.PANEL_PART) ? this.sidebarHeight - this.panelHeight + this.titlebarHeight : this.sidebarHeight + this.titlebarHeight); } getHorizontalSashLeft(sash: Sash): number { diff --git a/src/vs/workbench/browser/media/part.css b/src/vs/workbench/browser/media/part.css index f9f85749d90..fc87a52eaf3 100644 --- a/src/vs/workbench/browser/media/part.css +++ b/src/vs/workbench/browser/media/part.css @@ -36,6 +36,9 @@ font-weight: normal; -webkit-margin-before: 0; -webkit-margin-after: 0; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; } .monaco-workbench > .part > .title > .title-label a { diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index 6d1fa885470..773bbe0d763 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -21,7 +21,7 @@ import { activeContrastBorder, focusBorder } from 'vs/platform/theme/common/colo import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { ActivityAction, ActivityActionItem, ICompositeBarColors, ToggleCompositePinnedAction, ICompositeBar } from 'vs/workbench/browser/parts/compositebar/compositeBarActions'; +import { ActivityAction, ActivityActionItem, ICompositeBarColors, ToggleCompositePinnedAction, ICompositeBar } from 'vs/workbench/browser/parts/compositeBarActions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import URI from 'vs/base/common/uri'; diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 298c940d475..52d0ff4ccf3 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -8,7 +8,6 @@ import 'vs/css!./media/activitybarpart'; import * as nls from 'vs/nls'; import { illegalArgument } from 'vs/base/common/errors'; -import { $ } from 'vs/base/browser/builder'; import { ActionsOrientation, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { GlobalActivityExtensions, IGlobalActivityRegistry } from 'vs/workbench/common/activity'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -23,15 +22,15 @@ import { ToggleActivityBarVisibilityAction } from 'vs/workbench/browser/actions/ import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme'; import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; -import { CompositeBar } from 'vs/workbench/browser/parts/compositebar/compositeBar'; +import { CompositeBar } from 'vs/workbench/browser/parts/compositeBar'; import { isMacintosh } from 'vs/base/common/platform'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { scheduleAtNextAnimationFrame, Dimension } from 'vs/base/browser/dom'; +import { scheduleAtNextAnimationFrame, Dimension, addClass } from 'vs/base/browser/dom'; import { Color } from 'vs/base/common/color'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import URI from 'vs/base/common/uri'; -import { ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositebar/compositeBarActions'; +import { ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositeBarActions'; import { ViewletDescriptor } from 'vs/workbench/browser/viewlet'; interface IPlaceholderComposite { @@ -156,14 +155,19 @@ export class ActivitybarPart extends Part { } createContentArea(parent: HTMLElement): HTMLElement { - const $el = $(parent); - const $result = $('.content').appendTo($el); + const content = document.createElement('div'); + addClass(content, 'content'); + parent.appendChild(content); // Top Actionbar with action items for each viewlet action - this.compositeBar.create($result.getHTMLElement()); + this.compositeBar.create(content); // Top Actionbar with action items for each viewlet action - this.createGlobalActivityActionBar($('.global-activity').appendTo($result).getHTMLElement()); + const globalActivities = document.createElement('div'); + addClass(globalActivities, 'global-activity'); + content.appendChild(globalActivities); + + this.createGlobalActivityActionBar(globalActivities); // TODO@Ben: workaround for https://github.com/Microsoft/vscode/issues/45700 // It looks like there are rendering glitches on macOS with Chrome 61 when @@ -186,26 +190,26 @@ export class ActivitybarPart extends Part { }); } - return $result.getHTMLElement(); + return content; } updateStyles(): void { super.updateStyles(); // Part container - const container = $(this.getContainer()); + const container = this.getContainer(); const background = this.getColor(ACTIVITY_BAR_BACKGROUND); - container.style('background-color', background); + container.style.backgroundColor = background; const borderColor = this.getColor(ACTIVITY_BAR_BORDER) || this.getColor(contrastBorder); const isPositionLeft = this.partService.getSideBarPosition() === SideBarPosition.LEFT; - container.style('box-sizing', borderColor && isPositionLeft ? 'border-box' : null); - container.style('border-right-width', borderColor && isPositionLeft ? '1px' : null); - container.style('border-right-style', borderColor && isPositionLeft ? 'solid' : null); - container.style('border-right-color', isPositionLeft ? borderColor : null); - container.style('border-left-width', borderColor && !isPositionLeft ? '1px' : null); - container.style('border-left-style', borderColor && !isPositionLeft ? 'solid' : null); - container.style('border-left-color', !isPositionLeft ? borderColor : null); + container.style.boxSizing = borderColor && isPositionLeft ? 'border-box' : null; + container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : null; + container.style.borderRightStyle = borderColor && isPositionLeft ? 'solid' : null; + container.style.borderRightColor = isPositionLeft ? borderColor : null; + container.style.borderLeftWidth = borderColor && !isPositionLeft ? '1px' : null; + container.style.borderLeftStyle = borderColor && !isPositionLeft ? 'solid' : null; + container.style.borderLeftColor = !isPositionLeft ? borderColor : null; } private createGlobalActivityActionBar(container: HTMLElement): void { diff --git a/src/vs/workbench/browser/parts/compositebar/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts similarity index 99% rename from src/vs/workbench/browser/parts/compositebar/compositeBar.ts rename to src/vs/workbench/browser/parts/compositeBar.ts index 54f2748037a..8731222e1fe 100644 --- a/src/vs/workbench/browser/parts/compositebar/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -14,7 +14,7 @@ import { IBadge } from 'vs/workbench/services/activity/common/activity'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ActionBar, ActionsOrientation, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -import { CompositeActionItem, CompositeOverflowActivityAction, ICompositeActivity, CompositeOverflowActivityActionItem, ActivityAction, ICompositeBar, ICompositeBarColors } from 'vs/workbench/browser/parts/compositebar/compositeBarActions'; +import { CompositeActionItem, CompositeOverflowActivityAction, ICompositeActivity, CompositeOverflowActivityActionItem, ActivityAction, ICompositeBar, ICompositeBarColors } from 'vs/workbench/browser/parts/compositeBarActions'; import { TPromise } from 'vs/base/common/winjs.base'; import { Dimension, $, addDisposableListener, EventType, EventHelper } from 'vs/base/browser/dom'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; diff --git a/src/vs/workbench/browser/parts/compositebar/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts similarity index 98% rename from src/vs/workbench/browser/parts/compositebar/compositeBarActions.ts rename to src/vs/workbench/browser/parts/compositeBarActions.ts index 6d6647e8c5a..eed75df278b 100644 --- a/src/vs/workbench/browser/parts/compositebar/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -604,8 +604,10 @@ export class CompositeActionItem extends ActivityActionItem { protected _updateChecked(): void { if (this.getAction().checked) { this.$container.addClass('checked'); + this.$container.attr('aria-label', nls.localize('compositeActive', "{0} active", this.$container.getHTMLElement().title)); } else { this.$container.removeClass('checked'); + this.$container.attr('aria-label', this.$container.getHTMLElement().title); } } diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts index 136fc5bf794..e0e5dfa0fc8 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts @@ -85,7 +85,13 @@ export abstract class BreadcrumbsConfig { return { name, - get value() { return value; }, + get value() { + return value; + }, + set value(newValue: T) { + service.updateValue(name, newValue); + value = newValue; + }, onDidChange: onDidChange.event, dispose(): void { listener.dispose(); @@ -108,13 +114,13 @@ Registry.as(Extensions.Configuration).registerConfigurat type: 'boolean', default: false }, - 'breadcrumbs.useQuickPick': { - description: localize('useQuickPick', "Use quick pick instead of breadcrumb-pickers."), - type: 'boolean', - default: false - }, + // 'breadcrumbs.useQuickPick': { + // description: localize('useQuickPick', "Use quick pick instead of breadcrumb-pickers."), + // type: 'boolean', + // default: false + // }, 'breadcrumbs.filePath': { - description: localize('filepath', "Controls if and how file paths are shown in the breadcrumbs view."), + description: localize('filepath', "Controls whether and how file paths are shown in the breadcrumbs view."), type: 'string', default: 'on', enum: ['on', 'off', 'last'], @@ -125,12 +131,12 @@ Registry.as(Extensions.Configuration).registerConfigurat ] }, 'breadcrumbs.symbolPath': { - description: localize('symbolpath', "Controls if and how symbols are shown in the breadcrumbs view."), + description: localize('symbolpath', "Controls whether and how symbols are shown in the breadcrumbs view."), type: 'string', default: 'on', enum: ['on', 'off', 'last'], enumDescriptions: [ - localize('symbolpath.on', "Show all symbols the breadcrumbs view."), + localize('symbolpath.on', "Show all symbols in the breadcrumbs view."), localize('symbolpath.off', "Do not show symbols in the breadcrumbs view."), localize('symbolpath.last', "Only show the current symbol in the breadcrumbs view."), ] diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index ac16a00b45f..ad040a10a27 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -6,36 +6,40 @@ 'use strict'; import * as dom from 'vs/base/browser/dom'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { BreadcrumbsItem, BreadcrumbsWidget, IBreadcrumbsItemEvent } from 'vs/base/browser/ui/breadcrumbs/breadcrumbsWidget'; import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { dispose, IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; -import { isEqual, basenameOrAuthority } from 'vs/base/common/resources'; -import URI from 'vs/base/common/uri'; +import { combinedDisposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; +import { isEqual } from 'vs/base/common/resources'; import 'vs/css!./media/breadcrumbscontrol'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { Range } from 'vs/editor/common/core/range'; +import { symbolKindToCssClass } from 'vs/editor/common/modes'; import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { FileKind, IFileService } from 'vs/platform/files/common/files'; -import { IConstructorSignature2, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { attachBreadcrumbsStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { FileLabel } from 'vs/workbench/browser/labels'; +import { BreadcrumbsConfig, IBreadcrumbsService } from 'vs/workbench/browser/parts/editor/breadcrumbs'; import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel'; +import { createBreadcrumbsPicker, BreadcrumbsPicker } from 'vs/workbench/browser/parts/editor/breadcrumbsPicker'; import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorService, SIDE_GROUP, SIDE_GROUP_TYPE, ACTIVE_GROUP_TYPE, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; -import { IBreadcrumbsService, BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs'; -import { symbolKindToCssClass } from 'vs/editor/common/modes'; -import { BreadcrumbsPicker, BreadcrumbsFilePicker, BreadcrumbsOutlinePicker } from 'vs/workbench/browser/parts/editor/breadcrumbsPicker'; -import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; -import { Schemas } from 'vs/base/common/network'; +import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { localize } from 'vs/nls'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { tail } from 'vs/base/common/arrays'; +import { WorkbenchListFocusContextKey } from 'vs/platform/list/browser/listService'; class Item extends BreadcrumbsItem { @@ -69,20 +73,22 @@ class Item extends BreadcrumbsItem { render(container: HTMLElement): void { if (this.element instanceof FileElement) { // file/folder - if (this.options.showFileIcons) { - let label = this._instantiationService.createInstance(FileLabel, container, {}); - label.setFile(this.element.uri, { - hidePath: true, - fileKind: this.element.isFile ? FileKind.FILE : FileKind.FOLDER, - fileDecorations: { colors: this.options.showDecorationColors, badges: false } - }); - this._disposables.push(label); + let label = this._instantiationService.createInstance(FileLabel, container, {}); + label.setFile(this.element.uri, { + hidePath: true, + hideIcon: this.element.kind === FileKind.FOLDER || !this.options.showFileIcons, + fileKind: this.element.kind, + fileDecorations: { colors: this.options.showDecorationColors, badges: false }, + }); + dom.addClass(container, FileKind[this.element.kind].toLowerCase()); + this._disposables.push(label); - } else { - let label = new IconLabel(container); - label.setValue(basenameOrAuthority(this.element.uri)); - this._disposables.push(label); - } + } else if (this.element instanceof OutlineModel) { + // has outline element but not in one + let label = document.createElement('div'); + label.innerHTML = '…'; + label.className = 'hint-more'; + container.appendChild(label); } else if (this.element instanceof OutlineGroup) { // provider @@ -92,16 +98,15 @@ class Item extends BreadcrumbsItem { } else if (this.element instanceof OutlineElement) { // symbol - if (this.options.showSymbolIcons) { let icon = document.createElement('div'); - icon.className = `symbol-icon ${symbolKindToCssClass(this.element.symbol.kind)}`; + icon.className = symbolKindToCssClass(this.element.symbol.kind); container.appendChild(icon); - container.classList.add('shows-symbol-icon'); + dom.addClass(container, 'shows-symbol-icon'); } - let label = new IconLabel(container); - label.setValue(this.element.symbol.name.replace(/\r|\n|\r\n/g, '\u23CE')); + let title = this.element.symbol.name.replace(/\r|\n|\r\n/g, '\u23CE'); + label.setValue(title); this._disposables.push(label); } } @@ -111,6 +116,7 @@ export interface IBreadcrumbsControlOptions { showFileIcons: boolean; showSymbolIcons: boolean; showDecorationColors: boolean; + extraClasses: string[]; } export class BreadcrumbsControl { @@ -118,6 +124,7 @@ export class BreadcrumbsControl { static HEIGHT = 25; static readonly Payload_Reveal = {}; + static readonly Payload_RevealAside = {}; static readonly Payload_Pick = {}; static CK_BreadcrumbsVisible = new RawContextKey('breadcrumbsVisible', false); @@ -151,7 +158,8 @@ export class BreadcrumbsControl { @IBreadcrumbsService breadcrumbsService: IBreadcrumbsService, ) { this.domNode = document.createElement('div'); - dom.addClasses(this.domNode, 'breadcrumbs-control'); + dom.addClass(this.domNode, 'breadcrumbs-control'); + dom.addClasses(this.domNode, ..._options.extraClasses); dom.append(container, this.domNode); this._widget = new BreadcrumbsWidget(this.domNode); @@ -222,11 +230,19 @@ export class BreadcrumbsControl { let listener = model.onDidUpdate(updateBreadcrumbs); updateBreadcrumbs(); this._breadcrumbsDisposables = [model, listener]; + + // close picker on hide/update + this._breadcrumbsDisposables.push({ + dispose: () => { + if (this._breadcrumbsPickerShowing) { + this._contextViewService.hideContextView(this); + } + } + }); + return true; } - - private _onFocusEvent(event: IBreadcrumbsItemEvent): void { if (event.item && this._breadcrumbsPickerShowing) { return this._widget.setSelection(event.item); @@ -241,11 +257,12 @@ export class BreadcrumbsControl { this._editorGroup.focus(); const { element } = event.item as Item; - if (this._shouldRevealItem(event)) { + const group = this._getEditorGroup(event.payload); + if (group !== undefined) { // reveal the item this._widget.setFocused(undefined); this._widget.setSelection(undefined); - this._revealInEditor(element); + this._revealInEditor(event, element, group); return; } @@ -258,30 +275,48 @@ export class BreadcrumbsControl { } // show picker + let picker: BreadcrumbsPicker; this._contextViewService.showContextView({ - getAnchor() { - return event.node; - }, render: (parent: HTMLElement) => { - let ctor: IConstructorSignature2 = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker; - let res = this._instantiationService.createInstance(ctor, parent, element); - res.layout({ width: 220, height: 330 }); - let listener = res.onDidPickElement(data => { - this._contextViewService.hideContextView(); - this._widget.setFocused(undefined); - this._widget.setSelection(undefined); - if (data) { - this._revealInEditor(data); - } + picker = createBreadcrumbsPicker(this._instantiationService, parent, element); + let listener = picker.onDidPickElement(data => { + this._contextViewService.hideContextView(this); + this._revealInEditor(event, data.target, this._getEditorGroup(data.payload && data.payload.originalEvent)); }); this._breadcrumbsPickerShowing = true; this._updateCkBreadcrumbsActive(); - return combinedDisposable([listener, res]); + return combinedDisposable([listener, picker]); + }, + getAnchor() { + + let pickerHeight = 330; + let pickerWidth = Math.max(220, dom.getTotalWidth(event.node)); + let pickerArrowSize = 8; + let pickerArrowOffset: number; + + let data = dom.getDomNodePagePosition(event.node.firstChild as HTMLElement); + let y = data.top + data.height - pickerArrowSize; + let x = data.left; + if (x + pickerWidth >= window.innerWidth) { + x = window.innerWidth - pickerWidth; + } + if (event.payload instanceof StandardMouseEvent) { + pickerArrowOffset = event.payload.posx - x - pickerArrowSize; + } else { + pickerArrowOffset = (data.left + (data.width * .3)) - x; + } + picker.layout(pickerHeight, pickerWidth, pickerArrowSize, Math.max(0, pickerArrowOffset)); + picker.setInput(element); + return { x, y }; }, onHide: (data) => { this._breadcrumbsPickerShowing = false; this._updateCkBreadcrumbsActive(); + if (data === this) { + this._widget.setFocused(undefined); + this._widget.setSelection(undefined); + } } }); } @@ -291,48 +326,111 @@ export class BreadcrumbsControl { this._ckBreadcrumbsActive.set(value); } - private _revealInEditor(data: any): void { - if (URI.isUri(data)) { - // open new editor - this._editorService.openEditor({ resource: data }); - } else if (data instanceof FileElement) { - // - this._editorService.openEditor({ resource: data.uri }); + private _revealInEditor(event: IBreadcrumbsItemEvent, element: any, group: SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE): void { + if (element instanceof FileElement) { + if (element.kind === FileKind.FILE) { + // open file in editor + this._editorService.openEditor({ resource: element.uri }, group); + } else { + // show next picker + let items = this._widget.getItems(); + let idx = items.indexOf(event.item); + this._widget.setFocused(items[idx + 1]); + this._widget.setSelection(items[idx + 1], BreadcrumbsControl.Payload_Pick); + } - } else if (data instanceof OutlineElement) { - // - let model = OutlineModel.get(data); + } else if (element instanceof OutlineElement) { + // open symbol in editor + let model = OutlineModel.get(element); this._editorService.openEditor({ resource: model.textModel.uri, - options: { selection: Range.collapseToStart(data.symbol.selectionRange) } - }); + options: { selection: Range.collapseToStart(element.symbol.selectionRange) } + }, group); } } - private _shouldRevealItem({ payload }: IBreadcrumbsItemEvent): boolean { - return payload === BreadcrumbsControl.Payload_Reveal || (payload instanceof StandardMouseEvent && payload.metaKey); + private _getEditorGroup(data: StandardMouseEvent | object): SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE | undefined { + if (data === BreadcrumbsControl.Payload_RevealAside || (data instanceof StandardMouseEvent && data.altKey)) { + return SIDE_GROUP; + } else if (data === BreadcrumbsControl.Payload_Reveal || (data instanceof StandardMouseEvent && data.metaKey)) { + return ACTIVE_GROUP; + } else { + return undefined; + } } } //#region commands +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: 'breadcrumbs.toggle', + title: localize('cmd.toggle', "Toggle Breadcrumbs"), + category: localize('cmd.category', "View") + } +}); +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '5_editor', + order: 99, + command: { + id: 'breadcrumbs.toggle', + title: localize('cmd.toggle', "Toggle Breadcrumbs") + } +}); +CommandsRegistry.registerCommand('breadcrumbs.toggle', accessor => { + let config = accessor.get(IConfigurationService); + let value = BreadcrumbsConfig.IsEnabled.bindTo(config).value; + BreadcrumbsConfig.IsEnabled.bindTo(config).value = !value; +}); + +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: 'breadcrumbs.focusAndSelect', + title: localize('cmd.focus', "Focus Breadcrumbs"), + precondition: BreadcrumbsControl.CK_BreadcrumbsVisible + } +}); KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'breadcrumbs.focus', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + id: 'breadcrumbs.focusAndSelect', + weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_DOT, when: BreadcrumbsControl.CK_BreadcrumbsVisible, handler(accessor) { const groups = accessor.get(IEditorGroupsService); const breadcrumbs = accessor.get(IBreadcrumbsService); - //todo@joh focus last? - breadcrumbs.getWidget(groups.activeGroup.id).domFocus(); + const widget = breadcrumbs.getWidget(groups.activeGroup.id); + if (widget) { + const item = tail(widget.getItems()); + widget.setFocused(item); + widget.setSelection(item, BreadcrumbsControl.Payload_Pick); + } } }); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'breadcrumbs.focus', + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_SEMICOLON, + when: BreadcrumbsControl.CK_BreadcrumbsVisible, + handler(accessor) { + const groups = accessor.get(IEditorGroupsService); + const breadcrumbs = accessor.get(IBreadcrumbsService); + const widget = breadcrumbs.getWidget(groups.activeGroup.id); + if (widget) { + const item = tail(widget.getItems()); + widget.setFocused(item); + } + } +}); + KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'breadcrumbs.focusNext', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, primary: KeyCode.RightArrow, - secondary: [KeyMod.Shift | KeyCode.RightArrow], + secondary: [KeyMod.CtrlCmd | KeyCode.RightArrow], + mac: { + primary: KeyCode.RightArrow, + secondary: [KeyMod.Alt | KeyCode.RightArrow], + }, when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive), handler(accessor) { const groups = accessor.get(IEditorGroupsService); @@ -342,9 +440,13 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ }); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'breadcrumbs.focusPrevious', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, primary: KeyCode.LeftArrow, - secondary: [KeyMod.Shift | KeyCode.LeftArrow], + secondary: [KeyMod.CtrlCmd | KeyCode.LeftArrow], + mac: { + primary: KeyCode.LeftArrow, + secondary: [KeyMod.Alt | KeyCode.LeftArrow], + }, when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive), handler(accessor) { const groups = accessor.get(IEditorGroupsService); @@ -354,7 +456,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ }); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'breadcrumbs.selectFocused', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, primary: KeyCode.Enter, secondary: [KeyCode.DownArrow], when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive), @@ -367,9 +469,9 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ }); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'breadcrumbs.revealFocused', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), - primary: KeyMod.Shift | KeyCode.Enter, - secondary: [KeyCode.Space], + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyCode.Space, + secondary: [KeyMod.CtrlCmd | KeyCode.Enter], when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive), handler(accessor) { const groups = accessor.get(IEditorGroupsService); @@ -380,9 +482,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ }); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'breadcrumbs.selectEditor', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib + 1, primary: KeyCode.Escape, - secondary: [KeyMod.Shift | KeyCode.Escape], when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive), handler(accessor) { const groups = accessor.get(IEditorGroupsService); @@ -392,5 +493,16 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ groups.activeGroup.activeControl.focus(); } }); - +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'breadcrumbs.revealFocusedFromTreeAside', + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.CtrlCmd | KeyCode.Enter, + when: ContextKeyExpr.and(BreadcrumbsControl.CK_BreadcrumbsVisible, BreadcrumbsControl.CK_BreadcrumbsActive, WorkbenchListFocusContextKey), + handler(accessor) { + const groups = accessor.get(IEditorGroupsService); + const breadcrumbs = accessor.get(IBreadcrumbsService); + const widget = breadcrumbs.getWidget(groups.activeGroup.id); + widget.setSelection(widget.getFocused(), BreadcrumbsControl.Payload_RevealAside); + } +}); //#endregion diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts index 39e2c016c23..142765257c1 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts @@ -12,28 +12,28 @@ import { size } from 'vs/base/common/collections'; import { onUnexpectedError } from 'vs/base/common/errors'; import { debounceEvent, Emitter, Event } from 'vs/base/common/event'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import * as paths from 'vs/base/common/paths'; -import { isEqual } from 'vs/base/common/resources'; +import { isEqual, dirname } from 'vs/base/common/resources'; import URI from 'vs/base/common/uri'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IPosition } from 'vs/editor/common/core/position'; import { DocumentSymbolProviderRegistry } from 'vs/editor/common/modes'; -import { OutlineElement, OutlineGroup, OutlineModel } from 'vs/editor/contrib/documentSymbols/outlineModel'; +import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { Schemas } from 'vs/base/common/network'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs'; +import { FileKind } from 'vs/platform/files/common/files'; export class FileElement { constructor( readonly uri: URI, - readonly isFile: boolean + readonly kind: FileKind ) { } } -export type BreadcrumbElement = FileElement | OutlineGroup | OutlineElement; +export type BreadcrumbElement = FileElement | OutlineModel | OutlineGroup | OutlineElement; -type FileInfo = { path: FileElement[], folder: IWorkspaceFolder, showFolder: boolean }; +type FileInfo = { path: FileElement[], folder: IWorkspaceFolder }; export class EditorBreadcrumbsModel { @@ -43,7 +43,7 @@ export class EditorBreadcrumbsModel { private readonly _cfgFilePath: BreadcrumbsConfig<'on' | 'off' | 'last'>; private readonly _cfgSymbolPath: BreadcrumbsConfig<'on' | 'off' | 'last'>; - private _outlineElements: (OutlineGroup | OutlineElement)[] = []; + private _outlineElements: (OutlineModel | OutlineGroup | OutlineElement)[] = []; private _outlineDisposables: IDisposable[] = []; private _onDidUpdate = new Emitter(); @@ -101,14 +101,12 @@ export class EditorBreadcrumbsModel { if (uri.scheme === Schemas.untitled) { return { - showFolder: false, folder: undefined, path: [] }; } let info: FileInfo = { - showFolder: workspaceService.getWorkbenchState() === WorkbenchState.WORKSPACE, folder: workspaceService.getWorkspaceFolder(uri), path: [] }; @@ -117,8 +115,12 @@ export class EditorBreadcrumbsModel { if (info.folder && isEqual(info.folder.uri, uri)) { break; } - info.path.unshift(new FileElement(uri, info.path.length === 0)); - uri = uri.with({ path: paths.dirname(uri.path) }); + info.path.unshift(new FileElement(uri, info.path.length === 0 ? FileKind.FILE : FileKind.FOLDER)); + uri = dirname(uri); + } + + if (info.folder && workspaceService.getWorkbenchState() === WorkbenchState.WORKSPACE) { + info.path.unshift(new FileElement(info.folder.uri, FileKind.ROOT_FOLDER)); } return info; } @@ -163,29 +165,37 @@ export class EditorBreadcrumbsModel { }); OutlineModel.create(buffer, source.token).then(model => { + if (TreeElement.empty(model)) { + // empty -> no outline elements + this._updateOutlineElements([]); - // copy the model - model = model.adopt(); + } else { + // copy the model + model = model.adopt(); - this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition())); - this._outlineDisposables.push(this._editor.onDidChangeCursorPosition(_ => { - timeout.cancelAndSet(() => { - if (!buffer.isDisposed() && versionIdThen === buffer.getVersionId()) { - this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition())); - } - }, 150); - })); + this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition())); + this._outlineDisposables.push(this._editor.onDidChangeCursorPosition(_ => { + timeout.cancelAndSet(() => { + if (!buffer.isDisposed() && versionIdThen === buffer.getVersionId()) { + this._updateOutlineElements(this._getOutlineElements(model, this._editor.getPosition())); + } + }, 150); + })); + } }).catch(err => { this._updateOutlineElements([]); onUnexpectedError(err); }); } - private _getOutlineElements(model: OutlineModel, position: IPosition): (OutlineGroup | OutlineElement)[] { + private _getOutlineElements(model: OutlineModel, position: IPosition): (OutlineModel | OutlineGroup | OutlineElement)[] { if (!model) { return []; } let item: OutlineGroup | OutlineElement = model.getItemEnclosingPosition(position); + if (!item) { + return [model]; + } let chain: (OutlineGroup | OutlineElement)[] = []; while (item) { chain.push(item); @@ -201,14 +211,14 @@ export class EditorBreadcrumbsModel { return chain.reverse(); } - private _updateOutlineElements(elements: (OutlineGroup | OutlineElement)[]): void { + private _updateOutlineElements(elements: (OutlineModel | OutlineGroup | OutlineElement)[]): void { if (!equals(elements, this._outlineElements, EditorBreadcrumbsModel._outlineElementEquals)) { this._outlineElements = elements; this._onDidUpdate.fire(this); } } - private static _outlineElementEquals(a: OutlineGroup | OutlineElement, b: OutlineGroup | OutlineElement): boolean { + private static _outlineElementEquals(a: OutlineModel | OutlineGroup | OutlineElement, b: OutlineModel | OutlineGroup | OutlineElement): boolean { if (a === b) { return true; } else if (!a || !b) { diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index 65ccac4fffa..e6eef49b484 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -18,64 +18,83 @@ import { OutlineElement, OutlineModel, TreeElement } from 'vs/editor/contrib/doc import { OutlineDataSource, OutlineItemComparator, OutlineRenderer } from 'vs/editor/contrib/documentSymbols/outlineTree'; import { localize } from 'vs/nls'; import { FileKind, IFileService, IFileStat } from 'vs/platform/files/common/files'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation'; import { HighlightingWorkbenchTree, IHighlightingTreeConfiguration, IHighlightingRenderer } from 'vs/platform/list/browser/listService'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, DARK } from 'vs/platform/theme/common/themeService'; import { FileLabel } from 'vs/workbench/browser/labels'; import { BreadcrumbElement, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { breadcrumbsActiveSelectionBackground } from 'vs/platform/theme/common/colorRegistry'; +import { breadcrumbsPickerBackground } from 'vs/platform/theme/common/colorRegistry'; import { FuzzyScore, createMatches, fuzzyScore } from 'vs/base/common/filters'; +import { IWorkspaceContextService, IWorkspace, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + +export function createBreadcrumbsPicker(instantiationService: IInstantiationService, parent: HTMLElement, element: BreadcrumbElement): BreadcrumbsPicker { + let ctor: IConstructorSignature1 = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker; + return instantiationService.createInstance(ctor, parent); +} export abstract class BreadcrumbsPicker { protected readonly _disposables = new Array(); protected readonly _domNode: HTMLDivElement; - protected readonly _focus: dom.IFocusTracker; + protected readonly _arrow: HTMLDivElement; + protected readonly _treeContainer: HTMLDivElement; protected readonly _tree: HighlightingWorkbenchTree; + protected readonly _focus: dom.IFocusTracker; - protected readonly _onDidPickElement = new Emitter(); - - readonly onDidPickElement: Event = this._onDidPickElement.event; + private readonly _onDidPickElement = new Emitter<{ target: any, payload: any }>(); + readonly onDidPickElement: Event<{ target: any, payload: any }> = this._onDidPickElement.event; constructor( - container: HTMLElement, - input: BreadcrumbElement, + parent: HTMLElement, @IInstantiationService protected readonly _instantiationService: IInstantiationService, @IThemeService protected readonly _themeService: IThemeService, ) { this._domNode = document.createElement('div'); this._domNode.className = 'monaco-breadcrumbs-picker show-file-icons'; - const color = this._themeService.getTheme().getColor(breadcrumbsActiveSelectionBackground); - this._domNode.style.background = color.toString(); - this._domNode.style.boxShadow = `0px 5px 8px ${color.darken(.2)}`; - this._domNode.style.position = 'absolute'; - this._domNode.style.zIndex = '1000'; - container.appendChild(this._domNode); + parent.appendChild(this._domNode); this._focus = dom.trackFocus(this._domNode); - this._focus.onDidBlur(_ => this._onDidPickElement.fire(undefined), undefined, this._disposables); + this._focus.onDidBlur(_ => this._onDidPickElement.fire({ target: undefined, payload: undefined }), undefined, this._disposables); + + const theme = this._themeService.getTheme(); + const color = theme.getColor(breadcrumbsPickerBackground); + + this._arrow = document.createElement('div'); + this._arrow.style.width = '0'; + this._arrow.style.borderStyle = 'solid'; + this._arrow.style.borderWidth = '8px'; + this._arrow.style.borderColor = `transparent transparent ${color.toString()}`; + this._domNode.appendChild(this._arrow); + + this._treeContainer = document.createElement('div'); + this._treeContainer.style.background = color.toString(); + this._treeContainer.style.paddingTop = '2px'; + this._treeContainer.style.boxShadow = `0px 5px 8px ${(theme.type === DARK ? color.darken(.6) : color.darken(.2))}`; + this._domNode.appendChild(this._treeContainer); const treeConifg = this._completeTreeConfiguration({ dataSource: undefined, renderer: undefined }); - this._tree = this._instantiationService.createInstance(HighlightingWorkbenchTree, this._domNode, treeConifg, {}, { placeholder: localize('placeholder', "Find") }); + this._tree = this._instantiationService.createInstance( + HighlightingWorkbenchTree, + this._treeContainer, + treeConifg, + { useShadows: false }, + { placeholder: localize('placeholder', "Find") } + ); this._disposables.push(this._tree.onDidChangeSelection(e => { if (e.payload !== this._tree) { - setTimeout(_ => this._onDidChangeSelection(e)); // need to debounce here because this disposes the tree and the tree doesn't like to be disposed on click + const target = this._getTargetFromSelectionEvent(e); + if (!target) { + return; + } + setTimeout(_ => {// need to debounce here because this disposes the tree and the tree doesn't like to be disposed on click + this._onDidPickElement.fire({ target, payload: e.payload }); + }, 0); } })); - this._tree.setInput(this._getInput(input)).then(() => { - let selection = this._getInitialSelection(this._tree, input); - if (selection) { - this._tree.reveal(selection).then(() => { - this._tree.setSelection([selection], this._tree); - this._tree.setFocus(selection); - }); - } - }, onUnexpectedError); - - // this._input.focus(); - this._tree.domFocus(); + this._domNode.focus(); } dispose(): void { @@ -85,49 +104,102 @@ export abstract class BreadcrumbsPicker { this._focus.dispose(); } - layout(dim: dom.Dimension) { - this._domNode.style.width = `${dim.width}px`; - this._domNode.style.height = `${dim.height}px`; - this._tree.layout(dim.height, dim.width); + setInput(input: any): void { + let actualInput = this._getInput(input); + this._tree.setInput(actualInput).then(() => { + let selection = this._getInitialSelection(this._tree, input); + if (selection) { + this._tree.reveal(selection, .5).then(() => { + this._tree.setSelection([selection], this._tree); + this._tree.setFocus(selection); + this._tree.domFocus(); + }); + } else { + this._tree.focusFirst(); + this._tree.setSelection([this._tree.getFocus()], this._tree); + this._tree.domFocus(); + } + }, onUnexpectedError); + } + + layout(height: number, width: number, arrowSize: number, arrowOffset: number) { + + let treeHeight = height - 2 * arrowSize; + let elementHeight = 22; + let elementCount = treeHeight / elementHeight; + if (elementCount % 2 !== 1) { + treeHeight = elementHeight * (elementCount + 1); + } + let totalHeight = treeHeight + 2 + arrowSize; + + this._domNode.style.height = `${totalHeight}px`; + this._domNode.style.width = `${width}px`; + this._arrow.style.borderWidth = `${arrowSize}px`; + this._arrow.style.marginLeft = `${arrowOffset}px`; + this._treeContainer.style.height = `${treeHeight}px`; + this._treeContainer.style.width = `${width}px`; + this._tree.layout(); } protected abstract _getInput(input: BreadcrumbElement): any; protected abstract _getInitialSelection(tree: ITree, input: BreadcrumbElement): any; protected abstract _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration; - protected abstract _onDidChangeSelection(e: ISelectionEvent): void; + protected abstract _getTargetFromSelectionEvent(e: ISelectionEvent): any | undefined; } //#region - Files export class FileDataSource implements IDataSource { - private readonly _parents = new WeakMap(); + private readonly _parents = new WeakMap(); constructor( @IFileService private readonly _fileService: IFileService, ) { } - getId(tree: ITree, element: IFileStat | URI): string { - return URI.isUri(element) ? element.toString() : element.resource.toString(); + getId(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): string { + if (URI.isUri(element)) { + return element.toString(); + } else if (IWorkspace.isIWorkspace(element)) { + return element.id; + } else if (IWorkspaceFolder.isIWorkspaceFolder(element)) { + return element.uri.toString(); + } else { + return element.resource.toString(); + } } - hasChildren(tree: ITree, element: IFileStat | URI): boolean { - return URI.isUri(element) || element.isDirectory; + hasChildren(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): boolean { + return URI.isUri(element) || IWorkspace.isIWorkspace(element) || IWorkspaceFolder.isIWorkspaceFolder(element) || element.isDirectory; } - getChildren(tree: ITree, element: IFileStat | URI): TPromise { - return this._fileService.resolveFile( - URI.isUri(element) ? element : element.resource - ).then(stat => { - for (const child of stat.children) { - this._parents.set(child, stat); + getChildren(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): TPromise { + if (IWorkspace.isIWorkspace(element)) { + return TPromise.as(element.folders).then(folders => { + for (let child of folders) { + this._parents.set(element, child); + } + return folders; + }); + } + let uri: URI; + if (IWorkspaceFolder.isIWorkspaceFolder(element)) { + uri = element.uri; + } else if (URI.isUri(element)) { + uri = element; + } else { + uri = element.resource; + } + return this._fileService.resolveFile(uri).then(stat => { + for (let child of stat.children) { + this._parents.set(stat, child); } return stat.children; }); } - getParent(tree: ITree, element: IFileStat | URI): TPromise { - return TPromise.as(URI.isUri(element) ? undefined : this._parents.get(element)); + getParent(tree: ITree, element: IWorkspace | URI | IWorkspaceFolder | IFileStat): TPromise { + return TPromise.as(this._parents.get(element)); } } @@ -136,7 +208,8 @@ export class FileRenderer implements IRenderer, IHighlightingRenderer { private readonly _scores = new Map(); constructor( - @IInstantiationService private readonly _instantiationService: IInstantiationService + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IConfigurationService private readonly _configService: IConfigurationService, ) { } getHeight(tree: ITree, element: any): number { @@ -151,12 +224,23 @@ export class FileRenderer implements IRenderer, IHighlightingRenderer { return this._instantiationService.createInstance(FileLabel, container, { supportHighlights: true }); } - renderElement(tree: ITree, element: IFileStat, templateId: string, templateData: FileLabel): void { - templateData.setFile(element.resource, { + renderElement(tree: ITree, element: IFileStat | IWorkspaceFolder, templateId: string, templateData: FileLabel): void { + let fileDecorations = this._configService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations'); + let resource: URI; + let fileKind: FileKind; + if (IWorkspaceFolder.isIWorkspaceFolder(element)) { + resource = element.uri; + fileKind = FileKind.ROOT_FOLDER; + } else { + resource = element.resource; + fileKind = element.isDirectory ? FileKind.FOLDER : FileKind.FILE; + } + templateData.setFile(resource, { + fileKind, hidePath: true, - fileKind: element.isDirectory ? FileKind.FOLDER : FileKind.FILE, - fileDecorations: { colors: true, badges: true }, - matches: createMatches((this._scores.get(element.resource.toString()) || [, []])[1]) + fileDecorations: fileDecorations, + matches: createMatches((this._scores.get(resource.toString()) || [, []])[1]), + extraClasses: ['picker-item'] }); } @@ -169,9 +253,9 @@ export class FileRenderer implements IRenderer, IHighlightingRenderer { let topScore: FuzzyScore; let topElement: any; while (nav.next()) { - let element = nav.current() as IFileStat; + let element = nav.current() as IFileStat | IWorkspaceFolder; let score = fuzzyScore(pattern, element.name, undefined, true); - this._scores.set(element.resource.toString(), score); + this._scores.set(IWorkspaceFolder.isIWorkspaceFolder(element) ? element.uri.toString() : element.resource.toString(), score); if (!topScore || score && topScore[0] < score[0]) { topScore = score; topElement = element; @@ -182,31 +266,50 @@ export class FileRenderer implements IRenderer, IHighlightingRenderer { } export class FileSorter implements ISorter { - compare(tree: ITree, a: IFileStat, b: IFileStat): number { - if (a.isDirectory === b.isDirectory) { - // same type -> compare on names - return compareFileNames(a.name, b.name); - } else if (a.isDirectory) { - return -1; + compare(tree: ITree, a: IFileStat | IWorkspaceFolder, b: IFileStat | IWorkspaceFolder): number { + if (IWorkspaceFolder.isIWorkspaceFolder(a) && IWorkspaceFolder.isIWorkspaceFolder(b)) { + return a.index - b.index; } else { - return 1; + if ((a as IFileStat).isDirectory === (b as IFileStat).isDirectory) { + // same type -> compare on names + return compareFileNames(a.name, b.name); + } else if ((a as IFileStat).isDirectory) { + return -1; + } else { + return 1; + } } } } export class BreadcrumbsFilePicker extends BreadcrumbsPicker { + constructor( + parent: HTMLElement, + @IInstantiationService instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IWorkspaceContextService private readonly _workspaceService: IWorkspaceContextService, + ) { + super(parent, instantiationService, themeService); + } + protected _getInput(input: BreadcrumbElement): any { - let { uri } = (input as FileElement); - return dirname(uri); + let { uri, kind } = (input as FileElement); + if (kind === FileKind.ROOT_FOLDER) { + return this._workspaceService.getWorkspace(); + } else { + return dirname(uri); + } } protected _getInitialSelection(tree: ITree, input: BreadcrumbElement): any { let { uri } = (input as FileElement); let nav = tree.getNavigator(); while (nav.next()) { - if (isEqual(uri, (nav.current() as IFileStat).resource)) { - return nav.current(); + let cur = nav.current(); + let candidate = IWorkspaceFolder.isIWorkspaceFolder(cur) ? cur.uri : (cur as IFileStat).resource; + if (isEqual(uri, candidate)) { + return cur; } } return undefined; @@ -220,11 +323,10 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker { return config; } - protected _onDidChangeSelection(e: ISelectionEvent): void { + protected _getTargetFromSelectionEvent(e: ISelectionEvent): any | undefined { let [first] = e.selection; - let stat = first as IFileStat; - if (stat && !stat.isDirectory) { - this._onDidPickElement.fire(stat.resource); + if (first && !IWorkspaceFolder.isIWorkspaceFolder(first) && !(first as IFileStat).isDirectory) { + return new FileElement((first as IFileStat).resource, FileKind.FILE); } } } @@ -250,7 +352,7 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { } protected _getInitialSelection(_tree: ITree, input: BreadcrumbElement): any { - return input; + return input instanceof OutlineModel ? undefined : input; } protected _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration { @@ -260,13 +362,13 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { return config; } - protected _onDidChangeSelection(e: ISelectionEvent): void { + protected _getTargetFromSelectionEvent(e: ISelectionEvent): any | undefined { if (e.payload && e.payload.didClickOnTwistie) { return; } let [first] = e.selection; if (first instanceof OutlineElement) { - this._onDidPickElement.fire(first); + return first; } } } diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 20167466e78..c5dd8e397d1 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -43,7 +43,7 @@ import { import * as editorCommands from 'vs/workbench/browser/parts/editor/editorCommands'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { getQuickNavigateHandler, inQuickOpenContext } from 'vs/workbench/browser/parts/quickopen/quickopen'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { isMacintosh } from 'vs/base/common/platform'; import { AllEditorsPicker, ActiveEditorGroupPicker } from 'vs/workbench/browser/parts/editor/editorPicker'; @@ -380,7 +380,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousRecentlyUs const quickOpenNavigateNextInEditorPickerId = 'workbench.action.quickOpenNavigateNextInEditorPicker'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: quickOpenNavigateNextInEditorPickerId, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50), + weight: KeybindingWeight.WorkbenchContrib + 50, handler: getQuickNavigateHandler(quickOpenNavigateNextInEditorPickerId, true), when: editorPickerContext, primary: openNextEditorKeybinding.primary, @@ -390,7 +390,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const quickOpenNavigatePreviousInEditorPickerId = 'workbench.action.quickOpenNavigatePreviousInEditorPicker'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: quickOpenNavigatePreviousInEditorPickerId, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50), + weight: KeybindingWeight.WorkbenchContrib + 50, handler: getQuickNavigateHandler(quickOpenNavigatePreviousInEditorPickerId, false), when: editorPickerContext, primary: openPreviousEditorKeybinding.primary, @@ -536,3 +536,312 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorComman MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeSavedEditors', "Close Saved Editors in Group"), category } }); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeOtherEditors', "Close Other Editors in Group"), category } }); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: editorCommands.CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, title: nls.localize('closeRightEditors', "Close Editors to the Right in Group"), category } }); + +// File menu +MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { + group: '1_editor', + command: { + id: ReopenClosedEditorAction.ID, + title: nls.localize({ key: 'miReopenClosedEditor', comment: ['&& denotes a mnemonic'] }, "&&Reopen Closed Editor") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { + group: 'z_clear', + command: { + id: ClearRecentFilesAction.ID, + title: nls.localize({ key: 'miClearRecentOpen', comment: ['&& denotes a mnemonic'] }, "&&Clear Recently Opened") + }, + order: 1 +}); + +// Layout menu +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '2_appearance', + title: nls.localize({ key: 'miEditorLayout', comment: ['&& denotes a mnemonic'] }, "Editor &&Layout"), + submenu: MenuId.MenubarLayoutMenu, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '1_split', + command: { + id: editorCommands.SPLIT_EDITOR_UP, + title: nls.localize({ key: 'miSplitEditorUp', comment: ['&& denotes a mnemonic'] }, "Split &&Up") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '1_split', + command: { + id: editorCommands.SPLIT_EDITOR_DOWN, + title: nls.localize({ key: 'miSplitEditorDown', comment: ['&& denotes a mnemonic'] }, "Split &&Down") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '1_split', + command: { + id: editorCommands.SPLIT_EDITOR_LEFT, + title: nls.localize({ key: 'miSplitEditorLeft', comment: ['&& denotes a mnemonic'] }, "Split &&Left") + }, + order: 3 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '1_split', + command: { + id: editorCommands.SPLIT_EDITOR_RIGHT, + title: nls.localize({ key: 'miSplitEditorRight', comment: ['&& denotes a mnemonic'] }, "Split &&Right") + }, + order: 4 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '2_layouts', + command: { + id: EditorLayoutSingleAction.ID, + title: nls.localize({ key: 'miSingleColumnEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Single") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '2_layouts', + command: { + id: EditorLayoutTwoColumnsAction.ID, + title: nls.localize({ key: 'miTwoColumnsEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Two Columns") + }, + order: 3 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '2_layouts', + command: { + id: EditorLayoutThreeColumnsAction.ID, + title: nls.localize({ key: 'miThreeColumnsEditorLayout', comment: ['&& denotes a mnemonic'] }, "T&&hree Columns") + }, + order: 4 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '2_layouts', + command: { + id: EditorLayoutTwoRowsAction.ID, + title: nls.localize({ key: 'miTwoRowsEditorLayout', comment: ['&& denotes a mnemonic'] }, "T&&wo Rows") + }, + order: 5 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '2_layouts', + command: { + id: EditorLayoutThreeRowsAction.ID, + title: nls.localize({ key: 'miThreeRowsEditorLayout', comment: ['&& denotes a mnemonic'] }, "Three &&Rows") + }, + order: 6 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '2_layouts', + command: { + id: EditorLayoutTwoByTwoGridAction.ID, + title: nls.localize({ key: 'miTwoByTwoGridEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Grid (2x2)") + }, + order: 7 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '2_layouts', + command: { + id: EditorLayoutTwoRowsRightAction.ID, + title: nls.localize({ key: 'miTwoRowsRightEditorLayout', comment: ['&& denotes a mnemonic'] }, "Two R&&ows Right") + }, + order: 8 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { + group: '2_layouts', + command: { + id: EditorLayoutTwoColumnsBottomAction.ID, + title: nls.localize({ key: 'miTwoColumnsBottomEditorLayout', comment: ['&& denotes a mnemonic'] }, "Two &&Columns Bottom") + }, + order: 9 +}); + +// Main Menu Bar Contributions: + +// Forward/Back +MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { + group: '1_fwd_back', + command: { + id: 'workbench.action.navigateBack', + title: nls.localize({ key: 'miBack', comment: ['&& denotes a mnemonic'] }, "&&Back"), + precondition: ContextKeyExpr.has('canNavigateBack') + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { + group: '1_fwd_back', + command: { + id: 'workbench.action.navigateForward', + title: nls.localize({ key: 'miForward', comment: ['&& denotes a mnemonic'] }, "&&Forward"), + precondition: ContextKeyExpr.has('canNavigateForward') + }, + order: 2 +}); + +// Switch Editor +MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, { + group: '1_any', + command: { + id: 'workbench.action.nextEditor', + title: nls.localize({ key: 'miNextEditor', comment: ['&& denotes a mnemonic'] }, "&&Next Editor") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, { + group: '1_any', + command: { + id: 'workbench.action.previousEditor', + title: nls.localize({ key: 'miPreviousEditor', comment: ['&& denotes a mnemonic'] }, "&&Previous Editor") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, { + group: '2_used', + command: { + id: 'workbench.action.openNextRecentlyUsedEditorInGroup', + title: nls.localize({ key: 'miNextEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Used Editor in Group") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, { + group: '2_used', + command: { + id: 'workbench.action.openPreviousRecentlyUsedEditorInGroup', + title: nls.localize({ key: 'miPreviousEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Used Editor in Group") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { + group: '2_switch', + title: nls.localize({ key: 'miSwitchEditor', comment: ['&& denotes a mnemonic'] }, "Switch &&Editor"), + submenu: MenuId.MenubarSwitchEditorMenu, + order: 1 +}); + +// Switch Group +MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { + group: '1_focus_index', + command: { + id: 'workbench.action.focusFirstEditorGroup', + title: nls.localize({ key: 'miFocusFirstGroup', comment: ['&& denotes a mnemonic'] }, "Group &&1") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { + group: '1_focus_index', + command: { + id: 'workbench.action.focusSecondEditorGroup', + title: nls.localize({ key: 'miFocusSecondGroup', comment: ['&& denotes a mnemonic'] }, "Group &&2") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { + group: '1_focus_index', + command: { + id: 'workbench.action.focusThirdEditorGroup', + title: nls.localize({ key: 'miFocusThirdGroup', comment: ['&& denotes a mnemonic'] }, "Group &&3") + }, + order: 3 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { + group: '1_focus_index', + command: { + id: 'workbench.action.focusFourthEditorGroup', + title: nls.localize({ key: 'miFocusFourthGroup', comment: ['&& denotes a mnemonic'] }, "Group &&4") + }, + order: 4 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { + group: '1_focus_index', + command: { + id: 'workbench.action.focusFifthEditorGroup', + title: nls.localize({ key: 'miFocusFifthGroup', comment: ['&& denotes a mnemonic'] }, "Group &&5") + }, + order: 5 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { + group: '2_next_prev', + command: { + id: 'workbench.action.focusNextGroup', + title: nls.localize({ key: 'miNextGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Group") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { + group: '2_next_prev', + command: { + id: 'workbench.action.focusPreviousGroup', + title: nls.localize({ key: 'miPreviousGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Group") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { + group: '3_directional', + command: { + id: 'workbench.action.focusLeftGroup', + title: nls.localize({ key: 'miFocusLeftGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Left") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { + group: '3_directional', + command: { + id: 'workbench.action.focusRightGroup', + title: nls.localize({ key: 'miFocusRightGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Right") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { + group: '3_directional', + command: { + id: 'workbench.action.focusAboveGroup', + title: nls.localize({ key: 'miFocusAboveGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Above") + }, + order: 3 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { + group: '3_directional', + command: { + id: 'workbench.action.focusBelowGroup', + title: nls.localize({ key: 'miFocusBelowGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Below") + }, + order: 4 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { + group: '2_switch', + title: nls.localize({ key: 'miSwitchGroup', comment: ['&& denotes a mnemonic'] }, "Switch &&Group"), + submenu: MenuId.MenubarSwitchGroupMenu, + order: 2 +}); \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index 97918df94dc..b6545f0bdc6 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import * as types from 'vs/base/common/types'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { TextCompareEditorVisibleContext, EditorInput, IEditorIdentifier, IEditorCommandsContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, CloseDirection, IEditor, IEditorInput } from 'vs/workbench/common/editor'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; @@ -23,6 +23,7 @@ import { IEditorGroupsService, IEditorGroup, GroupDirection, GroupLocation, Grou import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; export const CLOSE_SAVED_EDITORS_COMMAND_ID = 'workbench.action.closeUnmodifiedEditors'; export const CLOSE_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeEditorsInGroup'; @@ -75,7 +76,7 @@ const isActiveEditorMoveArg = function (arg: ActiveEditorMoveArguments): boolean function registerActiveEditorMoveCommand(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: MOVE_ACTIVE_EDITOR_COMMAND_ID, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: EditorContextKeys.editorTextFocus, primary: null, handler: (accessor, args: any) => moveActiveEditor(args, accessor), @@ -222,7 +223,7 @@ export function mergeAllGroups(editorGroupService: IEditorGroupsService): void { function registerDiffEditorCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'workbench.action.compareEditor.nextChange', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: TextCompareEditorVisibleContext, primary: null, handler: accessor => navigateInDiffEditor(accessor, true) @@ -230,7 +231,7 @@ function registerDiffEditorCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'workbench.action.compareEditor.previousChange', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: TextCompareEditorVisibleContext, primary: null, handler: accessor => navigateInDiffEditor(accessor, false) @@ -247,7 +248,7 @@ function registerDiffEditorCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: TOGGLE_DIFF_INLINE_MODE, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: void 0, primary: void 0, handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { @@ -263,6 +264,14 @@ function registerDiffEditorCommands(): void { } } }); + + MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: TOGGLE_DIFF_INLINE_MODE, + title: nls.localize('toggleInlineView', "Compare: Toggle Inline View") + }, + when: ContextKeyExpr.has('textCompareEditorActive') + }); } function registerOpenEditorAtIndexCommands(): void { @@ -274,7 +283,7 @@ function registerOpenEditorAtIndexCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'workbench.action.openEditorAtIndex' + visibleIndex, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: void 0, primary: KeyMod.Alt | toKeyCode(visibleIndex), mac: { primary: KeyMod.WinCtrl | toKeyCode(visibleIndex) }, @@ -318,7 +327,7 @@ function registerFocusEditorGroupAtIndexCommands(): void { for (let groupIndex = 1; groupIndex < 8; groupIndex++) { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: toCommandId(groupIndex), - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: void 0, primary: KeyMod.CtrlCmd | toKeyCode(groupIndex), handler: accessor => { @@ -422,7 +431,7 @@ function registerCloseEditorCommands() { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: CLOSE_SAVED_EDITORS_COMMAND_ID, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: void 0, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_U), handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { @@ -442,7 +451,7 @@ function registerCloseEditorCommands() { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: void 0, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_W), handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { @@ -462,7 +471,7 @@ function registerCloseEditorCommands() { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: CLOSE_EDITOR_COMMAND_ID, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: void 0, primary: KeyMod.CtrlCmd | KeyCode.KEY_W, win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_W] }, @@ -490,7 +499,7 @@ function registerCloseEditorCommands() { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: CLOSE_EDITOR_GROUP_COMMAND_ID, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext), primary: KeyMod.CtrlCmd | KeyCode.KEY_W, win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_W] }, @@ -511,7 +520,7 @@ function registerCloseEditorCommands() { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: void 0, primary: void 0, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_T }, @@ -540,7 +549,7 @@ function registerCloseEditorCommands() { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: void 0, primary: void 0, handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { @@ -557,7 +566,7 @@ function registerCloseEditorCommands() { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: KEEP_EDITOR_COMMAND_ID, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: void 0, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.Enter), handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { @@ -574,7 +583,7 @@ function registerCloseEditorCommands() { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: SHOW_EDITORS_IN_GROUP, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: void 0, primary: void 0, handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 372e4e6999d..2f4c869314a 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -337,8 +337,10 @@ export class EditorGroupView extends Themable implements IEditorGroupView { target = (e as GestureEvent).initialTarget as HTMLElement; } - if (findParentWithClass(target, 'monaco-action-bar', this.titleContainer)) { - return; // not when clicking on actions + if (findParentWithClass(target, 'monaco-action-bar', this.titleContainer) || + findParentWithClass(target, 'monaco-breadcrumb-item', this.titleContainer) + ) { + return; // not when clicking on actions or breadcrumbs } // timeout to keep focus in editor after mouse up @@ -956,7 +958,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.doCloseInactiveEditor(editor); } - // Forward to title control & breadcrumbs + // Forward to title control this.titleAreaControl.closeEditor(editor); } @@ -1371,6 +1373,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this._onWillDispose.fire(); this.titleAreaControl.dispose(); + // this.editorControl = null; super.dispose(); } diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 36d3ffb96b5..553ff7e0c65 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -10,20 +10,20 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { Part } from 'vs/workbench/browser/part'; import { Dimension, isAncestor, toggleClass, addClass, $ } from 'vs/base/browser/dom'; import { Event, Emitter, once, Relay, anyEvent } from 'vs/base/common/event'; -import { contrastBorder, editorBackground, registerColor } from 'vs/platform/theme/common/colorRegistry'; +import { contrastBorder, editorBackground } from 'vs/platform/theme/common/colorRegistry'; import { GroupDirection, IAddGroupOptions, GroupsArrangement, GroupOrientation, IMergeGroupOptions, MergeGroupMode, ICopyEditorOptions, GroupsOrder, GroupChangeKind, GroupLocation, IFindGroupScope, EditorGroupLayout, GroupLayoutArgument } from 'vs/workbench/services/group/common/editorGroupsService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { Direction, SerializableGrid, Sizing, ISerializedGrid, Orientation, ISerializedNode, GridBranchNode, isGridBranchNode, GridNode, createSerializedGrid, Grid } from 'vs/base/browser/ui/grid/grid'; +import { Direction, SerializableGrid, Sizing, ISerializedGrid, Orientation, GridBranchNode, isGridBranchNode, GridNode, createSerializedGrid, Grid } from 'vs/base/browser/ui/grid/grid'; import { GroupIdentifier, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor'; import { values } from 'vs/base/common/map'; -import { EDITOR_GROUP_BORDER } from 'vs/workbench/common/theme'; +import { EDITOR_GROUP_BORDER, EDITOR_PANE_BACKGROUND } from 'vs/workbench/common/theme'; import { distinct } from 'vs/base/common/arrays'; import { IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptions, getEditorPartOptions, impactsEditorPartOptions, IEditorPartOptionsChangeEvent, EditorGroupsServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { assign } from 'vs/base/common/objects'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService } from 'vs/platform/storage/common/storage'; import { Scope } from 'vs/workbench/common/memento'; import { ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup'; import { TValueCallback, TPromise } from 'vs/base/common/winjs.base'; @@ -82,12 +82,6 @@ class GridWidgetView implements IView { } } -export const EDITOR_PANE_BACKGROUND = registerColor('editorPane.background', { - dark: editorBackground, - light: editorBackground, - hc: editorBackground -}, localize('editorPaneBackground', "Background color of the editor pane visible on the left and right side of the centered editor layout.")); - export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditorGroupsAccessor { _serviceBrand: any; @@ -708,7 +702,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor // Move/Copy editors over into target let index = (options && typeof options.index === 'number') ? options.index : targetView.count; sourceView.editors.forEach(editor => { - const inactive = !sourceView.isActive(editor); + const inactive = !sourceView.isActive(editor) || this._activeGroup !== sourceView; const copyOptions: ICopyEditorOptions = { index, inactive, preserveFocus: inactive }; if (options && options.mode === MergeGroupMode.COPY_EDITORS) { @@ -828,7 +822,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor } private doCreateGridControlWithPreviousState(): void { - const uiState = this.doGetPreviousState(); + const uiState = this.memento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY] as IEditorPartUIState; if (uiState && uiState.serializedGrid) { // MRU @@ -889,123 +883,6 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor this.onDidSetGridWidget.fire(); } - private doGetPreviousState(): IEditorPartUIState { - const legacyState = this.doGetPreviousLegacyState(); - if (legacyState) { - return legacyState; // TODO@ben remove after a while - } - - return this.memento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY] as IEditorPartUIState; - } - - private doGetPreviousLegacyState(): IEditorPartUIState { - const LEGACY_EDITOR_PART_UI_STATE_STORAGE_KEY = 'editorpart.uiState'; - const LEGACY_STACKS_MODEL_STORAGE_KEY = 'editorStacks.model'; - - interface ILegacyEditorPartUIState { - ratio: number[]; - groupOrientation: 'vertical' | 'horizontal'; - } - - interface ISerializedLegacyEditorStacksModel { - groups: ISerializedEditorGroup[]; - active: number; - } - - let legacyUIState: ISerializedLegacyEditorStacksModel; - const legacyUIStateRaw = this.storageService.get(LEGACY_STACKS_MODEL_STORAGE_KEY, StorageScope.WORKSPACE); - if (legacyUIStateRaw) { - try { - legacyUIState = JSON.parse(legacyUIStateRaw); - } catch (error) { /* ignore */ } - } - - if (legacyUIState) { - this.storageService.remove(LEGACY_STACKS_MODEL_STORAGE_KEY, StorageScope.WORKSPACE); - } - - const legacyPartState = this.memento[LEGACY_EDITOR_PART_UI_STATE_STORAGE_KEY] as ILegacyEditorPartUIState; - if (legacyPartState) { - delete this.memento[LEGACY_EDITOR_PART_UI_STATE_STORAGE_KEY]; - } - - if (legacyUIState && Array.isArray(legacyUIState.groups) && legacyUIState.groups.length > 0) { - const splitHorizontally = legacyPartState && legacyPartState.groupOrientation === 'horizontal'; - - const legacyState: IEditorPartUIState = Object.create(null); - - const positionOneGroup = legacyUIState.groups[0]; - const positionTwoGroup = legacyUIState.groups[1]; - const positionThreeGroup = legacyUIState.groups[2]; - - legacyState.activeGroup = legacyUIState.active; - legacyState.mostRecentActiveGroups = [legacyUIState.active]; - - if (positionTwoGroup || positionThreeGroup) { - if (!positionThreeGroup) { - legacyState.mostRecentActiveGroups.push(legacyState.activeGroup === 0 ? 1 : 0); - } else { - if (legacyState.activeGroup === 0) { - legacyState.mostRecentActiveGroups.push(1, 2); - } else if (legacyState.activeGroup === 1) { - legacyState.mostRecentActiveGroups.push(0, 2); - } else { - legacyState.mostRecentActiveGroups.push(0, 1); - } - } - } - - const toNode = function (group: ISerializedEditorGroup, size: number): ISerializedNode { - return { - data: group, - size, - type: 'leaf' - }; - }; - - const baseSize = 1200; // just some number because layout() was not called yet, but we only need the proportions - - // No split editor - if (!positionTwoGroup) { - legacyState.serializedGrid = { - width: baseSize, - height: baseSize, - orientation: splitHorizontally ? Orientation.VERTICAL : Orientation.HORIZONTAL, - root: toNode(positionOneGroup, baseSize) - }; - } - - // Split editor (2 or 3 columns) - else { - const children: ISerializedNode[] = []; - - const size = positionThreeGroup ? baseSize / 3 : baseSize / 2; - - children.push(toNode(positionOneGroup, size)); - children.push(toNode(positionTwoGroup, size)); - - if (positionThreeGroup) { - children.push(toNode(positionThreeGroup, size)); - } - - legacyState.serializedGrid = { - width: baseSize, - height: baseSize, - orientation: splitHorizontally ? Orientation.VERTICAL : Orientation.HORIZONTAL, - root: { - data: children, - size: baseSize, - type: 'branch' - } - }; - } - - return legacyState; - } - - return void 0; - } - private updateContainer(): void { toggleClass(this.container, 'empty', this.isEmpty()); } diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 7b24eca3f73..20a1bc2d68d 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -58,6 +58,7 @@ import { Schemas } from 'vs/base/common/network'; import { IAnchor } from 'vs/base/browser/ui/contextview/contextview'; import { Themable } from 'vs/workbench/common/theme'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; +import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; class SideBySideEditorEncodingSupport implements IEncodingSupport { constructor(private master: IEncodingSupport, private details: IEncodingSupport) { } @@ -834,6 +835,7 @@ export class ChangeModeAction extends Action { @IEditorService private editorService: IEditorService, @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService, @IQuickOpenService private quickOpenService: IQuickOpenService, + @IQuickInputService private quickInputService: IQuickInputService, @IPreferencesService private preferencesService: IPreferencesService, @IInstantiationService private instantiationService: IInstantiationService, @IUntitledEditorService private untitledEditorService: IUntitledEditorService @@ -844,7 +846,7 @@ export class ChangeModeAction extends Action { run(): TPromise { const activeTextEditorWidget = getCodeEditor(this.editorService.activeTextEditorWidget); if (!activeTextEditorWidget) { - return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]); + return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]); } const textModel = activeTextEditorWidget.getModel(); @@ -987,10 +989,10 @@ export class ChangeModeAction extends Action { const currentAssociation = this.modeService.getModeIdByFilenameOrFirstLine(basename); const languages = this.modeService.getRegisteredLanguageNames(); - const picks: IPickOpenEntry[] = languages.sort().map((lang, index) => { + const picks: IQuickPickItem[] = languages.sort().map((lang, index) => { const id = this.modeService.getModeIdForLanguageName(lang.toLowerCase()); - return { + return { id, label: lang, description: (id === currentAssociation) ? nls.localize('currentAssociation', "Current Association") : void 0 @@ -998,7 +1000,7 @@ export class ChangeModeAction extends Action { }); TPromise.timeout(50 /* quick open is sensitive to being opened so soon after another */).done(() => { - this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickLanguageToConfigure', "Select Language Mode to Associate with '{0}'", extension || basename) }).done(language => { + this.quickInputService.pick(picks, { placeHolder: nls.localize('pickLanguageToConfigure', "Select Language Mode to Associate with '{0}'", extension || basename) }).done(language => { if (language) { const fileAssociationsConfig = this.configurationService.inspect(FILES_ASSOCIATIONS_CONFIG); @@ -1030,7 +1032,7 @@ export class ChangeModeAction extends Action { } } -export interface IChangeEOLEntry extends IPickOpenEntry { +export interface IChangeEOLEntry extends IQuickPickItem { eol: EndOfLineSequence; } @@ -1043,7 +1045,8 @@ class ChangeIndentationAction extends Action { actionId: string, actionLabel: string, @IEditorService private editorService: IEditorService, - @IQuickOpenService private quickOpenService: IQuickOpenService + @IQuickOpenService private quickOpenService: IQuickOpenService, + @IQuickInputService private quickInputService: IQuickInputService ) { super(actionId, actionLabel); } @@ -1051,11 +1054,11 @@ class ChangeIndentationAction extends Action { run(): TPromise { const activeTextEditorWidget = getCodeEditor(this.editorService.activeTextEditorWidget); if (!activeTextEditorWidget) { - return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]); + return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]); } if (!isWritableCodeEditor(activeTextEditorWidget)) { - return this.quickOpenService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]); + return this.quickInputService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]); } const picks = [ @@ -1093,7 +1096,7 @@ export class ChangeEOLAction extends Action { actionId: string, actionLabel: string, @IEditorService private editorService: IEditorService, - @IQuickOpenService private quickOpenService: IQuickOpenService + @IQuickInputService private quickInputService: IQuickInputService ) { super(actionId, actionLabel); } @@ -1101,11 +1104,11 @@ export class ChangeEOLAction extends Action { run(): TPromise { const activeTextEditorWidget = getCodeEditor(this.editorService.activeTextEditorWidget); if (!activeTextEditorWidget) { - return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]); + return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]); } if (!isWritableCodeEditor(activeTextEditorWidget)) { - return this.quickOpenService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]); + return this.quickInputService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]); } const textModel = activeTextEditorWidget.getModel(); @@ -1117,7 +1120,7 @@ export class ChangeEOLAction extends Action { const selectedIndex = (textModel && textModel.getEOL() === '\n') ? 0 : 1; - return this.quickOpenService.pick(EOLOptions, { placeHolder: nls.localize('pickEndOfLine', "Select End of Line Sequence"), autoFocus: { autoFocusIndex: selectedIndex } }).then(eol => { + return this.quickInputService.pick(EOLOptions, { placeHolder: nls.localize('pickEndOfLine', "Select End of Line Sequence"), activeItem: EOLOptions[selectedIndex] }).then(eol => { if (eol) { const activeCodeEditor = getCodeEditor(this.editorService.activeTextEditorWidget); if (activeCodeEditor && isWritableCodeEditor(activeCodeEditor)) { @@ -1139,6 +1142,7 @@ export class ChangeEncodingAction extends Action { actionLabel: string, @IEditorService private editorService: IEditorService, @IQuickOpenService private quickOpenService: IQuickOpenService, + @IQuickInputService private quickInputService: IQuickInputService, @ITextResourceConfigurationService private textResourceConfigurationService: ITextResourceConfigurationService, @IFileService private fileService: IFileService ) { @@ -1147,19 +1151,19 @@ export class ChangeEncodingAction extends Action { run(): TPromise { if (!getCodeEditor(this.editorService.activeTextEditorWidget)) { - return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]); + return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]); } let activeControl = this.editorService.activeControl; let encodingSupport: IEncodingSupport = toEditorWithEncodingSupport(activeControl.input); if (!encodingSupport) { - return this.quickOpenService.pick([{ label: nls.localize('noFileEditor', "No file active at this time") }]); + return this.quickInputService.pick([{ label: nls.localize('noFileEditor', "No file active at this time") }]); } - let pickActionPromise: TPromise; + let pickActionPromise: TPromise; - let saveWithEncodingPick: IPickOpenEntry; - let reopenWithEncodingPick: IPickOpenEntry; + let saveWithEncodingPick: IQuickPickItem; + let reopenWithEncodingPick: IQuickPickItem; if (language === LANGUAGE_DEFAULT) { saveWithEncodingPick = { label: nls.localize('saveWithEncoding', "Save with Encoding") }; reopenWithEncodingPick = { label: nls.localize('reopenWithEncoding', "Reopen with Encoding") }; @@ -1173,7 +1177,7 @@ export class ChangeEncodingAction extends Action { } else if (!isWritableBaseEditor(activeControl)) { pickActionPromise = TPromise.as(reopenWithEncodingPick); } else { - pickActionPromise = this.quickOpenService.pick([reopenWithEncodingPick, saveWithEncodingPick], { placeHolder: nls.localize('pickAction', "Select Action"), matchOnDetail: true }); + pickActionPromise = this.quickInputService.pick([reopenWithEncodingPick, saveWithEncodingPick], { placeHolder: nls.localize('pickAction', "Select Action"), matchOnDetail: true }); } return pickActionPromise.then(action => { diff --git a/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css b/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css index 7b80fb89d06..e03eee35663 100644 --- a/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/breadcrumbscontrol.css @@ -11,8 +11,22 @@ opacity: .8; } +.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumb-item.selected .monaco-icon-label, +.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumb-item.focused .monaco-icon-label { + text-decoration-line: underline; +} + +.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumb-item.selected .hint-more, +.monaco-workbench>.part.editor>.content .editor-group-container .breadcrumbs-control .monaco-breadcrumb-item.focused .hint-more { + text-decoration-line: underline; +} + /* todo@joh move somewhere else */ +.monaco-workbench .monaco-breadcrumbs-picker .picker-item { + line-height: 22px; +} + .monaco-workbench .monaco-breadcrumbs-picker .highlighting-tree { height: 100%; overflow: hidden; diff --git a/src/vs/workbench/browser/parts/editor/media/editorgroupview.css b/src/vs/workbench/browser/parts/editor/media/editorgroupview.css index 455de722f55..37f00da2fdc 100644 --- a/src/vs/workbench/browser/parts/editor/media/editorgroupview.css +++ b/src/vs/workbench/browser/parts/editor/media/editorgroupview.css @@ -48,6 +48,7 @@ flex-wrap: nowrap; box-sizing: border-box; overflow: hidden; + justify-content: space-between; } .monaco-workbench > .part.editor > .content .editor-group-container > .title.tabs { diff --git a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css index c9d3d24d3c1..efd03a02391 100644 --- a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css @@ -3,6 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +.monaco-workbench > .part.editor > .content .editor-group-container > .title > .label-container { + display: flex; + justify-content: flex-start; + align-items: center; + overflow: hidden; + flex: auto; +} + /* Title Label */ .monaco-workbench > .part.editor > .content .editor-group-container > .title .title-label { @@ -23,26 +31,21 @@ /* Breadcrumbs */ -.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs { +.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs.breadcrumbs-control { flex: 1 50%; overflow: hidden; - line-height: 35px; - height: 35px; -} - -.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs .breadcrumbs-control { padding: 0 6px; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item { +.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs.breadcrumbs-control .monaco-breadcrumb-item { font-size: 0.9em; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs .breadcrumbs-control.preview .monaco-breadcrumb-item { +.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs.breadcrumbs-control.preview .monaco-breadcrumb-item { font-style: italic; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before { +.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs.breadcrumbs-control .monaco-breadcrumb-item::before { content: '/'; opacity: 1; height: inherit; @@ -50,35 +53,33 @@ background-image: none; } -.monaco-workbench.windows > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before { +.monaco-workbench.windows > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs.breadcrumbs-control .monaco-breadcrumb-item::before { content: '\\'; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs .breadcrumbs-control.relative-path .monaco-breadcrumb-item:nth-child(2)::before { - /* relative path -> hide first seperator */ +.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs.breadcrumbs-control .monaco-breadcrumb-item.root_folder::before, +.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs.breadcrumbs-control .monaco-breadcrumb-item.root_folder + .monaco-breadcrumb-item::before, +.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs.breadcrumbs-control.relative-path .monaco-breadcrumb-item:nth-child(2)::before { + /* workspace folder, item following workspace folder, or relative path -> hide first seperator */ display: none; } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.shows-symbol-icon, -.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child { +.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs.breadcrumbs-control .monaco-breadcrumb-item.root_folder::after { + /* use dot separator for workspace folder */ + content: '•'; + padding: 0 4px; +} + +.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs.breadcrumbs-control .monaco-breadcrumb-item:last-child { padding-right: 4px; /* does not have trailing separator*/ } -.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.shows-symbol-icon::before { - padding-right: 2px; - /* content: ' '; */ -} - -.monaco-workbench > .part.editor > .content .editor-group-container > .title .no-tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.shows-symbol-icon .symbol-icon { - width: 15px; - min-width: 15px; -} - /* Title Actions */ .monaco-workbench > .part.editor > .content .editor-group-container > .title .title-actions { display: flex; flex: initial; opacity: 0.5; + height: 35px; } .monaco-workbench > .part.editor > .content .editor-group-container.active > .title .title-actions { diff --git a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css index 6217b034240..74218a0d7a9 100644 --- a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css @@ -268,6 +268,14 @@ } .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item { - padding-right: 4px; + max-width: 260px; } +.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child { + padding-right: 8px; +} + +/* .monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:not(:last-child):not(:hover):not(.focused):not(.file) { + min-width: 33px; +} */ + diff --git a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts index 54ef00665ab..a5a99f0c2bd 100644 --- a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts @@ -31,15 +31,16 @@ export class NoTabsTitleControl extends TitleControl { // Gesture Support Gesture.addTarget(this.titleContainer); + const labelContainer = document.createElement('div'); + addClass(labelContainer, 'label-container'); + this.titleContainer.appendChild(labelContainer); + // Editor Label - this.editorLabel = this._register(this.instantiationService.createInstance(ResourceLabel, this.titleContainer, void 0)); + this.editorLabel = this._register(this.instantiationService.createInstance(ResourceLabel, labelContainer, void 0)); this._register(this.editorLabel.onClick(e => this.onTitleLabelClick(e))); // Breadcrumbs - const breadcrumbsContainer = document.createElement('div'); - addClass(breadcrumbsContainer, 'no-tabs-breadcrumbs'); - this.titleContainer.appendChild(breadcrumbsContainer); - this.createBreadcrumbsControl(breadcrumbsContainer, { showFileIcons: false, showSymbolIcons: true, showDecorationColors: false }); + this.createBreadcrumbsControl(labelContainer, { showFileIcons: false, showSymbolIcons: true, showDecorationColors: false, extraClasses: ['no-tabs-breadcrumbs'] }); // Right Actions Container const actionsContainer = document.createElement('div'); @@ -146,6 +147,10 @@ export class NoTabsTitleControl extends TitleControl { this.redraw(); } + protected handleBreadcrumbsEnablementChange(): void { + this.redraw(); + } + private ifActiveEditorChanged(fn: () => void): void { if ( !this.lastRenderedActiveEditor && this.group.activeEditor || // active editor changed from null => editor diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 4d2435680a2..2cf73b3bf5a 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -116,7 +116,7 @@ export class TabsTitleControl extends TitleControl { const breadcrumbsContainer = document.createElement('div'); addClass(breadcrumbsContainer, 'tabs-breadcrumbs'); this.titleContainer.appendChild(breadcrumbsContainer); - this.createBreadcrumbsControl(breadcrumbsContainer, { showFileIcons: true, showSymbolIcons: true, showDecorationColors: false }); + this.createBreadcrumbsControl(breadcrumbsContainer, { showFileIcons: true, showSymbolIcons: true, showDecorationColors: false, extraClasses: [] }); } private createScrollbar(): void { @@ -145,6 +145,11 @@ export class TabsTitleControl extends TitleControl { } } + protected handleBreadcrumbsEnablementChange(): void { + // relayout when breadcrumbs are enable/disabled + this.group.relayout(); + } + private registerContainerListeners(): void { // Group dragging diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index 3a123c9c218..91d2e0b3e83 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -101,11 +101,11 @@ export abstract class TitleControl extends Themable { if (!value && this.breadcrumbsControl) { this.breadcrumbsControl.dispose(); this.breadcrumbsControl = undefined; - this.group.relayout(); + this.handleBreadcrumbsEnablementChange(); } else if (value && !this.breadcrumbsControl) { this.breadcrumbsControl = this.instantiationService.createInstance(BreadcrumbsControl, container, options, this.group); this.breadcrumbsControl.update(); - this.group.relayout(); + this.handleBreadcrumbsEnablementChange(); } }); if (config.value) { @@ -113,6 +113,8 @@ export abstract class TitleControl extends Themable { } } + protected abstract handleBreadcrumbsEnablementChange(): void; + protected createEditorActionsToolBar(container: HTMLElement): void { const context = { groupId: this.group.id } as IEditorCommandsContext; diff --git a/src/vs/workbench/browser/parts/menubar/media/menubarpart.css b/src/vs/workbench/browser/parts/menubar/media/menubarpart.css deleted file mode 100644 index e1ee844a335..00000000000 --- a/src/vs/workbench/browser/parts/menubar/media/menubarpart.css +++ /dev/null @@ -1,47 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -.monaco-workbench > .part.menubar { - display: flex; - position: absolute; - font-size: 12px; - box-sizing: border-box; - padding-left: 35px; - padding-right: 138px; - height: 30px; -} - -.monaco-workbench.fullscreen > .part.menubar { - position: absolute; - width: 100%; - margin: 0px; - padding: 0px 5px; -} - -.monaco-workbench > .part.menubar > .menubar-menu-button { - display: flex; - flex-shrink: 0; - align-items: center; - box-sizing: border-box; - padding: 0px 8px; - position: relative; - cursor: default; - -webkit-app-region: no-drag; - zoom: 1; -} - -.menubar-menu-items-holder { - position: absolute; - left: 0px; - opacity: 1; -} - -.menubar-menu-items-holder.monaco-menu-container { - box-shadow: 0 2px 8px #A8A8A8; -} - -.vs-dark .menubar-menu-items-holder.monaco-menu-container { - box-shadow: 0 2px 8px #000; -} \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts b/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts deleted file mode 100644 index d010dfb31ec..00000000000 --- a/src/vs/workbench/browser/parts/menubar/menubar.contribution.ts +++ /dev/null @@ -1,1603 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vs/nls'; -import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { isMacintosh } from 'vs/base/common/platform'; - -recentMenuRegistration(); -fileMenuRegistration(); -editMenuRegistration(); -selectionMenuRegistration(); -viewMenuRegistration(); -appearanceMenuRegistration(); -layoutMenuRegistration(); -goMenuRegistration(); -debugMenuRegistration(); -tasksMenuRegistration(); -terminalMenuRegistration(); - -if (isMacintosh) { - windowMenuRegistration(); -} - -preferencesMenuRegistration(); -helpMenuRegistration(); - -// Menu registration - File Menu -function fileMenuRegistration() { - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '1_new', - command: { - id: 'workbench.action.files.newUntitledFile', - title: nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New File") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '1_new', - command: { - id: 'workbench.action.newWindow', - title: nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '2_open', - command: { - id: 'workbench.action.files.openFile', - title: nls.localize({ key: 'miOpenFile', comment: ['&& denotes a mnemonic'] }, "&&Open File...") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '2_open', - command: { - id: 'workbench.action.files.openFolder', - title: nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '2_open', - command: { - id: 'workbench.action.openWorkspace', - title: nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "Open Wor&&kspace...") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - title: nls.localize({ key: 'miOpenRecent', comment: ['&& denotes a mnemonic'] }, "Open &&Recent"), - submenu: MenuId.MenubarRecentMenu, - group: '2_open', - order: 4 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '3_workspace', - command: { - id: 'workbench.action.addRootFolder', - title: nls.localize({ key: 'miAddFolderToWorkspace', comment: ['&& denotes a mnemonic'] }, "A&&dd Folder to Workspace...") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '3_workspace', - command: { - id: 'workbench.action.saveWorkspaceAs', - title: nls.localize('miSaveWorkspaceAs', "Save Workspace As...") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '4_save', - command: { - id: 'workbench.action.files.save', - title: nls.localize({ key: 'miSave', comment: ['&& denotes a mnemonic'] }, "&&Save"), - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '4_save', - command: { - id: 'workbench.action.files.saveAs', - title: nls.localize({ key: 'miSaveAs', comment: ['&& denotes a mnemonic'] }, "Save &&As...") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '4_save', - command: { - id: 'workbench.action.files.saveAll', - title: nls.localize({ key: 'miSaveAll', comment: ['&& denotes a mnemonic'] }, "Save A&&ll") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '5_autosave', - command: { - id: 'workbench.action.toggleAutoSave', - title: nls.localize('miAutoSave', "Auto Save") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - title: nls.localize({ key: 'miPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences"), - submenu: MenuId.MenubarPreferencesMenu, - group: '5_autosave', - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '6_close', - command: { - id: '', - title: nls.localize({ key: 'miRevert', comment: ['&& denotes a mnemonic'] }, "Re&&vert File") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '6_close', - command: { - id: 'workbench.action.closeActiveEditor', - title: nls.localize({ key: 'miCloseEditor', comment: ['&& denotes a mnemonic'] }, "&&Close Editor") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '6_close', - command: { - id: 'workbench.action.closeFolder', - title: nls.localize({ key: 'miCloseFolder', comment: ['&& denotes a mnemonic'] }, "Close &&Folder") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '6_close', - command: { - id: 'workbench.action.closeWindow', - title: nls.localize({ key: 'miCloseWindow', comment: ['&& denotes a mnemonic'] }, "Clos&&e Window") - }, - order: 4 - }); - - if (!isMacintosh) { - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: 'z_Exit', - command: { - id: 'workbench.action.quit', - title: nls.localize({ key: 'miExit', comment: ['&& denotes a mnemonic'] }, "E&&xit") - }, - order: 1 - }); - } -} - -function editMenuRegistration() { - MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { - group: '1_do', - command: { - id: 'undo', - title: nls.localize({ key: 'miUndo', comment: ['&& denotes a mnemonic'] }, "&&Undo") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { - group: '1_do', - command: { - id: 'redo', - title: nls.localize({ key: 'miRedo', comment: ['&& denotes a mnemonic'] }, "&&Redo") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { - group: '2_ccp', - command: { - id: 'editor.action.clipboardCutAction', - title: nls.localize({ key: 'miCut', comment: ['&& denotes a mnemonic'] }, "Cu&&t") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { - group: '2_ccp', - command: { - id: 'editor.action.clipboardCopyAction', - title: nls.localize({ key: 'miCopy', comment: ['&& denotes a mnemonic'] }, "&&Copy") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { - group: '2_ccp', - command: { - id: 'editor.action.clipboardPasteAction', - title: nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { - group: '3_find', - command: { - id: 'actions.find', - title: nls.localize({ key: 'miFind', comment: ['&& denotes a mnemonic'] }, "&&Find") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { - group: '3_find', - command: { - id: 'editor.action.startFindReplaceAction', - title: nls.localize({ key: 'miReplace', comment: ['&& denotes a mnemonic'] }, "&&Replace") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { - group: '4_find_global', - command: { - id: 'workbench.action.findInFiles', - title: nls.localize({ key: 'miFindInFiles', comment: ['&& denotes a mnemonic'] }, "Find &&in Files") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { - group: '4_find_global', - command: { - - id: 'workbench.action.replaceInFiles', - title: nls.localize({ key: 'miReplaceInFiles', comment: ['&& denotes a mnemonic'] }, "Replace &&in Files") - }, - order: 2 - }); - - - /// - - - MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { - group: '5_insert', - command: { - id: 'editor.action.commentLine', - title: nls.localize({ key: 'miToggleLineComment', comment: ['&& denotes a mnemonic'] }, "&&Toggle Line Comment") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { - group: '5_insert', - command: { - id: 'editor.action.blockComment', - title: nls.localize({ key: 'miToggleBlockComment', comment: ['&& denotes a mnemonic'] }, "Toggle &&Block Comment") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { - group: '5_insert', - command: { - id: 'editor.emmet.action.expandAbbreviation', - title: nls.localize({ key: 'miEmmetExpandAbbreviation', comment: ['&& denotes a mnemonic'] }, "Emmet: E&&xpand Abbreviation") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { - group: '5_insert', - command: { - id: 'workbench.action.showEmmetCommands', - title: nls.localize({ key: 'miShowEmmetCommands', comment: ['&& denotes a mnemonic'] }, "E&&mmet...") - }, - order: 2 - }); -} - -function selectionMenuRegistration() { - MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { - group: '1_basic', - command: { - id: 'editor.action.selectAll', - title: nls.localize({ key: 'miSelectAll', comment: ['&& denotes a mnemonic'] }, "&&Select All") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { - group: '1_basic', - command: { - id: 'editor.action.smartSelect.grow', - title: nls.localize({ key: 'miSmartSelectGrow', comment: ['&& denotes a mnemonic'] }, "&&Expand Selection") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { - group: '1_basic', - command: { - id: 'editor.action.smartSelect.shrink', - title: nls.localize({ key: 'miSmartSelectShrink', comment: ['&& denotes a mnemonic'] }, "&&Shrink Selection") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { - group: '2_line', - command: { - id: 'editor.action.copyLinesUpAction', - title: nls.localize({ key: 'miCopyLinesUp', comment: ['&& denotes a mnemonic'] }, "&&Copy Line Up") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { - group: '2_line', - command: { - id: 'editor.action.copyLinesDownAction', - title: nls.localize({ key: 'miCopyLinesDown', comment: ['&& denotes a mnemonic'] }, "Co&&py Line Down") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { - group: '2_line', - command: { - id: 'editor.action.moveLinesUpAction', - title: nls.localize({ key: 'miMoveLinesUp', comment: ['&& denotes a mnemonic'] }, "Mo&&ve Line Up") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { - group: '2_line', - command: { - id: 'editor.action.moveLinesDownAction', - title: nls.localize({ key: 'miMoveLinesDown', comment: ['&& denotes a mnemonic'] }, "Move &&Line Down") - }, - order: 4 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { - group: '3_multi', - command: { - id: 'workbench.action.toggleMultiCursorModifier', - title: nls.localize('miMultiCursorAlt', "Switch to Alt+Click for Multi-Cursor") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { - group: '3_multi', - command: { - id: 'editor.action.insertCursorAbove', - title: nls.localize({ key: 'miInsertCursorAbove', comment: ['&& denotes a mnemonic'] }, "&&Add Cursor Above") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { - group: '3_multi', - command: { - id: 'editor.action.insertCursorBelow', - title: nls.localize({ key: 'miInsertCursorBelow', comment: ['&& denotes a mnemonic'] }, "A&&dd Cursor Below") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { - group: '3_multi', - command: { - id: 'editor.action.insertCursorAtEndOfEachLineSelected', - title: nls.localize({ key: 'miInsertCursorAtEndOfEachLineSelected', comment: ['&& denotes a mnemonic'] }, "Add C&&ursors to Line Ends") - }, - order: 4 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { - group: '3_multi', - command: { - id: 'editor.action.addSelectionToNextFindMatch', - title: nls.localize({ key: 'miAddSelectionToNextFindMatch', comment: ['&& denotes a mnemonic'] }, "Add &&Next Occurrence") - }, - order: 5 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { - group: '3_multi', - command: { - id: 'editor.action.addSelectionToPreviousFindMatch', - title: nls.localize({ key: 'miAddSelectionToPreviousFindMatch', comment: ['&& denotes a mnemonic'] }, "Add P&&revious Occurrence") - }, - order: 6 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { - group: '3_multi', - command: { - id: 'editor.action.selectHighlights', - title: nls.localize({ key: 'miSelectHighlights', comment: ['&& denotes a mnemonic'] }, "Select All &&Occurrences") - }, - order: 7 - }); -} - -function recentMenuRegistration() { - // Editor - MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { - group: '1_editor', - command: { - id: 'workbench.action.reopenClosedEditor', - title: nls.localize({ key: 'miReopenClosedEditor', comment: ['&& denotes a mnemonic'] }, "&&Reopen Closed Editor") - }, - order: 1 - }); - - // More - MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { - group: 'y_more', - command: { - id: 'workbench.action.openRecent', - title: nls.localize({ key: 'miMore', comment: ['&& denotes a mnemonic'] }, "&&More...") - }, - order: 1 - }); - - // Clear - MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { - group: 'z_clear', - command: { - id: 'workbench.action.clearRecentFiles', - title: nls.localize({ key: 'miClearRecentOpen', comment: ['&& denotes a mnemonic'] }, "&&Clear Recently Opened") - }, - order: 1 - }); - -} - -function viewMenuRegistration() { - - // Command Palette - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '1_open', - command: { - id: 'workbench.action.showCommands', - title: nls.localize({ key: 'miCommandPalette', comment: ['&& denotes a mnemonic'] }, "&&Command Palette...") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '1_open', - command: { - id: 'workbench.action.openView', - title: nls.localize({ key: 'miOpenView', comment: ['&& denotes a mnemonic'] }, "&&Open View...") - }, - order: 2 - }); - - // TODO: Appearance Submenu - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '2_appearance', - title: nls.localize({ key: 'miAppearance', comment: ['&& denotes a mnemonic'] }, "&&Appearance"), - submenu: MenuId.MenubarAppearanceMenu, - order: 1 - }); - - // TODO: Editor Layout Submenu - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '2_appearance', - title: nls.localize({ key: 'miEditorLayout', comment: ['&& denotes a mnemonic'] }, "Editor &&Layout"), - submenu: MenuId.MenubarLayoutMenu, - order: 2 - }); - - // Viewlets - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '3_views', - command: { - id: 'workbench.view.explorer', - title: nls.localize({ key: 'miViewExplorer', comment: ['&& denotes a mnemonic'] }, "&&Explorer") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '3_views', - command: { - id: 'workbench.view.search', - title: nls.localize({ key: 'miViewSearch', comment: ['&& denotes a mnemonic'] }, "&&Search") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '3_views', - command: { - id: 'workbench.view.scm', - title: nls.localize({ key: 'miViewSCM', comment: ['&& denotes a mnemonic'] }, "S&&CM") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '3_views', - command: { - id: 'workbench.view.debug', - title: nls.localize({ key: 'miViewDebug', comment: ['&& denotes a mnemonic'] }, "&&Debug") - }, - order: 4 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '3_views', - command: { - id: 'workbench.view.extensions', - title: nls.localize({ key: 'miViewExtensions', comment: ['&& denotes a mnemonic'] }, "E&&xtensions") - }, - order: 5 - }); - - // Panels - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '4_panels', - command: { - id: 'workbench.action.output.toggleOutput', - title: nls.localize({ key: 'miToggleOutput', comment: ['&& denotes a mnemonic'] }, "&&Output") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '4_panels', - command: { - id: 'workbench.debug.action.toggleRepl', - title: nls.localize({ key: 'miToggleDebugConsole', comment: ['&& denotes a mnemonic'] }, "De&&bug Console") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '4_panels', - command: { - id: 'workbench.action.terminal.toggleTerminal', - title: nls.localize({ key: 'miToggleIntegratedTerminal', comment: ['&& denotes a mnemonic'] }, "&&Integrated Terminal") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '4_panels', - command: { - id: 'workbench.actions.view.problems', - title: nls.localize({ key: 'miMarker', comment: ['&& denotes a mnemonic'] }, "&&Problems") - }, - order: 4 - }); - - // Toggle Editor Settings - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '5_editor', - command: { - id: 'workbench.action.toggleWordWrap', - title: nls.localize({ key: 'miToggleWordWrap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Word Wrap") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '5_editor', - command: { - id: 'workbench.action.toggleMinimap', - title: nls.localize({ key: 'miToggleMinimap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Minimap") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '5_editor', - command: { - id: 'workbench.action.toggleRenderWhitespace', - title: nls.localize({ key: 'miToggleRenderWhitespace', comment: ['&& denotes a mnemonic'] }, "Toggle &&Render Whitespace") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { - group: '5_editor', - command: { - id: 'workbench.action.toggleRenderControlCharacters', - title: nls.localize({ key: 'miToggleRenderControlCharacters', comment: ['&& denotes a mnemonic'] }, "Toggle &&Control Characters") - }, - order: 4 - }); -} - -function appearanceMenuRegistration() { - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '1_toggle_view', - command: { - id: 'workbench.action.toggleFullScreen', - title: nls.localize({ key: 'miToggleFullScreen', comment: ['&& denotes a mnemonic'] }, "Toggle &&Full Screen") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '1_toggle_view', - command: { - id: 'workbench.action.toggleZenMode', - title: nls.localize('miToggleZenMode', "Toggle Zen Mode") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '1_toggle_view', - command: { - id: 'workbench.action.toggleCenteredLayout', - title: nls.localize('miToggleCenteredLayout', "Toggle Centered Layout") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '1_toggle_view', - command: { - id: 'workbench.action.toggleMenuBar', - title: nls.localize({ key: 'miToggleMenuBar', comment: ['&& denotes a mnemonic'] }, "Toggle Menu &&Bar") - }, - order: 4 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '2_workbench_layout', - command: { - id: 'workbench.action.toggleSidebarVisibility', - title: nls.localize({ key: 'miToggleSidebar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Side Bar") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '2_workbench_layout', - command: { - id: 'workbench.action.toggleSidebarPosition', - title: nls.localize({ key: 'miMoveSidebarLeftRight', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Left/Right") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '2_workbench_layout', - command: { - id: 'workbench.action.toggleStatusbarVisibility', - title: nls.localize({ key: 'miToggleStatusbar', comment: ['&& denotes a mnemonic'] }, "&&Toggle Status Bar") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '2_workbench_layout', - command: { - id: 'workbench.action.toggleActivityBarVisibility', - title: nls.localize({ key: 'miToggleActivityBar', comment: ['&& denotes a mnemonic'] }, "Toggle &&Activity Bar") - }, - order: 4 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '2_workbench_layout', - command: { - id: 'workbench.action.togglePanel', - title: nls.localize({ key: 'miTogglePanel', comment: ['&& denotes a mnemonic'] }, "Toggle &&Panel") - }, - order: 5 - }); - - // Zoom - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '3_zoom', - command: { - id: 'workbench.action.zoomIn', - title: nls.localize({ key: 'miZoomIn', comment: ['&& denotes a mnemonic'] }, "&&Zoom In") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '3_zoom', - command: { - id: 'workbench.action.zoomOut', - title: nls.localize({ key: 'miZoomOut', comment: ['&& denotes a mnemonic'] }, "&&Zoom Out") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { - group: '3_zoom', - command: { - id: 'workbench.action.zoomReset', - title: nls.localize({ key: 'miZoomReset', comment: ['&& denotes a mnemonic'] }, "&&Reset Zoom") - }, - order: 3 - }); -} - -function layoutMenuRegistration() { - // Split - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '1_split', - command: { - id: 'workbench.action.splitEditorUp', - title: nls.localize({ key: 'miSplitEditorUp', comment: ['&& denotes a mnemonic'] }, "Split &&Up") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '1_split', - command: { - id: 'workbench.action.splitEditorDown', - title: nls.localize({ key: 'miSplitEditorDown', comment: ['&& denotes a mnemonic'] }, "Split &&Down") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '1_split', - command: { - id: 'workbench.action.splitEditorLeft', - title: nls.localize({ key: 'miSplitEditorLeft', comment: ['&& denotes a mnemonic'] }, "Split &&Left") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '1_split', - command: { - id: 'workbench.action.splitEditorRight', - title: nls.localize({ key: 'miSplitEditorRight', comment: ['&& denotes a mnemonic'] }, "Split &&Right") - }, - order: 4 - }); - - // Layouts - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '2_layouts', - command: { - id: 'workbench.action.editorLayoutSingle', - title: nls.localize({ key: 'miSingleColumnEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Single") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '2_layouts', - command: { - id: 'workbench.action.editorLayoutCentered', - title: nls.localize({ key: 'miCenteredEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Centered") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '2_layouts', - command: { - id: 'workbench.action.editorLayoutTwoColumns', - title: nls.localize({ key: 'miTwoColumnsEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Two Columns") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '2_layouts', - command: { - id: 'workbench.action.editorLayoutThreeColumns', - title: nls.localize({ key: 'miThreeColumnsEditorLayout', comment: ['&& denotes a mnemonic'] }, "T&&hree Columns") - }, - order: 4 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '2_layouts', - command: { - id: 'workbench.action.editorLayoutTwoRows', - title: nls.localize({ key: 'miTwoRowsEditorLayout', comment: ['&& denotes a mnemonic'] }, "T&&wo Rows") - }, - order: 5 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '2_layouts', - command: { - id: 'workbench.action.editorLayoutThreeRows', - title: nls.localize({ key: 'miThreeRowsEditorLayout', comment: ['&& denotes a mnemonic'] }, "Three &&Rows") - }, - order: 6 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '2_layouts', - command: { - id: 'workbench.action.editorLayoutTwoByTwoGrid', - title: nls.localize({ key: 'miTwoByTwoGridEditorLayout', comment: ['&& denotes a mnemonic'] }, "&&Grid (2x2)") - }, - order: 7 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '2_layouts', - command: { - id: 'workbench.action.editorLayoutTwoRowsRight', - title: nls.localize({ key: 'miTwoRowsRightEditorLayout', comment: ['&& denotes a mnemonic'] }, "Two R&&ows Right") - }, - order: 8 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: '2_layouts', - command: { - id: 'workbench.action.editorLayoutTwoColumnsBottom', - title: nls.localize({ key: 'miTwoColumnsBottomEditorLayout', comment: ['&& denotes a mnemonic'] }, "Two &&Columns Bottom") - }, - order: 9 - }); - - // Flip - MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { - group: 'z_flip', - command: { - id: 'workbench.action.toggleEditorGroupLayout', - title: nls.localize({ key: 'miToggleEditorLayout', comment: ['&& denotes a mnemonic'] }, "Flip &&Layout") - }, - order: 1 - }); - -} - -function goMenuRegistration() { - // Forward/Back - MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: '1_fwd_back', - command: { - id: 'workbench.action.navigateBack', - title: nls.localize({ key: 'miBack', comment: ['&& denotes a mnemonic'] }, "&&Back") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: '1_fwd_back', - command: { - id: 'workbench.action.navigateForward', - title: nls.localize({ key: 'miForward', comment: ['&& denotes a mnemonic'] }, "&&Forward") - }, - order: 2 - }); - - // Switch Editor - MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: '2_switch_editor', - command: { - id: 'workbench.action.nextEditor', - title: nls.localize({ key: 'miNextEditor', comment: ['&& denotes a mnemonic'] }, "&&Next Editor") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: '2_switch_editor', - command: { - id: 'workbench.action.previousEditor', - title: nls.localize({ key: 'miPreviousEditor', comment: ['&& denotes a mnemonic'] }, "&&Previous Editor") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: '2_switch_editor', - command: { - id: 'workbench.action.openNextRecentlyUsedEditorInGroup', - title: nls.localize({ key: 'miNextEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Used Editor in Group") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: '2_switch_editor', - command: { - id: 'workbench.action.openPreviousRecentlyUsedEditorInGroup', - title: nls.localize({ key: 'miPreviousEditorInGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Used Editor in Group") - }, - order: 4 - }); - - // Switch Group - MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: '3_switch_group', - command: { - id: 'workbench.action.focusNextGroup', - title: nls.localize({ key: 'miNextGroup', comment: ['&& denotes a mnemonic'] }, "&&Next Group") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: '3_switch_group', - command: { - id: 'workbench.action.focusPreviousGroup', - title: nls.localize({ key: 'miPreviousGroup', comment: ['&& denotes a mnemonic'] }, "&&Previous Group") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: '3_switch_group', - command: { - id: 'workbench.action.focusLeftGroup', - title: nls.localize({ key: 'miFocusLeftGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Left") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: '3_switch_group', - command: { - id: 'workbench.action.focusRightGroup', - title: nls.localize({ key: 'miFocusRightGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Right") - }, - order: 4 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: '3_switch_group', - command: { - id: 'workbench.action.focusAboveGroup', - title: nls.localize({ key: 'miFocusAboveGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Above") - }, - order: 5 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: '3_switch_group', - command: { - id: 'workbench.action.focusBelowGroup', - title: nls.localize({ key: 'miFocusBelowGroup', comment: ['&& denotes a mnemonic'] }, "Group &&Below") - }, - order: 6 - }); - - // Go to - MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: 'z_go_to', - command: { - id: 'workbench.action.quickOpen', - title: nls.localize({ key: 'miGotoFile', comment: ['&& denotes a mnemonic'] }, "Go to &&File...") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: 'z_go_to', - command: { - id: 'workbench.action.gotoSymbol', - title: nls.localize({ key: 'miGotoSymbolInFile', comment: ['&& denotes a mnemonic'] }, "Go to &&Symbol in File...") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: 'z_go_to', - command: { - id: 'workbench.action.showAllSymbols', - title: nls.localize({ key: 'miGotoSymbolInWorkspace', comment: ['&& denotes a mnemonic'] }, "Go to Symbol in &&Workspace...") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: 'z_go_to', - command: { - id: 'editor.action.goToDeclaration', - title: nls.localize({ key: 'miGotoDefinition', comment: ['&& denotes a mnemonic'] }, "Go to &&Definition") - }, - order: 4 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: 'z_go_to', - command: { - id: 'editor.action.goToTypeDefinition', - title: nls.localize({ key: 'miGotoDefinition', comment: ['&& denotes a mnemonic'] }, "Go to &&Definition") - }, - order: 5 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: 'z_go_to', - command: { - id: 'editor.action.goToImplementation', - title: nls.localize({ key: 'miGotoImplementation', comment: ['&& denotes a mnemonic'] }, "Go to &&Implementation") - }, - order: 6 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: 'z_go_to', - command: { - id: 'workbench.action.gotoLine', - title: nls.localize({ key: 'miGotoLine', comment: ['&& denotes a mnemonic'] }, "Go to &&Line...") - }, - order: 7 - }); -} - -function debugMenuRegistration() { - // Start/Stop Debug - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '1_debug', - command: { - id: 'workbench.action.debug.start', - title: nls.localize({ key: 'miStartDebugging', comment: ['&& denotes a mnemonic'] }, "&&Start Debugging") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '1_debug', - command: { - id: 'workbench.action.debug.run', - title: nls.localize({ key: 'miStartWithoutDebugging', comment: ['&& denotes a mnemonic'] }, "Start &&Without Debugging") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '1_debug', - command: { - id: 'workbench.action.debug.stop', - title: nls.localize({ key: 'miStopDebugging', comment: ['&& denotes a mnemonic'] }, "&&Stop Debugging") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '1_debug', - command: { - id: 'workbench.action.debug.restart', - title: nls.localize({ key: 'miRestart Debugging', comment: ['&& denotes a mnemonic'] }, "&&Restart Debugging") - }, - order: 4 - }); - - // Configuration - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '2_configuration', - command: { - id: 'workbench.action.debug.configure', - title: nls.localize({ key: 'miOpenConfigurations', comment: ['&& denotes a mnemonic'] }, "Open &&Configurations") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '2_configuration', - command: { - id: 'debug.addConfiguration', - title: nls.localize({ key: 'miAddConfiguration', comment: ['&& denotes a mnemonic'] }, "Add Configuration...") - }, - order: 2 - }); - - // Step Commands - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '3_step', - command: { - id: 'workbench.action.debug.stepOver', - title: nls.localize({ key: 'miStepOver', comment: ['&& denotes a mnemonic'] }, "Step &&Over") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '3_step', - command: { - id: 'workbench.action.debug.stepInto', - title: nls.localize({ key: 'miStepInto', comment: ['&& denotes a mnemonic'] }, "Step &&Into") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '3_step', - command: { - id: 'workbench.action.debug.stepOut', - title: nls.localize({ key: 'miStepOut', comment: ['&& denotes a mnemonic'] }, "Step O&&ut") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '3_step', - command: { - id: 'workbench.action.debug.continue', - title: nls.localize({ key: 'miContinue', comment: ['&& denotes a mnemonic'] }, "&&Continue") - }, - order: 4 - }); - - // New Breakpoints - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '4_new_breakpoint', - command: { - id: 'editor.debug.action.toggleBreakpoint', - title: nls.localize({ key: 'miToggleBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Breakpoint") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '4_new_breakpoint', - command: { - id: 'editor.debug.action.conditionalBreakpoint', - title: nls.localize({ key: 'miConditionalBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Conditional Breakpoint...") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '4_new_breakpoint', - command: { - id: 'editor.debug.action.toggleInlineBreakpoint', - title: nls.localize({ key: 'miInlineBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle Inline Breakp&&oint") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '4_new_breakpoint', - command: { - id: 'workbench.debug.viewlet.action.addFunctionBreakpointAction', - title: nls.localize({ key: 'miFunctionBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Function Breakpoint...") - }, - order: 4 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '4_new_breakpoint', - command: { - id: 'editor.debug.action.toggleLogPoint', - title: nls.localize({ key: 'miLogPoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Logpoint...") - }, - order: 5 - }); - - // Modify Breakpoints - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '5_breakpoints', - command: { - id: 'workbench.debug.viewlet.action.enableAllBreakpoints', - title: nls.localize({ key: 'miEnableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Enable All Breakpoints") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '5_breakpoints', - command: { - id: 'workbench.debug.viewlet.action.disableAllBreakpoints', - title: nls.localize({ key: 'miDisableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Disable A&&ll Breakpoints") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { - group: '5_breakpoints', - command: { - id: 'workbench.debug.viewlet.action.removeAllBreakpoints', - title: nls.localize({ key: 'miRemoveAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Remove &&All Breakpoints") - }, - order: 3 - }); - -} - -function tasksMenuRegistration() { - // Run Tasks - MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { - group: '1_run', - command: { - id: 'workbench.action.tasks.runTask', - title: nls.localize({ key: 'miRunTask', comment: ['&& denotes a mnemonic'] }, "&&Run Task...") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { - group: '1_run', - command: { - id: 'workbench.action.tasks.build', - title: nls.localize({ key: 'miBuildTask', comment: ['&& denotes a mnemonic'] }, "Run &&Build Task...") - }, - order: 2 - }); - - // Manage Tasks - MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { - group: '2_manage', - command: { - id: 'workbench.action.tasks.showTasks', - title: nls.localize({ key: 'miRunningTask', comment: ['&& denotes a mnemonic'] }, "Show Runnin&&g Tasks...") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { - group: '2_manage', - command: { - id: 'workbench.action.tasks.restartTask', - title: nls.localize({ key: 'miRestartTask', comment: ['&& denotes a mnemonic'] }, "R&&estart Running Task...") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { - group: '2_manage', - command: { - id: 'workbench.action.tasks.terminate', - title: nls.localize({ key: 'miTerminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task...") - }, - order: 3 - }); - - // Configure Tasks - MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { - group: '3_configure', - command: { - id: 'workbench.action.tasks.configureTaskRunner', - title: nls.localize({ key: 'miConfigureTask', comment: ['&& denotes a mnemonic'] }, "&&Configure Tasks...") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { - group: '3_configure', - command: { - id: 'workbench.action.tasks.configureDefaultBuildTask', - title: nls.localize({ key: 'miConfigureBuildTask', comment: ['&& denotes a mnemonic'] }, "Configure De&&fault Build Task...") - }, - order: 2 - }); - -} - -function windowMenuRegistration() { - -} - -function preferencesMenuRegistration() { - MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { - group: '1_settings', - command: { - id: 'workbench.action.openSettings2', - title: nls.localize({ key: 'miOpenSettings', comment: ['&& denotes a mnemonic'] }, "&&Settings") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { - group: '2_keybindings', - command: { - id: 'workbench.action.openGlobalKeybindings', - title: nls.localize({ key: 'miOpenKeymap', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { - group: '2_keybindings', - command: { - id: 'workbench.extensions.action.showRecommendedKeymapExtensions', - title: nls.localize({ key: 'miOpenKeymapExtensions', comment: ['&& denotes a mnemonic'] }, "&&Keymap Extensions") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { - group: '3_snippets', - command: { - id: 'workbench.action.openSnippets', - title: nls.localize({ key: 'miOpenSnippets', comment: ['&& denotes a mnemonic'] }, "User &&Snippets") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { - group: '4_themes', - command: { - id: 'workbench.action.selectTheme', - title: nls.localize({ key: 'miSelectColorTheme', comment: ['&& denotes a mnemonic'] }, "&&Color Theme") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { - group: '4_themes', - command: { - id: 'workbench.action.selectIconTheme', - title: nls.localize({ key: 'miSelectIconTheme', comment: ['&& denotes a mnemonic'] }, "File &&Icon Theme") - }, - order: 2 - }); -} - -function helpMenuRegistration() { - // Welcome - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '1_welcome', - command: { - id: 'workbench.action.showWelcomePage', - title: nls.localize({ key: 'miWelcome', comment: ['&& denotes a mnemonic'] }, "&&Welcome") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '1_welcome', - command: { - id: 'workbench.action.showInteractivePlayground', - title: nls.localize({ key: 'miInteractivePlayground', comment: ['&& denotes a mnemonic'] }, "&&Interactive Playground") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '1_welcome', - command: { - id: 'workbench.action.openDocumentationUrl', - title: nls.localize({ key: 'miDocumentation', comment: ['&& denotes a mnemonic'] }, "&&Documentation") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '1_welcome', - command: { - id: 'update.showCurrentReleaseNotes', - title: nls.localize({ key: 'miReleaseNotes', comment: ['&& denotes a mnemonic'] }, "&&Release Notes") - }, - order: 4 - }); - - // Reference - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '2_reference', - command: { - id: 'workbench.action.keybindingsReference', - title: nls.localize({ key: 'miKeyboardShortcuts', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts Reference") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '2_reference', - command: { - id: 'workbench.action.openIntroductoryVideosUrl', - title: nls.localize({ key: 'miIntroductoryVideos', comment: ['&& denotes a mnemonic'] }, "Introductory &&Videos") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '2_reference', - command: { - id: 'workbench.action.openTipsAndTricksUrl', - title: nls.localize({ key: 'miTipsAndTricks', comment: ['&& denotes a mnemonic'] }, "&&Tips and Tricks") - }, - order: 3 - }); - - // Feedback - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '3_feedback', - command: { - id: 'workbench.action.openTwitterUrl', - title: nls.localize({ key: 'miTwitter', comment: ['&& denotes a mnemonic'] }, "&&Join us on Twitter") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '3_feedback', - command: { - id: 'workbench.action.openRequestFeatureUrl', - title: nls.localize({ key: 'miUserVoice', comment: ['&& denotes a mnemonic'] }, "&&Search Feature Requests") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '3_feedback', - command: { - id: 'workbench.action.openIssueReporter', - title: nls.localize({ key: 'miReportIssue', comment: ['&& denotes a mnemonic', 'Translate this to "Report Issue in English" in all languages please!'] }, "Report &&Issue") - }, - order: 3 - }); - - // Legal - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '4_legal', - command: { - id: 'workbench.action.openLicenseUrl', - title: nls.localize({ key: 'miLicense', comment: ['&& denotes a mnemonic'] }, "View &&License") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '4_legal', - command: { - id: 'workbench.action.openPrivacyStatementUrl', - title: nls.localize({ key: 'miPrivacyStatement', comment: ['&& denotes a mnemonic'] }, "&&Privacy Statement") - }, - order: 2 - }); - - // Tools - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '5_tools', - command: { - id: 'workbench.action.toggleDevTools', - title: nls.localize({ key: 'miToggleDevTools', comment: ['&& denotes a mnemonic'] }, "&&Toggle Developer Tools") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '5_tools', - command: { - id: 'workbench.action.openProcessExplorer', - title: nls.localize({ key: 'miOpenProcessExplorerer', comment: ['&& denotes a mnemonic'] }, "Open &&Process Explorer") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '5_tools', - command: { - id: 'workbench.action.showAccessibilityOptions', - title: nls.localize({ key: 'miAccessibilityOptions', comment: ['&& denotes a mnemonic'] }, "Accessibility &&Options") - }, - order: 3 - }); - - // About - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: 'z_about', - command: { - id: 'workbench.action.showAboutDialog', - title: nls.localize({ key: 'miAbout', comment: ['&& denotes a mnemonic'] }, "&&About") - }, - order: 1 - }); -} - -function terminalMenuRegistration() { - - // Manage - - MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { - group: '1_manage', - command: { - id: 'workbench.action.terminal.new', - title: nls.localize({ key: 'miNewTerminal', comment: ['&& denotes a mnemonic'] }, "&&New Terminal") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { - group: '1_manage', - command: { - id: 'workbench.action.terminal.split', - title: nls.localize({ key: 'miSplitTerminal', comment: ['&& denotes a mnemonic'] }, "&&Split Terminal") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { - group: '1_manage', - command: { - id: 'workbench.action.terminal.kill', - title: nls.localize({ key: 'miKillTerminal', comment: ['&& denotes a mnemonic'] }, "&&Kill Terminal") - }, - order: 3 - }); - - // Run - - MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { - group: '2_run', - command: { - id: 'workbench.action.terminal.clear', - title: nls.localize({ key: 'miClear', comment: ['&& denotes a mnemonic'] }, "&&Clear") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { - group: '2_run', - command: { - id: 'workbench.action.terminal.runActiveFile', - title: nls.localize({ key: 'miRunActiveFile', comment: ['&& denotes a mnemonic'] }, "Run &&Active File") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { - group: '2_run', - command: { - id: 'workbench.action.terminal.runSelectedFile', - title: nls.localize({ key: 'miRunSelectedText', comment: ['&& denotes a mnemonic'] }, "Run &&Selected Text") - }, - order: 3 - }); - - // Selection - - MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { - group: '3_selection', - command: { - id: 'workbench.action.terminal.scrollToPreviousCommand', - title: nls.localize({ key: 'miScrollToPreviousCommand', comment: ['&& denotes a mnemonic'] }, "Scroll To Previous Command") - }, - order: 1 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { - group: '3_selection', - command: { - id: 'workbench.action.terminal.scrollToNextCommand', - title: nls.localize({ key: 'miScrollToNextCommand', comment: ['&& denotes a mnemonic'] }, "Scroll To Next Command") - }, - order: 2 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { - group: '3_selection', - command: { - id: 'workbench.action.terminal.selectToPreviousCommand', - title: nls.localize({ key: 'miSelectToPreviousCommand', comment: ['&& denotes a mnemonic'] }, "Select To Previous Command") - }, - order: 3 - }); - - MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { - group: '3_selection', - command: { - id: 'workbench.action.terminal.selectToNextCommand', - title: nls.localize({ key: 'miSelectToNextCommand', comment: ['&& denotes a mnemonic'] }, "Select To Next Command") - }, - order: 4 - }); -} \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css index 73fad28fcc0..24552a04bd6 100644 --- a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css +++ b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css @@ -115,7 +115,6 @@ /** Notification: Source */ .monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-source { - opacity: 0.7; flex: 1; font-size: 12px; overflow: hidden; /* always give away space to buttons container */ diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts index 858f3bbe848..9519578fe60 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts @@ -7,7 +7,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { INotificationViewItem, isNotificationViewItem } from 'vs/workbench/common/notifications'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; @@ -88,7 +88,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl // Hide Notifications Center KeybindingsRegistry.registerCommandAndKeybindingRule({ id: HIDE_NOTIFICATIONS_CENTER, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50), + weight: KeybindingWeight.WorkbenchContrib + 50, when: NotificationsCenterVisibleContext, primary: KeyCode.Escape, handler: accessor => center.hide() @@ -106,7 +106,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl // Clear Notification KeybindingsRegistry.registerCommandAndKeybindingRule({ id: CLEAR_NOTIFICATION, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: NotificationFocusedContext, primary: KeyCode.Delete, mac: { @@ -123,7 +123,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl // Expand Notification KeybindingsRegistry.registerCommandAndKeybindingRule({ id: EXPAND_NOTIFICATION, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: NotificationFocusedContext, primary: KeyCode.RightArrow, handler: (accessor, args?: any) => { @@ -137,7 +137,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl // Collapse Notification KeybindingsRegistry.registerCommandAndKeybindingRule({ id: COLLAPSE_NOTIFICATION, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: NotificationFocusedContext, primary: KeyCode.LeftArrow, handler: (accessor, args?: any) => { @@ -151,7 +151,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl // Toggle Notification KeybindingsRegistry.registerCommandAndKeybindingRule({ id: TOGGLE_NOTIFICATION, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: NotificationFocusedContext, primary: KeyCode.Space, secondary: [KeyCode.Enter], @@ -166,7 +166,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl // Hide Toasts KeybindingsRegistry.registerCommandAndKeybindingRule({ id: HIDE_NOTIFICATION_TOAST, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50), + weight: KeybindingWeight.WorkbenchContrib + 50, when: NotificationsToastsVisibleContext, primary: KeyCode.Escape, handler: accessor => toasts.hide() @@ -178,7 +178,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl // Focus Next Toast KeybindingsRegistry.registerCommandAndKeybindingRule({ id: FOCUS_NEXT_NOTIFICATION_TOAST, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(NotificationFocusedContext, NotificationsToastsVisibleContext), primary: KeyCode.DownArrow, handler: (accessor) => { @@ -189,7 +189,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl // Focus Previous Toast KeybindingsRegistry.registerCommandAndKeybindingRule({ id: FOCUS_PREVIOUS_NOTIFICATION_TOAST, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(NotificationFocusedContext, NotificationsToastsVisibleContext), primary: KeyCode.UpArrow, handler: (accessor) => { @@ -200,7 +200,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl // Focus First Toast KeybindingsRegistry.registerCommandAndKeybindingRule({ id: FOCUS_FIRST_NOTIFICATION_TOAST, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(NotificationFocusedContext, NotificationsToastsVisibleContext), primary: KeyCode.PageUp, secondary: [KeyCode.Home], @@ -212,7 +212,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl // Focus Last Toast KeybindingsRegistry.registerCommandAndKeybindingRule({ id: FOCUS_LAST_NOTIFICATION_TOAST, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(NotificationFocusedContext, NotificationsToastsVisibleContext), primary: KeyCode.PageDown, secondary: [KeyCode.End], diff --git a/src/vs/workbench/browser/parts/panel/media/panelpart.css b/src/vs/workbench/browser/parts/panel/media/panelpart.css index 0c58440f0d4..cfd1137cf35 100644 --- a/src/vs/workbench/browser/parts/panel/media/panelpart.css +++ b/src/vs/workbench/browser/parts/panel/media/panelpart.css @@ -81,16 +81,19 @@ } .monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .badge { - margin-left: 4px; + margin-left: 8px; } .monaco-workbench > .part.panel > .title > .panel-switcher-container > .monaco-action-bar .badge .badge-content { - padding: 0.2em 0.5em; + padding: 0.3em 0.5em; border-radius: 1em; font-weight: normal; text-align: center; - display: inline; -} + display: inline-block; + min-width: 1.6em; + line-height: 1em; + box-sizing: border-box; + } /** Actions */ diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index 654186a1c39..54d4f61d040 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -10,11 +10,11 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { Action } from 'vs/base/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/actions'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IPartService, Parts, Position } from 'vs/workbench/services/part/common/partService'; -import { ActivityAction } from 'vs/workbench/browser/parts/compositebar/compositeBarActions'; +import { ActivityAction } from 'vs/workbench/browser/parts/compositeBarActions'; import { IActivity } from 'vs/workbench/common/activity'; export class ClosePanelAction extends Action { @@ -177,3 +177,12 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMaximizedP actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClosePanelAction, ClosePanelAction.ID, ClosePanelAction.LABEL), 'View: Close Panel', nls.localize('view', "View")); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(TogglePanelPositionAction, TogglePanelPositionAction.ID, TogglePanelPositionAction.LABEL), 'View: Toggle Panel Position', nls.localize('view', "View")); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMaximizedPanelAction, ToggleMaximizedPanelAction.ID, undefined), 'View: Toggle Panel Position', nls.localize('view', "View")); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '2_workbench_layout', + command: { + id: TogglePanelAction.ID, + title: nls.localize({ key: 'miTogglePanel', comment: ['&& denotes a mnemonic'] }, "Toggle &&Panel") + }, + order: 5 +}); diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 40c3fa928ea..13df252c82f 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -7,7 +7,6 @@ import 'vs/css!./media/panelpart'; import { TPromise } from 'vs/base/common/winjs.base'; import { IAction } from 'vs/base/common/actions'; import { Event } from 'vs/base/common/event'; -import { $ } from 'vs/base/browser/builder'; import { Registry } from 'vs/platform/registry/common/platform'; import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { IPanel } from 'vs/workbench/common/panel'; @@ -24,8 +23,8 @@ import { ClosePanelAction, TogglePanelPositionAction, PanelActivityAction, Toggl import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { PANEL_BACKGROUND, PANEL_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_INACTIVE_TITLE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER, PANEL_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme'; import { activeContrastBorder, focusBorder, contrastBorder, editorBackground, badgeBackground, badgeForeground } from 'vs/platform/theme/common/colorRegistry'; -import { CompositeBar } from 'vs/workbench/browser/parts/compositebar/compositeBar'; -import { ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositebar/compositeBarActions'; +import { CompositeBar } from 'vs/workbench/browser/parts/compositeBar'; +import { ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositeBarActions'; import { IBadge } from 'vs/workbench/services/activity/common/activity'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { Dimension } from 'vs/base/browser/dom'; @@ -151,12 +150,12 @@ export class PanelPart extends CompositePart implements IPanelService { updateStyles(): void { super.updateStyles(); - const container = $(this.getContainer()); - container.style('background-color', this.getColor(PANEL_BACKGROUND)); - container.style('border-left-color', this.getColor(PANEL_BORDER) || this.getColor(contrastBorder)); + const container = this.getContainer(); + container.style.backgroundColor = this.getColor(PANEL_BACKGROUND); + container.style.borderLeftColor = this.getColor(PANEL_BORDER) || this.getColor(contrastBorder); - const title = $(this.getTitleArea()); - title.style('border-top-color', this.getColor(PANEL_BORDER) || this.getColor(contrastBorder)); + const title = this.getTitleArea(); + title.style.borderTopColor = this.getColor(PANEL_BORDER) || this.getColor(contrastBorder); } openPanel(id: string, focus?: boolean): TPromise { diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.contribution.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.contribution.ts index cc7fb7c7577..7e5bc7f43a0 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.contribution.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.contribution.ts @@ -5,7 +5,7 @@ 'use strict'; import { QuickPickManyToggle, BackAction } from 'vs/workbench/browser/parts/quickinput/quickInput'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; @@ -15,4 +15,4 @@ import { inQuickOpenContext } from 'vs/workbench/browser/parts/quickopen/quickop KeybindingsRegistry.registerCommandAndKeybindingRule(QuickPickManyToggle); const registry = Registry.as(ActionExtensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(BackAction, BackAction.ID, BackAction.LABEL, { primary: null, win: { primary: KeyMod.Alt | KeyCode.LeftArrow }, mac: { primary: KeyMod.WinCtrl | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_MINUS } }, inQuickOpenContext, KeybindingsRegistry.WEIGHT.workbenchContrib(50)), 'Back'); +registry.registerWorkbenchAction(new SyncActionDescriptor(BackAction, BackAction.ID, BackAction.LABEL, { primary: null, win: { primary: KeyMod.Alt | KeyCode.LeftArrow }, mac: { primary: KeyMod.WinCtrl | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_MINUS } }, inQuickOpenContext, KeybindingWeight.WorkbenchContrib + 50), 'Back'); diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.css b/src/vs/workbench/browser/parts/quickinput/quickInput.css index fbcdcbe684c..35094940935 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.css +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.css @@ -69,6 +69,11 @@ margin-left: 5px; } +.quick-input-visible-count { + position: absolute; + left: -10000px; +} + .quick-input-count { align-self: center; position: absolute; @@ -163,4 +168,27 @@ .quick-input-list .monaco-highlighted-label .highlight { font-weight: bold; -} \ No newline at end of file +} + +.quick-input-list .quick-input-list-entry-action-bar { + display: none; + flex: 0; + overflow: visible; +} + +.quick-input-list .quick-input-list-entry-action-bar .action-label.icon { + margin: 0; + width: 19px; + height: 100%; + background-position: center; + background-repeat: no-repeat; +} + +.quick-input-list .quick-input-list-entry-action-bar ul:last-child .action-label.icon { + margin-right: 3px; +} + +.quick-input-list .quick-input-list-entry:hover .quick-input-list-entry-action-bar, +.quick-input-list .monaco-list-row.focused .quick-input-list-entry-action-bar { + display: flex; +} diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts index b56ee969d43..0746e309b9e 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts @@ -7,7 +7,7 @@ import 'vs/css!./quickInput'; import { Component } from 'vs/workbench/common/component'; -import { IQuickInputService, IQuickPickItem, IPickOptions, IInputOptions, IQuickNavigateConfiguration, IQuickPick, IQuickInput, IQuickInputButton, IInputBox } from 'vs/platform/quickinput/common/quickInput'; +import { IQuickInputService, IQuickPickItem, IPickOptions, IInputOptions, IQuickNavigateConfiguration, IQuickPick, IQuickInput, IQuickInputButton, IInputBox, IQuickPickItemButtonEvent } from 'vs/platform/quickinput/common/quickInput'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import * as dom from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -34,15 +34,15 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import Severity from 'vs/base/common/severity'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { ICommandAndKeybindingRule, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { ICommandAndKeybindingRule, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { inQuickOpenContext } from 'vs/workbench/browser/parts/quickopen/quickopen'; import { ActionBar, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { Action } from 'vs/base/common/actions'; import URI from 'vs/base/common/uri'; -import { IdGenerator } from 'vs/base/common/idGenerator'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { equals } from 'vs/base/common/arrays'; import { TimeoutTimer } from 'vs/base/common/async'; +import { getIconClass } from 'vs/workbench/browser/parts/quickinput/quickInputUtils'; const $ = dom.$; @@ -62,6 +62,7 @@ interface QuickInputUI { rightActionBar: ActionBar; checkAll: HTMLInputElement; inputBox: QuickInputBox; + visibleCount: CountBadge; count: CountBadge; message: HTMLElement; progressBar: ProgressBar; @@ -72,6 +73,7 @@ interface QuickInputUI { show(controller: QuickInput): void; setVisibilities(visibilities: Visibilities): void; setEnabled(enabled: boolean): void; + setContextKey(contextKey?: string): void; hide(): void; } @@ -79,6 +81,7 @@ type Visibilities = { title?: boolean; checkAll?: boolean; inputBox?: boolean; + visibleCount?: boolean; count?: boolean; message?: boolean; list?: boolean; @@ -92,6 +95,7 @@ class QuickInput implements IQuickInput { private _totalSteps: number; protected visible = false; private _enabled = true; + private _contextKey: string; private _busy = false; private _ignoreFocusOut = false; private _buttons: IQuickInputButton[] = []; @@ -146,6 +150,15 @@ class QuickInput implements IQuickInput { this.update(); } + get contextKey() { + return this._contextKey; + } + + set contextKey(contextKey: string) { + this._contextKey = contextKey; + this.update(); + } + get busy() { return this._busy; } @@ -180,7 +193,7 @@ class QuickInput implements IQuickInput { if (this.visible) { return; } - this.disposables.push( + this.visibleDisposables.push( this.ui.onDidTriggerButton(button => { if (this.buttons.indexOf(button) !== -1) { this.onDidTriggerButtonEmitter.fire(button); @@ -233,20 +246,21 @@ class QuickInput implements IQuickInput { this.ui.leftActionBar.clear(); const leftButtons = this.buttons.filter(button => button === backButton); this.ui.leftActionBar.push(leftButtons.map((button, index) => { - const action = new Action(`id-${index}`, '', getIconClass(button.iconPath), true, () => this.onDidTriggerButtonEmitter.fire(button)); + const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath), true, () => this.onDidTriggerButtonEmitter.fire(button)); action.tooltip = button.tooltip; return action; }), { icon: true, label: false }); this.ui.rightActionBar.clear(); const rightButtons = this.buttons.filter(button => button !== backButton); this.ui.rightActionBar.push(rightButtons.map((button, index) => { - const action = new Action(`id-${index}`, '', getIconClass(button.iconPath), true, () => this.onDidTriggerButtonEmitter.fire(button)); + const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath), true, () => this.onDidTriggerButtonEmitter.fire(button)); action.tooltip = button.tooltip; return action; }), { icon: true, label: false }); } this.ui.ignoreFocusOut = this.ignoreFocusOut; this.ui.setEnabled(this.enabled); + this.ui.setContextKey(this.contextKey); } private getTitle() { @@ -297,7 +311,9 @@ class QuickPick extends QuickInput implements IQuickPi private selectedItemsUpdated = false; private selectedItemsToConfirm: T[] = []; private onDidChangeSelectionEmitter = new Emitter(); - private quickNavigate = false; + private onDidTriggerItemButtonEmitter = new Emitter>(); + + quickNavigate: IQuickNavigateConfiguration; constructor(ui: QuickInputUI) { super(ui); @@ -306,6 +322,7 @@ class QuickPick extends QuickInput implements IQuickPi this.onDidAcceptEmitter, this.onDidChangeActiveEmitter, this.onDidChangeSelectionEmitter, + this.onDidTriggerItemButtonEmitter, ); } @@ -392,6 +409,8 @@ class QuickPick extends QuickInput implements IQuickPi onDidChangeSelection = this.onDidChangeSelectionEmitter.event; + onDidTriggerItemButton = this.onDidTriggerItemButtonEmitter.event; + show() { if (!this.visible) { this.visibleDisposables.push( @@ -415,7 +434,31 @@ class QuickPick extends QuickInput implements IQuickPi } break; case KeyCode.UpArrow: - this.ui.list.focus('Previous'); + if (this.ui.list.getFocusedElements().length) { + this.ui.list.focus('Previous'); + } else { + this.ui.list.focus('Last'); + } + if (this.canSelectMany) { + this.ui.list.domFocus(); + } + break; + case KeyCode.PageDown: + if (this.ui.list.getFocusedElements().length) { + this.ui.list.focus('NextPage'); + } else { + this.ui.list.focus('First'); + } + if (this.canSelectMany) { + this.ui.list.domFocus(); + } + break; + case KeyCode.PageUp: + if (this.ui.list.getFocusedElements().length) { + this.ui.list.focus('PreviousPage'); + } else { + this.ui.list.focus('Last'); + } if (this.canSelectMany) { this.ui.list.domFocus(); } @@ -460,76 +503,24 @@ class QuickPick extends QuickInput implements IQuickPi this._selectedItems = checkedItems as T[]; this.onDidChangeSelectionEmitter.fire(checkedItems as T[]); }), + this.ui.list.onButtonTriggered(event => this.onDidTriggerItemButtonEmitter.fire(event as IQuickPickItemButtonEvent)), + this.registerQuickNavigation() ); } super.show(); } - protected update() { - super.update(); - if (!this.visible) { - return; - } - if (this.ui.inputBox.value !== this.value) { - this.ui.inputBox.value = this.value; - } - if (this.ui.inputBox.placeholder !== (this.placeholder || '')) { - this.ui.inputBox.placeholder = (this.placeholder || ''); - } - if (this.itemsUpdated) { - this.itemsUpdated = false; - this.ui.list.setElements(this.items); - this.ui.list.filter(this.ui.inputBox.value); - this.ui.checkAll.checked = this.ui.list.getAllVisibleChecked(); - this.ui.count.setCount(this.ui.list.getCheckedCount()); - if (!this.canSelectMany) { - this.ui.list.focus('First'); + private registerQuickNavigation() { + return dom.addDisposableListener(this.ui.container, dom.EventType.KEY_UP, (e: KeyboardEvent) => { + if (this.canSelectMany || !this.quickNavigate) { + return; } - } - if (this.ui.container.classList.contains('show-checkboxes') !== this.canSelectMany) { - if (this.canSelectMany) { - this.ui.list.clearFocus(); - } else { - this.ui.list.focus('First'); - } - } - if (this.activeItemsUpdated) { - this.activeItemsUpdated = false; - this.activeItemsToConfirm = this._activeItems; - this.ui.list.setFocusedElements(this.activeItems); - if (this.activeItemsToConfirm === this._activeItems) { - this.activeItemsToConfirm = null; - } - } - if (this.selectedItemsUpdated) { - this.selectedItemsUpdated = false; - this.selectedItemsToConfirm = this._selectedItems; - if (this.canSelectMany) { - this.ui.list.setCheckedElements(this.selectedItems); - } else { - this.ui.list.setSelectedElements(this.selectedItems); - } - if (this.selectedItemsToConfirm === this._selectedItems) { - this.selectedItemsToConfirm = null; - } - } - this.ui.list.matchOnDescription = this.matchOnDescription; - this.ui.list.matchOnDetail = this.matchOnDetail; - this.ui.setVisibilities(this.canSelectMany ? { title: !!this.title || !!this.step, checkAll: true, inputBox: true, count: true, ok: true, list: true } : { title: !!this.title || !!this.step, inputBox: true, list: true }); - } - configureQuickNavigate(quickNavigate: IQuickNavigateConfiguration) { - if (this.canSelectMany || this.quickNavigate) { - return; - } - this.quickNavigate = true; - - this.disposables.push(dom.addDisposableListener(this.ui.container, dom.EventType.KEY_UP, (e: KeyboardEvent) => { const keyboardEvent: StandardKeyboardEvent = new StandardKeyboardEvent(e as KeyboardEvent); const keyCode = keyboardEvent.keyCode; // Select element when keys are pressed that signal it - const quickNavKeys = quickNavigate.keybindings; + const quickNavKeys = this.quickNavigate.keybindings; const wasTriggerKeyPressed = keyCode === KeyCode.Enter || quickNavKeys.some(k => { const [firstPart, chordPart] = k.getParts(); if (chordPart) { @@ -564,7 +555,61 @@ class QuickPick extends QuickInput implements IQuickPi this.onDidChangeSelectionEmitter.fire(this.selectedItems); this.onDidAcceptEmitter.fire(); } - })); + }); + } + + protected update() { + super.update(); + if (!this.visible) { + return; + } + if (this.ui.inputBox.value !== this.value) { + this.ui.inputBox.value = this.value; + } + if (this.ui.inputBox.placeholder !== (this.placeholder || '')) { + this.ui.inputBox.placeholder = (this.placeholder || ''); + } + if (this.itemsUpdated) { + this.itemsUpdated = false; + this.ui.list.setElements(this.items); + this.ui.list.filter(this.ui.inputBox.value); + this.ui.checkAll.checked = this.ui.list.getAllVisibleChecked(); + this.ui.visibleCount.setCount(this.ui.list.getVisibleCount()); + this.ui.count.setCount(this.ui.list.getCheckedCount()); + if (!this.canSelectMany) { + this.ui.list.focus('First'); + } + } + if (this.ui.container.classList.contains('show-checkboxes') !== this.canSelectMany) { + if (this.canSelectMany) { + this.ui.list.clearFocus(); + } else { + this.ui.list.focus('First'); + } + } + if (this.activeItemsUpdated) { + this.activeItemsUpdated = false; + this.activeItemsToConfirm = this._activeItems; + this.ui.list.setFocusedElements(this.activeItems); + if (this.activeItemsToConfirm === this._activeItems) { + this.activeItemsToConfirm = null; + } + } + if (this.selectedItemsUpdated) { + this.selectedItemsUpdated = false; + this.selectedItemsToConfirm = this._selectedItems; + if (this.canSelectMany) { + this.ui.list.setCheckedElements(this.selectedItems); + } else { + this.ui.list.setSelectedElements(this.selectedItems); + } + if (this.selectedItemsToConfirm === this._selectedItems) { + this.selectedItemsToConfirm = null; + } + } + this.ui.list.matchOnDescription = this.matchOnDescription; + this.ui.list.matchOnDetail = this.matchOnDetail; + this.ui.setVisibilities(this.canSelectMany ? { title: !!this.title || !!this.step, checkAll: true, inputBox: true, visibleCount: true, count: true, ok: true, list: true } : { title: !!this.title || !!this.step, inputBox: true, visibleCount: true, list: true }); } } @@ -705,6 +750,7 @@ export class QuickInputService extends Component implements IQuickInputService { private layoutDimensions: dom.Dimension; private titleBar: HTMLElement; private filterContainer: HTMLElement; + private visibleCountContainer: HTMLElement; private countContainer: HTMLElement; private okContainer: HTMLElement; private ok: Button; @@ -712,6 +758,7 @@ export class QuickInputService extends Component implements IQuickInputService { private enabled = true; private inQuickOpenWidgets: Record = {}; private inQuickOpenContext: IContextKey; + private contexts: { [id: string]: IContextKey; } = Object.create(null); private onDidAcceptEmitter = this._register(new Emitter()); private onDidTriggerButtonEmitter = this._register(new Emitter()); @@ -725,7 +772,7 @@ export class QuickInputService extends Component implements IQuickInputService { @IQuickOpenService private quickOpenService: IQuickOpenService, @IEditorGroupsService private editorGroupService: IEditorGroupsService, @IKeybindingService private keybindingService: IKeybindingService, - @IContextKeyService contextKeyService: IContextKeyService, + @IContextKeyService private contextKeyService: IContextKeyService, @IThemeService themeService: IThemeService ) { super(QuickInputService.ID, themeService); @@ -751,13 +798,43 @@ export class QuickInputService extends Component implements IQuickInputService { } } + private setContextKey(id?: string) { + let key: IContextKey; + if (id) { + key = this.contexts[id]; + if (!key) { + key = new RawContextKey(id, false) + .bindTo(this.contextKeyService); + this.contexts[id] = key; + } + } + + if (key && key.get()) { + return; // already active context + } + + this.resetContextKeys(); + + if (key) { + key.set(true); + } + } + + private resetContextKeys() { + for (const key in this.contexts) { + if (this.contexts[key].get()) { + this.contexts[key].reset(); + } + } + } + private create() { if (this.ui) { return; } const workbench = document.getElementById(this.partService.getWorkbenchElementId()); - const container = dom.append(workbench, $('.quick-input-widget')); + const container = dom.append(workbench, $('.quick-input-widget.show-file-icons')); container.tabIndex = -1; container.style.display = 'none'; @@ -789,7 +866,12 @@ export class QuickInputService extends Component implements IQuickInputService { const inputBox = this._register(new QuickInputBox(this.filterContainer)); + this.visibleCountContainer = dom.append(this.filterContainer, $('.quick-input-visible-count')); + this.visibleCountContainer.setAttribute('aria-live', 'polite'); + const visibleCount = new CountBadge(this.visibleCountContainer, { countFormat: localize({ key: 'quickInput.visibleCount', comment: ['This tells the user how many items are shown in a list of items to select from. The items can be anything. Currently not visible, but read by screen readers.'] }, "{0} Results") }); + this.countContainer = dom.append(this.filterContainer, $('.quick-input-count')); + this.countContainer.setAttribute('aria-live', 'polite'); const count = new CountBadge(this.countContainer, { countFormat: localize({ key: 'quickInput.countSelected', comment: ['This tells the user how many items are selected in a list of items to select from. The items can be anything.'] }, "{0} Selected") }); this._register(attachBadgeStyler(count, this.themeService)); @@ -811,6 +893,9 @@ export class QuickInputService extends Component implements IQuickInputService { this._register(list.onChangedAllVisibleChecked(checked => { checkAll.checked = checked; })); + this._register(list.onChangedVisibleCount(c => { + visibleCount.setCount(c); + })); this._register(list.onChangedCheckedCount(c => { count.setCount(c); })); @@ -818,20 +903,15 @@ export class QuickInputService extends Component implements IQuickInputService { // Defer to avoid the input field reacting to the triggering key. setTimeout(() => { inputBox.setFocus(); - list.clearFocus(); + if (this.controller instanceof QuickPick && this.controller.canSelectMany) { + list.clearFocus(); + } }, 0); })); - this._register(dom.addDisposableListener(container, 'focusout', (e: FocusEvent) => { - if (e.relatedTarget === container) { - (e.target).focus(); - return; - } - for (let element = e.relatedTarget; element; element = element.parentElement) { - if (element === container) { - return; - } - } + const focusTracker = dom.trackFocus(container); + this._register(focusTracker); + this._register(focusTracker.onDidBlur(() => { if (!this.ui.ignoreFocusOut && !this.environmentService.args['sticky-quickopen'] && this.configurationService.getValue(CLOSE_ON_FOCUS_LOST_CONFIG)) { this.hide(true); } @@ -849,18 +929,22 @@ export class QuickInputService extends Component implements IQuickInputService { break; case KeyCode.Tab: if (!event.altKey && !event.ctrlKey && !event.metaKey) { - const inputs = [].slice.call(container.querySelectorAll('.action-label.icon')); + const selectors = ['.action-label.icon']; if (container.classList.contains('show-checkboxes')) { - inputs.push(...[].slice.call(container.querySelectorAll('input'))); + selectors.push('input'); } else { - inputs.push(...[].slice.call(container.querySelectorAll('input[type=text]'))); + selectors.push('input[type=text]'); } - if (event.shiftKey && event.target === inputs[0]) { + if (this.ui.list.isDisplayed()) { + selectors.push('.monaco-list'); + } + const stops = container.querySelectorAll(selectors.join(', ')); + if (event.shiftKey && event.target === stops[0]) { dom.EventHelper.stop(e, true); - inputs[inputs.length - 1].focus(); - } else if (!event.shiftKey && event.target === inputs[inputs.length - 1]) { + stops[stops.length - 1].focus(); + } else if (!event.shiftKey && event.target === stops[stops.length - 1]) { dom.EventHelper.stop(e, true); - inputs[0].focus(); + stops[0].focus(); } } break; @@ -876,6 +960,7 @@ export class QuickInputService extends Component implements IQuickInputService { rightActionBar, checkAll, inputBox, + visibleCount, count, message, progressBar, @@ -887,11 +972,12 @@ export class QuickInputService extends Component implements IQuickInputService { hide: () => this.hide(), setVisibilities: visibilities => this.setVisibilities(visibilities), setEnabled: enabled => this.setEnabled(enabled), + setContextKey: contextKey => this.setContextKey(contextKey), }; this.updateStyles(); } - pick>(picks: TPromise, options: O = {}, token: CancellationToken = CancellationToken.None): TPromise { + pick>(picks: TPromise | T[], options: O = {}, token: CancellationToken = CancellationToken.None): TPromise { return new TPromise((resolve, reject) => { if (token.isCancellationRequested) { resolve(undefined); @@ -927,6 +1013,17 @@ export class QuickInputService extends Component implements IQuickInputService { } } }), + input.onDidTriggerItemButton(event => options.onDidTriggerItemButton && options.onDidTriggerItemButton({ + ...event, + removeItem: () => { + const index = input.items.indexOf(event.item); + if (index !== -1) { + const items = input.items.slice(); + items.splice(index, 1); + input.items = items; + } + } + })), token.onCancellationRequested(() => { input.hide(); }), @@ -940,16 +1037,22 @@ export class QuickInputService extends Component implements IQuickInputService { input.ignoreFocusOut = options.ignoreFocusLost; input.matchOnDescription = options.matchOnDescription; input.matchOnDetail = options.matchOnDetail; + input.quickNavigate = options.quickNavigate; + input.contextKey = options.contextKey; input.busy = true; - picks.then(items => { - input.busy = false; - input.items = items; - if (input.canSelectMany) { - input.selectedItems = items.filter(item => item.picked); - } - }); + TPromise.join([picks, options.activeItem]) + .then(([items, activeItem]) => { + input.busy = false; + input.items = items; + if (input.canSelectMany) { + input.selectedItems = items.filter(item => item.picked); + } + if (activeItem) { + input.activeItems = [activeItem]; + } + }); input.show(); - picks.then(null, err => { + TPromise.wrap(picks).then(null, err => { reject(err); input.hide(); }); @@ -1043,6 +1146,7 @@ export class QuickInputService extends Component implements IQuickInputService { this.ui.inputBox.placeholder = ''; this.ui.inputBox.password = false; this.ui.inputBox.showDecoration(Severity.Ignore); + this.ui.visibleCount.setCount(0); this.ui.count.setCount(0); this.ui.message.textContent = ''; this.ui.progressBar.stop(); @@ -1055,6 +1159,7 @@ export class QuickInputService extends Component implements IQuickInputService { backButton.tooltip = keybinding ? localize('quickInput.backWithKeybinding', "Back ({0})", keybinding.getLabel()) : localize('quickInput.back', "Back"); this.inQuickOpen('quickInput', true); + this.resetContextKeys(); this.ui.container.style.display = ''; this.updateLayout(); @@ -1065,6 +1170,7 @@ export class QuickInputService extends Component implements IQuickInputService { this.ui.title.style.display = visibilities.title ? '' : 'none'; this.ui.checkAll.style.display = visibilities.checkAll ? '' : 'none'; this.filterContainer.style.display = visibilities.inputBox ? '' : 'none'; + this.visibleCountContainer.style.display = visibilities.visibleCount ? '' : 'none'; this.countContainer.style.display = visibilities.count ? '' : 'none'; this.okContainer.style.display = visibilities.ok ? '' : 'none'; this.ui.message.style.display = visibilities.message ? '' : 'none'; @@ -1094,6 +1200,7 @@ export class QuickInputService extends Component implements IQuickInputService { if (controller) { this.controller = null; this.inQuickOpen('quickInput', false); + this.resetContextKeys(); this.ui.container.style.display = 'none'; if (!focusLost) { this.editorGroupService.activeGroup.focus(); @@ -1118,7 +1225,7 @@ export class QuickInputService extends Component implements IQuickInputService { if (this.isDisplayed() && this.ui.list.isDisplayed()) { this.ui.list.focus(next ? 'Next' : 'Previous'); if (quickNavigate && this.controller instanceof QuickPick) { - this.controller.configureQuickNavigate(quickNavigate); + this.controller.quickNavigate = quickNavigate; } } } @@ -1181,28 +1288,9 @@ export class QuickInputService extends Component implements IQuickInputService { } } -const iconPathToClass = {}; -const iconClassGenerator = new IdGenerator('quick-input-button-icon-'); - -function getIconClass(iconPath: { dark: URI; light?: URI; }) { - let iconClass: string; - - const key = iconPath.dark.toString(); - if (iconPathToClass[key]) { - iconClass = iconPathToClass[key]; - } else { - iconClass = iconClassGenerator.nextId(); - dom.createCSSRule(`.${iconClass}`, `background-image: url("${(iconPath.light || iconPath.dark).toString()}")`); - dom.createCSSRule(`.vs-dark .${iconClass}, .hc-black .${iconClass}`, `background-image: url("${iconPath.dark.toString()}")`); - iconPathToClass[key] = iconClass; - } - - return iconClass; -} - export const QuickPickManyToggle: ICommandAndKeybindingRule = { id: 'workbench.action.quickPickManyToggle', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: inQuickOpenContext, primary: undefined, handler: accessor => { diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts index 6655db6f7bf..ff832f65ebe 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts @@ -11,7 +11,7 @@ import * as dom from 'vs/base/browser/dom'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { WorkbenchList } from 'vs/platform/list/browser/listService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; +import { IQuickPickItem, IQuickPickItemButtonEvent } from 'vs/platform/quickinput/common/quickInput'; import { IMatch } from 'vs/base/common/filters'; import { matchesFuzzyOcticonAware, parseOcticons } from 'vs/base/common/octicon'; import { compareAnything } from 'vs/base/common/comparers'; @@ -26,6 +26,9 @@ import { range } from 'vs/base/common/arrays'; import * as platform from 'vs/base/common/platform'; import { listFocusBackground } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Action } from 'vs/base/common/actions'; +import { getIconClass } from 'vs/workbench/browser/parts/quickinput/quickInputUtils'; const $ = dom.$; @@ -33,6 +36,7 @@ interface IListElement { index: number; item: IQuickPickItem; checked: boolean; + fireButtonTriggered: (event: IQuickPickItemButtonEvent) => void; } class ListElement implements IListElement { @@ -55,6 +59,7 @@ class ListElement implements IListElement { labelHighlights?: IMatch[]; descriptionHighlights?: IMatch[]; detailHighlights?: IMatch[]; + fireButtonTriggered: (event: IQuickPickItemButtonEvent) => void; constructor(init: IListElement) { assign(this, init); @@ -65,6 +70,7 @@ interface IListElementTemplateData { checkbox: HTMLInputElement; label: IconLabel; detail: HighlightedLabel; + actionBar: ActionBar; element: ListElement; toDisposeElement: IDisposable[]; toDisposeTemplate: IDisposable[]; @@ -105,6 +111,11 @@ class ListElementRenderer implements IRenderer { + const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath), true, () => { + element.fireButtonTriggered({ + button, + item: element.item + }); + return null; + }); + action.tooltip = button.tooltip; + return action; + }), { icon: true, label: false }); + } } - disposeElement(): void { - // noop + disposeElement(element: ListElement, index: number, data: IListElementTemplateData): void { + data.toDisposeElement = dispose(data.toDisposeElement); } disposeTemplate(data: IListElementTemplateData): void { @@ -160,8 +188,12 @@ export class QuickInputList { onChangedAllVisibleChecked: Event = this._onChangedAllVisibleChecked.event; private _onChangedCheckedCount = new Emitter(); onChangedCheckedCount: Event = this._onChangedCheckedCount.event; + private _onChangedVisibleCount = new Emitter(); + onChangedVisibleCount: Event = this._onChangedVisibleCount.event; private _onChangedCheckedElements = new Emitter(); onChangedCheckedElements: Event = this._onChangedCheckedElements.event; + private _onButtonTriggered = new Emitter>(); + onButtonTriggered = this._onButtonTriggered.event; private _onLeave = new Emitter(); onLeave: Event = this._onLeave.event; private _fireCheckedEvents = true; @@ -191,12 +223,14 @@ export class QuickInputList { } break; case KeyCode.UpArrow: + case KeyCode.PageUp: const focus1 = this.list.getFocus(); if (focus1.length === 1 && focus1[0] === 0) { this._onLeave.fire(); } break; case KeyCode.DownArrow: + case KeyCode.PageDown: const focus2 = this.list.getFocus(); if (focus2.length === 1 && focus2[0] === this.list.length - 1) { this._onLeave.fire(); @@ -255,6 +289,17 @@ export class QuickInputList { return count; } + getVisibleCount() { + let count = 0; + const elements = this.elements; + for (let i = 0, n = elements.length; i < n; i++) { + if (!elements[i].hidden) { + count++; + } + } + return count; + } + setAllVisibleChecked(checked: boolean) { try { this._fireCheckedEvents = false; @@ -271,10 +316,12 @@ export class QuickInputList { setElements(elements: IQuickPickItem[]): void { this.elementDisposables = dispose(this.elementDisposables); + const fireButtonTriggered = (event: IQuickPickItemButtonEvent) => this.fireButtonTriggered(event); this.elements = elements.map((item, index) => new ListElement({ index, item, - checked: false + checked: false, + fireButtonTriggered })); this.elementDisposables.push(...this.elements.map(element => element.onChecked(() => this.fireCheckedEvents()))); @@ -284,6 +331,7 @@ export class QuickInputList { }, new Map()); this.list.splice(0, this.list.length, this.elements); this.list.setFocus([]); + this._onChangedVisibleCount.fire(this.elements.length); } getFocusedElements() { @@ -338,10 +386,10 @@ export class QuickInputList { return; } - if (what === 'Next' && this.list.getFocus()[0] === this.list.length - 1) { + if ((what === 'Next' || what === 'NextPage') && this.list.getFocus()[0] === this.list.length - 1) { what = 'First'; } - if (what === 'Previous' && this.list.getFocus()[0] === 0) { + if ((what === 'Previous' || what === 'PreviousPage') && this.list.getFocus()[0] === 0) { what = 'Last'; } @@ -415,6 +463,7 @@ export class QuickInputList { this.list.layout(); this._onChangedAllVisibleChecked.fire(this.getAllVisibleChecked()); + this._onChangedVisibleCount.fire(shownElements.length); } toggleCheckbox() { @@ -451,6 +500,10 @@ export class QuickInputList { this._onChangedCheckedElements.fire(this.getCheckedElements()); } } + + private fireButtonTriggered(event: IQuickPickItemButtonEvent) { + this._onButtonTriggered.fire(event); + } } function compareEntries(elementA: ListElement, elementB: ListElement, lookFor: string): number { diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputUtils.ts b/src/vs/workbench/browser/parts/quickinput/quickInputUtils.ts new file mode 100644 index 00000000000..ef37fbf76dc --- /dev/null +++ b/src/vs/workbench/browser/parts/quickinput/quickInputUtils.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * 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!./quickInput'; +import * as dom from 'vs/base/browser/dom'; +import URI from 'vs/base/common/uri'; +import { IdGenerator } from 'vs/base/common/idGenerator'; + +const iconPathToClass = {}; +const iconClassGenerator = new IdGenerator('quick-input-button-icon-'); + +export function getIconClass(iconPath: { dark: URI; light?: URI; }) { + let iconClass: string; + + const key = iconPath.dark.toString(); + if (iconPathToClass[key]) { + iconClass = iconPathToClass[key]; + } else { + iconClass = iconClassGenerator.nextId(); + dom.createCSSRule(`.${iconClass}`, `background-image: url("${(iconPath.light || iconPath.dark).toString()}")`); + dom.createCSSRule(`.vs-dark .${iconClass}, .hc-black .${iconClass}`, `background-image: url("${iconPath.dark.toString()}")`); + iconPathToClass[key] = iconClass; + } + + return iconClass; +} diff --git a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts index 7cab9eec672..e35dd586dd8 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts @@ -21,7 +21,6 @@ import { Mode, IEntryRunContext, IAutoFocus, IQuickNavigateConfiguration, IModel import { QuickOpenEntry, QuickOpenModel, QuickOpenEntryGroup, compareEntries, QuickOpenItemAccessorClass } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { QuickOpenWidget, HideReason } from 'vs/base/parts/quickopen/browser/quickOpenWidget'; import { ContributableActionProvider } from 'vs/workbench/browser/actions'; -import * as labels from 'vs/base/common/labels'; import { ITextFileService, AutoSaveMode } from 'vs/workbench/services/textfile/common/textfiles'; import { Registry } from 'vs/platform/registry/common/platform'; import { IResourceInput } from 'vs/platform/editor/common/editor'; @@ -37,7 +36,6 @@ import * as errors from 'vs/base/common/errors'; import { IPickOpenEntry, IFilePickOpenEntry, IQuickOpenService, IShowOptions, IPickOpenItem, IStringPickOptions, ITypedPickOptions } from 'vs/platform/quickOpen/common/quickOpen'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -57,6 +55,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { Dimension, addClass } from 'vs/base/browser/dom'; import { IEditorService, ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; const HELP_PREFIX = '?'; @@ -1150,9 +1149,8 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry { @IModeService private modeService: IModeService, @IModelService private modelService: IModelService, @ITextFileService private textFileService: ITextFileService, - @IWorkspaceContextService contextService: IWorkspaceContextService, @IConfigurationService private configurationService: IConfigurationService, - @IEnvironmentService environmentService: IEnvironmentService, + @IUriDisplayService uriDisplayService: IUriDisplayService, @IFileService fileService: IFileService ) { super(editorService); @@ -1167,8 +1165,8 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry { } else { const resourceInput = input as IResourceInput; this.resource = resourceInput.resource; - this.label = labels.getBaseLabel(resourceInput.resource); - this.description = labels.getPathLabel(resources.dirname(this.resource), environmentService, contextService); + this.label = resources.basenameOrAuthority(resourceInput.resource); + this.description = uriDisplayService.getLabel(resources.dirname(this.resource), true); this.dirty = this.resource && this.textFileService.isDirty(this.resource); if (this.dirty && this.textFileService.getAutoSaveMode() === AutoSaveMode.AFTER_SHORT_DELAY) { diff --git a/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.ts b/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.ts index 3f4ad79d9b0..95a8a593377 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickopen.contribution.ts @@ -9,14 +9,14 @@ import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { RemoveFromEditorHistoryAction } from 'vs/workbench/browser/parts/quickopen/quickOpenController'; import { QuickOpenSelectNextAction, QuickOpenSelectPreviousAction, inQuickOpenContext, getQuickNavigateHandler, QuickOpenNavigateNextAction, QuickOpenNavigatePreviousAction, defaultQuickOpenContext, QUICKOPEN_ACTION_ID, QUICKOPEN_ACION_LABEL } from 'vs/workbench/browser/parts/quickopen/quickopen'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'workbench.action.closeQuickOpen', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: inQuickOpenContext, primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape], handler: accessor => { @@ -29,7 +29,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'workbench.action.acceptSelectedQuickOpenItem', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: inQuickOpenContext, primary: null, handler: accessor => { @@ -42,7 +42,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'workbench.action.focusQuickOpen', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: inQuickOpenContext, primary: null, handler: accessor => { @@ -59,7 +59,7 @@ const globalQuickOpenKeybinding = { primary: KeyMod.CtrlCmd | KeyCode.KEY_P, sec KeybindingsRegistry.registerKeybindingRule({ id: QUICKOPEN_ACTION_ID, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: undefined, primary: globalQuickOpenKeybinding.primary, secondary: globalQuickOpenKeybinding.secondary, @@ -70,8 +70,8 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: QUICKOPEN_ACTION_ID, title: QUICKOPEN_ACION_LABEL } }); -registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenSelectNextAction, QuickOpenSelectNextAction.ID, QuickOpenSelectNextAction.LABEL, { primary: null, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_N } }, inQuickOpenContext, KeybindingsRegistry.WEIGHT.workbenchContrib(50)), 'Select Next in Quick Open'); -registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenSelectPreviousAction, QuickOpenSelectPreviousAction.ID, QuickOpenSelectPreviousAction.LABEL, { primary: null, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_P } }, inQuickOpenContext, KeybindingsRegistry.WEIGHT.workbenchContrib(50)), 'Select Previous in Quick Open'); +registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenSelectNextAction, QuickOpenSelectNextAction.ID, QuickOpenSelectNextAction.LABEL, { primary: null, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_N } }, inQuickOpenContext, KeybindingWeight.WorkbenchContrib + 50), 'Select Next in Quick Open'); +registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenSelectPreviousAction, QuickOpenSelectPreviousAction.ID, QuickOpenSelectPreviousAction.LABEL, { primary: null, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_P } }, inQuickOpenContext, KeybindingWeight.WorkbenchContrib + 50), 'Select Previous in Quick Open'); registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenNavigateNextAction, QuickOpenNavigateNextAction.ID, QuickOpenNavigateNextAction.LABEL), 'Navigate Next in Quick Open'); registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenNavigatePreviousAction, QuickOpenNavigatePreviousAction.ID, QuickOpenNavigatePreviousAction.LABEL), 'Navigate Previous in Quick Open'); registry.registerWorkbenchAction(new SyncActionDescriptor(RemoveFromEditorHistoryAction, RemoveFromEditorHistoryAction.ID, RemoveFromEditorHistoryAction.LABEL), 'Remove From History'); @@ -79,7 +79,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(RemoveFromEditorHistor const quickOpenNavigateNextInFilePickerId = 'workbench.action.quickOpenNavigateNextInFilePicker'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: quickOpenNavigateNextInFilePickerId, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50), + weight: KeybindingWeight.WorkbenchContrib + 50, handler: getQuickNavigateHandler(quickOpenNavigateNextInFilePickerId, true), when: defaultQuickOpenContext, primary: globalQuickOpenKeybinding.primary, @@ -90,7 +90,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const quickOpenNavigatePreviousInFilePickerId = 'workbench.action.quickOpenNavigatePreviousInFilePicker'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: quickOpenNavigatePreviousInFilePickerId, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50), + weight: KeybindingWeight.WorkbenchContrib + 50, handler: getQuickNavigateHandler(quickOpenNavigatePreviousInFilePickerId, false), when: defaultQuickOpenContext, primary: globalQuickOpenKeybinding.primary | KeyMod.Shift, diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index 88e7acb9c01..556cdfd222e 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -26,8 +26,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { SIDE_BAR_TITLE_FOREGROUND, SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND, SIDE_BAR_BORDER } from 'vs/workbench/common/theme'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { Dimension, EventType } from 'vs/base/browser/dom'; -import { $ } from 'vs/base/browser/builder'; +import { Dimension, EventType, addDisposableListener } from 'vs/base/browser/dom'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; export class SidebarPart extends CompositePart { @@ -77,7 +76,10 @@ export class SidebarPart extends CompositePart { createTitleArea(parent: HTMLElement): HTMLElement { const titleArea = super.createTitleArea(parent); - $(titleArea).on(EventType.CONTEXT_MENU, (e: MouseEvent) => this.onTitleAreaContextMenu(new StandardMouseEvent(e)), this.toDispose); + + this._register(addDisposableListener(titleArea, EventType.CONTEXT_MENU, e => { + this.onTitleAreaContextMenu(new StandardMouseEvent(e)); + })); return titleArea; } @@ -86,19 +88,19 @@ export class SidebarPart extends CompositePart { super.updateStyles(); // Part container - const container = $(this.getContainer()); + const container = this.getContainer(); - container.style('background-color', this.getColor(SIDE_BAR_BACKGROUND)); - container.style('color', this.getColor(SIDE_BAR_FOREGROUND)); + container.style.backgroundColor = this.getColor(SIDE_BAR_BACKGROUND); + container.style.color = this.getColor(SIDE_BAR_FOREGROUND); const borderColor = this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder); const isPositionLeft = this.partService.getSideBarPosition() === SideBarPosition.LEFT; - container.style('border-right-width', borderColor && isPositionLeft ? '1px' : null); - container.style('border-right-style', borderColor && isPositionLeft ? 'solid' : null); - container.style('border-right-color', isPositionLeft ? borderColor : null); - container.style('border-left-width', borderColor && !isPositionLeft ? '1px' : null); - container.style('border-left-style', borderColor && !isPositionLeft ? 'solid' : null); - container.style('border-left-color', !isPositionLeft ? borderColor : null); + container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : null; + container.style.borderRightStyle = borderColor && isPositionLeft ? 'solid' : null; + container.style.borderRightColor = isPositionLeft ? borderColor : null; + container.style.borderLeftWidth = borderColor && !isPositionLeft ? '1px' : null; + container.style.borderLeftStyle = borderColor && !isPositionLeft ? 'solid' : null; + container.style.borderLeftColor = !isPositionLeft ? borderColor : null; } openViewlet(id: string, focus?: boolean): TPromise { diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index 226ccc2b80d..d0c0302f07c 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -9,7 +9,7 @@ import 'vs/css!./media/statusbarpart'; import * as nls from 'vs/nls'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { TPromise } from 'vs/base/common/winjs.base'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { $ } from 'vs/base/browser/builder'; import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -86,15 +86,13 @@ export class StatusbarPart extends Part implements IStatusbarService { container.appendChild(el); } - return { - dispose: () => { - $(el).destroy(); + return toDisposable(() => { + $(el).destroy(); - if (toDispose) { - toDispose.dispose(); - } + if (toDispose) { + toDispose.dispose(); } - }; + }); } private getEntries(alignment: StatusbarAlignment): HTMLElement[] { @@ -136,22 +134,22 @@ export class StatusbarPart extends Part implements IStatusbarService { protected updateStyles(): void { super.updateStyles(); - const container = $(this.getContainer()); + const container = this.getContainer(); // Background colors const backgroundColor = this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_BACKGROUND : STATUS_BAR_NO_FOLDER_BACKGROUND); - container.style('background-color', backgroundColor); - container.style('color', this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_FOREGROUND : STATUS_BAR_NO_FOLDER_FOREGROUND)); + container.style.backgroundColor = backgroundColor; + container.style.color = this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_FOREGROUND : STATUS_BAR_NO_FOLDER_FOREGROUND); // Border color const borderColor = this.getColor(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY ? STATUS_BAR_BORDER : STATUS_BAR_NO_FOLDER_BORDER) || this.getColor(contrastBorder); - container.style('border-top-width', borderColor ? '1px' : null); - container.style('border-top-style', borderColor ? 'solid' : null); - container.style('border-top-color', borderColor); + container.style.borderTopWidth = borderColor ? '1px' : null; + container.style.borderTopStyle = borderColor ? 'solid' : null; + container.style.borderTopColor = borderColor; // Notification Beak if (!this.styleElement) { - this.styleElement = createStyleSheet(container.getHTMLElement()); + this.styleElement = createStyleSheet(container); } this.styleElement.innerHTML = `.monaco-workbench > .part.statusbar > .statusbar-item.has-beak:before { border-bottom-color: ${backgroundColor}; }`; diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index a8330e6fc88..42965aae65c 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -6,26 +6,37 @@ .monaco-workbench > .part.titlebar { box-sizing: border-box; width: 100%; - font-size: 12px; padding: 0 70px; overflow: hidden; flex-shrink: 0; align-items: center; justify-content: center; user-select: none; - -webkit-app-region: drag; zoom: 1; /* prevent zooming */ line-height: 22px; height: 22px; display: flex; } +.monaco-workbench > .part.titlebar > .titlebar-drag-region { + top: 0; + left: 0; + display: block; + position: absolute; + width: 100%; + height: 100%; + z-index: -1; + -webkit-app-region: drag; +} + .monaco-workbench > .part.titlebar > .window-title { flex: 0 1 auto; + font-size: 12px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; - -webkit-app-region: drag; + margin-left: auto; + margin-right: auto; zoom: 1; /* prevent zooming */ } @@ -36,7 +47,12 @@ padding: 0; height: 30px; line-height: 30px; - justify-content: space-between; + justify-content: left; + overflow: visible; +} + +.monaco-workbench.linux > .part.titlebar > .window-title { + font-size: inherit; } .monaco-workbench.windows > .part.titlebar > .resizer, @@ -48,22 +64,26 @@ height: 20%; } -.monaco-workbench.windows > .part.titlebar > .window-title, -.monaco-workbench.linux > .part.titlebar > .window-title { - order: 2; +.monaco-workbench.windows.fullscreen > .part.titlebar > .resizer, +.monaco-workbench.linux.fullscreen > .part.titlebar > .resizer { + display: none; } + .monaco-workbench > .part.titlebar > .window-appicon { width: 35px; - margin-right: 113px; - -webkit-app-region: no-drag; + height: 100%; position: relative; z-index: 99; - order: 1; background-image: url('code-icon.svg'); background-repeat: no-repeat; background-position: center center; background-size: 16px; + flex-shrink: 0; +} + +.monaco-workbench.fullscreen > .part.titlebar > .window-appicon { + display: none; } .monaco-workbench > .part.titlebar > .window-controls-container { @@ -76,76 +96,106 @@ -webkit-app-region: no-drag; height: 100%; width: 138px; - order: 3; } -.monaco-workbench > .part.titlebar > .window-controls-container > .window-icon { +.monaco-workbench.fullscreen > .part.titlebar > .window-controls-container { + display: none; +} + +.monaco-workbench > .part.titlebar > .window-controls-container > .window-icon-bg { display: inline-block; -webkit-app-region: no-drag; - -webkit-transition: background-color .1s; - transition: background-color .1s; height: 100%; width: 33.34%; - background-size: 21.74%; - background-position: center center; - background-repeat: no-repeat; } -.monaco-workbench > .part.titlebar > .window-controls-container > .window-icon svg { +.monaco-workbench > .part.titlebar > .window-controls-container .window-icon svg { shape-rendering: crispEdges; text-align: center; } -.monaco-workbench > .part.titlebar.titlebar > .window-controls-container > .window-close { - background-image: url('chrome-close-dark.svg'); - order: 3; +.monaco-workbench > .part.titlebar.titlebar > .window-controls-container .window-close { + -webkit-mask: url('chrome-close.svg') no-repeat 50% 50%; } -.monaco-workbench > .part.titlebar.titlebar.light > .window-controls-container > .window-close { - background-image: url('chrome-close.svg'); - order: 3; +.monaco-workbench > .part.titlebar.titlebar > .window-controls-container .window-unmaximize { + -webkit-mask: url('chrome-restore.svg') no-repeat 50% 50%; } -.monaco-workbench > .part.titlebar.titlebar > .window-controls-container > .window-unmaximize { - background-image: url('chrome-restore-dark.svg'); - order: 2; +.monaco-workbench > .part.titlebar > .window-controls-container .window-maximize { + -webkit-mask: url('chrome-maximize.svg') no-repeat 50% 50%; } -.monaco-workbench > .part.titlebar.titlebar.light > .window-controls-container > .window-unmaximize { - background-image: url('chrome-restore.svg'); - order: 2; +.monaco-workbench > .part.titlebar > .window-controls-container .window-minimize { + -webkit-mask: url('chrome-minimize.svg') no-repeat 50% 50%; } -.monaco-workbench > .part.titlebar > .window-controls-container > .window-maximize { - background-image: url('chrome-maximize-dark.svg'); - order: 2; +.monaco-workbench > .part.titlebar > .window-controls-container > .window-icon-bg > .window-icon { + height: 100%; + width: 100%; + -webkit-mask-size: 23.1%; } -.monaco-workbench > .part.titlebar.light > .window-controls-container > .window-maximize { - background-image: url('chrome-maximize.svg'); - order: 2; -} - -.monaco-workbench > .part.titlebar > .window-controls-container > .window-minimize { - background-image: url('chrome-minimize-dark.svg'); - order: 1; -} - -.monaco-workbench > .part.titlebar.light > .window-controls-container > .window-minimize { - background-image: url('chrome-minimize.svg'); - order: 1; -} - -.monaco-workbench > .part.titlebar > .window-controls-container > .window-icon:hover { +.monaco-workbench > .part.titlebar > .window-controls-container > .window-icon-bg:hover { background-color: rgba(255, 255, 255, 0.1); } -.monaco-workbench > .part.titlebar.light > .window-controls-container > .window-icon:hover { +.monaco-workbench > .part.titlebar.light > .window-controls-container > .window-icon-bg:hover { background-color: rgba(0, 0, 0, 0.1); } -.monaco-workbench > .part.titlebar > .window-controls-container > .window-close:hover, - .monaco-workbench > .part.titlebar.light > .window-controls-container > .window-close:hover { +.monaco-workbench > .part.titlebar > .window-controls-container > .window-icon-bg.window-close-bg:hover { background-color: rgba(232, 17, 35, 0.9); - background-image: url('chrome-close-dark.svg'); +} + +.monaco-workbench > .part.titlebar > .window-controls-container .window-icon.window-close:hover { + background-color: white; +} + +/* Menubar styles */ + +.monaco-workbench .menubar { + display: flex; + flex-shrink: 1; + box-sizing: border-box; + height: 30px; + -webkit-app-region: no-drag; + overflow: hidden; + flex-wrap: wrap; +} + +.monaco-workbench.fullscreen .menubar { + margin: 0px; + padding: 0px 5px; +} + +.monaco-workbench .menubar > .menubar-menu-button { + align-items: center; + box-sizing: border-box; + padding: 0px 8px; + cursor: default; + -webkit-app-region: no-drag; + zoom: 1; + white-space: nowrap; +} + +.monaco-workbench .menubar .menubar-menu-items-holder { + position: absolute; + left: 0px; + opacity: 1; + z-index: 2000; +} + +.monaco-workbench .menubar .menubar-menu-items-holder.monaco-menu-container { + font-family: "Segoe WPC", "Segoe UI", ".SFNSDisplay-Light", "SFUIText-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback"; + outline: 0; + border: none; +} + +.monaco-workbench .menubar .menubar-menu-items-holder.monaco-menu-container :focus { + outline: 0; +} + +.hc-black .monaco-workbench .menubar .menubar-menu-items-holder.monaco-menu-container { + border: 2px solid #6FC3DF; } \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/menubar/menubarPart.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts similarity index 75% rename from src/vs/workbench/browser/parts/menubar/menubarPart.ts rename to src/vs/workbench/browser/parts/titlebar/menubarControl.ts index 9d738bebe34..8bf59473f91 100644 --- a/src/vs/workbench/browser/parts/menubar/menubarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -5,12 +5,9 @@ 'use strict'; -import 'vs/workbench/browser/parts/menubar/menubar.contribution'; -import 'vs/css!./media/menubarpart'; import * as nls from 'vs/nls'; import * as browser from 'vs/base/browser/browser'; -import { Part } from 'vs/workbench/browser/part'; -import { IMenubarService, IMenubarMenu, IMenubarMenuItemAction, IMenubarData } from 'vs/platform/menubar/common/menubar'; +import { IMenubarMenu, IMenubarMenuItemAction, IMenubarMenuItemSubmenu, IMenubarKeybinding } from 'vs/platform/menubar/common/menubar'; import { IMenuService, MenuId, IMenu, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { IWindowService, MenuBarVisibility, IWindowsService } from 'vs/platform/windows/common/windows'; @@ -20,20 +17,23 @@ import { Builder, $ } from 'vs/base/browser/builder'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { EventType, Dimension } from 'vs/base/browser/dom'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { isWindows, isMacintosh } from 'vs/base/common/platform'; +import { isMacintosh } from 'vs/base/common/platform'; import { Menu, IMenuOptions, SubmenuAction } from 'vs/base/browser/ui/menu/menu'; import { KeyCode } from 'vs/base/common/keyCodes'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { Event, Emitter } from 'vs/base/common/event'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable, dispose } from 'vs/base/common/lifecycle'; import { domEvent } from 'vs/base/browser/event'; import { IRecentlyOpened } from 'vs/platform/history/common/history'; -import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, getWorkspaceLabel } from 'vs/platform/workspaces/common/workspaces'; -import { getPathLabel } from 'vs/base/common/labels'; +import { IWorkspaceIdentifier, getWorkspaceLabel, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { RunOnceScheduler } from 'vs/base/common/async'; import { MENUBAR_SELECTION_FOREGROUND, MENUBAR_SELECTION_BACKGROUND, MENUBAR_SELECTION_BORDER, TITLE_BAR_ACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_FOREGROUND, MENU_BACKGROUND, MENU_FOREGROUND, MENU_SELECTION_BACKGROUND, MENU_SELECTION_FOREGROUND, MENU_SELECTION_BORDER } from 'vs/workbench/common/theme'; +import URI from 'vs/base/common/uri'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; +import { foreground } from 'vs/platform/theme/common/colorRegistry'; +import { IUpdateService, StateType } from 'vs/platform/update/common/update'; interface CustomMenu { title: string; @@ -49,7 +49,7 @@ enum MenubarState { OPEN } -export class MenubarPart extends Part { +export class MenubarControl extends Disposable { private keys = [ 'files.autoSave', @@ -59,7 +59,7 @@ export class MenubarPart extends Part { 'workbench.statusBar.visible', 'workbench.activityBar.visible', 'window.enableMenuBarMnemonics', - // 'window.nativeTabs' + 'window.nativeTabs' ]; private topLevelMenus: { @@ -104,32 +104,27 @@ export class MenubarPart extends Part { private updatePending: boolean; private _modifierKeyStatus: IModifierKeyStatus; private _focusState: MenubarState; + private openedViaKeyboard: boolean; - private _onVisibilityChange: Emitter; + private _onVisibilityChange: Emitter; - private initialSizing: { - menuButtonPaddingLeftRight?: number; - menubarHeight?: number; - menubarPaddingLeft?: number; - menubarPaddingRight?: number; - menubarFontSize?: number; - } = {}; - - private static MAX_MENU_RECENT_ENTRIES = 5; + private static MAX_MENU_RECENT_ENTRIES = 10; constructor( id: string, @IThemeService themeService: IThemeService, - @IMenubarService private menubarService: IMenubarService, @IMenuService private menuService: IMenuService, @IWindowService private windowService: IWindowService, @IWindowsService private windowsService: IWindowsService, @IContextKeyService private contextKeyService: IContextKeyService, @IKeybindingService private keybindingService: IKeybindingService, @IConfigurationService private configurationService: IConfigurationService, - @IEnvironmentService private environmentService: IEnvironmentService + @IEnvironmentService private environmentService: IEnvironmentService, + @IUriDisplayService private uriDisplayService: IUriDisplayService, + @IUpdateService private updateService: IUpdateService ) { - super(id, { hasTitle: false }, themeService); + + super(); this.topLevelMenus = { 'File': this._register(this.menuService.createMenu(MenuId.MenubarFileMenu, this.contextKeyService)), @@ -144,17 +139,17 @@ export class MenubarPart extends Part { }; if (isMacintosh) { - this.topLevelMenus['Window'] = this._register(this.menuService.createMenu(MenuId.MenubarWindowMenu, this.contextKeyService)); + this.topLevelMenus['Preferences'] = this._register(this.menuService.createMenu(MenuId.MenubarPreferencesMenu, this.contextKeyService)); } - this.menuUpdater = this._register(new RunOnceScheduler(() => this.doSetupMenubar(), 0)); + this.menuUpdater = this._register(new RunOnceScheduler(() => this.doSetupMenubar(), 100)); this.actionRunner = this._register(new ActionRunner()); this._register(this.actionRunner.onDidBeforeRun(() => { this.setUnfocusedState(); })); - this._onVisibilityChange = this._register(new Emitter()); + this._onVisibilityChange = this._register(new Emitter()); if (isMacintosh || this.currentTitlebarStyleSetting !== 'custom') { for (let topLevelMenuName of Object.keys(this.topLevelMenus)) { @@ -181,10 +176,6 @@ export class MenubarPart extends Part { return enableMenuBarMnemonics; } - private get currentMultiCursorSetting(): string { - return this.configurationService.getValue('editor.multiCursorModifier'); - } - private get currentAutoSaveSetting(): string { return this.configurationService.getValue('files.autoSave'); } @@ -300,7 +291,7 @@ export class MenubarPart extends Part { } if (this.focusedMenu) { - this.showCustomMenu(this.focusedMenu.index); + this.showCustomMenu(this.focusedMenu.index, this.openedViaKeyboard); } break; } @@ -321,7 +312,7 @@ export class MenubarPart extends Part { } private onDidChangeFullscreen(): void { - this.updateStyles(); + this.setUnfocusedState(); } private onDidChangeWindowFocus(hasFocus: boolean): void { @@ -339,37 +330,35 @@ export class MenubarPart extends Part { if (this.keys.some(key => event.affectsConfiguration(key))) { this.setupMenubar(); } + + if (event.affectsConfiguration('window.menuBarVisibility')) { + this.setUnfocusedState(); + } } private setUnfocusedState(): void { - this.focusState = this.currentMenubarVisibility === 'toggle' ? MenubarState.HIDDEN : MenubarState.VISIBLE; + this.focusState = this.currentMenubarVisibility === 'toggle' || this.currentMenubarVisibility === 'hidden' ? MenubarState.HIDDEN : MenubarState.VISIBLE; } private hideMenubar(): void { - this._onVisibilityChange.fire(new Dimension(0, 0)); - this.container.style('visibility', 'hidden'); + this.container.style('display', 'none'); + this._onVisibilityChange.fire(false); } private showMenubar(): void { - this._onVisibilityChange.fire(this.getMenubarItemsDimensions()); - this.container.style('visibility', null); + this.container.style('display', 'flex'); + this._onVisibilityChange.fire(true); } private onModifierKeyToggled(modifierKeyStatus: IModifierKeyStatus): void { this._modifierKeyStatus = modifierKeyStatus; - const altKeyAlone = modifierKeyStatus.lastKeyPressed === 'alt' && !modifierKeyStatus.ctrlKey && !modifierKeyStatus.shiftKey; const allModifiersReleased = !modifierKeyStatus.altKey && !modifierKeyStatus.ctrlKey && !modifierKeyStatus.shiftKey; - if (this.currentMenubarVisibility === 'toggle') { - if (altKeyAlone) { - if (!this.isVisible) { - this.focusState = MenubarState.VISIBLE; - } - } else if (!allModifiersReleased && !this.isFocused) { - this.focusState = MenubarState.HIDDEN; - } + if (this.currentMenubarVisibility === 'hidden') { + return; } + if (allModifiersReleased && modifierKeyStatus.lastKeyPressed === 'alt' && modifierKeyStatus.lastKeyReleased === 'alt') { if (!this.isFocused) { this.focusedMenu = { index: 0 }; @@ -401,7 +390,7 @@ export class MenubarPart extends Part { this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e))); // Listen to update service - // this.updateService.onStateChange(() => this.setupMenubar()); + this.updateService.onStateChange(() => this.setupMenubar()); // Listen for context changes this._register(this.contextKeyService.onDidChangeContext(() => this.setupMenubar())); @@ -428,23 +417,22 @@ export class MenubarPart extends Part { private doSetupMenubar(): void { if (!isMacintosh && this.currentTitlebarStyleSetting === 'custom') { this.setupCustomMenubar(); - } else { - this.setupNativeMenubar(); } + + // TODO@sbatten Uncomment to bring back dynamic menubar + // else { + // // Send menus to main process to be rendered by Electron + // const menubarData = {}; + // if (this.getMenubarMenus(menubarData)) { + // this.menubarService.updateMenubar(this.windowService.getCurrentWindowId(), menubarData, this.getAdditionalKeybindings()); + // } + // } } private setupMenubar(): void { this.menuUpdater.schedule(); } - private setupNativeMenubar(): void { - // TODO@sbatten: Remove once native menubar is ready - if (isMacintosh && isWindows) { - this.menubarService.updateMenubar(this.windowService.getCurrentWindowId(), this.getMenubarMenus()); - } - } - - private clearMnemonic(topLevelElement: HTMLElement): void { topLevelElement.accessKey = null; } @@ -467,16 +455,6 @@ export class MenubarPart extends Part { private calculateActionLabel(action: IAction | IMenubarMenuItemAction): string { let label = action.label; switch (action.id) { - case 'workbench.action.toggleMultiCursorModifier': - if (this.currentMultiCursorSetting === 'ctrlCmd') { - label = nls.localize('miMultiCursorAlt', "Switch to Alt+Click for Multi-Cursor"); - } else { - label = isMacintosh - ? nls.localize('miMultiCursorCmd', "Switch to Cmd+Click for Multi-Cursor") - : nls.localize('miMultiCursorCtrl', "Switch to Ctrl+Click for Multi-Cursor"); - } - break; - case 'workbench.action.toggleSidebarPosition': if (this.currentSidebarPosition !== 'right') { label = nls.localize({ key: 'miMoveSidebarRight', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Right"); @@ -511,20 +489,23 @@ export class MenubarPart extends Part { private createOpenRecentMenuAction(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string, commandId: string, isFile: boolean): IAction { let label: string; - let path: string; + let uri: URI; - if (isSingleFolderWorkspaceIdentifier(workspace) || typeof workspace === 'string') { - label = getPathLabel(workspace, this.environmentService); - path = workspace; + if (isSingleFolderWorkspaceIdentifier(workspace)) { + label = getWorkspaceLabel(workspace, this.environmentService, this.uriDisplayService, { verbose: true }); + uri = workspace; + } else if (isWorkspaceIdentifier(workspace)) { + label = getWorkspaceLabel(workspace, this.environmentService, this.uriDisplayService, { verbose: true }); + uri = URI.file(workspace.configPath); } else { - label = getWorkspaceLabel(workspace, this.environmentService, { verbose: true }); - path = workspace.configPath; + uri = URI.file(workspace); + label = this.uriDisplayService.getLabel(uri); } return new Action(commandId, label, undefined, undefined, (event) => { const openInNewWindow = event && ((!isMacintosh && (event.ctrlKey || event.shiftKey)) || (isMacintosh && (event.metaKey || event.altKey))); - return this.windowService.openWindow([path], { + return this.windowService.openWindow([uri], { forceNewWindow: openInNewWindow, forceOpenWorkspaceAsFile: isFile }); @@ -541,7 +522,7 @@ export class MenubarPart extends Part { const result: IAction[] = []; if (workspaces.length > 0) { - for (let i = 0; i < MenubarPart.MAX_MENU_RECENT_ENTRIES && i < workspaces.length; i++) { + for (let i = 0; i < MenubarControl.MAX_MENU_RECENT_ENTRIES && i < workspaces.length; i++) { result.push(this.createOpenRecentMenuAction(workspaces[i], 'openRecentWorkspace', false)); } @@ -549,7 +530,7 @@ export class MenubarPart extends Part { } if (files.length > 0) { - for (let i = 0; i < MenubarPart.MAX_MENU_RECENT_ENTRIES && i < files.length; i++) { + for (let i = 0; i < MenubarControl.MAX_MENU_RECENT_ENTRIES && i < files.length; i++) { result.push(this.createOpenRecentMenuAction(files[i], 'openRecentFile', false)); } @@ -559,12 +540,58 @@ export class MenubarPart extends Part { return result; } + private getUpdateAction(): IAction | null { + const state = this.updateService.state; + + switch (state.type) { + case StateType.Uninitialized: + return null; + + case StateType.Idle: + const windowId = this.windowService.getCurrentWindowId(); + 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"), null, 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()); + } + } + private insertActionsBefore(nextAction: IAction, target: IAction[]): void { switch (nextAction.id) { case 'workbench.action.openRecent': target.push(...this.getOpenRecentActions()); break; + case 'workbench.action.showAboutDialog': + if (!isMacintosh) { + const updateAction = this.getUpdateAction(); + if (updateAction) { + target.push(updateAction); + target.push(new Separator()); + } + } + + break; + default: break; } @@ -658,6 +685,7 @@ export class MenubarPart extends Part { if ((event.equals(KeyCode.DownArrow) || event.equals(KeyCode.Enter)) && !this.isOpen) { this.focusedMenu = { index: menuIndex }; + this.openedViaKeyboard = true; this.focusState = MenubarState.OPEN; } else { eventHandled = false; @@ -670,6 +698,11 @@ export class MenubarPart extends Part { }); this.customMenus[menuIndex].buttonElement.on(EventType.CLICK, (e) => { + // This should only happen for mnemonics and we shouldn't trigger them + if (this.currentMenubarVisibility === 'hidden') { + return; + } + if (this._modifierKeyStatus && (this._modifierKeyStatus.shiftKey || this._modifierKeyStatus.ctrlKey)) { return; // supress keyboard shortcuts that shouldn't conflict } @@ -679,10 +712,11 @@ export class MenubarPart extends Part { this.setUnfocusedState(); } else { this.cleanupCustomMenu(); - this.showCustomMenu(menuIndex); + this.showCustomMenu(menuIndex, this.openedViaKeyboard); } } else { this.focusedMenu = { index: menuIndex }; + this.openedViaKeyboard = (e as MouseEvent).detail === 0; // Indicates mouse was not clicked this.focusState = MenubarState.OPEN; } @@ -694,7 +728,7 @@ export class MenubarPart extends Part { if (this.isOpen && !this.isCurrentMenu(menuIndex)) { this.customMenus[menuIndex].buttonElement.domFocus(); this.cleanupCustomMenu(); - this.showCustomMenu(menuIndex); + this.showCustomMenu(menuIndex, false); } else if (this.isFocused && !this.isOpen) { this.focusedMenu = { index: menuIndex }; this.customMenus[menuIndex].buttonElement.domFocus(); @@ -796,43 +830,97 @@ export class MenubarPart extends Part { } } - private getMenubarMenus(): IMenubarData { - let ret: IMenubarData = {}; + private getMenubarKeybinding(id: string): IMenubarKeybinding { + const binding = this.keybindingService.lookupKeybinding(id); + if (!binding) { + return null; + } - for (let topLevelMenuName of Object.keys(this.topLevelMenus)) { - const menu = this.topLevelMenus[topLevelMenuName]; - let menubarMenu: IMenubarMenu = { items: [] }; - let groups = menu.getActions(); - for (let group of groups) { - const [, actions] = group; + // first try to resolve a native accelerator + const electronAccelerator = binding.getElectronAccelerator(); + if (electronAccelerator) { + return { id, label: electronAccelerator, isNative: true }; + } - actions.forEach(menuItemAction => { + // we need this fallback to support keybindings that cannot show in electron menus (e.g. chords) + const acceleratorLabel = binding.getLabel(); + if (acceleratorLabel) { + return { id, label: acceleratorLabel, isNative: false }; + } + + return null; + } + + private populateMenuItems(menu: IMenu, menuToPopulate: IMenubarMenu) { + let groups = menu.getActions(); + for (let group of groups) { + const [, actions] = group; + + actions.forEach(menuItem => { + + if (menuItem instanceof SubmenuItemAction) { + const submenu = { items: [] }; + this.populateMenuItems(this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService), submenu); + + let menubarSubmenuItem: IMenubarMenuItemSubmenu = { + id: menuItem.id, + label: menuItem.label, + submenu: submenu + }; + + menuToPopulate.items.push(menubarSubmenuItem); + } else { let menubarMenuItem: IMenubarMenuItemAction = { - id: menuItemAction.id, - label: menuItemAction.label, - checked: menuItemAction.checked, - enabled: menuItemAction.enabled + id: menuItem.id, + label: menuItem.label, + checked: menuItem.checked, + enabled: menuItem.enabled, + keybinding: this.getMenubarKeybinding(menuItem.id) }; this.setCheckedStatus(menubarMenuItem); menubarMenuItem.label = this.calculateActionLabel(menubarMenuItem); - menubarMenu.items.push(menubarMenuItem); - }); + menuToPopulate.items.push(menubarMenuItem); + } + }); - menubarMenu.items.push({ id: 'vscode.menubar.separator' }); - } - - if (menubarMenu.items.length > 0) { - menubarMenu.items.pop(); - } - - ret[topLevelMenuName] = menubarMenu; + menuToPopulate.items.push({ id: 'vscode.menubar.separator' }); } - return ret; + if (menuToPopulate.items.length > 0) { + menuToPopulate.items.pop(); + } } + // private getAdditionalKeybindings(): Array { + // const keybindings = []; + // if (isMacintosh) { + // keybindings.push(this.getMenubarKeybinding('workbench.action.quit')); + // } + + // return keybindings; + // } + + // private getMenubarMenus(menubarData: IMenubarData): boolean { + // if (!menubarData) { + // return false; + // } + + // for (let topLevelMenuName of Object.keys(this.topLevelMenus)) { + // const menu = this.topLevelMenus[topLevelMenuName]; + // let menubarMenu: IMenubarMenu = { items: [] }; + // this.populateMenuItems(menu, menubarMenu); + // if (menubarMenu.items.length === 0) { + // // Menus are incomplete + // return false; + // } + // menubarData[topLevelMenuName] = menubarMenu; + // } + + // return true; + // } + private isCurrentMenu(menuIndex: number): boolean { if (!this.focusedMenu) { return false; @@ -857,17 +945,15 @@ export class MenubarPart extends Part { } } - private showCustomMenu(menuIndex: number): void { + private showCustomMenu(menuIndex: number, selectFirst = true): void { const customMenu = this.customMenus[menuIndex]; let menuHolder = $(customMenu.buttonElement).div({ class: 'menubar-menu-items-holder' }); $(menuHolder.getHTMLElement().parentElement).addClass('open'); - - menuHolder.addClass('menubar-menu-items-holder-open context-view'); menuHolder.style({ - 'zoom': `${1 / browser.getZoomFactor()}`, - 'top': `${this.container.getClientArea().height * browser.getZoomFactor()}px` + 'top': `${this.container.getClientArea().height}px`, + 'left': `${customMenu.buttonElement.getHTMLElement().getBoundingClientRect().left}px` }); let menuOptions: IMenuOptions = { @@ -889,7 +975,7 @@ export class MenubarPart extends Part { }, 100); })); - menuWidget.focus(); + menuWidget.focus(selectFirst); this.focusedMenu = { index: menuIndex, @@ -898,54 +984,20 @@ export class MenubarPart extends Part { }; } - public get onVisibilityChange(): Event { + public get onVisibilityChange(): Event { return this._onVisibilityChange.event; } - public layout(dimension: Dimension): Dimension[] { - // To prevent zooming we need to adjust the font size with the zoom factor - if (this.customMenus) { - if (typeof this.initialSizing.menubarFontSize !== 'number') { - this.initialSizing.menubarFontSize = parseInt(this.container.getComputedStyle().fontSize, 10); - } - - if (typeof this.initialSizing.menubarHeight !== 'number') { - this.initialSizing.menubarHeight = parseInt(this.container.getComputedStyle().height, 10); - } - - if (typeof this.initialSizing.menubarPaddingLeft !== 'number') { - this.initialSizing.menubarPaddingLeft = parseInt(this.container.getComputedStyle().paddingLeft, 10); - } - - if (typeof this.initialSizing.menubarPaddingRight !== 'number') { - this.initialSizing.menubarPaddingRight = parseInt(this.container.getComputedStyle().paddingRight, 10); - } - - if (typeof this.initialSizing.menuButtonPaddingLeftRight !== 'number') { - this.initialSizing.menuButtonPaddingLeftRight = parseInt(this.customMenus[0].buttonElement.getComputedStyle().paddingLeft, 10); - } - - this.container.style({ - height: `${this.initialSizing.menubarHeight / browser.getZoomFactor()}px`, - 'padding-left': `${this.initialSizing.menubarPaddingLeft / browser.getZoomFactor()}px`, - 'padding-right': `${this.initialSizing.menubarPaddingRight / browser.getZoomFactor()}px`, - 'font-size': `${this.initialSizing.menubarFontSize / browser.getZoomFactor()}px`, - }); - - this.customMenus.forEach(customMenu => { - customMenu.buttonElement.style({ - 'padding': `0 ${this.initialSizing.menuButtonPaddingLeftRight / browser.getZoomFactor()}px` - }); - }); + public layout(dimension: Dimension) { + if (this.container) { + this.container.style({ height: `${dimension.height}px` }); } - if (this.currentMenubarVisibility === 'toggle') { + if (!this.isVisible) { this.hideMenubar(); } else { this.showMenubar(); } - - return super.layout(dimension); } public getMenubarItemsDimensions(): Dimension { @@ -958,16 +1010,16 @@ export class MenubarPart extends Part { return new Dimension(0, 0); } - public createContentArea(parent: HTMLElement): HTMLElement { + public create(parent: HTMLElement): HTMLElement { this.container = $(parent); - if (!isWindows) { - return this.container.getHTMLElement(); - } - // Build the menubar if (this.container) { this.doSetupMenubar(); + + if (!isMacintosh && this.currentTitlebarStyleSetting === 'custom') { + this.setUnfocusedState(); + } } return this.container.getHTMLElement(); @@ -978,7 +1030,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const menubarActiveWindowFgColor = theme.getColor(TITLE_BAR_ACTIVE_FOREGROUND); if (menubarActiveWindowFgColor) { collector.addRule(` - .monaco-workbench > .part.menubar > .menubar-menu-button { + .monaco-workbench .menubar > .menubar-menu-button { color: ${menubarActiveWindowFgColor}; } `); @@ -987,7 +1039,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const menubarInactiveWindowFgColor = theme.getColor(TITLE_BAR_INACTIVE_FOREGROUND); if (menubarInactiveWindowFgColor) { collector.addRule(` - .monaco-workbench > .part.menubar.inactive > .menubar-menu-button { + .monaco-workbench .menubar.inactive > .menubar-menu-button { color: ${menubarInactiveWindowFgColor}; } `); @@ -997,9 +1049,9 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const menubarSelectedFgColor = theme.getColor(MENUBAR_SELECTION_FOREGROUND); if (menubarSelectedFgColor) { collector.addRule(` - .monaco-workbench > .part.menubar > .menubar-menu-button.open, - .monaco-workbench > .part.menubar > .menubar-menu-button:focus, - .monaco-workbench > .part.menubar > .menubar-menu-button:hover { + .monaco-workbench .menubar > .menubar-menu-button.open, + .monaco-workbench .menubar > .menubar-menu-button:focus, + .monaco-workbench .menubar > .menubar-menu-button:hover { color: ${menubarSelectedFgColor}; } `); @@ -1008,9 +1060,9 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const menubarSelectedBgColor = theme.getColor(MENUBAR_SELECTION_BACKGROUND); if (menubarSelectedBgColor) { collector.addRule(` - .monaco-workbench > .part.menubar > .menubar-menu-button.open, - .monaco-workbench > .part.menubar > .menubar-menu-button:focus, - .monaco-workbench > .part.menubar > .menubar-menu-button:hover { + .monaco-workbench .menubar > .menubar-menu-button.open, + .monaco-workbench .menubar > .menubar-menu-button:focus, + .monaco-workbench .menubar > .menubar-menu-button:hover { background-color: ${menubarSelectedBgColor}; } `); @@ -1019,24 +1071,33 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const menubarSelectedBorderColor = theme.getColor(MENUBAR_SELECTION_BORDER); if (menubarSelectedBorderColor) { collector.addRule(` - .monaco-workbench > .part.menubar > .menubar-menu-button:hover { + .monaco-workbench .menubar > .menubar-menu-button:hover { outline: dashed 1px; } - .monaco-workbench > .part.menubar > .menubar-menu-button.open, - .monaco-workbench > .part.menubar > .menubar-menu-button:focus { + .monaco-workbench .menubar > .menubar-menu-button.open, + .monaco-workbench .menubar > .menubar-menu-button:focus { outline: solid 1px; } - .monaco-workbench > .part.menubar > .menubar-menu-button.open, - .monaco-workbench > .part.menubar > .menubar-menu-button:focus, - .monaco-workbench > .part.menubar > .menubar-menu-button:hover { + .monaco-workbench .menubar > .menubar-menu-button.open, + .monaco-workbench .menubar > .menubar-menu-button:focus, + .monaco-workbench .menubar > .menubar-menu-button:hover { outline-offset: -1px; outline-color: ${menubarSelectedBorderColor}; } `); } + const menuShadow = theme.getColor('widget.shadow'); + if (menuShadow) { + collector.addRule(` + .monaco-shell .monaco-workbench .monaco-menu-container { + box-shadow: 0 2px 4px ${menuShadow}; + } + `); + } + const menuBgColor = theme.getColor(MENU_BACKGROUND); if (menuBgColor) { collector.addRule(` @@ -1047,7 +1108,11 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { `); } - const menuFgColor = theme.getColor(MENU_FOREGROUND); + let menuFgColor = theme.getColor(MENU_FOREGROUND); + if (!menuFgColor) { + menuFgColor = theme.getColor(foreground); + } + if (menuFgColor) { collector.addRule(` .monaco-shell .monaco-menu .monaco-action-bar.vertical, @@ -1060,8 +1125,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const selectedMenuItemBgColor = theme.getColor(MENU_SELECTION_BACKGROUND); if (menuBgColor) { collector.addRule(` - .monaco-shell .monaco-menu .monaco-action-bar.vertical .action-item.focused, - .monaco-shell .monaco-menu .monaco-action-bar.vertical .action-item:hover:not(.disabled) { + .monaco-shell .monaco-menu .monaco-action-bar.vertical .action-item.focused { background-color: ${selectedMenuItemBgColor}; } `); @@ -1070,8 +1134,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const selectedMenuItemFgColor = theme.getColor(MENU_SELECTION_FOREGROUND); if (selectedMenuItemFgColor) { collector.addRule(` - .monaco-shell .monaco-menu .monaco-action-bar.vertical .action-item.focused, - .monaco-shell .monaco-menu .monaco-action-bar.vertical .action-item:hover:not(.disabled) { + .monaco-shell .monaco-menu .monaco-action-bar.vertical .action-item.focused { color: ${selectedMenuItemFgColor}; } `); @@ -1081,17 +1144,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { if (selectedMenuItemBorderColor) { collector.addRule(` .monaco-shell .monaco-menu .monaco-action-bar.vertical .action-item.focused { - outline: solid 1px; - } - - .monaco-shell .monaco-menu .monaco-action-bar.vertical .action-item:hover:not(.disabled) { - outline: dashed 1px; - } - - .monaco-shell .monaco-menu .monaco-action-bar.vertical .action-item.focused, - .monaco-shell .monaco-menu .monaco-action-bar.vertical .action-item:hover:not(.disabled) { - outline-offset: -1px; - outline-color: ${selectedMenuItemBorderColor}; + border: 1px solid ${selectedMenuItemBorderColor}; } `); } @@ -1153,6 +1206,10 @@ class ModifierKeyEmitter extends Emitter { this._keyStatus.lastKeyReleased = undefined; } + if (this._keyStatus.lastKeyPressed !== this._keyStatus.lastKeyReleased) { + this._keyStatus.lastKeyPressed = undefined; + } + this._keyStatus.altKey = e.altKey; this._keyStatus.ctrlKey = e.ctrlKey; this._keyStatus.shiftKey = e.shiftKey; @@ -1161,6 +1218,9 @@ class ModifierKeyEmitter extends Emitter { this.fire(this._keyStatus); } })); + this._subscriptions.push(domEvent(document.body, 'mousedown')(e => { + this._keyStatus.lastKeyPressed = undefined; + })); this._subscriptions.push(domEvent(window, 'blur')(e => { this._keyStatus.lastKeyPressed = undefined; @@ -1185,4 +1245,4 @@ class ModifierKeyEmitter extends Emitter { super.dispose(); this._subscriptions = dispose(this._subscriptions); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 61f11d7209c..fe01f90cbe0 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -21,18 +21,20 @@ import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/co import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import * as nls from 'vs/nls'; -import * as labels from 'vs/base/common/labels'; import { EditorInput, toResource, Verbosity } from 'vs/workbench/common/editor'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { TITLE_BAR_ACTIVE_BACKGROUND, TITLE_BAR_ACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_BACKGROUND, TITLE_BAR_BORDER } from 'vs/workbench/common/theme'; import { isMacintosh, isWindows, isLinux } from 'vs/base/common/platform'; import URI from 'vs/base/common/uri'; import { Color } from 'vs/base/common/color'; import { trim } from 'vs/base/common/strings'; import { addDisposableListener, EventType, EventHelper, Dimension } from 'vs/base/browser/dom'; -import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { MenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { template, getBaseLabel } from 'vs/base/common/labels'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; export class TitlebarPart extends Part implements ITitleService { @@ -46,13 +48,16 @@ export class TitlebarPart extends Part implements ITitleService { private titleContainer: Builder; private title: Builder; + private dragRegion: Builder; private windowControls: Builder; private maxRestoreControl: Builder; private appIcon: Builder; + private menubarPart: MenubarControl; + private menubar: Builder; + private resizer: Builder; private pendingTitle: string; private representedFileName: string; - private menubarWidth: number; private initialSizing: { titleFontSize?: number; @@ -76,8 +81,9 @@ export class TitlebarPart extends Part implements ITitleService { @IEditorService private editorService: IEditorService, @IEnvironmentService private environmentService: IEnvironmentService, @IWorkspaceContextService private contextService: IWorkspaceContextService, - @IPartService private partService: IPartService, - @IThemeService themeService: IThemeService + @IInstantiationService private instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IUriDisplayService private uriDisplayService: IUriDisplayService ) { super(id, { hasTitle: false }, themeService); @@ -95,7 +101,6 @@ export class TitlebarPart extends Part implements ITitleService { this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.setTitle(this.getWindowTitle()))); this._register(this.contextService.onDidChangeWorkbenchState(() => this.setTitle(this.getWindowTitle()))); this._register(this.contextService.onDidChangeWorkspaceName(() => this.setTitle(this.getWindowTitle()))); - this._register(this.partService.onMenubarVisibilityChange(this.onMenubarVisibilityChanged, this)); } private onBlur(): void { @@ -114,10 +119,19 @@ export class TitlebarPart extends Part implements ITitleService { } } - private onMenubarVisibilityChanged(dimension: Dimension): void { - this.menubarWidth = dimension.width; + private onMenubarVisibilityChanged(visible: boolean) { + if (isWindows || isLinux) { + // Hide title when toggling menu bar + if (this.configurationService.getValue('window.menuBarVisibility') === 'toggle' && visible) { + this.title.style('visibility', 'hidden'); - this.updateLayout(); + // Hack to fix issue #52522 with layered webkit-app-region elements appearing under cursor + this.dragRegion.hide(); + this.dragRegion.showDelayed(50); + } else { + this.title.style('visibility', null); + } + } } private onActiveEditorChange(): void { @@ -225,15 +239,15 @@ export class TitlebarPart extends Part implements ITitleService { const activeEditorMedium = editor ? editor.getTitle(Verbosity.MEDIUM) : activeEditorShort; const activeEditorLong = editor ? editor.getTitle(Verbosity.LONG) : activeEditorMedium; const rootName = workspace.name; - const rootPath = root ? labels.getPathLabel(root, this.environmentService) : ''; + const rootPath = root ? this.uriDisplayService.getLabel(root) : ''; const folderName = folder ? folder.name : ''; - const folderPath = folder ? labels.getPathLabel(folder.uri, this.environmentService) : ''; + const folderPath = folder ? this.uriDisplayService.getLabel(folder.uri) : ''; const dirty = editor && editor.isDirty() ? TitlebarPart.TITLE_DIRTY : ''; const appName = this.environmentService.appNameLong; const separator = TitlebarPart.TITLE_SEPARATOR; const titleTemplate = this.configurationService.getValue('window.title'); - return labels.template(titleTemplate, { + return template(titleTemplate, { activeEditorShort, activeEditorLong, activeEditorMedium, @@ -250,17 +264,26 @@ export class TitlebarPart extends Part implements ITitleService { createContentArea(parent: HTMLElement): HTMLElement { this.titleContainer = $(parent); + // Draggable region that we can manipulate for #52522 + this.dragRegion = $(this.titleContainer).div({ class: 'titlebar-drag-region' }); + // App Icon (Windows/Linux) if (!isMacintosh) { this.appIcon = $(this.titleContainer).div({ class: 'window-appicon' }); + } - if (isWindows) { - this.appIcon.on(EventType.DBLCLICK, e => { - EventHelper.stop(e, true); + // Menubar: the menubar part which is responsible for populating both the custom and native menubars + this.menubarPart = this.instantiationService.createInstance(MenubarControl, 'workbench.parts.titlebar.menubar'); + this.menubar = $(this.titleContainer).div({ + 'class': ['menubar'], + id: 'workbench.parts.titlebar.menubar', + role: 'menubar' + }); - this.windowService.closeWindow().then(null, errors.onUnexpectedError); - }); - } + this.menubarPart.create(this.menubar.getHTMLElement()); + + if (!isMacintosh) { + this._register(this.menubarPart.onVisibilityChange(e => this.onMenubarVisibilityChanged(e))); } // Title @@ -272,11 +295,13 @@ export class TitlebarPart extends Part implements ITitleService { } // Maximize/Restore on doubleclick - this.titleContainer.on(EventType.DBLCLICK, (e) => { - EventHelper.stop(e); + if (isMacintosh) { + this.titleContainer.on(EventType.DBLCLICK, (e) => { + EventHelper.stop(e); - this.onTitleDoubleclick(); - }); + this.onTitleDoubleclick(); + }); + } // Context menu on title this.title.on([EventType.CONTEXT_MENU, EventType.MOUSE_DOWN], (e: MouseEvent) => { @@ -292,12 +317,12 @@ export class TitlebarPart extends Part implements ITitleService { this.windowControls = $(this.titleContainer).div({ class: 'window-controls-container' }); // Minimize - $(this.windowControls).div({ class: 'window-icon window-minimize' }).on(EventType.CLICK, () => { + $($(this.windowControls).div({ class: 'window-icon-bg' })).div({ class: 'window-icon window-minimize' }).on(EventType.CLICK, () => { this.windowService.minimizeWindow().then(null, errors.onUnexpectedError); }); // Restore - this.maxRestoreControl = $(this.windowControls).div({ class: 'window-icon window-max-restore' }).on(EventType.CLICK, () => { + this.maxRestoreControl = $($(this.windowControls).div({ class: 'window-icon-bg' })).div({ class: 'window-icon window-max-restore' }).on(EventType.CLICK, () => { this.windowService.isMaximized().then((maximized) => { if (maximized) { return this.windowService.unmaximizeWindow(); @@ -308,16 +333,16 @@ export class TitlebarPart extends Part implements ITitleService { }); // Close - $(this.windowControls).div({ class: 'window-icon window-close' }).on(EventType.CLICK, () => { + $($(this.windowControls).div({ class: 'window-icon-bg window-close-bg' })).div({ class: 'window-icon window-close' }).on(EventType.CLICK, () => { this.windowService.closeWindow().then(null, errors.onUnexpectedError); }); + // Resizer + this.resizer = $(this.titleContainer).div({ class: 'resizer' }); + const isMaximized = this.windowService.getConfiguration().maximized ? true : false; this.onDidChangeMaximized(isMaximized); this.windowService.onDidChangeMaximize(this.onDidChangeMaximized, this); - - // Resizer - $(this.titleContainer).div({ class: 'resizer' }); } // Since the title area is used to drag the window, we do not want to steal focus from the @@ -335,16 +360,22 @@ export class TitlebarPart extends Part implements ITitleService { } private onDidChangeMaximized(maximized: boolean) { - if (!this.maxRestoreControl) { - return; + if (this.maxRestoreControl) { + if (maximized) { + this.maxRestoreControl.removeClass('window-maximize'); + this.maxRestoreControl.addClass('window-unmaximize'); + } else { + this.maxRestoreControl.removeClass('window-unmaximize'); + this.maxRestoreControl.addClass('window-maximize'); + } } - if (maximized) { - this.maxRestoreControl.removeClass('window-maximize'); - this.maxRestoreControl.addClass('window-unmaximize'); - } else { - this.maxRestoreControl.removeClass('window-unmaximize'); - this.maxRestoreControl.addClass('window-maximize'); + if (this.resizer) { + if (maximized) { + this.resizer.hide(); + } else { + this.resizer.show(); + } } } @@ -353,6 +384,12 @@ export class TitlebarPart extends Part implements ITitleService { // Part container if (this.titleContainer) { + if (this.isInactive) { + this.titleContainer.addClass('inactive'); + } else { + this.titleContainer.removeClass('inactive'); + } + const titleBackground = this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_BACKGROUND : TITLE_BAR_ACTIVE_BACKGROUND); this.titleContainer.style('background-color', titleBackground); if (Color.fromHex(titleBackground).isLighter()) { @@ -407,9 +444,9 @@ export class TitlebarPart extends Part implements ITitleService { let label: string; if (!isFile) { - label = labels.getBaseLabel(paths.dirname(path)); + label = getBaseLabel(paths.dirname(path)); } else { - label = labels.getBaseLabel(path); + label = getBaseLabel(path); } actions.push(new ShowItemInFolderAction(path, label || paths.sep, this.windowsService)); @@ -432,84 +469,84 @@ export class TitlebarPart extends Part implements ITitleService { } } - private updateLayout() { - - // To prevent zooming we need to adjust the font size with the zoom factor + private updateLayout(dimension: Dimension) { + // Store initital title sizing if we need to prevent zooming if (typeof this.initialSizing.titleFontSize !== 'number') { - this.initialSizing.titleFontSize = parseInt(this.titleContainer.getComputedStyle().fontSize, 10); + this.initialSizing.titleFontSize = parseInt(this.title.getComputedStyle().fontSize, 10); } if (typeof this.initialSizing.titlebarHeight !== 'number') { - this.initialSizing.titlebarHeight = parseInt(this.titleContainer.getComputedStyle().height, 10); + this.initialSizing.titlebarHeight = parseInt(this.title.getComputedStyle().height, 10); } - // Set font size and line height - const newHeight = this.initialSizing.titlebarHeight / getZoomFactor(); - this.titleContainer.style({ - fontSize: `${this.initialSizing.titleFontSize / getZoomFactor()}px`, - 'line-height': `${newHeight}px` - }); + // Only prevent zooming behavior on macOS or when the menubar is not visible + if (isMacintosh || this.configurationService.getValue('window.menuBarVisibility') === 'hidden') { + // To prevent zooming we need to adjust the font size with the zoom factor + const newHeight = this.initialSizing.titlebarHeight / getZoomFactor(); + this.title.style({ + fontSize: `${this.initialSizing.titleFontSize / getZoomFactor()}px`, + 'line-height': `${newHeight}px` + }); - // Windows/Linux specific layout - if (isWindows || isLinux) { - if (typeof this.initialSizing.controlsWidth !== 'number') { - this.initialSizing.controlsWidth = parseInt(this.windowControls.getComputedStyle().width, 10); + // Windows/Linux specific layout + if (isWindows || isLinux) { + if (typeof this.initialSizing.controlsWidth !== 'number') { + this.initialSizing.controlsWidth = parseInt(this.windowControls.getComputedStyle().width, 10); + } + + if (typeof this.initialSizing.appIconWidth !== 'number') { + this.initialSizing.appIconWidth = parseInt(this.appIcon.getComputedStyle().width, 10); + } + + if (typeof this.initialSizing.appIconSize !== 'number') { + this.initialSizing.appIconSize = parseInt(this.appIcon.getComputedStyle().backgroundSize, 10); + } + + const currentAppIconHeight = parseInt(this.appIcon.getComputedStyle().height, 10); + const newControlsWidth = this.initialSizing.controlsWidth / getZoomFactor(); + const newAppIconWidth = this.initialSizing.appIconWidth / getZoomFactor(); + const newAppIconSize = this.initialSizing.appIconSize / getZoomFactor(); + + // Adjust app icon mimic menubar + this.appIcon.style({ + 'width': `${newAppIconWidth}px`, + 'background-size': `${newAppIconSize}px`, + 'padding-top': `${(newHeight - currentAppIconHeight) / 2.0}px`, + 'padding-bottom': `${(newHeight - currentAppIconHeight) / 2.0}px` + }); + + // Adjust windows controls + this.windowControls.style({ + 'width': `${newControlsWidth}px` + }); } + } else { + // We need to undo zoom prevention + this.title.style({ + fontSize: null, + 'line-height': null + }); - if (typeof this.initialSizing.appIconWidth !== 'number') { - this.initialSizing.appIconWidth = parseInt(this.appIcon.getComputedStyle().width, 10); - } - - if (typeof this.initialSizing.appIconSize !== 'number') { - this.initialSizing.appIconSize = parseInt(this.appIcon.getComputedStyle().backgroundSize, 10); - } - - const currentAppIconHeight = parseInt(this.appIcon.getComputedStyle().height, 10); - const newControlsWidth = this.initialSizing.controlsWidth / getZoomFactor(); - const newAppIconWidth = this.initialSizing.appIconWidth / getZoomFactor(); - const newAppIconSize = this.initialSizing.appIconSize / getZoomFactor(); - - if (!this.menubarWidth) { - this.menubarWidth = 0; - } - - // If we can center the title in the titlebar, we should - const fullWidth = parseInt(this.titleContainer.getComputedStyle().width, 10); - const titleWidth = parseInt(this.title.getComputedStyle().width, 10); - const freeSpace = fullWidth - newAppIconWidth - newControlsWidth - titleWidth; - const leftSideTitle = newAppIconWidth + (freeSpace / 2); - - let bufferWidth = this.menubarWidth; - if (newAppIconWidth + this.menubarWidth < leftSideTitle) { - bufferWidth = 0; - } - - // Adjust app icon mimic menubar this.appIcon.style({ - 'width': `${newAppIconWidth}px`, - 'background-size': `${newAppIconSize}px`, - 'margin-right': `${newControlsWidth - newAppIconWidth + bufferWidth}px`, - 'padding-top': `${(newHeight - currentAppIconHeight) / 2.0}px`, - 'padding-bottom': `${(newHeight - currentAppIconHeight) / 2.0}px` + 'width': null, + 'background-size': null, + 'padding-top': null, + 'padding-bottom': null }); - // Adjust windows controls this.windowControls.style({ - 'width': `${newControlsWidth}px` + 'width': null }); + } - // Hide title when toggling menu bar - let menubarToggled = this.configurationService.getValue('window.menuBarVisibility') === 'toggle'; - if (menubarToggled && this.menubarWidth) { - this.title.style('visibility', 'hidden'); - } else { - this.title.style('visibility', null); - } + if (this.menubarPart) { + const menubarDimension = new Dimension(undefined, dimension.height); + this.menubarPart.layout(menubarDimension); } } layout(dimension: Dimension): Dimension[] { - this.updateLayout(); + this.updateLayout(dimension); return super.layout(dimension); } @@ -525,3 +562,23 @@ class ShowItemInFolderAction extends Action { return this.windowsService.showItemInFolder(this.path); } } + +registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { + const titlebarActiveFg = theme.getColor(TITLE_BAR_ACTIVE_FOREGROUND); + if (titlebarActiveFg) { + collector.addRule(` + .monaco-workbench > .part.titlebar > .window-controls-container .window-icon { + background-color: ${titlebarActiveFg}; + } + `); + } + + const titlebarInactiveFg = theme.getColor(TITLE_BAR_INACTIVE_FOREGROUND); + if (titlebarInactiveFg) { + collector.addRule(` + .monaco-workbench > .part.titlebar.inactive > .window-controls-container .window-icon { + background-color: ${titlebarInactiveFg}; + } + `); + } +}); diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index 54306015001..bf04ef7a0c0 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -202,12 +202,18 @@ export class CustomTreeViewer extends Disposable implements ITreeViewer { @IExtensionService private extensionService: IExtensionService, @IWorkbenchThemeService private themeService: IWorkbenchThemeService, @IInstantiationService private instantiationService: IInstantiationService, - @ICommandService private commandService: ICommandService + @ICommandService private commandService: ICommandService, + @IConfigurationService private configurationService: IConfigurationService ) { super(); this.root = new Root(); this._register(this.themeService.onDidFileIconThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/)); this._register(this.themeService.onThemeChange(() => this.doRefresh([this.root]) /** soft refresh **/)); + this._register(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('explorer.decorations')) { + this.doRefresh([this.root]); /** soft refresh **/ + } + })); } get dataProvider(): ITreeViewDataProvider { @@ -442,7 +448,6 @@ class TreeDataSource implements IDataSource { } interface ITreeExplorerTemplateData { - label: HTMLElement; resourceLabel: ResourceLabel; icon: HTMLElement; actionBar: ActionBar; @@ -459,7 +464,8 @@ class TreeRenderer implements IRenderer { private menus: TreeMenus, private actionItemProvider: IActionItemProvider, @IInstantiationService private instantiationService: IInstantiationService, - @IWorkbenchThemeService private themeService: IWorkbenchThemeService + @IWorkbenchThemeService private themeService: IWorkbenchThemeService, + @IConfigurationService private configurationService: IConfigurationService, ) { } @@ -475,37 +481,32 @@ class TreeRenderer implements IRenderer { DOM.addClass(container, 'custom-view-tree-node-item'); const icon = DOM.append(container, DOM.$('.custom-view-tree-node-item-icon')); - const label = DOM.append(container, DOM.$('.custom-view-tree-node-item-label')); const resourceLabel = this.instantiationService.createInstance(ResourceLabel, container, {}); - const actionsContainer = DOM.append(container, DOM.$('.actions')); + DOM.addClass(resourceLabel.element, 'custom-view-tree-node-item-resourceLabel'); + const actionsContainer = DOM.append(resourceLabel.element, DOM.$('.actions')); const actionBar = new ActionBar(actionsContainer, { actionItemProvider: this.actionItemProvider, actionRunner: new MultipleSelectionActionRunner(() => tree.getSelection()) }); - return { label, resourceLabel, icon, actionBar, aligner: new Aligner(container, tree, this.themeService) }; + return { resourceLabel, icon, actionBar, aligner: new Aligner(container, tree, this.themeService) }; } renderElement(tree: ITree, node: ITreeItem, templateId: string, templateData: ITreeExplorerTemplateData): void { const resource = node.resourceUri ? URI.revive(node.resourceUri) : null; const label = node.label ? node.label : resource ? basename(resource.path) : ''; const icon = this.themeService.getTheme().type === LIGHT ? node.icon : node.iconDark; + const title = node.tooltip ? node.tooltip : resource ? void 0 : label; // reset templateData.resourceLabel.clear(); templateData.actionBar.clear(); - templateData.label.textContent = ''; - DOM.removeClass(templateData.label, 'custom-view-tree-node-item-label'); - DOM.removeClass(templateData.resourceLabel.element, 'custom-view-tree-node-item-resourceLabel'); if ((resource || node.themeIcon) && !icon) { - const title = node.tooltip ? node.tooltip : resource ? void 0 : label; - templateData.resourceLabel.setLabel({ name: label, resource: resource ? resource : URI.parse('_icon_resource') }, { fileKind: this.getFileKind(node), title }); - DOM.addClass(templateData.resourceLabel.element, 'custom-view-tree-node-item-resourceLabel'); + const fileDecorations = this.configurationService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations'); + templateData.resourceLabel.setLabel({ name: label, resource: resource ? resource : URI.parse('_icon_resource') }, { fileKind: this.getFileKind(node), title, fileDecorations: fileDecorations, extraClasses: ['custom-view-tree-node-item-resourceLabel'] }); } else { - templateData.label.textContent = label; - DOM.addClass(templateData.label, 'custom-view-tree-node-item-label'); - templateData.label.title = typeof node.tooltip === 'string' ? node.tooltip : label; + templateData.resourceLabel.setLabel({ name: label }, { title, hideIcon: true, extraClasses: ['custom-view-tree-node-item-resourceLabel'] }); } templateData.icon.style.backgroundImage = icon ? `url('${icon}')` : ''; diff --git a/src/vs/workbench/browser/parts/views/media/panelviewlet.css b/src/vs/workbench/browser/parts/views/media/panelviewlet.css index 7d6c4e36d60..9691ecbdeb7 100644 --- a/src/vs/workbench/browser/parts/views/media/panelviewlet.css +++ b/src/vs/workbench/browser/parts/views/media/panelviewlet.css @@ -10,5 +10,4 @@ font-size: 11px; -webkit-margin-before: 0; -webkit-margin-after: 0; - display: flex; } diff --git a/src/vs/workbench/browser/parts/views/media/views.css b/src/vs/workbench/browser/parts/views/media/views.css index 7d9c1184814..2bffac61f30 100644 --- a/src/vs/workbench/browser/parts/views/media/views.css +++ b/src/vs/workbench/browser/parts/views/media/views.css @@ -58,6 +58,10 @@ display: inline-block; } +.tree-explorer-viewlet-tree-view .monaco-tree .monaco-tree-row { + padding-right: 12px; +} + .tree-explorer-viewlet-tree-view .monaco-tree .monaco-tree-row .custom-view-tree-node-item { display: flex; height: 22px; @@ -68,8 +72,7 @@ flex-wrap: nowrap } -.tree-explorer-viewlet-tree-view .monaco-tree .monaco-tree-row .custom-view-tree-node-item .custom-view-tree-node-item-resourceLabel, -.tree-explorer-viewlet-tree-view .monaco-tree .monaco-tree-row .custom-view-tree-node-item > .custom-view-tree-node-item-label { +.tree-explorer-viewlet-tree-view .monaco-tree .monaco-tree-row .custom-view-tree-node-item .custom-view-tree-node-item-resourceLabel { flex: 1; text-overflow: ellipsis; overflow: hidden; @@ -85,18 +88,25 @@ -webkit-font-smoothing: antialiased; } -.tree-explorer-viewlet-tree-view .monaco-tree .monaco-tree-row .custom-view-tree-node-item > .actions { - display: none; - padding-right: 6px; +.tree-explorer-viewlet-tree-view .monaco-tree .monaco-tree-row .custom-view-tree-node-item > .custom-view-tree-node-item-resourceLabel .monaco-icon-label-description-container { + flex: 1; } -.tree-explorer-viewlet-tree-view .monaco-tree .monaco-tree-row:hover .custom-view-tree-node-item > .actions, -.tree-explorer-viewlet-tree-view .monaco-tree .monaco-tree-row.selected .custom-view-tree-node-item > .actions, -.tree-explorer-viewlet-tree-view .monaco-tree .monaco-tree-row.focused .custom-view-tree-node-item > .actions { +.tree-explorer-viewlet-tree-view .monaco-tree .monaco-tree-row .custom-view-tree-node-item > .custom-view-tree-node-item-resourceLabel::after { + padding-right: 0px; +} + +.tree-explorer-viewlet-tree-view .monaco-tree .monaco-tree-row .custom-view-tree-node-item > .custom-view-tree-node-item-resourceLabel > .actions { + display: none; +} + +.tree-explorer-viewlet-tree-view .monaco-tree .monaco-tree-row:hover .custom-view-tree-node-item > .custom-view-tree-node-item-resourceLabel > .actions, +.tree-explorer-viewlet-tree-view .monaco-tree .monaco-tree-row.selected .custom-view-tree-node-item > .custom-view-tree-node-item-resourceLabel > .actions, +.tree-explorer-viewlet-tree-view .monaco-tree .monaco-tree-row.focused .custom-view-tree-node-item > .custom-view-tree-node-item-resourceLabel > .actions { display: block; } -.tree-explorer-viewlet-tree-view .monaco-tree .custom-view-tree-node-item > .actions .action-label { +.tree-explorer-viewlet-tree-view .monaco-tree .custom-view-tree-node-item > .custom-view-tree-node-item-resourceLabel > .actions .action-label { width: 16px; height: 100%; background-position: 50% 50%; diff --git a/src/vs/workbench/browser/parts/views/viewsViewlet.ts b/src/vs/workbench/browser/parts/views/viewsViewlet.ts index 183b36edfb6..36268d9a636 100644 --- a/src/vs/workbench/browser/parts/views/viewsViewlet.ts +++ b/src/vs/workbench/browser/parts/views/viewsViewlet.ts @@ -377,9 +377,8 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView private computeInitialSizes(): { [id: string]: number } { let sizes = {}; if (this.dimension) { - let totalWeight = 0; for (const viewDescriptor of this.viewsModel.visibleViewDescriptors) { - sizes[viewDescriptor.id] = this.dimension.height * (viewDescriptor.weight || 20) / totalWeight; + sizes[viewDescriptor.id] = this.dimension.height * (viewDescriptor.weight || 20) / 100; } } return sizes; diff --git a/src/vs/workbench/common/actions.ts b/src/vs/workbench/common/actions.ts index a2533c808d4..c8bd1928dfd 100644 --- a/src/vs/workbench/common/actions.ts +++ b/src/vs/workbench/common/actions.ts @@ -6,7 +6,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { Registry } from 'vs/platform/registry/common/platform'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -41,7 +41,7 @@ Registry.add(Extensions.WorkbenchActions, new class implements IWorkbenchActionR // keybinding const when = descriptor.keybindingContext; - const weight = (typeof descriptor.keybindingWeight === 'undefined' ? KeybindingsRegistry.WEIGHT.workbenchContrib() : descriptor.keybindingWeight); + const weight = (typeof descriptor.keybindingWeight === 'undefined' ? KeybindingWeight.WorkbenchContrib : descriptor.keybindingWeight); const keybindings = descriptor.keybindings; KeybindingsRegistry.registerKeybindingRule({ id: descriptor.id, diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index e49e2d26fc5..8162f48a02e 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -25,6 +25,7 @@ export const EditorsVisibleContext = new RawContextKey('editorIsOpen', export const EditorGroupActiveEditorDirtyContext = new RawContextKey('groupActiveEditorDirty', false); export const NoEditorsVisibleContext: ContextKeyExpr = EditorsVisibleContext.toNegated(); export const TextCompareEditorVisibleContext = new RawContextKey('textCompareEditorVisible', false); +export const TextCompareEditorActiveContext = new RawContextKey('textCompareEditorActive', false); export const ActiveEditorGroupEmptyContext = new RawContextKey('activeEditorGroupEmpty', false); export const MultipleEditorGroupsContext = new RawContextKey('multipleEditorGroups', false); export const SingleEditorGroupsContext = MultipleEditorGroupsContext.toNegated(); diff --git a/src/vs/workbench/common/editor/untitledEditorInput.ts b/src/vs/workbench/common/editor/untitledEditorInput.ts index ee49dbe7211..27d9f0d2cdd 100644 --- a/src/vs/workbench/common/editor/untitledEditorInput.ts +++ b/src/vs/workbench/common/editor/untitledEditorInput.ts @@ -8,19 +8,17 @@ import { TPromise } from 'vs/base/common/winjs.base'; import URI from 'vs/base/common/uri'; import { suggestFilename } from 'vs/base/common/mime'; import { memoize } from 'vs/base/common/decorators'; -import * as labels from 'vs/base/common/labels'; import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry'; import * as paths from 'vs/base/common/paths'; import * as resources from 'vs/base/common/resources'; import { EditorInput, IEncodingSupport, EncodingMode, ConfirmResult, Verbosity } from 'vs/workbench/common/editor'; import { UntitledEditorModel } from 'vs/workbench/common/editor/untitledEditorModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { Event, Emitter } from 'vs/base/common/event'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { telemetryURIDescriptor } from 'vs/platform/telemetry/common/telemetryUtils'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IHashService } from 'vs/workbench/services/hash/common/hashService'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; /** * An editor input to be used for untitled text buffers. @@ -46,10 +44,9 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport private initialValue: string, private preferredEncoding: string, @IInstantiationService private instantiationService: IInstantiationService, - @IWorkspaceContextService private contextService: IWorkspaceContextService, @ITextFileService private textFileService: ITextFileService, - @IEnvironmentService private environmentService: IEnvironmentService, - @IHashService private hashService: IHashService + @IHashService private hashService: IHashService, + @IUriDisplayService private uriDisplayService: IUriDisplayService ) { super(); @@ -82,17 +79,17 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport @memoize private get shortDescription(): string { - return paths.basename(labels.getPathLabel(resources.dirname(this.resource), this.environmentService)); + return paths.basename(this.uriDisplayService.getLabel(resources.dirname(this.resource))); } @memoize private get mediumDescription(): string { - return labels.getPathLabel(resources.dirname(this.resource), this.environmentService, this.contextService); + return this.uriDisplayService.getLabel(resources.dirname(this.resource), true); } @memoize private get longDescription(): string { - return labels.getPathLabel(resources.dirname(this.resource), this.environmentService); + return this.uriDisplayService.getLabel(resources.dirname(this.resource)); } getDescription(verbosity: Verbosity = Verbosity.MEDIUM): string { @@ -124,12 +121,12 @@ export class UntitledEditorInput extends EditorInput implements IEncodingSupport @memoize private get mediumTitle(): string { - return labels.getPathLabel(this.resource, this.environmentService, this.contextService); + return this.uriDisplayService.getLabel(this.resource, true); } @memoize private get longTitle(): string { - return labels.getPathLabel(this.resource, this.environmentService); + return this.uriDisplayService.getLabel(this.resource); } getTitle(verbosity: Verbosity): string { diff --git a/src/vs/workbench/common/resources.ts b/src/vs/workbench/common/resources.ts index 1801c5ad38c..f58e4867ebe 100644 --- a/src/vs/workbench/common/resources.ts +++ b/src/vs/workbench/common/resources.ts @@ -9,7 +9,6 @@ import URI from 'vs/base/common/uri'; import * as paths from 'vs/base/common/paths'; import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IModeService } from 'vs/editor/common/services/modeService'; -import { IFileService } from 'vs/platform/files/common/files'; export class ResourceContextKey implements IContextKey { @@ -19,7 +18,6 @@ export class ResourceContextKey implements IContextKey { static Resource = new RawContextKey('resource', undefined); static Extension = new RawContextKey('resourceExtname', undefined); static HasResource = new RawContextKey('resourceSet', false); - static IsFile = new RawContextKey('resourceIsFile', false); private _resourceKey: IContextKey; private _schemeKey: IContextKey; @@ -27,12 +25,10 @@ export class ResourceContextKey implements IContextKey { private _langIdKey: IContextKey; private _extensionKey: IContextKey; private _hasResource: IContextKey; - private _isFile: IContextKey; constructor( @IContextKeyService contextKeyService: IContextKeyService, - @IModeService private readonly _modeService: IModeService, - @IFileService private readonly _fileService: IFileService + @IModeService private readonly _modeService: IModeService ) { this._schemeKey = ResourceContextKey.Scheme.bindTo(contextKeyService); this._filenameKey = ResourceContextKey.Filename.bindTo(contextKeyService); @@ -40,7 +36,6 @@ export class ResourceContextKey implements IContextKey { this._resourceKey = ResourceContextKey.Resource.bindTo(contextKeyService); this._extensionKey = ResourceContextKey.Extension.bindTo(contextKeyService); this._hasResource = ResourceContextKey.HasResource.bindTo(contextKeyService); - this._isFile = ResourceContextKey.IsFile.bindTo(contextKeyService); } set(value: URI) { @@ -50,7 +45,6 @@ export class ResourceContextKey implements IContextKey { this._langIdKey.set(value && this._modeService.getModeIdByFilenameOrFirstLine(value.fsPath)); this._extensionKey.set(value && paths.extname(value.fsPath)); this._hasResource.set(!!value); - this._isFile.set(value && this._fileService.canHandleResource(value)); } reset(): void { @@ -60,7 +54,6 @@ export class ResourceContextKey implements IContextKey { this._langIdKey.reset(); this._extensionKey.reset(); this._hasResource.reset(); - this._isFile.reset(); } get(): URI { diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index 307a1f1c24b..8dbf7693c79 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -117,6 +117,12 @@ export const TAB_UNFOCUSED_INACTIVE_FOREGROUND = registerColor('tab.unfocusedIna // < --- Editors --- > +export const EDITOR_PANE_BACKGROUND = registerColor('editorPane.background', { + dark: editorBackground, + light: editorBackground, + hc: editorBackground +}, nls.localize('editorPaneBackground', "Background color of the editor pane visible on the left and right side of the centered editor layout.")); + registerColor('editorGroup.background', { dark: null, light: null, @@ -161,7 +167,7 @@ export const EDITOR_GROUP_BORDER = registerColor('editorGroup.border', { export const EDITOR_DRAG_AND_DROP_BACKGROUND = registerColor('editorGroup.dropBackground', { dark: Color.fromHex('#53595D').transparent(0.5), - light: Color.fromHex('#3399FF').transparent(0.18), + light: Color.fromHex('#2677CB').transparent(0.18), hc: null }, nls.localize('editorDragAndDropBackground', "Background color when dragging editors around. The color should have transparency so that the editor contents can still shine through.")); @@ -188,7 +194,7 @@ export const PANEL_ACTIVE_TITLE_FOREGROUND = registerColor('panelTitle.activeFor }, nls.localize('panelActiveTitleForeground', "Title color for the active panel. Panels are shown below the editor area and contain views like output and integrated terminal.")); export const PANEL_INACTIVE_TITLE_FOREGROUND = registerColor('panelTitle.inactiveForeground', { - dark: transparent(PANEL_ACTIVE_TITLE_FOREGROUND, 0.5), + dark: transparent(PANEL_ACTIVE_TITLE_FOREGROUND, 0.6), light: transparent(PANEL_ACTIVE_TITLE_FOREGROUND, 0.75), hc: Color.white }, nls.localize('panelInactiveTitleForeground', "Title color for the inactive panel. Panels are shown below the editor area and contain views like output and integrated terminal.")); @@ -201,7 +207,7 @@ export const PANEL_ACTIVE_TITLE_BORDER = registerColor('panelTitle.activeBorder' export const PANEL_DRAG_AND_DROP_BACKGROUND = registerColor('panel.dropBackground', { dark: Color.white.transparent(0.12), - light: Color.fromHex('#3399FF').transparent(0.18), + light: Color.fromHex('#2677CB').transparent(0.18), hc: Color.white.transparent(0.12) }, nls.localize('panelDragAndDropBackground', "Drag and drop feedback color for the panel title items. The color should have transparency so that the panel entries can still shine through. Panels are shown below the editor area and contain views like output and integrated terminal.")); @@ -435,7 +441,7 @@ export const MENU_SELECTION_BACKGROUND = registerColor('menu.selectionBackground export const MENU_SELECTION_BORDER = registerColor('menu.selectionBorder', { dark: null, light: null, - hc: activeContrastBorder + hc: null }, nls.localize('menuSelectionBorder', "Border color of the selected menu item in menus.")); // < --- Notifications --- > diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index ba637549b16..b1049fab080 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -288,7 +288,6 @@ export interface ITreeItem { command?: Command; children?: ITreeItem[]; - } export interface ITreeViewDataProvider { diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index c6fb39e0bc0..c8e4a2288f6 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -9,7 +9,7 @@ import 'vs/css!./media/actions'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; -import { Action } from 'vs/base/common/actions'; +import { Action, IAction } from 'vs/base/common/actions'; import { IWindowService, IWindowsService, MenuBarVisibility } from 'vs/platform/windows/common/windows'; import * as nls from 'vs/nls'; import product from 'vs/platform/node/product'; @@ -19,7 +19,6 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; -import * as paths from 'vs/base/common/paths'; import { isMacintosh, isLinux, language } from 'vs/base/common/platform'; import { IQuickOpenService, IFilePickOpenEntry, ISeparator, IPickOpenAction, IPickOpenItem } from 'vs/platform/quickOpen/common/quickOpen'; import * as browser from 'vs/base/browser/browser'; @@ -33,10 +32,10 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import * as os from 'os'; import { webFrame, shell } from 'electron'; -import { getPathLabel, getBaseLabel } from 'vs/base/common/labels'; +import { getBaseLabel } from 'vs/base/common/labels'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { IPanel } from 'vs/workbench/common/panel'; -import { IWorkspaceIdentifier, getWorkspaceLabel, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, getWorkspaceLabel, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { FileKind } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionService, ActivationTimes } from 'vs/workbench/services/extensions/common/extensions'; @@ -50,6 +49,12 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { Context } from 'vs/platform/contextkey/browser/contextKeyService'; import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; +import { dirname } from 'vs/base/common/resources'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { IQuickInputService, IQuickPickItem, IQuickInputButton } from 'vs/platform/quickinput/common/quickInput'; +import { getIconClasses } from 'vs/workbench/browser/labels'; // --- actions @@ -491,7 +496,9 @@ export class ShowStartupPerformance extends Action { } i += 1; - ticks[stat.type].push(new Tick(stat, nextStat)); + if (ticks[stat.type]) { + ticks[stat.type].push(new Tick(stat, nextStat)); + } } ticks[LoaderEventType.BeginInvokeFactory].sort(Tick.compareUsingStartTimestamp); @@ -573,20 +580,24 @@ export class ReloadWindowWithExtensionsDisabledAction extends Action { } export abstract class BaseSwitchWindow extends Action { - private closeWindowAction: CloseWindowAction; + + private closeWindowAction: IQuickInputButton = { + iconClass: 'action-remove-from-recently-opened', + tooltip: nls.localize('close', "Close Window") + }; constructor( id: string, label: string, private windowsService: IWindowsService, private windowService: IWindowService, - private quickOpenService: IQuickOpenService, + private quickInputService: IQuickInputService, private keybindingService: IKeybindingService, - private instantiationService: IInstantiationService + private modelService: IModelService, + private modeService: IModeService, ) { super(id, label); - this.closeWindowAction = this.instantiationService.createInstance(CloseWindowAction); } protected abstract isQuickNavigate(): boolean; @@ -596,56 +607,35 @@ export abstract class BaseSwitchWindow extends Action { return this.windowsService.getWindows().then(windows => { const placeHolder = nls.localize('switchWindowPlaceHolder', "Select a window to switch to"); - const picks = windows.map(win => ({ - payload: win.id, - resource: win.filename ? URI.file(win.filename) : win.folderPath ? URI.file(win.folderPath) : win.workspace ? URI.file(win.workspace.configPath) : void 0, - fileKind: win.filename ? FileKind.FILE : win.workspace ? FileKind.ROOT_FOLDER : win.folderPath ? FileKind.FOLDER : FileKind.FILE, - label: win.title, - description: (currentWindowId === win.id) ? nls.localize('current', "Current Window") : void 0, - run: () => { - setTimeout(() => { - // Bug: somehow when not running this code in a timeout, it is not possible to use this picker - // with quick navigate keys (not able to trigger quick navigate once running it once). - this.windowsService.showWindow(win.id).done(null, errors.onUnexpectedError); - }); - }, - action: (!this.isQuickNavigate() && currentWindowId !== win.id) ? this.closeWindowAction : void 0 - } as IFilePickOpenEntry)); - - this.quickOpenService.pick(picks, { - contextKey: 'inWindowsPicker', - autoFocus: { autoFocusFirstEntry: true }, - placeHolder, - quickNavigateConfiguration: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : void 0 + const picks = windows.map(win => { + const resource = win.filename ? URI.file(win.filename) : win.folderUri ? win.folderUri : win.workspace ? URI.file(win.workspace.configPath) : void 0; + 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") : void 0, + buttons: (!this.isQuickNavigate() && currentWindowId !== win.id) ? [this.closeWindowAction] : void 0 + } as (IQuickPickItem & { payload: number }); }); - }); - } - dispose(): void { - super.dispose(); + const autoFocusIndex = (picks.indexOf(picks.filter(pick => pick.payload === currentWindowId)[0]) + 1) % picks.length; - this.closeWindowAction.dispose(); - } -} - -class CloseWindowAction extends Action implements IPickOpenAction { - - static readonly ID = 'workbench.action.closeWindow'; - static readonly LABEL = nls.localize('close', "Close Window"); - - constructor( - @IWindowsService private windowsService: IWindowsService - ) { - super(CloseWindowAction.ID, CloseWindowAction.LABEL); - - this.class = 'action-remove-from-recently-opened'; - } - - run(item: IPickOpenItem): TPromise { - return this.windowsService.closeWindow(item.getPayload()).then(() => { - item.remove(); - - return true; + return this.quickInputService.pick(picks, { + contextKey: 'inWindowsPicker', + activeItem: picks[autoFocusIndex], + placeHolder, + quickNavigate: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : void 0, + onDidTriggerItemButton: context => { + this.windowsService.closeWindow(context.item.payload).then(() => { + context.removeItem(); + }); + } + }); + }).then(pick => { + if (pick) { + this.windowsService.showWindow(pick.payload).done(null, errors.onUnexpectedError); + } }); } } @@ -660,11 +650,13 @@ export class SwitchWindow extends BaseSwitchWindow { label: string, @IWindowsService windowsService: IWindowsService, @IWindowService windowService: IWindowService, - @IQuickOpenService quickOpenService: IQuickOpenService, + @IQuickInputService quickInputService: IQuickInputService, @IKeybindingService keybindingService: IKeybindingService, + @IModelService modelService: IModelService, + @IModeService modeService: IModeService, @IInstantiationService instantiationService: IInstantiationService ) { - super(id, label, windowsService, windowService, quickOpenService, keybindingService, instantiationService); + super(id, label, windowsService, windowService, quickInputService, keybindingService, modelService, modeService); } protected isQuickNavigate(): boolean { @@ -682,11 +674,13 @@ export class QuickSwitchWindow extends BaseSwitchWindow { label: string, @IWindowsService windowsService: IWindowsService, @IWindowService windowService: IWindowService, - @IQuickOpenService quickOpenService: IQuickOpenService, + @IQuickInputService quickInputService: IQuickInputService, @IKeybindingService keybindingService: IKeybindingService, + @IModelService modelService: IModelService, + @IModeService modeService: IModeService, @IInstantiationService instantiationService: IInstantiationService ) { - super(id, label, windowsService, windowService, quickOpenService, keybindingService, instantiationService); + super(id, label, windowsService, windowService, quickInputService, keybindingService, modelService, modeService); } protected isQuickNavigate(): boolean { @@ -697,7 +691,6 @@ export class QuickSwitchWindow extends BaseSwitchWindow { export const inRecentFilesPickerContextKey = 'inRecentFilesPicker'; export abstract class BaseOpenRecentAction extends Action { - private removeAction: RemoveFromRecentlyOpened; constructor( id: string, @@ -706,12 +699,11 @@ export abstract class BaseOpenRecentAction extends Action { private quickOpenService: IQuickOpenService, private contextService: IWorkspaceContextService, private environmentService: IEnvironmentService, + private uriDisplayService: IUriDisplayService, private keybindingService: IKeybindingService, - instantiationService: IInstantiationService + private instantiationService: IInstantiationService ) { super(id, label); - - this.removeAction = instantiationService.createInstance(RemoveFromRecentlyOpened); } protected abstract isQuickNavigate(): boolean; @@ -723,22 +715,26 @@ export abstract class BaseOpenRecentAction extends Action { private openRecent(recentWorkspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[], recentFiles: string[]): void { - function toPick(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier, separator: ISeparator, fileKind: FileKind, environmentService: IEnvironmentService, removeAction?: RemoveFromRecentlyOpened): IFilePickOpenEntry { - let path: string; + function toPick(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string, separator: ISeparator, fileKind: FileKind, environmentService: IEnvironmentService, uriDisplayService: IUriDisplayService, action: IAction): IFilePickOpenEntry { + let resource: URI; let label: string; let description: string; if (isSingleFolderWorkspaceIdentifier(workspace)) { - path = workspace; - label = getBaseLabel(path); - description = getPathLabel(paths.dirname(path), environmentService); + resource = workspace; + label = getWorkspaceLabel(workspace, environmentService, uriDisplayService); + description = uriDisplayService.getLabel(dirname(resource)); + } else if (isWorkspaceIdentifier(workspace)) { + resource = URI.file(workspace.configPath); + label = getWorkspaceLabel(workspace, environmentService, uriDisplayService); + description = uriDisplayService.getLabel(dirname(resource)); } else { - path = workspace.configPath; - label = getWorkspaceLabel(workspace, environmentService); - description = getPathLabel(paths.dirname(workspace.configPath), environmentService); + resource = URI.file(workspace); + label = getBaseLabel(workspace); + description = uriDisplayService.getLabel(dirname(resource)); } return { - resource: URI.file(path), + resource, fileKind, label, description, @@ -747,20 +743,20 @@ export abstract class BaseOpenRecentAction extends Action { setTimeout(() => { // Bug: somehow when not running this code in a timeout, it is not possible to use this picker // with quick navigate keys (not able to trigger quick navigate once running it once). - runPick(path, fileKind === FileKind.FILE, context); + runPick(resource, fileKind === FileKind.FILE, context); }); }, - action: removeAction + action }; } - const runPick = (path: string, isFile: boolean, context: IEntryRunContext) => { + const runPick = (resource: URI, isFile: boolean, context: IEntryRunContext) => { const forceNewWindow = context.keymods.ctrlCmd; - this.windowService.openWindow([path], { forceNewWindow, forceOpenWorkspaceAsFile: isFile }); + this.windowService.openWindow([resource], { forceNewWindow, forceOpenWorkspaceAsFile: isFile }); }; - const workspacePicks: IFilePickOpenEntry[] = recentWorkspaces.map((workspace, index) => toPick(workspace, index === 0 ? { label: nls.localize('workspaces', "workspaces") } : void 0, isSingleFolderWorkspaceIdentifier(workspace) ? FileKind.FOLDER : FileKind.ROOT_FOLDER, this.environmentService, !this.isQuickNavigate() ? this.removeAction : void 0)); - const filePicks: IFilePickOpenEntry[] = recentFiles.map((p, index) => toPick(p, index === 0 ? { label: nls.localize('files', "files"), border: true } : void 0, FileKind.FILE, this.environmentService, !this.isQuickNavigate() ? this.removeAction : void 0)); + const workspacePicks: IFilePickOpenEntry[] = recentWorkspaces.map((workspace, index) => toPick(workspace, index === 0 ? { label: nls.localize('workspaces', "workspaces") } : void 0, isSingleFolderWorkspaceIdentifier(workspace) ? FileKind.FOLDER : FileKind.ROOT_FOLDER, this.environmentService, this.uriDisplayService, !this.isQuickNavigate() ? this.instantiationService.createInstance(RemoveFromRecentlyOpened, workspace) : void 0)); + const filePicks: IFilePickOpenEntry[] = recentFiles.map((p, index) => toPick(p, index === 0 ? { label: nls.localize('files', "files"), border: true } : void 0, FileKind.FILE, this.environmentService, this.uriDisplayService, !this.isQuickNavigate() ? this.instantiationService.createInstance(RemoveFromRecentlyOpened, p) : void 0)); // focus second entry if the first recent workspace is the current workspace let autoFocusSecondEntry: boolean = recentWorkspaces[0] && this.contextService.isCurrentWorkspace(recentWorkspaces[0]); @@ -773,12 +769,6 @@ export abstract class BaseOpenRecentAction extends Action { quickNavigateConfiguration: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : void 0 }).done(null, errors.onUnexpectedError); } - - dispose(): void { - super.dispose(); - - this.removeAction.dispose(); - } } class RemoveFromRecentlyOpened extends Action implements IPickOpenAction { @@ -787,6 +777,7 @@ class RemoveFromRecentlyOpened extends Action implements IPickOpenAction { static readonly LABEL = nls.localize('remove', "Remove from Recently Opened"); constructor( + private path: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string), @IWindowsService private windowsService: IWindowsService ) { super(RemoveFromRecentlyOpened.ID, RemoveFromRecentlyOpened.LABEL); @@ -795,7 +786,7 @@ class RemoveFromRecentlyOpened extends Action implements IPickOpenAction { } run(item: IPickOpenItem): TPromise { - return this.windowsService.removeFromRecentlyOpened([item.getResource().fsPath]).then(() => { + return this.windowsService.removeFromRecentlyOpened([this.path]).then(() => { item.remove(); return true; @@ -816,9 +807,10 @@ export class OpenRecentAction extends BaseOpenRecentAction { @IWorkspaceContextService contextService: IWorkspaceContextService, @IEnvironmentService environmentService: IEnvironmentService, @IKeybindingService keybindingService: IKeybindingService, - @IInstantiationService instantiationService: IInstantiationService + @IInstantiationService instantiationService: IInstantiationService, + @IUriDisplayService uriDisplayService: IUriDisplayService ) { - super(id, label, windowService, quickOpenService, contextService, environmentService, keybindingService, instantiationService); + super(id, label, windowService, quickOpenService, contextService, environmentService, uriDisplayService, keybindingService, instantiationService); } protected isQuickNavigate(): boolean { @@ -839,9 +831,10 @@ export class QuickOpenRecentAction extends BaseOpenRecentAction { @IWorkspaceContextService contextService: IWorkspaceContextService, @IEnvironmentService environmentService: IEnvironmentService, @IKeybindingService keybindingService: IKeybindingService, - @IInstantiationService instantiationService: IInstantiationService + @IInstantiationService instantiationService: IInstantiationService, + @IUriDisplayService uriDisplayService: IUriDisplayService ) { - super(id, label, windowService, quickOpenService, contextService, environmentService, keybindingService, instantiationService); + super(id, label, windowService, quickOpenService, contextService, environmentService, uriDisplayService, keybindingService, instantiationService); } protected isQuickNavigate(): boolean { @@ -1454,6 +1447,24 @@ export class DecreaseViewSizeAction extends BaseResizeViewAction { } } +export class NewWindowTab extends Action { + + static readonly ID = 'workbench.action.newWindowTab'; + static readonly LABEL = nls.localize('newTab', "New Window Tab"); + + constructor( + id: string, + label: string, + @IWindowsService private windowsService: IWindowsService + ) { + super(NewWindowTab.ID, NewWindowTab.LABEL); + } + + run(): TPromise { + return this.windowsService.newWindowTab().then(() => true); + } +} + export class ShowPreviousWindowTab extends Action { static readonly ID = 'workbench.action.showPreviousWindowTab'; @@ -1640,25 +1651,6 @@ export class OpenPrivacyStatementUrlAction extends Action { } } -export class ShowAccessibilityOptionsAction extends Action { - - static readonly ID = 'workbench.action.showAccessibilityOptions'; - static LABEL = nls.localize('accessibilityOptions', "Accessibility Options"); - - constructor( - id: string, - label: string, - @IWindowsService private windowsService: IWindowsService - ) { - super(id, label); - } - - run(): TPromise { - return this.windowsService.openAccessibilityOptions(); - } -} - - export class ShowAboutDialogAction extends Action { static readonly ID = 'workbench.action.showAboutDialog'; diff --git a/src/vs/workbench/electron-browser/bootstrap/index.js b/src/vs/workbench/electron-browser/bootstrap/index.js index 692122631e7..8311ab796ae 100644 --- a/src/vs/workbench/electron-browser/bootstrap/index.js +++ b/src/vs/workbench/electron-browser/bootstrap/index.js @@ -70,8 +70,8 @@ function uriFromPath(_path) { } function readFile(file) { - return new Promise(function(resolve, reject) { - fs.readFile(file, 'utf8', function(err, data) { + return new Promise(function (resolve, reject) { + fs.readFile(file, 'utf8', function (err, data) { if (err) { reject(err); return; @@ -81,6 +81,51 @@ function readFile(file) { }); } +function showPartsSplash(configuration) { + perf.mark('willShowPartsSplash'); + + // TODO@Ben remove me after a while + perf.mark('willAccessLocalStorage'); + let storage = window.localStorage; + perf.mark('didAccessLocalStorage'); + + let data; + try { + let raw = storage.getItem('storage://global/parts-splash-data'); + data = JSON.parse(raw); + } catch (e) { + // ignore + } + + if (data) { + const splash = document.createElement('div'); + splash.id = data.id; + const { layoutInfo, colorInfo } = data; + + // ensure there is enough space + layoutInfo.sideBarWidth = Math.min(layoutInfo.sideBarWidth, window.innerWidth - (layoutInfo.activityBarWidth + layoutInfo.editorPartMinWidth)); + + if (configuration.folderUri || configuration.workspace) { + // folder or workspace -> status bar color, sidebar + splash.innerHTML = ` +
+
+
+
+ `; + } else { + // empty -> speical status bar color, no sidebar + splash.innerHTML = ` +
+
+
+ `; + } + document.body.appendChild(splash); + } + perf.mark('didShowPartsSplash'); +} + const writeFile = (file, content) => new Promise((c, e) => fs.writeFile(file, content, 'utf8', err => err ? e(err) : c())); function registerListeners(enableDeveloperTools) { @@ -159,6 +204,8 @@ function main() { assign(process.env, configuration.userEnv); perf.importEntries(configuration.perfEntries); + showPartsSplash(configuration); + // Get the nls configuration into the process.env as early as possible. var nlsConfig = { availableLanguages: {} }; const config = process.env['VSCODE_NLS_CONFIG']; @@ -171,7 +218,7 @@ function main() { if (nlsConfig._resolvedLanguagePackCoreLocation) { let bundles = Object.create(null); - nlsConfig.loadBundle = function(bundle, language, cb) { + nlsConfig.loadBundle = function (bundle, language, cb) { let result = bundles[bundle]; if (result) { cb(undefined, result); diff --git a/src/vs/workbench/electron-browser/commands.ts b/src/vs/workbench/electron-browser/commands.ts index 63fce9d272b..3853ebdfab4 100644 --- a/src/vs/workbench/electron-browser/commands.ts +++ b/src/vs/workbench/electron-browser/commands.ts @@ -7,7 +7,7 @@ import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; import { List } from 'vs/base/browser/ui/list/listWidget'; @@ -19,6 +19,7 @@ import { range } from 'vs/base/common/arrays'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ITree } from 'vs/base/parts/tree/browser/tree'; import { InEditorZenModeContext, NoEditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench/common/editor'; +import { ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; // --- List Commands @@ -32,6 +33,7 @@ function ensureDOMFocus(widget: ListWidget): void { } } +export const QUIT_ID = 'workbench.action.quit'; export function registerCommands(): void { function focusDown(accessor: ServicesAccessor, arg2?: number): void { @@ -63,7 +65,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'list.focusDown', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: WorkbenchListFocusContextKey, primary: KeyCode.DownArrow, mac: { @@ -104,8 +106,8 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'list.expandSelectionDown', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), - when: WorkbenchListFocusContextKey, + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and(WorkbenchListFocusContextKey, WorkbenchListSupportsMultiSelectContextKey), primary: KeyMod.Shift | KeyCode.DownArrow, handler: (accessor, arg2) => { const focused = accessor.get(IListService).lastFocusedList; @@ -165,7 +167,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'list.focusUp', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: WorkbenchListFocusContextKey, primary: KeyCode.UpArrow, mac: { @@ -177,8 +179,8 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'list.expandSelectionUp', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), - when: WorkbenchListFocusContextKey, + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and(WorkbenchListFocusContextKey, WorkbenchListSupportsMultiSelectContextKey), primary: KeyMod.Shift | KeyCode.UpArrow, handler: (accessor, arg2) => { const focused = accessor.get(IListService).lastFocusedList; @@ -211,7 +213,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'list.collapse', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: WorkbenchListFocusContextKey, primary: KeyCode.LeftArrow, mac: { @@ -241,7 +243,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'list.expand', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: WorkbenchListFocusContextKey, primary: KeyCode.RightArrow, handler: (accessor) => { @@ -267,7 +269,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'list.focusPageUp', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: WorkbenchListFocusContextKey, primary: KeyCode.PageUp, handler: (accessor) => { @@ -296,7 +298,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'list.focusPageDown', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: WorkbenchListFocusContextKey, primary: KeyCode.PageDown, handler: (accessor) => { @@ -325,7 +327,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'list.focusFirst', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: WorkbenchListFocusContextKey, primary: KeyCode.Home, handler: accessor => listFocusFirst(accessor) @@ -333,7 +335,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'list.focusFirstChild', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: WorkbenchListFocusContextKey, primary: null, handler: accessor => listFocusFirst(accessor, { fromFocused: true }) @@ -364,7 +366,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'list.focusLast', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: WorkbenchListFocusContextKey, primary: KeyCode.End, handler: accessor => listFocusLast(accessor) @@ -372,7 +374,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'list.focusLastChild', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: WorkbenchListFocusContextKey, primary: null, handler: accessor => listFocusLast(accessor, { fromFocused: true }) @@ -403,7 +405,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'list.select', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: WorkbenchListFocusContextKey, primary: KeyCode.Enter, mac: { @@ -434,7 +436,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'list.selectAll', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(WorkbenchListFocusContextKey, WorkbenchListSupportsMultiSelectContextKey), primary: KeyMod.CtrlCmd | KeyCode.KEY_A, handler: (accessor) => { @@ -450,7 +452,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'list.toggleExpand', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: WorkbenchListFocusContextKey, primary: KeyCode.Space, handler: (accessor) => { @@ -470,7 +472,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'list.clear', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(WorkbenchListFocusContextKey, WorkbenchListHasSelectionOrFocus), primary: KeyCode.Escape, handler: (accessor) => { @@ -516,7 +518,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'workbench.action.closeWindow', // close the window when the last editor is closed by reusing the same keybinding - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(NoEditorsVisibleContext, SingleEditorGroupsContext), primary: KeyMod.CtrlCmd | KeyCode.KEY_W, handler: accessor => { @@ -527,7 +529,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'workbench.action.exitZenMode', - weight: KeybindingsRegistry.WEIGHT.editorContrib(-1000), + weight: KeybindingWeight.EditorContrib - 1000, handler(accessor: ServicesAccessor, configurationOrName: any) { const partService = accessor.get(IPartService); partService.toggleZenMode(); @@ -537,8 +539,8 @@ export function registerCommands(): void { }); KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'workbench.action.quit', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + id: QUIT_ID, + weight: KeybindingWeight.WorkbenchContrib, handler(accessor: ServicesAccessor) { const windowsService = accessor.get(IWindowsService); windowsService.quit(); @@ -548,7 +550,7 @@ export function registerCommands(): void { win: { primary: void 0 } }); - CommandsRegistry.registerCommand('_workbench.removeFromRecentlyOpened', function (accessor: ServicesAccessor, path: string) { + CommandsRegistry.registerCommand('_workbench.removeFromRecentlyOpened', function (accessor: ServicesAccessor, path: string | ISingleFolderWorkspaceIdentifier) { const windowsService = accessor.get(IWindowsService); return windowsService.removeFromRecentlyOpened([path]).then(() => void 0); diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index d72216a4c8d..c8a4525a481 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -14,12 +14,12 @@ 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, OpenIssueReporterAction, ReportPerformanceIssueUsingReporterAction, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, ToggleMenuBarAction, CloseWorkspaceAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, NavigateUpAction, NavigateDownAction, NavigateLeftAction, NavigateRightAction, IncreaseViewSizeAction, DecreaseViewSizeAction, ShowStartupPerformance, ToggleSharedProcessAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey, ShowAboutDialogAction, InspectContextKeysAction, OpenProcessExplorer, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction, ShowAccessibilityOptionsAction } from 'vs/workbench/electron-browser/actions'; -import { registerCommands } from 'vs/workbench/electron-browser/commands'; +import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenIssueReporterAction, ReportPerformanceIssueUsingReporterAction, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, ToggleMenuBarAction, CloseWorkspaceAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, NavigateUpAction, NavigateDownAction, NavigateLeftAction, NavigateRightAction, IncreaseViewSizeAction, DecreaseViewSizeAction, ShowStartupPerformance, ToggleSharedProcessAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey, ShowAboutDialogAction, InspectContextKeysAction, OpenProcessExplorer, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction, OpenRecentAction } from 'vs/workbench/electron-browser/actions'; +import { registerCommands, QUIT_ID } from 'vs/workbench/electron-browser/commands'; import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, DuplicateWorkspaceInNewWindowAction, OpenFileFolderAction, OpenFileAction, OpenFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { inQuickOpenContext, getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -70,7 +70,6 @@ workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenTw workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenRequestFeatureUrlAction, OpenRequestFeatureUrlAction.ID, OpenRequestFeatureUrlAction.LABEL), 'Help: Search Feature Requests', helpCategory); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenLicenseUrlAction, OpenLicenseUrlAction.ID, OpenLicenseUrlAction.LABEL), 'Help: View License', helpCategory); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenPrivacyStatementUrlAction, OpenPrivacyStatementUrlAction.ID, OpenPrivacyStatementUrlAction.LABEL), 'Help: Privacy Statement', helpCategory); -workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowAccessibilityOptionsAction, ShowAccessibilityOptionsAction.ID, ShowAccessibilityOptionsAction.LABEL), 'Help: Accessibility Options', helpCategory); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ShowAboutDialogAction, ShowAboutDialogAction.ID, ShowAboutDialogAction.LABEL), 'Help: About', helpCategory); workbenchActionsRegistry.registerWorkbenchAction( @@ -135,7 +134,7 @@ const recentFilesPickerContext = ContextKeyExpr.and(inQuickOpenContext, ContextK const quickOpenNavigateNextInRecentFilesPickerId = 'workbench.action.quickOpenNavigateNextInRecentFilesPicker'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: quickOpenNavigateNextInRecentFilesPickerId, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50), + weight: KeybindingWeight.WorkbenchContrib + 50, handler: getQuickNavigateHandler(quickOpenNavigateNextInRecentFilesPickerId, true), when: recentFilesPickerContext, primary: KeyMod.CtrlCmd | KeyCode.KEY_R, @@ -145,13 +144,329 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const quickOpenNavigatePreviousInRecentFilesPicker = 'workbench.action.quickOpenNavigatePreviousInRecentFilesPicker'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: quickOpenNavigatePreviousInRecentFilesPicker, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50), + weight: KeybindingWeight.WorkbenchContrib + 50, handler: getQuickNavigateHandler(quickOpenNavigatePreviousInRecentFilesPicker, false), when: recentFilesPickerContext, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_R, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_R } }); +// Menu registration - file menu + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '1_new', + command: { + id: NewWindowAction.ID, + title: nls.localize({ key: 'miNewWindow', comment: ['&& denotes a mnemonic'] }, "New &&Window") + }, + order: 2 +}); + +if (!isMacintosh) { + 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 + }); +} else { + MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '2_open', + command: { + id: OpenFileFolderAction.ID, + title: nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...") + }, + order: 1 + }); +} + +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 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + title: nls.localize({ key: 'miOpenRecent', comment: ['&& denotes a mnemonic'] }, "Open &&Recent"), + submenu: MenuId.MenubarRecentMenu, + group: '2_open', + order: 4 +}); + + +// More +MenuRegistry.appendMenuItem(MenuId.MenubarRecentMenu, { + group: 'y_more', + command: { + id: OpenRecentAction.ID, + title: nls.localize({ key: 'miMore', comment: ['&& denotes a mnemonic'] }, "&&More...") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '3_workspace', + command: { + id: AddRootFolderAction.ID, + title: nls.localize({ key: 'miAddFolderToWorkspace', comment: ['&& denotes a mnemonic'] }, "A&&dd Folder to Workspace...") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '3_workspace', + command: { + id: SaveWorkspaceAsAction.ID, + title: nls.localize('miSaveWorkspaceAs', "Save Workspace As...") + }, + order: 2 +}); + +if (!isMacintosh) { + MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + title: nls.localize({ key: 'miPreferences', comment: ['&& denotes a mnemonic'] }, "&&Preferences"), + submenu: MenuId.MenubarPreferencesMenu, + group: '5_autosave', + order: 2 + }); +} + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '6_close', + command: { + id: CloseWorkspaceAction.ID, + title: nls.localize({ key: 'miCloseFolder', comment: ['&& denotes a mnemonic'] }, "Close &&Folder"), + precondition: new RawContextKey('workspaceFolderCount', 0).notEqualsTo('0') + }, + order: 3, + when: new RawContextKey('workbenchState', '').notEqualsTo('workspace') +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '6_close', + command: { + id: CloseWorkspaceAction.ID, + title: nls.localize({ key: 'miCloseWorkspace', comment: ['&& denotes a mnemonic'] }, "Close &&Workspace") + }, + order: 3, + when: new RawContextKey('workbenchState', '').isEqualTo('workspace') +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '6_close', + command: { + id: CloseCurrentWindowAction.ID, + title: nls.localize({ key: 'miCloseWindow', comment: ['&& denotes a mnemonic'] }, "Clos&&e Window") + }, + order: 4 +}); + +if (!isMacintosh) { + MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: 'z_Exit', + command: { + id: QUIT_ID, + title: nls.localize({ key: 'miExit', comment: ['&& denotes a mnemonic'] }, "E&&xit") + }, + order: 1 + }); +} + +// 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 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '1_toggle_view', + command: { + id: ToggleMenuBarAction.ID, + title: nls.localize({ key: 'miToggleMenuBar', comment: ['&& denotes a mnemonic'] }, "Toggle Menu &&Bar") + }, + order: 4 +}); + +// Zoom + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '3_zoom', + command: { + id: ZoomInAction.ID, + title: nls.localize({ key: 'miZoomIn', comment: ['&& denotes a mnemonic'] }, "&&Zoom In") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '3_zoom', + command: { + id: ZoomOutAction.ID, + title: nls.localize({ key: 'miZoomOut', comment: ['&& denotes a mnemonic'] }, "&&Zoom Out") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { + group: '3_zoom', + command: { + id: ZoomResetAction.ID, + title: nls.localize({ key: 'miZoomReset', comment: ['&& denotes a mnemonic'] }, "&&Reset Zoom") + }, + order: 3 +}); + +// Help + +MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '1_welcome', + command: { + id: 'workbench.action.openDocumentationUrl', + title: nls.localize({ key: 'miDocumentation', comment: ['&& denotes a mnemonic'] }, "&&Documentation") + }, + order: 3 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '1_welcome', + command: { + id: 'update.showCurrentReleaseNotes', + title: nls.localize({ key: 'miReleaseNotes', comment: ['&& denotes a mnemonic'] }, "&&Release Notes") + }, + order: 4 +}); + +// Reference +MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '2_reference', + command: { + id: 'workbench.action.keybindingsReference', + title: nls.localize({ key: 'miKeyboardShortcuts', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts Reference") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '2_reference', + command: { + id: 'workbench.action.openIntroductoryVideosUrl', + title: nls.localize({ key: 'miIntroductoryVideos', comment: ['&& denotes a mnemonic'] }, "Introductory &&Videos") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '2_reference', + command: { + id: 'workbench.action.openTipsAndTricksUrl', + title: nls.localize({ key: 'miTipsAndTricks', comment: ['&& denotes a mnemonic'] }, "&&Tips and Tricks") + }, + order: 3 +}); + +// Feedback +MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '3_feedback', + command: { + id: 'workbench.action.openTwitterUrl', + title: nls.localize({ key: 'miTwitter', comment: ['&& denotes a mnemonic'] }, "&&Join us on Twitter") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '3_feedback', + command: { + id: 'workbench.action.openRequestFeatureUrl', + title: nls.localize({ key: 'miUserVoice', comment: ['&& denotes a mnemonic'] }, "&&Search Feature Requests") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '3_feedback', + command: { + id: 'workbench.action.openIssueReporter', + title: nls.localize({ key: 'miReportIssue', comment: ['&& denotes a mnemonic', 'Translate this to "Report Issue in English" in all languages please!'] }, "Report &&Issue") + }, + order: 3 +}); + +// Legal +MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '4_legal', + command: { + id: 'workbench.action.openLicenseUrl', + title: nls.localize({ key: 'miLicense', comment: ['&& denotes a mnemonic'] }, "View &&License") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '4_legal', + command: { + id: 'workbench.action.openPrivacyStatementUrl', + title: nls.localize({ key: 'miPrivacyStatement', comment: ['&& denotes a mnemonic'] }, "&&Privacy Statement") + }, + order: 2 +}); + +// Tools +MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '5_tools', + command: { + id: 'workbench.action.toggleDevTools', + title: nls.localize({ key: 'miToggleDevTools', comment: ['&& denotes a mnemonic'] }, "&&Toggle Developer Tools") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '5_tools', + command: { + id: 'workbench.action.openProcessExplorer', + title: nls.localize({ key: 'miOpenProcessExplorerer', comment: ['&& denotes a mnemonic'] }, "Open &&Process Explorer") + }, + order: 2 +}); + +// About +if (!isMacintosh) { + MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: 'z_about', + command: { + id: 'workbench.action.showAboutDialog', + title: nls.localize({ key: 'miAbout', comment: ['&& denotes a mnemonic'] }, "&&About") + }, + order: 1 + }); +} + // Configuration: Workbench const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); @@ -163,7 +478,7 @@ configurationRegistry.registerConfiguration({ 'properties': { 'workbench.editor.showTabs': { 'type': 'boolean', - 'description': nls.localize('showEditorTabs', "Controls if opened editors should show in tabs or not."), + 'description': nls.localize('showEditorTabs', "Controls whether opened editors should show in tabs or not."), 'default': true }, 'workbench.editor.labelFormat': { @@ -176,52 +491,58 @@ configurationRegistry.registerConfiguration({ nls.localize('workbench.editor.labelFormat.long', "Show the name of the file followed by it's absolute path.") ], 'default': 'default', - 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by parenthesis are not to be translated.'], key: 'tabDescription' }, - "Controls the format of the label for an editor. Changing this setting can for example make it easier to understand the location of a file:\n- short: 'parent'\n- medium: 'workspace/src/parent'\n- long: '/home/user/workspace/src/parent'\n- default: '.../parent', when another tab shares the same title, or the relative workspace path if tabs are disabled"), + 'description': nls.localize({ + comment: ['This is the description for a setting. Values surrounded by parenthesis are not to be translated.'], + key: 'tabDescription' + }, "Controls the format of the label for an editor."), }, 'workbench.editor.tabCloseButton': { 'type': 'string', 'enum': ['left', 'right', 'off'], 'default': 'right', - 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorTabCloseButton' }, "Controls the position of the editor's tabs close buttons or disables them when set to 'off'.") + 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorTabCloseButton' }, "Controls the position of the editor's tabs close buttons, or disables them when set to 'off'.") }, 'workbench.editor.tabSizing': { 'type': 'string', 'enum': ['fit', 'shrink'], 'default': 'fit', - 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'tabSizing' }, "Controls the sizing of editor tabs. Set to 'fit' to keep tabs always large enough to show the full editor label. Set to 'shrink' to allow tabs to get smaller when the available space is not enough to show all tabs at once.") + 'enumDescriptions': [ + nls.localize('workbench.editor.tabSizing.fit', "Always keep tabs large enough to show the full editor label."), + nls.localize('workbench.editor.tabSizing.shrink', "Allow tabs to get smaller when the available space is not enough to show all tabs at once.") + ], + 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'tabSizing' }, "Controls the sizing of editor tabs.") }, 'workbench.editor.showIcons': { 'type': 'boolean', - 'description': nls.localize('showIcons', "Controls if opened editors should show with an icon or not. This requires an icon theme to be enabled as well."), + 'description': nls.localize('showIcons', "Controls whether opened editors should show with an icon or not. This requires an icon theme to be enabled as well."), 'default': true }, 'workbench.editor.enablePreview': { 'type': 'boolean', - 'description': nls.localize('enablePreview', "Controls if opened editors show as preview. Preview editors are reused until they are kept (e.g. via double click or editing) and show up with an italic font style."), + 'description': nls.localize('enablePreview', "Controls whether opened editors show as preview. Preview editors are reused until they are kept (e.g. via double click or editing) and show up with an italic font style."), 'default': true }, 'workbench.editor.enablePreviewFromQuickOpen': { 'type': 'boolean', - 'description': nls.localize('enablePreviewFromQuickOpen', "Controls if opened editors from Quick Open show as preview. Preview editors are reused until they are kept (e.g. via double click or editing)."), + 'description': nls.localize('enablePreviewFromQuickOpen', "Controls whether opened editors from Quick Open show as preview. Preview editors are reused until they are kept (e.g. via double click or editing)."), 'default': true }, 'workbench.editor.closeOnFileDelete': { 'type': 'boolean', - 'description': nls.localize('closeOnFileDelete', "Controls if editors showing a file should close automatically when the file is deleted or renamed by some other process. Disabling this will keep the editor open as dirty on such an event. Note that deleting from within the application will always close the editor and that dirty files will never close to preserve your data."), - 'default': true + 'description': nls.localize('closeOnFileDelete', "Controls whether editors showing a file that was opened during the session should close automatically when getting deleted or renamed by some other process. Disabling this will keep the editor open on such an event. Note that deleting from within the application will always close the editor and that dirty files will never close to preserve your data."), + 'default': false }, 'workbench.editor.openPositioning': { 'type': 'string', 'enum': ['left', 'right', 'first', 'last'], 'default': 'right', - 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorOpenPositioning' }, "Controls where editors open. Select 'left' or 'right' to open editors to the left or right of the currently active one. Select 'first' or 'last' to open editors independently from the currently active one.") + 'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'editorOpenPositioning' }, "Controls where editors open. Select `left` or `right` to open editors to the left or right of the currently active one. Select `first` or `last` to open editors independently from the currently active one.") }, 'workbench.editor.openSideBySideDirection': { 'type': 'string', 'enum': ['right', 'down'], 'default': 'right', - 'description': nls.localize('sideBySideDirection', "Controls the default direction of editors that are opened side by side (e.g. from the explorer). By default, editors will open on the right hand side of the currently active one. If changed to open down, the editors will open below the currently active one.") + 'markdownDescription': nls.localize('sideBySideDirection', "Controls the default direction of editors that are opened side by side (e.g. from the explorer). By default, editors will open on the right hand side of the currently active one. If changed to `down`, the editors will open below the currently active one.") }, 'workbench.editor.closeEmptyGroups': { 'type': 'boolean', @@ -230,7 +551,7 @@ configurationRegistry.registerConfiguration({ }, 'workbench.editor.revealIfOpen': { 'type': 'boolean', - 'description': nls.localize('revealIfOpen', "Controls if an editor is revealed in any of the visible groups if opened. If disabled, an editor will prefer to open in the currently active editor group. If enabled, an already opened editor will be revealed instead of opened again in the currently active editor group. Note that there are some cases where this setting is ignored, e.g. when forcing an editor to open in a specific group or to the side of the currently active group."), + 'description': nls.localize('revealIfOpen', "Controls whether an editor is revealed in any of the visible groups if opened. If disabled, an editor will prefer to open in the currently active editor group. If enabled, an already opened editor will be revealed instead of opened again in the currently active editor group. Note that there are some cases where this setting is ignored, e.g. when forcing an editor to open in a specific group or to the side of the currently active group."), 'default': false }, 'workbench.editor.swipeToNavigate': { @@ -246,17 +567,22 @@ configurationRegistry.registerConfiguration({ }, 'workbench.commandPalette.preserveInput': { 'type': 'boolean', - 'description': nls.localize('preserveInput', "Controls if the last typed input to the command palette should be restored when opening it the next time."), + 'description': nls.localize('preserveInput', "Controls whether the last typed input to the command palette should be restored when opening it the next time."), 'default': false }, 'workbench.quickOpen.closeOnFocusLost': { 'type': 'boolean', - 'description': nls.localize('closeOnFocusLost', "Controls if Quick Open should close automatically once it loses focus."), + 'description': nls.localize('closeOnFocusLost', "Controls whether Quick Open should close automatically once it loses focus."), 'default': true }, 'workbench.settings.openDefaultSettings': { 'type': 'boolean', - 'description': nls.localize('openDefaultSettings', "Controls if opening settings also opens an editor showing all default settings."), + 'description': nls.localize('openDefaultSettings', "Controls whether opening settings also opens an editor showing all default settings."), + 'default': true + }, + 'workbench.settings.openDefaultKeybindings': { + 'type': 'boolean', + 'description': nls.localize('openDefaultKeybindings', "Controls whether opening keybinding settings also opens an editor showing all default keybindings."), 'default': true }, 'workbench.sideBar.location': { @@ -291,7 +617,7 @@ configurationRegistry.registerConfiguration({ 'enum': ['default', 'antialiased', 'none', 'auto'], 'default': 'default', 'description': - nls.localize('fontAliasing', "Controls font aliasing method in the workbench.\n- default: Sub-pixel font smoothing. On most non-retina displays this will give the sharpest text\n- antialiased: Smooth the font on the level of the pixel, as opposed to the subpixel. Can make the font appear lighter overall\n- none: Disables font smoothing. Text will show with jagged sharp edges\n- auto: Applies `default` or `antialiased` automatically based on the DPI of displays."), + nls.localize('fontAliasing', "Controls font aliasing method in the workbench."), 'enumDescriptions': [ nls.localize('workbench.fontAliasing.default', "Sub-pixel font smoothing. On most non-retina displays this will give the sharpest text."), nls.localize('workbench.fontAliasing.antialiased', "Smooth the font on the level of the pixel, as opposed to the subpixel. Can make the font appear lighter overall."), @@ -302,15 +628,16 @@ configurationRegistry.registerConfiguration({ }, 'workbench.settings.enableNaturalLanguageSearch': { 'type': 'boolean', - 'description': nls.localize('enableNaturalLanguageSettingsSearch', "Controls whether to enable the natural language search mode for settings."), + 'description': nls.localize('enableNaturalLanguageSettingsSearch', "Controls whether to enable the natural language search mode for settings. The natural language search is provided by an online service."), 'default': true, - 'scope': ConfigurationScope.WINDOW + 'scope': ConfigurationScope.WINDOW, + 'tags': ['usesOnlineServices'] }, 'workbench.settings.settingsSearchTocBehavior': { 'type': 'string', 'enum': ['hide', 'filter', 'show'], 'description': nls.localize('settingsSearchTocBehavior', "Controls the behavior of the settings editor Table of Contents while searching."), - 'default': 'hide', + 'default': 'filter', 'scope': ConfigurationScope.WINDOW }, 'workbench.settings.tocVisible': { @@ -318,6 +645,12 @@ configurationRegistry.registerConfiguration({ 'description': nls.localize('settingsTocVisible', "Controls whether the settings editor Table of Contents is visible."), 'default': true, 'scope': ConfigurationScope.WINDOW + }, + 'workbench.enableExperiments': { + 'type': 'boolean', + 'description': nls.localize('workbench.enableExperiments', "Fetches experiments to run from a Microsoft online service."), + 'default': true, + 'tags': ['usesOnlineServices'] } } }); @@ -334,41 +667,41 @@ configurationRegistry.registerConfiguration({ 'type': 'string', 'enum': ['on', 'off', 'default'], 'enumDescriptions': [ - nls.localize('window.openFilesInNewWindow.on', "Files will open in a new window"), - nls.localize('window.openFilesInNewWindow.off', "Files will open in the window with the files' folder open or the last active window"), + nls.localize('window.openFilesInNewWindow.on', "Files will open in a new window."), + nls.localize('window.openFilesInNewWindow.off', "Files will open in the window with the files' folder open or the last active window."), isMacintosh ? - nls.localize('window.openFilesInNewWindow.defaultMac', "Files will open in the window with the files' folder open or the last active window unless opened via the Dock or from Finder") : - nls.localize('window.openFilesInNewWindow.default', "Files will open in a new window unless picked from within the application (e.g. via the File menu)") + nls.localize('window.openFilesInNewWindow.defaultMac', "Files will open in the window with the files' folder open or the last active window unless opened via the Dock or from Finder.") : + nls.localize('window.openFilesInNewWindow.default', "Files will open in a new window unless picked from within the application (e.g. via the File menu).") ], 'default': 'off', 'scope': ConfigurationScope.APPLICATION, - 'description': + 'markdownDescription': isMacintosh ? - nls.localize('openFilesInNewWindowMac', "Controls if files should open in a new window.\n- default: files will open in the window with the files' folder open or the last active window unless opened via the Dock or from Finder\n- on: files will open in a new window\n- off: files will open in the window with the files' folder open or 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).") : - nls.localize('openFilesInNewWindow', "Controls if files should open in a new window.\n- default: files will open in a new window unless picked from within the application (e.g. via the File menu)\n- on: files will open in a new window\n- off: files will open in the window with the files' folder open or 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).") + 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)") + 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, - 'description': nls.localize('openFoldersInNewWindow', "Controls if folders should open in a new window or replace the last active window.\n- default: folders will open in a new window unless a folder is picked from within the application (e.g. via the File menu)\n- on: folders will open in a new window\n- off: folders will 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).") + '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'], 'enumDescriptions': [ - nls.localize('window.openWithoutArgumentsInNewWindow.on', "Open a new empty window"), - nls.localize('window.openWithoutArgumentsInNewWindow.off', "Focus the last active running instance") + nls.localize('window.openWithoutArgumentsInNewWindow.on', "Open a new empty window."), + nls.localize('window.openWithoutArgumentsInNewWindow.off', "Focus the last active running instance.") ], 'default': isMacintosh ? 'off' : 'on', 'scope': ConfigurationScope.APPLICATION, - 'description': nls.localize('openWithoutArgumentsInNewWindow', "Controls if a new empty window should open when starting a second instance without arguments or if the last running instance should get focus.\n- on: open a new empty window\n- off: the last active running instance will get focus\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).") + 'description': nls.localize('openWithoutArgumentsInNewWindow', "Controls whether a new empty window should open when starting a second instance without arguments or if the last running instance should get focus.\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.restoreWindows': { 'type': 'string', @@ -381,13 +714,13 @@ configurationRegistry.registerConfiguration({ ], 'default': 'one', 'scope': ConfigurationScope.APPLICATION, - 'description': nls.localize('restoreWindows', "Controls how windows are being reopened after a restart. Select 'none' to always start with an empty workspace, 'one' to reopen the last window you worked on, 'folders' to reopen all windows that had folders opened or 'all' to reopen all windows of your last session.") + 'description': nls.localize('restoreWindows', "Controls how windows are being reopened after a restart.") }, 'window.restoreFullscreen': { 'type': 'boolean', 'default': false, 'scope': ConfigurationScope.APPLICATION, - 'description': nls.localize('restoreFullscreen', "Controls if a window should restore to full screen mode if it was exited in full screen mode.") + 'description': nls.localize('restoreFullscreen', "Controls whether a window should restore to full screen mode if it was exited in full screen mode.") }, 'window.zoomLevel': { 'type': 'number', @@ -397,8 +730,8 @@ configurationRegistry.registerConfiguration({ 'window.title': { 'type': 'string', 'default': isMacintosh ? '${activeEditorShort}${separator}${rootName}' : '${dirty}${activeEditorShort}${separator}${rootName}${separator}${appName}', - 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by parenthesis are not to be translated.'], key: 'title' }, - "Controls the window title based on the active editor. Variables are substituted based on the context:\n\${activeEditorShort}: the file name (e.g. myFile.txt)\n\${activeEditorMedium}: the path of the file relative to the workspace folder (e.g. myFolder/myFile.txt)\n\${activeEditorLong}: the full path of the file (e.g. /Users/Development/myProject/myFolder/myFile.txt)\n\${folderName}: name of the workspace folder the file is contained in (e.g. myFolder)\n\${folderPath}: file path of the workspace folder the file is contained in (e.g. /Users/Development/myFolder)\n\${rootName}: name of the workspace (e.g. myFolder or myWorkspace)\n\${rootPath}: file path of the workspace (e.g. /Users/Development/myWorkspace)\n\${appName}: e.g. VS Code\n\${dirty}: a dirty indicator if the active editor is dirty\n\${separator}: a conditional separator (\" - \") that only shows when surrounded by variables with values or static text") + 'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by parenthesis are not to be translated.'], key: 'title' }, + "Controls the window title based on the active editor. Variables are substituted based on the context:\n- `\${activeEditorShort}`: the file name (e.g. myFile.txt).\n- `\${activeEditorMedium}`: the path of the file relative to the workspace folder (e.g. myFolder/myFile.txt).\n- `\${activeEditorLong}`: the full path of the file (e.g. /Users/Development/myProject/myFolder/myFile.txt).\n- `\${folderName}`: name of the workspace folder the file is contained in (e.g. myFolder).\n- `\${folderPath}`: file path of the workspace folder the file is contained in (e.g. /Users/Development/myFolder).\n- `\${rootName}`: name of the workspace (e.g. myFolder or myWorkspace).\n- `\${rootPath}`: file path of the workspace (e.g. /Users/Development/myWorkspace).\n- `\${appName}`: e.g. VS Code.\n- `\${dirty}`: a dirty indicator if the active editor is dirty.\n- `\${separator}`: a conditional separator (\" - \") that only shows when surrounded by variables with values or static text.") }, 'window.newWindowDimensions': { 'type': 'string', @@ -411,12 +744,12 @@ configurationRegistry.registerConfiguration({ ], 'default': 'default', 'scope': ConfigurationScope.APPLICATION, - 'description': nls.localize('newWindowDimensions', "Controls the dimensions of opening a new window when at least one window is already opened. By default, a new window will open in the center of the screen with small dimensions. When set to 'inherit', the window will get the same dimensions as the last window that was active. When set to 'maximized', the window will open maximized and fullscreen if configured to 'fullscreen'. Note that this setting does not have an impact on the first window that is opened. The first window will always restore the size and location as you left it before closing.") + 'description': nls.localize('newWindowDimensions', "Controls the dimensions of opening a new window when at least one window is already opened. Note that this setting does not have an impact on the first window that is opened. The first window will always restore the size and location as you left it before closing.") }, 'window.closeWhenEmpty': { 'type': 'boolean', 'default': false, - 'description': nls.localize('closeWhenEmpty', "Controls if closing the last editor should also close the window. This setting only applies for windows that do not show folders.") + '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', @@ -463,7 +796,7 @@ configurationRegistry.registerConfiguration({ 'type': 'boolean', 'default': false, 'scope': ConfigurationScope.APPLICATION, - 'description': nls.localize('window.smoothScrollingWorkaround', "Enable this workaround if scrolling is no longer smooth after restoring a minimized VS Code window. This is a workaround for an issue (https://github.com/Microsoft/vscode/issues/13612) where scrolling starts to lag on devices with precision trackpads like the Surface devices from Microsoft. Enabling this workaround can result in a little bit of layout flickering after restoring the window from minimized state but is otherwise harmless. Note: in order for this workaround to function, make sure to also set 'window.titleBarStyle: native'."), + 'markdownDescription': nls.localize('window.smoothScrollingWorkaround', "Enable this workaround if scrolling is no longer smooth after restoring a minimized VS Code window. This is a workaround for an issue (https://github.com/Microsoft/vscode/issues/13612) where scrolling starts to lag on devices with precision trackpads like the Surface devices from Microsoft. Enabling this workaround can result in a little bit of layout flickering after restoring the window from minimized state but is otherwise harmless. Note: in order for this workaround to function, make sure to also set `#window.titleBarStyle#` to `native`."), 'included': isWindows }, 'window.clickThroughInactive': { @@ -486,32 +819,32 @@ configurationRegistry.registerConfiguration({ 'zenMode.fullScreen': { 'type': 'boolean', 'default': true, - 'description': nls.localize('zenMode.fullScreen', "Controls if turning on Zen Mode also puts the workbench into full screen mode.") + 'description': nls.localize('zenMode.fullScreen', "Controls whether turning on Zen Mode also puts the workbench into full screen mode.") }, 'zenMode.centerLayout': { 'type': 'boolean', 'default': true, - 'description': nls.localize('zenMode.centerLayout', "Controls if turning on Zen Mode also centers the layout.") + 'description': nls.localize('zenMode.centerLayout', "Controls whether turning on Zen Mode also centers the layout.") }, 'zenMode.hideTabs': { 'type': 'boolean', 'default': true, - 'description': nls.localize('zenMode.hideTabs', "Controls if turning on Zen Mode also hides workbench tabs.") + 'description': nls.localize('zenMode.hideTabs', "Controls whether turning on Zen Mode also hides workbench tabs.") }, 'zenMode.hideStatusBar': { 'type': 'boolean', 'default': true, - 'description': nls.localize('zenMode.hideStatusBar', "Controls if turning on Zen Mode also hides the status bar at the bottom of the workbench.") + 'description': nls.localize('zenMode.hideStatusBar', "Controls whether turning on Zen Mode also hides the status bar at the bottom of the workbench.") }, 'zenMode.hideActivityBar': { 'type': 'boolean', 'default': true, - 'description': nls.localize('zenMode.hideActivityBar', "Controls if turning on Zen Mode also hides the activity bar at the left of the workbench.") + 'description': nls.localize('zenMode.hideActivityBar', "Controls whether turning on Zen Mode also hides the activity bar at the left of the workbench.") }, 'zenMode.restore': { 'type': 'boolean', 'default': false, - 'description': nls.localize('zenMode.restore', "Controls if a window should restore to zen mode if it was exited in zen mode.") + 'description': nls.localize('zenMode.restore', "Controls whether a window should restore to zen mode if it was exited in zen mode.") } } }); diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index a51785aea26..05c7a8a054e 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -39,7 +39,7 @@ import { IUpdateService } from 'vs/platform/update/common/update'; import { URLHandlerChannel, URLServiceChannelClient } from 'vs/platform/url/common/urlIpc'; import { IURLService } from 'vs/platform/url/common/url'; import { WorkspacesChannelClient } from 'vs/platform/workspaces/common/workspacesIpc'; -import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspacesService, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; import * as fs from 'fs'; import { ConsoleLogService, MultiplexLogService, ILogService } from 'vs/platform/log/common/log'; @@ -49,6 +49,7 @@ import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log import { RelayURLService } from 'vs/platform/url/common/urlService'; import { MenubarChannelClient } from 'vs/platform/menubar/common/menubarIpc'; import { IMenubarService } from 'vs/platform/menubar/common/menubar'; +import { Schemas } from 'vs/base/common/network'; gracefulFs.gracefulify(fs); // enable gracefulFs @@ -115,22 +116,24 @@ function openWorkbench(configuration: IWindowConfiguration): TPromise { } function createAndInitializeWorkspaceService(configuration: IWindowConfiguration, environmentService: EnvironmentService): TPromise { - return validateSingleFolderPath(configuration).then(() => { + const folderUri = configuration.folderUri ? uri.revive(configuration.folderUri) : null; + return validateFolderUri(folderUri, configuration.verbose).then(validatedFolderUri => { + const workspaceService = new WorkspaceService(environmentService); - return workspaceService.initialize(configuration.workspace || configuration.folderPath || configuration).then(() => workspaceService, error => workspaceService); + return workspaceService.initialize(configuration.workspace || validatedFolderUri || configuration).then(() => workspaceService, error => workspaceService); }); } -function validateSingleFolderPath(configuration: IWindowConfiguration): TPromise { +function validateFolderUri(folderUri: ISingleFolderWorkspaceIdentifier, verbose: boolean): TPromise { - // Return early if we do not have a single folder path - if (!configuration.folderPath) { - return TPromise.as(void 0); + // Return early if we do not have a single folder uri or if it is a non file uri + if (!folderUri || folderUri.scheme !== Schemas.file) { + return TPromise.as(folderUri); } // Otherwise: use realpath to resolve symbolic links to the truth - return realpath(configuration.folderPath).then(realFolderPath => { + return realpath(folderUri.fsPath).then(realFolderPath => { // For some weird reason, node adds a trailing slash to UNC paths // we never ever want trailing slashes as our workspace path unless @@ -140,19 +143,19 @@ function validateSingleFolderPath(configuration: IWindowConfiguration): TPromise realFolderPath = strings.rtrim(realFolderPath, paths.nativeSep); } - return realFolderPath; + return uri.file(realFolderPath); }, error => { - if (configuration.verbose) { + if (verbose) { errors.onUnexpectedError(error); } // Treat any error case as empty workbench case (no folder path) return null; - }).then(realFolderPathOrNull => { + }).then(realFolderUriOrNull => { // Update config with real path if we have one - configuration.folderPath = realFolderPathOrNull; + return realFolderUriOrNull; }); } @@ -192,10 +195,7 @@ function createStorageService(workspaceService: IWorkspaceContextService, enviro if (disableStorage) { storage = inMemoryLocalStorageInstance; } else { - // TODO@Ben remove me after a while - perf.mark('willAccessLocalStorage'); storage = window.localStorage; - perf.mark('didAccessLocalStorage'); } return new StorageService(storage, storage, workspaceId, secondaryWorkspaceId); diff --git a/src/vs/workbench/electron-browser/media/shell.css b/src/vs/workbench/electron-browser/media/shell.css index 0463dc38c96..7a2b1a8207e 100644 --- a/src/vs/workbench/electron-browser/media/shell.css +++ b/src/vs/workbench/electron-browser/media/shell.css @@ -15,11 +15,16 @@ /* Font Families (with CJK support) */ -.monaco-shell { font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif; } -.monaco-shell:lang(zh-Hans) { font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; } -.monaco-shell:lang(zh-Hant) { font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Microsoft Jhenghei", "PingFang TC", "Source Han Sans TC", "Source Han Sans", "Source Han Sans TW", sans-serif; } -.monaco-shell:lang(ja) { font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Meiryo", "Hiragino Kaku Gothic Pro", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", "Sazanami Gothic", "IPA Gothic", sans-serif; } -.monaco-shell:lang(ko) { font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Malgun Gothic", "Nanum Gothic", "Dotom", "Apple SD Gothic Neo", "AppleGothic", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; } +.monaco-shell, +.monaco-shell .monaco-menu-container .monaco-menu { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif; } +.monaco-shell:lang(zh-Hans), +.monaco-shell:lang(zh-Hans) .monaco-menu-container .monaco-menu { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; } +.monaco-shell:lang(zh-Hant), +.monaco-shell:lang(zh-Hant) .monaco-menu-container .monaco-menu { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Microsoft Jhenghei", "PingFang TC", "Source Han Sans TC", "Source Han Sans", "Source Han Sans TW", sans-serif; } +.monaco-shell:lang(ja), +.monaco-shell:lang(ja) .monaco-menu-container .monaco-menu { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Meiryo", "Hiragino Kaku Gothic Pro", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", "Sazanami Gothic", "IPA Gothic", sans-serif; } +.monaco-shell:lang(ko), +.monaco-shell:lang(ko) .monaco-menu-container .monaco-menu { font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Noto Sans", "Malgun Gothic", "Nanum Gothic", "Dotom", "Apple SD Gothic Neo", "AppleGothic", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @@ -69,9 +74,13 @@ padding: .5em 0; } +.monaco-shell .monaco-menu .monaco-action-bar.vertical .action-menu-item { + height: 1.8em; +} + .monaco-shell .monaco-menu .monaco-action-bar.vertical .action-label:not(.separator), .monaco-shell .monaco-menu .monaco-action-bar.vertical .keybinding { - padding: 0.5em 2em; + padding: 0 1.5em; } .monaco-shell .monaco-menu .monaco-action-bar.vertical .action-label.separator { @@ -80,7 +89,7 @@ } .monaco-shell .monaco-menu .monaco-action-bar.vertical .submenu-indicator { - padding: 0.5em 1em; + padding: 0 1em; } .monaco-shell .monaco-menu .action-item { @@ -107,7 +116,6 @@ .monaco-shell input[type="button"]:active, .monaco-shell input[type="checkbox"]:active, .monaco-shell .monaco-tree .monaco-tree-row -.monaco-action-bar .action-item [tabindex="0"]:hover, .monaco-shell .monaco-tree.focused.no-focused-item:active:before { outline: 0 !important; /* fixes some flashing outlines from showing up when clicking */ } diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index 73dde75f2bb..7ad21d04307 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -515,6 +515,7 @@ export class WorkbenchShell extends Disposable { } } + registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { // Foreground diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index a87a8f73f31..b6b6953d245 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -9,7 +9,6 @@ import * as nls from 'vs/nls'; import URI from 'vs/base/common/uri'; import * as errors from 'vs/base/common/errors'; import { TPromise } from 'vs/base/common/winjs.base'; -import * as arrays from 'vs/base/common/arrays'; import * as objects from 'vs/base/common/objects'; import * as DOM from 'vs/base/browser/dom'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -21,13 +20,11 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { IWindowsService, IWindowService, IWindowSettings, IPath, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest, IRunActionInWindowRequest } from 'vs/platform/windows/common/windows'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { IWorkbenchThemeService, VS_HC_THEME, VS_DARK_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService'; 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 { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; import { Themable } from 'vs/workbench/common/theme'; import { ipcRenderer as ipc, webFrame } from 'electron'; @@ -66,7 +63,7 @@ export class ElectronWindow extends Themable { private previousConfiguredZoomLevel: number; private addFoldersScheduler: RunOnceScheduler; - private pendingFoldersToAdd: IAddFoldersRequest[]; + private pendingFoldersToAdd: URI[]; constructor( @IEditorService private editorService: EditorServiceImpl, @@ -77,9 +74,7 @@ export class ElectronWindow extends Themable { @IWorkbenchThemeService protected themeService: IWorkbenchThemeService, @INotificationService private notificationService: INotificationService, @ICommandService private commandService: ICommandService, - @IExtensionService private extensionService: IExtensionService, @IContextMenuService private contextMenuService: IContextMenuService, - @IKeybindingService private keybindingService: IKeybindingService, @ITelemetryService private telemetryService: ITelemetryService, @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService, @IFileService private fileService: IFileService, @@ -141,23 +136,6 @@ export class ElectronWindow extends Themable { }); }); - // Support resolve keybindings event - ipc.on('vscode:resolveKeybindings', (event: any, rawActionIds: string) => { - let actionIds: string[] = []; - try { - actionIds = JSON.parse(rawActionIds); - } catch (error) { - // should not happen - } - - // Resolve keys using the keybinding service and send back to browser process - this.resolveKeybindings(actionIds).done(keybindings => { - if (keybindings.length) { - ipc.send('vscode:keybindingsResolved', JSON.stringify(keybindings)); - } - }, () => errors.onUnexpectedError); - }); - ipc.on('vscode:reportError', (event: any, error: string) => { if (error) { const errorParsed = JSON.parse(error); @@ -376,35 +354,10 @@ export class ElectronWindow extends Themable { } } - private resolveKeybindings(actionIds: string[]): TPromise<{ id: string; label: string, isNative: boolean; }[]> { - return TPromise.join([this.lifecycleService.when(LifecyclePhase.Running), this.extensionService.whenInstalledExtensionsRegistered()]).then(() => { - return arrays.coalesce(actionIds.map(id => { - const binding = this.keybindingService.lookupKeybinding(id); - if (!binding) { - return null; - } - - // first try to resolve a native accelerator - const electronAccelerator = binding.getElectronAccelerator(); - if (electronAccelerator) { - return { id, label: electronAccelerator, isNative: true }; - } - - // we need this fallback to support keybindings that cannot show in electron menus (e.g. chords) - const acceleratorLabel = binding.getLabel(); - if (acceleratorLabel) { - return { id, label: acceleratorLabel, isNative: false }; - } - - return null; - })); - }); - } - private onAddFoldersRequest(request: IAddFoldersRequest): void { // Buffer all pending requests - this.pendingFoldersToAdd.push(request); + this.pendingFoldersToAdd.push(...request.foldersToAdd.map(f => URI.revive(f))); // Delay the adding of folders a bit to buffer in case more requests are coming if (!this.addFoldersScheduler.isScheduled()) { @@ -415,8 +368,8 @@ export class ElectronWindow extends Themable { private doAddFolders(): void { const foldersToAdd: IWorkspaceFolderCreationData[] = []; - this.pendingFoldersToAdd.forEach(request => { - foldersToAdd.push(...request.foldersToAdd.map(folderToAdd => ({ uri: URI.file(folderToAdd.filePath) }))); + this.pendingFoldersToAdd.forEach(folder => { + foldersToAdd.push(({ uri: folder })); }); this.pendingFoldersToAdd = []; diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 00aae6933f9..bfed9637ad6 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -12,7 +12,6 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { IDisposable, dispose, toDisposable, Disposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import * as DOM from 'vs/base/browser/dom'; -import { Builder, $ } from 'vs/base/browser/builder'; import { RunOnceScheduler } from 'vs/base/common/async'; import * as browser from 'vs/base/browser/browser'; import * as perf from 'vs/base/common/performance'; @@ -23,14 +22,13 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; import { IResourceInput } from 'vs/platform/editor/common/editor'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; -import { IEditorInputFactoryRegistry, Extensions as EditorExtensions, TextCompareEditorVisibleContext, TEXT_DIFF_EDITOR_ID, EditorsVisibleContext, InEditorZenModeContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, IUntitledResourceInput, IResourceDiffInput, SplitEditorsVertically } from 'vs/workbench/common/editor'; +import { IEditorInputFactoryRegistry, Extensions as EditorExtensions, TextCompareEditorVisibleContext, TEXT_DIFF_EDITOR_ID, EditorsVisibleContext, InEditorZenModeContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, IUntitledResourceInput, IResourceDiffInput, SplitEditorsVertically, TextCompareEditorActiveContext } from 'vs/workbench/common/editor'; import { HistoryService } from 'vs/workbench/services/history/electron-browser/history'; import { ActivitybarPart } from 'vs/workbench/browser/parts/activitybar/activitybarPart'; import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart'; import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; import { StatusbarPart } from 'vs/workbench/browser/parts/statusbar/statusbarPart'; import { TitlebarPart } from 'vs/workbench/browser/parts/titlebar/titlebarPart'; -import { MenubarPart } from 'vs/workbench/browser/parts/menubar/menubarPart'; import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; import { WorkbenchLayout } from 'vs/workbench/browser/layout'; import { IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions'; @@ -86,7 +84,7 @@ import { MenuService } from 'vs/workbench/services/actions/common/menuService'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { OpenRecentAction, ToggleDevToolsAction, ReloadWindowAction, ShowPreviousWindowTab, MoveWindowTabToNewWindow, MergeAllWindowTabs, ShowNextWindowTab, ToggleWindowTabsBar, ReloadWindowWithExtensionsDisabledAction } from 'vs/workbench/electron-browser/actions'; +import { OpenRecentAction, ToggleDevToolsAction, ReloadWindowAction, ShowPreviousWindowTab, MoveWindowTabToNewWindow, MergeAllWindowTabs, ShowNextWindowTab, ToggleWindowTabsBar, ReloadWindowWithExtensionsDisabledAction, NewWindowTab } from 'vs/workbench/electron-browser/actions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; import { WorkspaceEditingService } from 'vs/workbench/services/workspace/node/workspaceEditingService'; @@ -118,6 +116,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; import { WorkbenchThemeService } from 'vs/workbench/services/themes/electron-browser/workbenchThemeService'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IUriDisplayService, UriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; interface WorkbenchParams { configuration: IWindowConfiguration; @@ -151,8 +150,7 @@ const Identifiers = { SIDEBAR_PART: 'workbench.parts.sidebar', PANEL_PART: 'workbench.parts.panel', EDITOR_PART: 'workbench.parts.editor', - STATUSBAR_PART: 'workbench.parts.statusbar', - MENUBAR_PART: 'workbench.parts.menubar' + STATUSBAR_PART: 'workbench.parts.statusbar' }; function getWorkbenchStateString(state: WorkbenchState): string { @@ -191,7 +189,7 @@ export class Workbench extends Disposable implements IPartService { _serviceBrand: any; private workbenchParams: WorkbenchParams; - private workbench: Builder; + private workbench: HTMLElement; private workbenchStarted: boolean; private workbenchCreated: boolean; private workbenchShutdown: boolean; @@ -209,7 +207,6 @@ export class Workbench extends Disposable implements IPartService { private workbenchLayout: WorkbenchLayout; private titlebarPart: TitlebarPart; - private menubarPart: MenubarPart; private activitybarPart: ActivitybarPart; private sidebarPart: SidebarPart; private panelPart: PanelPart; @@ -298,10 +295,13 @@ export class Workbench extends Disposable implements IPartService { } private createWorkbench(): void { - this.workbench = $().div({ - 'class': `monaco-workbench ${isWindows ? 'windows' : isLinux ? 'linux' : 'mac'}`, - id: Identifiers.WORKBENCH_CONTAINER - }); + this.workbench = document.createElement('div'); + this.workbench.id = Identifiers.WORKBENCH_CONTAINER; + DOM.addClasses(this.workbench, 'monaco-workbench', isWindows ? 'windows' : isLinux ? 'linux' : 'mac'); + + this._register(DOM.addDisposableListener(this.workbench, DOM.EventType.SCROLL, () => { + this.workbench.scrollTop = 0; // Prevent workbench from scrolling #55456 + })); } private createGlobalActions(): void { @@ -317,6 +317,7 @@ export class Workbench extends Disposable implements IPartService { // Actions for macOS native tabs management (only when enabled) const windowConfig = this.configurationService.getValue(); if (windowConfig && windowConfig.window && windowConfig.window.nativeTabs) { + registry.registerWorkbenchAction(new SyncActionDescriptor(NewWindowTab, NewWindowTab.ID, NewWindowTab.LABEL), 'New Window Tab'); registry.registerWorkbenchAction(new SyncActionDescriptor(ShowPreviousWindowTab, ShowPreviousWindowTab.ID, ShowPreviousWindowTab.LABEL), 'Show Previous Window Tab'); registry.registerWorkbenchAction(new SyncActionDescriptor(ShowNextWindowTab, ShowNextWindowTab.ID, ShowNextWindowTab.LABEL), 'Show Next Window Tab'); registry.registerWorkbenchAction(new SyncActionDescriptor(MoveWindowTabToNewWindow, MoveWindowTabToNewWindow.ID, MoveWindowTabToNewWindow.LABEL), 'Move Window Tab to New Window'); @@ -334,6 +335,11 @@ export class Workbench extends Disposable implements IPartService { // Clipboard serviceCollection.set(IClipboardService, new ClipboardService()); + // Uri Display + const uriDisplayService = new UriDisplayService(this.environmentService, this.contextService); + serviceCollection.set(IUriDisplayService, uriDisplayService); + this.configurationService.acquireUriDisplayService(uriDisplayService); + // Status bar this.statusbarPart = this.instantiationService.createInstance(StatusbarPart, Identifiers.STATUSBAR_PART); this._register(toDisposable(() => this.statusbarPart.shutdown())); @@ -353,7 +359,7 @@ export class Workbench extends Disposable implements IPartService { serviceCollection.set(IListService, this.instantiationService.createInstance(ListService)); // Context view service - this.contextViewService = this.instantiationService.createInstance(ContextViewService, this.workbench.getHTMLElement()); + this.contextViewService = this.instantiationService.createInstance(ContextViewService, this.workbench); serviceCollection.set(IContextViewService, this.contextViewService); // Use themable context menus when custom titlebar is enabled to match custom menubar @@ -412,9 +418,6 @@ export class Workbench extends Disposable implements IPartService { // History serviceCollection.set(IHistoryService, new SyncDescriptor(HistoryService)); - // Menubar - this.menubarPart = this.instantiationService.createInstance(MenubarPart, Identifiers.MENUBAR_PART); - // Backup File Service if (this.workbenchParams.configuration.backupPath) { this.backupFileService = this.instantiationService.createInstance(BackupFileService, this.workbenchParams.configuration.backupPath); @@ -512,9 +515,10 @@ export class Workbench extends Disposable implements IPartService { // Apply as CSS class const isFullscreen = browser.isFullscreen(); if (isFullscreen) { - this.workbench.addClass('fullscreen'); + DOM.addClass(this.workbench, 'fullscreen'); } else { - this.workbench.removeClass('fullscreen'); + DOM.removeClass(this.workbench, 'fullscreen'); + if (this.zenMode.transitionedToFullScreen && this.zenMode.active) { this.toggleZenMode(); } @@ -595,17 +599,24 @@ export class Workbench extends Disposable implements IPartService { private handleContextKeys(): void { this.inZenMode = InEditorZenModeContext.bindTo(this.contextKeyService); + (new RawContextKey('isMac', isMacintosh)).bindTo(this.contextKeyService); + (new RawContextKey('isLinux', isLinux)).bindTo(this.contextKeyService); + (new RawContextKey('isWindows', isWindows)).bindTo(this.contextKeyService); + const sidebarVisibleContextRaw = new RawContextKey('sidebarVisible', false); this.sideBarVisibleContext = sidebarVisibleContextRaw.bindTo(this.contextKeyService); const editorsVisibleContext = EditorsVisibleContext.bindTo(this.contextKeyService); const textCompareEditorVisible = TextCompareEditorVisibleContext.bindTo(this.contextKeyService); + const textCompareEditorActive = TextCompareEditorActiveContext.bindTo(this.contextKeyService); const activeEditorGroupEmpty = ActiveEditorGroupEmptyContext.bindTo(this.contextKeyService); const multipleEditorGroups = MultipleEditorGroupsContext.bindTo(this.contextKeyService); const updateEditorContextKeys = () => { + const activeControl = this.editorService.activeControl; const visibleEditors = this.editorService.visibleControls; + textCompareEditorActive.set(activeControl && activeControl.getId() === TEXT_DIFF_EDITOR_ID); textCompareEditorVisible.set(visibleEditors.some(control => control.getId() === TEXT_DIFF_EDITOR_ID)); if (visibleEditors.length > 0) { @@ -916,9 +927,9 @@ export class Workbench extends Disposable implements IPartService { // Adjust CSS if (hidden) { - this.workbench.addClass('nostatusbar'); + DOM.addClass(this.workbench, 'nostatusbar'); } else { - this.workbench.removeClass('nostatusbar'); + DOM.removeClass(this.workbench, 'nostatusbar'); } // Layout @@ -943,10 +954,9 @@ export class Workbench extends Disposable implements IPartService { this.workbenchLayout = this.instantiationService.createInstance( WorkbenchLayout, this.container, - this.workbench.getHTMLElement(), + this.workbench, { titlebar: this.titlebarPart, - menubar: this.menubarPart, activitybar: this.activitybarPart, editor: this.editorPart, sidebar: this.sidebarPart, @@ -964,13 +974,15 @@ export class Workbench extends Disposable implements IPartService { // Apply sidebar state as CSS class if (this.sideBarHidden) { - this.workbench.addClass('nosidebar'); + DOM.addClass(this.workbench, 'nosidebar'); } + if (this.panelHidden) { - this.workbench.addClass('nopanel'); + DOM.addClass(this.workbench, 'nopanel'); } + if (this.statusBarHidden) { - this.workbench.addClass('nostatusbar'); + DOM.addClass(this.workbench, 'nostatusbar'); } // Apply font aliasing @@ -978,12 +990,11 @@ export class Workbench extends Disposable implements IPartService { // Apply fullscreen state if (browser.isFullscreen()) { - this.workbench.addClass('fullscreen'); + DOM.addClass(this.workbench, 'fullscreen'); } // Create Parts this.createTitlebarPart(); - this.createMenubarPart(); this.createActivityBarPart(); this.createSidebarPart(); this.createEditorPart(); @@ -994,94 +1005,63 @@ export class Workbench extends Disposable implements IPartService { this.createNotificationsHandlers(); // Add Workbench to DOM - this.workbench.appendTo(this.container); + this.container.appendChild(this.workbench); } private createTitlebarPart(): void { - const titlebarContainer = $(this.workbench).div({ - 'class': ['part', 'titlebar'], - id: Identifiers.TITLEBAR_PART, - role: 'contentinfo' - }); + const titlebarContainer = this.createPart(Identifiers.TITLEBAR_PART, ['part', 'titlebar'], 'contentinfo'); - this.titlebarPart.create(titlebarContainer.getHTMLElement()); - } - - private createMenubarPart(): void { - const menubarContainer = $(this.workbench).div({ - 'class': ['part', 'menubar'], - id: Identifiers.MENUBAR_PART, - role: 'menubar' - }); - - this.menubarPart.create(menubarContainer.getHTMLElement()); - - this._register(this.menubarPart.onVisibilityChange((dimension => { - this._onMenubarVisibilityChange.fire(dimension); - }))); + this.titlebarPart.create(titlebarContainer); } private createActivityBarPart(): void { - const activitybarPartContainer = $(this.workbench) - .div({ - 'class': ['part', 'activitybar', this.sideBarPosition === Position.LEFT ? 'left' : 'right'], - id: Identifiers.ACTIVITYBAR_PART, - role: 'navigation' - }); + const activitybarPartContainer = this.createPart(Identifiers.ACTIVITYBAR_PART, ['part', 'activitybar', this.sideBarPosition === Position.LEFT ? 'left' : 'right'], 'navigation'); - this.activitybarPart.create(activitybarPartContainer.getHTMLElement()); + this.activitybarPart.create(activitybarPartContainer); } private createSidebarPart(): void { - const sidebarPartContainer = $(this.workbench) - .div({ - 'class': ['part', 'sidebar', this.sideBarPosition === Position.LEFT ? 'left' : 'right'], - id: Identifiers.SIDEBAR_PART, - role: 'complementary' - }); + const sidebarPartContainer = this.createPart(Identifiers.SIDEBAR_PART, ['part', 'sidebar', this.sideBarPosition === Position.LEFT ? 'left' : 'right'], 'complementary'); - this.sidebarPart.create(sidebarPartContainer.getHTMLElement()); + this.sidebarPart.create(sidebarPartContainer); } private createPanelPart(): void { - const panelPartContainer = $(this.workbench) - .div({ - 'class': ['part', 'panel', this.panelPosition === Position.BOTTOM ? 'bottom' : 'right'], - id: Identifiers.PANEL_PART, - role: 'complementary' - }); + const panelPartContainer = this.createPart(Identifiers.PANEL_PART, ['part', 'panel', this.panelPosition === Position.BOTTOM ? 'bottom' : 'right'], 'complementary'); - this.panelPart.create(panelPartContainer.getHTMLElement()); + this.panelPart.create(panelPartContainer); } private createEditorPart(): void { - const editorContainer = $(this.workbench) - .div({ - 'class': ['part', 'editor'], - id: Identifiers.EDITOR_PART, - role: 'main' - }); + const editorContainer = this.createPart(Identifiers.EDITOR_PART, ['part', 'editor'], 'main'); - this.editorPart.create(editorContainer.getHTMLElement()); + this.editorPart.create(editorContainer); } private createStatusbarPart(): void { - const statusbarContainer = $(this.workbench).div({ - 'class': ['part', 'statusbar'], - id: Identifiers.STATUSBAR_PART, - role: 'contentinfo' - }); + const statusbarContainer = this.createPart(Identifiers.STATUSBAR_PART, ['part', 'statusbar'], 'contentinfo'); - this.statusbarPart.create(statusbarContainer.getHTMLElement()); + this.statusbarPart.create(statusbarContainer); + } + + private createPart(id: string, classes: string[], role: string): HTMLElement { + const part = document.createElement('div'); + classes.forEach(clazz => DOM.addClass(part, clazz)); + part.id = id; + part.setAttribute('role', role); + + this.workbench.appendChild(part); + + return part; } private createNotificationsHandlers(): void { // Notifications Center - this.notificationsCenter = this._register(this.instantiationService.createInstance(NotificationsCenter, this.workbench.getHTMLElement(), this.notificationService.model)); + this.notificationsCenter = this._register(this.instantiationService.createInstance(NotificationsCenter, this.workbench, this.notificationService.model)); // Notifications Toasts - this.notificationsToasts = this._register(this.instantiationService.createInstance(NotificationsToasts, this.workbench.getHTMLElement(), this.notificationService.model)); + this.notificationsToasts = this._register(this.instantiationService.createInstance(NotificationsToasts, this.workbench, this.notificationService.model)); // Notifications Alerts this._register(this.instantiationService.createInstance(NotificationsAlerts, this.notificationService.model)); @@ -1135,13 +1115,10 @@ export class Workbench extends Disposable implements IPartService { private _onTitleBarVisibilityChange: Emitter = this._register(new Emitter()); get onTitleBarVisibilityChange(): Event { return this._onTitleBarVisibilityChange.event; } - private _onMenubarVisibilityChange: Emitter = this._register(new Emitter()); - get onMenubarVisibilityChange(): Event { return this._onMenubarVisibilityChange.event; } - get onEditorLayout(): Event { return this.editorPart.onDidLayout; } isCreated(): boolean { - return this.workbenchCreated && this.workbenchStarted; + return !!(this.workbenchCreated && this.workbenchStarted); } hasFocus(part: Parts): boolean { @@ -1160,9 +1137,6 @@ export class Workbench extends Disposable implements IPartService { case Parts.TITLEBAR_PART: container = this.titlebarPart.getContainer(); break; - case Parts.MENUBAR_PART: - container = this.menubarPart.getContainer(); - break; case Parts.ACTIVITYBAR_PART: container = this.activitybarPart.getContainer(); break; @@ -1186,9 +1160,7 @@ export class Workbench extends Disposable implements IPartService { isVisible(part: Parts): boolean { switch (part) { case Parts.TITLEBAR_PART: - return this.getCustomTitleBarStyle() === 'custom' && !browser.isFullscreen(); - case Parts.MENUBAR_PART: - return !isMacintosh && this.isVisible(Parts.TITLEBAR_PART) && !(this.menubarVisibility === 'hidden' || (this.menubarVisibility === 'default' && browser.isFullscreen())); + return this.getCustomTitleBarStyle() === 'custom' && (!browser.isFullscreen() || this.menubarVisibility === 'visible' || this.menubarVisibility === 'toggle'); case Parts.SIDEBAR_PART: return !this.sideBarHidden; case Parts.PANEL_PART: @@ -1206,6 +1178,9 @@ export class Workbench extends Disposable implements IPartService { let offset = 0; if (this.isVisible(Parts.TITLEBAR_PART)) { offset = this.workbenchLayout.partLayoutInfo.titlebar.height; + if (isMacintosh || this.menubarVisibility === 'hidden') { + offset /= browser.getZoomFactor(); + } } return offset; @@ -1336,9 +1311,9 @@ export class Workbench extends Disposable implements IPartService { // Adjust CSS if (hidden) { - this.workbench.addClass('nosidebar'); + DOM.addClass(this.workbench, 'nosidebar'); } else { - this.workbench.removeClass('nosidebar'); + DOM.removeClass(this.workbench, 'nosidebar'); } // If sidebar becomes hidden, also hide the current active Viewlet if any @@ -1387,9 +1362,9 @@ export class Workbench extends Disposable implements IPartService { // Adjust CSS if (hidden) { - this.workbench.addClass('nopanel'); + DOM.addClass(this.workbench, 'nopanel'); } else { - this.workbench.removeClass('nopanel'); + DOM.removeClass(this.workbench, 'nopanel'); } // If panel part becomes hidden, also hide the current active panel if any @@ -1467,6 +1442,10 @@ export class Workbench extends Disposable implements IPartService { } } + getMenubarVisibility(): MenuBarVisibility { + return this.menubarVisibility; + } + getPanelPosition(): Position { return this.panelPosition; } diff --git a/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts b/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts index 54006b5ff83..513c3d605ce 100644 --- a/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts +++ b/src/vs/workbench/parts/cli/electron-browser/cli.contribution.ts @@ -10,7 +10,6 @@ import * as pfs from 'vs/base/node/pfs'; import * as platform from 'vs/base/common/platform'; import { nfcall } from 'vs/base/common/async'; import { TPromise } from 'vs/base/common/winjs.base'; -import URI from 'vs/base/common/uri'; import { Action } from 'vs/base/common/actions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -20,6 +19,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; import { ILogService } from 'vs/platform/log/common/log'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; function ignore(code: string, value: T = null): (err: any) => TPromise { return err => err.code === code ? TPromise.as(value) : TPromise.wrapError(err); @@ -28,7 +28,7 @@ function ignore(code: string, value: T = null): (err: any) => TPromise { let _source: string = null; function getSource(): string { if (!_source) { - const root = URI.parse(require.toUrl('')).fsPath; + const root = getPathFromAmdModule(require, ''); _source = path.resolve(root, '..', 'bin', 'code'); } return _source; diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.ts b/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.ts index a9e4ec6741e..c7242e8ecdc 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/accessibility.ts @@ -30,7 +30,7 @@ import * as platform from 'vs/base/common/platform'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import URI from 'vs/base/common/uri'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; const CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE = new RawContextKey('accessibilityHelpWidgetVisible', false); @@ -286,7 +286,8 @@ class ShowAccessibilityHelpAction extends EditorAction { precondition: null, kbOpts: { kbExpr: EditorContextKeys.focus, - primary: KeyMod.Alt | KeyCode.F1 + primary: KeyMod.Alt | KeyCode.F1, + weight: KeybindingWeight.EditorContrib } }); } @@ -309,7 +310,7 @@ registerEditorCommand(new AccessibilityHelpCommand({ precondition: CONTEXT_ACCESSIBILITY_WIDGET_VISIBLE, handler: x => x.hide(), kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(100), + weight: KeybindingWeight.EditorContrib + 100, kbExpr: EditorContextKeys.focus, primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape] } diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts b/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts index 15a56872cdc..4b1038f2039 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts @@ -89,7 +89,7 @@ export class LanguageConfigurationFileHandler { } private _handleConfigFile(languageIdentifier: LanguageIdentifier, configFileLocation: URI): void { - this._fileService.resolveContent(configFileLocation).then((contents) => { + this._fileService.resolveContent(configFileLocation, { encoding: 'utf8' }).then((contents) => { const errors: ParseError[] = []; const configuration = parse(contents.value.toString(), errors); if (errors.length) { diff --git a/src/vs/workbench/parts/debug/electron-browser/simpleDebugEditor.ts b/src/vs/workbench/parts/codeEditor/electron-browser/simpleEditorOptions.ts similarity index 53% rename from src/vs/workbench/parts/debug/electron-browser/simpleDebugEditor.ts rename to src/vs/workbench/parts/codeEditor/electron-browser/simpleEditorOptions.ts index 720b75f1cad..809dbd0584d 100644 --- a/src/vs/workbench/parts/debug/electron-browser/simpleDebugEditor.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/simpleEditorOptions.ts @@ -5,8 +5,6 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; - -// Allowed Editor Contributions: import { MenuPreventer } from 'vs/workbench/parts/codeEditor/electron-browser/menuPreventer'; import { SelectionClipboard } from 'vs/workbench/parts/codeEditor/electron-browser/selectionClipboard'; import { ContextMenuController } from 'vs/editor/contrib/contextmenu/contextmenu'; @@ -14,44 +12,41 @@ import { SuggestController } from 'vs/editor/contrib/suggest/suggestController'; import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; import { TabCompletionController } from 'vs/workbench/parts/snippets/electron-browser/tabCompletion'; -export class SimpleDebugEditor { +export function getSimpleCodeEditorWidgetOptions(): ICodeEditorWidgetOptions { + return { + isSimpleWidget: true, + contributions: [ + MenuPreventer, + SelectionClipboard, + ContextMenuController, + SuggestController, + SnippetController2, + TabCompletionController, + ] + }; +} - public static getCodeEditorWidgetOptions(): ICodeEditorWidgetOptions { - return { - isSimpleWidget: true, - contributions: [ - MenuPreventer, - SelectionClipboard, - ContextMenuController, - SuggestController, - SnippetController2, - TabCompletionController, - ] - }; - } - - public static getEditorOptions(): IEditorOptions { - return { - wordWrap: 'on', - overviewRulerLanes: 0, - glyphMargin: false, - lineNumbers: 'off', - folding: false, - selectOnLineNumbers: false, - hideCursorInOverviewRuler: true, - selectionHighlight: false, - scrollbar: { - horizontal: 'hidden' - }, - lineDecorationsWidth: 0, - overviewRulerBorder: false, - scrollBeyondLastLine: false, - renderLineHighlight: 'none', - fixedOverflowWidgets: true, - acceptSuggestionOnEnter: 'smart', - minimap: { - enabled: false - } - }; - } +export function getSimpleEditorOptions(): IEditorOptions { + return { + wordWrap: 'on', + overviewRulerLanes: 0, + glyphMargin: false, + lineNumbers: 'off', + folding: false, + selectOnLineNumbers: false, + hideCursorInOverviewRuler: true, + selectionHighlight: false, + scrollbar: { + horizontal: 'hidden' + }, + lineDecorationsWidth: 0, + overviewRulerBorder: false, + scrollBeyondLastLine: false, + renderLineHighlight: 'none', + fixedOverflowWidgets: true, + acceptSuggestionOnEnter: 'smart', + minimap: { + enabled: false + } + }; } diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts index 8361e7b855b..36617f7b8c3 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts @@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { Action } from 'vs/base/common/actions'; import { TPromise } from 'vs/base/common/winjs.base'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; export class ToggleMinimapAction extends Action { public static readonly ID = 'editor.action.toggleMinimap'; @@ -32,3 +32,12 @@ export class ToggleMinimapAction extends Action { const registry = Registry.as(ActionExtensions.WorkbenchActions); registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMinimapAction, ToggleMinimapAction.ID, ToggleMinimapAction.LABEL), 'View: Toggle Minimap'); + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '5_editor', + command: { + id: ToggleMinimapAction.ID, + title: nls.localize({ key: 'miToggleMinimap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Minimap") + }, + order: 2 +}); diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.ts index e7b79739e01..608554450f8 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.ts @@ -6,11 +6,15 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; +import * as platform from 'vs/base/common/platform'; import { Registry } from 'vs/platform/registry/common/platform'; import { Action } from 'vs/base/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; export class ToggleMultiCursorModifierAction extends Action { @@ -35,5 +39,55 @@ export class ToggleMultiCursorModifierAction extends Action { } } +const multiCursorModifier = new RawContextKey('multiCursorModifier', 'altKey'); + +class MultiCursorModifierContextKeyController implements IWorkbenchContribution { + + private readonly _multiCursorModifier: IContextKey; + + constructor( + @IConfigurationService private readonly configurationService: IConfigurationService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + this._multiCursorModifier = multiCursorModifier.bindTo(contextKeyService); + configurationService.onDidChangeConfiguration((e) => { + if (e.affectsConfiguration('editor.multiCursorModifier')) { + this._update(); + } + }); + } + + private _update(): void { + const editorConf = this.configurationService.getValue<{ multiCursorModifier: 'ctrlCmd' | 'alt' }>('editor'); + const value = (editorConf.multiCursorModifier === 'ctrlCmd' ? 'ctrlCmd' : 'altKey'); + this._multiCursorModifier.set(value); + } +} + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(MultiCursorModifierContextKeyController, LifecyclePhase.Running); + + const registry = Registry.as(Extensions.WorkbenchActions); registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleMultiCursorModifierAction, ToggleMultiCursorModifierAction.ID, ToggleMultiCursorModifierAction.LABEL), 'Toggle Multi-Cursor Modifier'); +MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { + group: '3_multi', + command: { + id: ToggleMultiCursorModifierAction.ID, + title: nls.localize('miMultiCursorAlt', "Switch to Alt+Click for Multi-Cursor") + }, + when: multiCursorModifier.isEqualTo('ctrlCmd'), + order: 1 +}); +MenuRegistry.appendMenuItem(MenuId.MenubarSelectionMenu, { + group: '3_multi', + command: { + id: ToggleMultiCursorModifierAction.ID, + title: ( + platform.isMacintosh + ? nls.localize('miMultiCursorCmd', "Switch to Cmd+Click for Multi-Cursor") + : nls.localize('miMultiCursorCtrl', "Switch to Ctrl+Click for Multi-Cursor") + ) + }, + when: multiCursorModifier.isEqualTo('altKey'), + order: 1 +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.ts index 7c35e71b00c..42c4e6bf068 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderControlCharacter.ts @@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { Action } from 'vs/base/common/actions'; import { TPromise } from 'vs/base/common/winjs.base'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; export class ToggleRenderControlCharacterAction extends Action { @@ -33,3 +33,12 @@ export class ToggleRenderControlCharacterAction extends Action { const registry = Registry.as(ActionExtensions.WorkbenchActions); registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleRenderControlCharacterAction, ToggleRenderControlCharacterAction.ID, ToggleRenderControlCharacterAction.LABEL), 'View: Toggle Control Characters'); + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '5_editor', + command: { + id: ToggleRenderControlCharacterAction.ID, + title: nls.localize({ key: 'miToggleRenderControlCharacters', comment: ['&& denotes a mnemonic'] }, "Toggle &&Control Characters") + }, + order: 4 +}); diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.ts index 856e35dce60..d9a61a863eb 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleRenderWhitespace.ts @@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { Action } from 'vs/base/common/actions'; import { TPromise } from 'vs/base/common/winjs.base'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; export class ToggleRenderWhitespaceAction extends Action { @@ -41,3 +41,12 @@ export class ToggleRenderWhitespaceAction extends Action { const registry = Registry.as(ActionExtensions.WorkbenchActions); registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleRenderWhitespaceAction, ToggleRenderWhitespaceAction.ID, ToggleRenderWhitespaceAction.LABEL), 'View: Toggle Render Whitespace'); + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '5_editor', + command: { + id: ToggleRenderWhitespaceAction.ID, + title: nls.localize({ key: 'miToggleRenderWhitespace', comment: ['&& denotes a mnemonic'] }, "Toggle &&Render Whitespace") + }, + order: 3 +}); diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts b/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts index bd18b1271c4..947ec9d0f42 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts @@ -18,6 +18,7 @@ import { InternalEditorOptions, EDITOR_DEFAULTS } from 'vs/editor/common/config/ import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; const transientWordWrapState = 'transientWordWrapState'; const isWordWrapMinifiedKey = 'isWordWrapMinified'; @@ -130,17 +131,19 @@ function applyWordWrapState(editor: ICodeEditor, state: IWordWrapState): void { }); } +const TOGGLE_WORD_WRAP_ID = 'editor.action.toggleWordWrap'; class ToggleWordWrapAction extends EditorAction { constructor() { super({ - id: 'editor.action.toggleWordWrap', + id: TOGGLE_WORD_WRAP_ID, label: nls.localize('toggle.wordwrap', "View: Toggle Word Wrap"), alias: 'View: Toggle Word Wrap', precondition: null, kbOpts: { kbExpr: null, - primary: KeyMod.Alt | KeyCode.KEY_Z + primary: KeyMod.Alt | KeyCode.KEY_Z, + weight: KeybindingWeight.EditorContrib } }); } @@ -255,7 +258,7 @@ registerEditorAction(ToggleWordWrapAction); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { - id: 'editor.action.toggleWordWrap', + id: TOGGLE_WORD_WRAP_ID, title: nls.localize('unwrapMinified', "Disable wrapping for this file"), iconLocation: { dark: URI.parse(require.toUrl('vs/workbench/parts/codeEditor/electron-browser/media/WordWrap_16x.svg')) } }, @@ -269,7 +272,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { - id: 'editor.action.toggleWordWrap', + id: TOGGLE_WORD_WRAP_ID, title: nls.localize('wrapMinified', "Enable wrapping for this file"), iconLocation: { dark: URI.parse(require.toUrl('vs/workbench/parts/codeEditor/electron-browser/media/WordWrap_16x.svg')) } }, @@ -281,3 +284,14 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { ContextKeyExpr.not(isWordWrapMinifiedKey) ) }); + + +// View menu +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '5_editor', + command: { + id: TOGGLE_WORD_WRAP_ID, + title: nls.localize({ key: 'miToggleWordWrap', comment: ['&& denotes a mnemonic'] }, "Toggle &&Word Wrap") + }, + order: 1 +}); diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/workbenchReferenceSearch.ts b/src/vs/workbench/parts/codeEditor/electron-browser/workbenchReferenceSearch.ts index 3b0d4b2c532..260c669fa73 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/workbenchReferenceSearch.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/workbenchReferenceSearch.ts @@ -4,16 +4,12 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ReferencesController } from 'vs/editor/contrib/referenceSearch/referencesController'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; @@ -23,29 +19,21 @@ export class WorkbenchReferencesController extends ReferencesController { public constructor( editor: ICodeEditor, @IContextKeyService contextKeyService: IContextKeyService, - @ICodeEditorService codeEditorService: ICodeEditorService, - @ITextModelService textModelResolverService: ITextModelService, + @ICodeEditorService editorService: ICodeEditorService, @INotificationService notificationService: INotificationService, @IInstantiationService instantiationService: IInstantiationService, - @IWorkspaceContextService contextService: IWorkspaceContextService, @IStorageService storageService: IStorageService, - @IThemeService themeService: IThemeService, @IConfigurationService configurationService: IConfigurationService, - @optional(IEnvironmentService) environmentService: IEnvironmentService ) { super( false, editor, contextKeyService, - codeEditorService, - textModelResolverService, + editorService, notificationService, instantiationService, - contextService, storageService, - themeService, configurationService, - environmentService ); } } diff --git a/src/vs/workbench/parts/comments/common/commentModel.ts b/src/vs/workbench/parts/comments/common/commentModel.ts index 4fb5b74843c..305acad3ce4 100644 --- a/src/vs/workbench/parts/comments/common/commentModel.ts +++ b/src/vs/workbench/parts/comments/common/commentModel.ts @@ -67,8 +67,12 @@ export class CommentsModel { this.resourceCommentThreads = flatten(values(this.commentThreadsMap)); } - public updateCommentThreads(event: CommentThreadChangedEvent): void { + public updateCommentThreads(event: CommentThreadChangedEvent): boolean { const { owner, removed, changed, added } = event; + if (!this.commentThreadsMap.has(owner)) { + return false; + } + let threadsForOwner = this.commentThreadsMap.get(owner); removed.forEach(thread => { @@ -96,10 +100,20 @@ export class CommentsModel { matchingResourceData.commentThreads[index] = ResourceWithCommentThreads.createCommentNode(URI.parse(matchingResourceData.id), thread); }); - threadsForOwner = threadsForOwner.concat(this.groupByResource(added)); + added.forEach(thread => { + const existingResource = threadsForOwner.filter(resourceWithThreads => resourceWithThreads.resource.toString() === thread.resource); + if (existingResource.length) { + const resource = existingResource[0]; + resource.commentThreads.push(ResourceWithCommentThreads.createCommentNode(resource.resource, thread)); + } else { + threadsForOwner.push(new ResourceWithCommentThreads(URI.parse(thread.resource), [thread])); + } + }); this.commentThreadsMap.set(owner, threadsForOwner); this.resourceCommentThreads = flatten(values(this.commentThreadsMap)); + + return removed.length > 0 || changed.length > 0 || added.length > 0; } public hasCommentThreads(): boolean { diff --git a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts index eb83433e3a6..de324d7d2f0 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts @@ -25,7 +25,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IModelService } from 'vs/editor/common/services/modelService'; import { SimpleCommentEditor } from './simpleCommentEditor'; import URI from 'vs/base/common/uri'; -import { transparent, editorForeground, inputValidationErrorBorder, textLinkActiveForeground, textLinkForeground, focusBorder } from 'vs/platform/theme/common/colorRegistry'; +import { transparent, editorForeground, textLinkActiveForeground, textLinkForeground, focusBorder, textBlockQuoteBackground, textBlockQuoteBorder, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -354,9 +354,14 @@ export class ReviewZoneWidget extends ZoneWidget { this._localToDispose.push(this._commentEditor.onKeyDown((ev: IKeyboardEvent) => { const hasExistingComments = this._commentThread.comments.length > 0; - if (this._commentEditor.getModel().getValueLength() === 0 && ev.keyCode === KeyCode.Escape && hasExistingComments) { - if (dom.hasClass(this._commentForm, 'expand')) { - dom.removeClass(this._commentForm, 'expand'); + + if (this._commentEditor.getModel().getValueLength() === 0 && ev.keyCode === KeyCode.Escape) { + if (hasExistingComments) { + if (dom.hasClass(this._commentForm, 'expand')) { + dom.removeClass(this._commentForm, 'expand'); + } + } else { + this.dispose(); } } })); @@ -366,23 +371,17 @@ export class ReviewZoneWidget extends ZoneWidget { const button = new Button(formActions); attachButtonStyler(button, this.themeService); button.label = 'Add comment'; - button.onDidClick(async () => { - if (!this._commentEditor.getValue()) { - this._commentEditor.focus(); - this._commentEditor.getDomNode().style.outline = `1px solid ${this.themeService.getTheme().getColor(inputValidationErrorBorder)}`; - - this._disposables.push(this._commentEditor.onDidChangeModelContent(_ => { - if (!this._commentEditor.getValue()) { - this._commentEditor.getDomNode().style.outline = `1px solid ${this.themeService.getTheme().getColor(inputValidationErrorBorder)}`; - } else { - this._commentEditor.getDomNode().style.outline = ''; - } - })); - - return; + button.enabled = false; + this._localToDispose.push(this._commentEditor.onDidChangeModelContent(_ => { + if (this._commentEditor.getValue()) { + button.enabled = true; + } else { + button.enabled = false; } + })); + button.onDidClick(async () => { let newCommentThread; if (this._commentThread.threadId) { // reply @@ -477,26 +476,28 @@ export class ReviewZoneWidget extends ZoneWidget { } private setCommentEditorDecorations() { - let model = this._commentEditor.getModel(); - let valueLength = model.getValueLength(); - const hasExistingComments = this._commentThread.comments.length > 0; - let placeholder = valueLength > 0 ? '' : (hasExistingComments ? 'Reply...' : 'Type a new comment'); - const decorations = [{ - range: { - startLineNumber: 0, - endLineNumber: 0, - startColumn: 0, - endColumn: 1 - }, - renderOptions: { - after: { - contentText: placeholder, - color: transparent(editorForeground, 0.4)(this.themeService.getTheme()).toString() + const model = this._commentEditor && this._commentEditor.getModel(); + if (model) { + let valueLength = model.getValueLength(); + const hasExistingComments = this._commentThread.comments.length > 0; + let placeholder = valueLength > 0 ? '' : (hasExistingComments ? 'Reply...' : 'Type a new comment'); + const decorations = [{ + range: { + startLineNumber: 0, + endLineNumber: 0, + startColumn: 0, + endColumn: 1 + }, + renderOptions: { + after: { + contentText: placeholder, + color: transparent(editorForeground, 0.4)(this.themeService.getTheme()).toString() + } } - } - }]; + }]; - this._commentEditor.setDecorations(COMMENTEDITOR_DECORATION_KEY, decorations); + this._commentEditor.setDecorations(COMMENTEDITOR_DECORATION_KEY, decorations); + } } private mouseDownInfo: { lineNumber: number, iconClicked: boolean }; @@ -577,9 +578,29 @@ export class ReviewZoneWidget extends ZoneWidget { const focusColor = theme.getColor(focusBorder); if (focusColor) { content.push(`.monaco-editor .review-widget .body .review-comment a:focus { outline: 1px solid ${focusColor}; }`); + content.push(`.monaco-editor .review-widget .body .comment-form .monaco-editor.focused { outline: 1px solid ${focusColor}; }`); + } + + const blockQuoteBackground = theme.getColor(textBlockQuoteBackground); + if (blockQuoteBackground) { + content.push(`.monaco-editor .review-widget .body .review-comment blockquote { background: ${blockQuoteBackground}; }`); + } + + const blockQuoteBOrder = theme.getColor(textBlockQuoteBorder); + if (blockQuoteBOrder) { + content.push(`.monaco-editor .review-widget .body .review-comment blockquote { border-color: ${blockQuoteBOrder}; }`); + } + + const hcBorder = theme.getColor(contrastBorder); + if (hcBorder) { + content.push(`.monaco-editor .review-widget .body .comment-form .review-thread-reply-button { outline-color: ${hcBorder}; }`); + content.push(`.monaco-editor .review-widget .body .comment-form .monaco-editor { outline: 1px solid ${hcBorder}; }`); } this._styleElement.innerHTML = content.join('\n'); + + // Editor decorations should also be responsive to theme changes + this.setCommentEditorDecorations(); } show(rangeOrPos: IRange | IPosition, heightInLines: number): void { diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts b/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts index 60cd5072a24..2b088806cbf 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts @@ -5,11 +5,13 @@ 'use strict'; import 'vs/css!./media/review'; +import * as nls from 'vs/nls'; import { $ } from 'vs/base/browser/builder'; +import { findFirstInSorted } from 'vs/base/common/arrays'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ICodeEditor, IEditorMouseEvent, IViewZone } from 'vs/editor/browser/editorBrowser'; -import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { registerEditorContribution, EditorAction, registerEditorAction } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; @@ -17,7 +19,7 @@ import * as modes from 'vs/editor/common/modes'; import { peekViewEditorBackground, peekViewResultsBackground, peekViewResultsSelectionBackground } from 'vs/editor/contrib/referenceSearch/referencesWidget'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { editorForeground } from 'vs/platform/theme/common/colorRegistry'; import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; @@ -155,6 +157,56 @@ export class ReviewController implements IEditorContribution { } } + public nextCommentThread(): void { + if (!this._commentWidgets.length) { + return; + } + + const after = this.editor.getSelection().getEndPosition(); + const sortedWidgets = this._commentWidgets.sort((a, b) => { + if (a.commentThread.range.startLineNumber < b.commentThread.range.startLineNumber) { + return -1; + } + + if (a.commentThread.range.startLineNumber > b.commentThread.range.startLineNumber) { + return 1; + } + + if (a.commentThread.range.startColumn < b.commentThread.range.startColumn) { + return -1; + } + + if (a.commentThread.range.startColumn > b.commentThread.range.startColumn) { + return 1; + } + + return 0; + }); + + let idx = findFirstInSorted(sortedWidgets, widget => { + if (widget.commentThread.range.startLineNumber > after.lineNumber) { + return true; + } + + if (widget.commentThread.range.startLineNumber < after.lineNumber) { + return false; + } + + if (widget.commentThread.range.startColumn > after.column) { + return true; + } + return false; + }); + + if (idx === this._commentWidgets.length) { + this._commentWidgets[0].reveal(); + this.editor.setSelection(this._commentWidgets[0].commentThread.range); + } else { + sortedWidgets[idx].reveal(); + this.editor.setSelection(sortedWidgets[idx].commentThread.range); + } + } + getId(): string { return ID; } @@ -191,6 +243,7 @@ export class ReviewController implements IEditorContribution { this._commentWidgets = []; this.localToDispose.push(this.editor.onMouseMove(e => this.onEditorMouseMove(e))); + this.localToDispose.push(this.editor.onMouseLeave(() => this.onMouseLeave())); this.localToDispose.push(this.editor.onDidChangeModelContent(() => { if (this._newCommentGlyph) { this.editor.removeContentWidget(this._newCommentGlyph); @@ -286,6 +339,12 @@ export class ReviewController implements IEditorContribution { } } + private onMouseLeave(): void { + if (this._newCommentGlyph) { + this.editor.removeContentWidget(this._newCommentGlyph); + } + } + private getNewCommentAction(line: number): { replyCommand: modes.Command, ownerId: number } { for (let i = 0; i < this._commentInfos.length; i++) { const commentInfo = this._commentInfos[i]; @@ -360,12 +419,31 @@ export class ReviewController implements IEditorContribution { } } -registerEditorContribution(ReviewController); +export class NextCommentThreadAction extends EditorAction { + constructor() { + super({ + id: 'editor.action.nextCommentThreadAction', + label: nls.localize('nextCommentThreadAction', "Go to Next Comment Thread"), + alias: 'Go to Next Comment Thread', + precondition: null, + }); + } + + public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + let controller = ReviewController.get(editor); + if (controller) { + controller.nextCommentThread(); + } + } +} + +registerEditorContribution(ReviewController); +registerEditorAction(NextCommentThreadAction); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'closeReviewPanel', - weight: KeybindingsRegistry.WEIGHT.editorContrib(), + weight: KeybindingWeight.EditorContrib, primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape], when: ctxReviewPanelVisible, diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts b/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts index 837b56fc238..4701e552987 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsPanel.ts @@ -23,6 +23,7 @@ import { ICommentService, IWorkspaceCommentThreadsEvent } from 'vs/workbench/par import { IEditorService, ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { textLinkForeground, textLinkActiveForeground, focusBorder } from 'vs/platform/theme/common/colorRegistry'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; export const COMMENTS_PANEL_ID = 'workbench.panel.comments'; export const COMMENTS_PANEL_TITLE = 'Comments'; @@ -40,6 +41,7 @@ export class CommentsPanel extends Panel { @ICommentService private commentService: ICommentService, @IEditorService private editorService: IEditorService, @ICommandService private commandService: ICommandService, + @IOpenerService private openerService: IOpenerService, @ITelemetryService telemetryService: ITelemetryService, @IThemeService themeService: IThemeService ) { @@ -130,7 +132,7 @@ export class CommentsPanel extends Panel { private createTree(): void { this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, { dataSource: new CommentsDataSource(), - renderer: new CommentsModelRenderer(this.instantiationService), + renderer: new CommentsModelRenderer(this.instantiationService, this.openerService), accessibilityProvider: new DefaultAccessibilityProvider, controller: new DefaultController(), dnd: new DefaultDragAndDrop(), @@ -260,7 +262,9 @@ export class CommentsPanel extends Panel { } private onCommentsUpdated(e: CommentThreadChangedEvent): void { - this.commentsModel.updateCommentThreads(e); - this.refresh(); + const didUpdate = this.commentsModel.updateCommentThreads(e); + if (didUpdate) { + this.refresh(); + } } } diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsTreeViewer.ts b/src/vs/workbench/parts/comments/electron-browser/commentsTreeViewer.ts index df16d651425..d8672d23d57 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsTreeViewer.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsTreeViewer.ts @@ -5,9 +5,13 @@ import * as dom from 'vs/base/browser/dom'; import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { Disposable } from 'vs/base/common/lifecycle'; +import URI from 'vs/base/common/uri'; import { Promise, TPromise } from 'vs/base/common/winjs.base'; import { IDataSource, IFilter, IRenderer as ITreeRenderer, ITree } from 'vs/base/parts/tree/browser/tree'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; import { FileLabel } from 'vs/workbench/browser/labels'; import { CommentNode, CommentsModel, ResourceWithCommentThreads } from 'vs/workbench/parts/comments/common/commentModel'; @@ -59,6 +63,7 @@ interface ICommentThreadTemplateData { icon: HTMLImageElement; userName: HTMLSpanElement; commentText: HTMLElement; + disposables: Disposable[]; } export class CommentsModelRenderer implements ITreeRenderer { @@ -67,7 +72,8 @@ export class CommentsModelRenderer implements ITreeRenderer { constructor( - @IInstantiationService private instantiationService: IInstantiationService + @IInstantiationService private instantiationService: IInstantiationService, + @IOpenerService private openerService: IOpenerService ) { } @@ -99,6 +105,10 @@ export class CommentsModelRenderer implements ITreeRenderer { switch (templateId) { case CommentsModelRenderer.RESOURCE_ID: (templateData).resourceLabel.dispose(); + break; + case CommentsModelRenderer.COMMENT_ID: + (templateData).disposables.forEach(disposeable => disposeable.dispose()); + break; } } @@ -124,6 +134,7 @@ export class CommentsModelRenderer implements ITreeRenderer { const labelContainer = dom.append(container, dom.$('.comment-container')); data.userName = dom.append(labelContainer, dom.$('.user')); data.commentText = dom.append(labelContainer, dom.$('.text')); + data.disposables = []; return data; } @@ -134,7 +145,18 @@ export class CommentsModelRenderer implements ITreeRenderer { private renderCommentElement(tree: ITree, element: CommentNode, templateData: ICommentThreadTemplateData) { templateData.userName.textContent = element.comment.userName; - templateData.commentText.innerHTML = renderMarkdown(element.comment.body, { inline: true }).innerHTML; + templateData.commentText.innerHTML = ''; + const renderedComment = renderMarkdown(element.comment.body, { + inline: true, + actionHandler: { + callback: (content) => { + this.openerService.open(URI.parse(content)).then(void 0, onUnexpectedError); + }, + disposeables: templateData.disposables + } + }); + + templateData.commentText.appendChild(renderedComment); } } diff --git a/src/vs/workbench/parts/comments/electron-browser/media/panel.css b/src/vs/workbench/parts/comments/electron-browser/media/panel.css index 45268e182f1..3c2b37bcf3f 100644 --- a/src/vs/workbench/parts/comments/electron-browser/media/panel.css +++ b/src/vs/workbench/parts/comments/electron-browser/media/panel.css @@ -30,8 +30,15 @@ opacity: 0.5; } +.comments-panel .comments-panel-container .tree-container .comment-container .text { + flex: 1; + min-width: 0; +} + .comments-panel .comments-panel-container .tree-container .comment-container .text * { margin: 0; + text-overflow: ellipsis; + overflow: hidden; } .comments-panel .comments-panel-container .message-box-container { @@ -49,6 +56,5 @@ .comments-panel .comments-panel-container .tree-container .comment-container { line-height: 22px; - text-overflow: ellipsis; - overflow: hidden; + margin-right: 5px; } \ No newline at end of file diff --git a/src/vs/workbench/parts/comments/electron-browser/media/review.css b/src/vs/workbench/parts/comments/electron-browser/media/review.css index b39b972ab61..dd28fb338ef 100644 --- a/src/vs/workbench/parts/comments/electron-browser/media/review.css +++ b/src/vs/workbench/parts/comments/electron-browser/media/review.css @@ -11,10 +11,7 @@ } .monaco-editor .comment-hint{ - display: flex; - align-items: center; - justify-content: center; - height: 16px; + height: 20px; width: 20px; padding-left: 2px; background: url('comment.svg') center center no-repeat; @@ -38,6 +35,13 @@ display: flex; } +.monaco-editor .review-widget .body .review-comment blockquote { + margin: 0 7px 0 5px; + padding: 0 16px 0 10px; + border-left-width: 5px; + border-left-style: solid; +} + .monaco-editor .review-widget .body .review-comment .avatar-container { margin-top: 4px !important; } @@ -145,6 +149,7 @@ white-space: nowrap; border: 0px; cursor: text; + outline: 1px solid transparent; } .monaco-editor .review-widget .body .comment-form .review-thread-reply-button:focus { diff --git a/src/vs/workbench/parts/debug/browser/baseDebugView.ts b/src/vs/workbench/parts/debug/browser/baseDebugView.ts index e3cc797de24..54f3838448a 100644 --- a/src/vs/workbench/parts/debug/browser/baseDebugView.ts +++ b/src/vs/workbench/parts/debug/browser/baseDebugView.ts @@ -83,16 +83,16 @@ export function renderExpressionValue(expressionOrValue: IExpression | string, c } } - if (options.maxValueLength && value.length > options.maxValueLength) { + if (options.maxValueLength && value && value.length > options.maxValueLength) { value = value.substr(0, options.maxValueLength) + '...'; } if (value && !options.preserveWhitespace) { container.textContent = replaceWhitespace(value); } else { - container.textContent = value; + container.textContent = value || ''; } if (options.showHover) { - container.title = value; + container.title = value || ''; } } @@ -103,18 +103,15 @@ export function renderVariable(variable: Variable, data: IVariableTemplateData, dom.toggleClass(data.name, 'virtual', !!variable.presentationHint && variable.presentationHint.kind === 'virtual'); } - if (variable.value) { - data.name.textContent += (typeof variable.name === 'string') ? ':' : ''; - renderExpressionValue(variable, data.value, { - showChanged, - maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET, - preserveWhitespace: false, - showHover: true, - colorize: true - }); - } else { - data.value.textContent = ''; - data.value.title = ''; + renderExpressionValue(variable, data.value, { + showChanged, + maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET, + preserveWhitespace: false, + showHover: true, + colorize: true + }); + if (variable.value && typeof variable.name === 'string') { + data.name.textContent += ':'; } } diff --git a/src/vs/workbench/parts/debug/browser/breakpointsView.ts b/src/vs/workbench/parts/debug/browser/breakpointsView.ts index edf399c0e2a..2a869ab101f 100644 --- a/src/vs/workbench/parts/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/parts/debug/browser/breakpointsView.ts @@ -16,11 +16,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { Constants } from 'vs/editor/common/core/uint'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { getPathLabel } from 'vs/base/common/labels'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { basename } from 'vs/base/common/paths'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { TPromise } from 'vs/base/common/winjs.base'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IVirtualDelegate, IListContextMenuEvent, IRenderer } from 'vs/base/browser/ui/list/list'; @@ -36,9 +32,18 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; const $ = dom.$; +function createCheckbox(): HTMLInputElement { + const checkbox = $('input'); + checkbox.type = 'checkbox'; + checkbox.tabIndex = -1; + + return checkbox; +} + export class BreakpointsView extends ViewletPanel { private static readonly MAX_VISIBLE_FILES = 9; @@ -279,8 +284,7 @@ class BreakpointsRenderer implements IRenderer$('input'); - data.checkbox.type = 'checkbox'; + data.checkbox = createCheckbox(); data.toDispose = []; data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => { this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context); @@ -320,12 +323,12 @@ class BreakpointsRenderer implements IRenderer$('input'); - data.checkbox.type = 'checkbox'; + data.checkbox = createCheckbox(); data.toDispose = []; data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => { this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context); @@ -416,8 +418,7 @@ class FunctionBreakpointsRenderer implements IRenderer$('input'); - data.checkbox.type = 'checkbox'; + data.checkbox = createCheckbox(); data.toDispose = []; data.toDispose.push(dom.addStandardDisposableListener(data.checkbox, 'change', (e) => { this.debugService.enableOrDisableBreakpoints(!data.context.enabled, data.context); diff --git a/src/vs/workbench/parts/debug/browser/debugActionItems.ts b/src/vs/workbench/parts/debug/browser/debugActionItems.ts index 42d3ff235fd..8590c1bc66b 100644 --- a/src/vs/workbench/parts/debug/browser/debugActionItems.ts +++ b/src/vs/workbench/parts/debug/browser/debugActionItems.ts @@ -72,6 +72,7 @@ export class StartDebugActionItem implements IActionItem { dom.addClass(container, 'start-debug-action-item'); this.start = dom.append(container, $('.icon')); this.start.title = this.action.label; + this.start.setAttribute('role', 'button'); this.start.tabIndex = 0; this.toDispose.push(dom.addDisposableListener(this.start, dom.EventType.CLICK, () => { diff --git a/src/vs/workbench/parts/debug/browser/debugActions.ts b/src/vs/workbench/parts/debug/browser/debugActions.ts index 26931ec59c2..04fd4058642 100644 --- a/src/vs/workbench/parts/debug/browser/debugActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugActions.ts @@ -9,6 +9,7 @@ import * as lifecycle from 'vs/base/common/lifecycle'; import { TPromise } from 'vs/base/common/winjs.base'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ICommandService } from 'vs/platform/commands/common/commands'; +import * as aria from 'vs/base/browser/ui/aria/aria'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IFileService } from 'vs/platform/files/common/files'; import { IDebugService, State, ISession, IThread, IEnablement, IBreakpoint, IStackFrame, REPL_ID, SessionState } @@ -691,6 +692,7 @@ export class ClearReplAction extends AbstractDebugAction { public run(): TPromise { this.debugService.removeReplExpressions(); + aria.status(nls.localize('debugConsoleCleared', "Debug console was cleared")); // focus back to repl return this.panelService.openPanel(REPL_ID, true); diff --git a/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts b/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts index 545751a774d..19f6fb170af 100644 --- a/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts +++ b/src/vs/workbench/parts/debug/browser/debugActionsWidget.ts @@ -94,7 +94,7 @@ export class DebugActionsWidget extends Themable implements IWorkbenchContributi this.updateScheduler = this._register(new RunOnceScheduler(() => { const state = this.debugService.state; const toolBarLocation = this.configurationService.getValue('debug').toolBarLocation; - if (state === State.Inactive || toolBarLocation === 'docked' || toolBarLocation === 'hidden') { + if (state === State.Inactive || state === State.Initializing || toolBarLocation === 'docked' || toolBarLocation === 'hidden') { return this.hide(); } diff --git a/src/vs/workbench/parts/debug/browser/debugCommands.ts b/src/vs/workbench/parts/debug/browser/debugCommands.ts index bf06733430f..7c09e489c3a 100644 --- a/src/vs/workbench/parts/debug/browser/debugCommands.ts +++ b/src/vs/workbench/parts/debug/browser/debugCommands.ts @@ -8,10 +8,10 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { TPromise } from 'vs/base/common/winjs.base'; import { List } from 'vs/base/browser/ui/list/listWidget'; import * as errors from 'vs/base/common/errors'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IListService } from 'vs/platform/list/browser/listService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL, CONTEXT_EXPRESSION_SELECTED, CONTEXT_BREAKPOINT_SELECTED } from 'vs/workbench/parts/debug/common/debug'; +import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_EXPRESSION_SELECTED, CONTEXT_BREAKPOINT_SELECTED } from 'vs/workbench/parts/debug/common/debug'; import { Expression, Variable, Breakpoint, FunctionBreakpoint } from 'vs/workbench/parts/debug/common/debugModel'; import { IExtensionsViewlet, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/parts/extensions/common/extensions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -25,11 +25,14 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { InputFocusedContext } from 'vs/platform/workbench/common/contextkeys'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +export const ADD_CONFIGURATION_ID = 'debug.addConfiguration'; +export const TOGGLE_INLINE_BREAKPOINT_ID = 'editor.debug.action.toggleInlineBreakpoint'; + export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'debug.toggleBreakpoint', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(5), + weight: KeybindingWeight.WorkbenchContrib + 5, when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_FOCUSED, InputFocusedContext.toNegated()), primary: KeyCode.Space, handler: (accessor) => { @@ -47,7 +50,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'debug.enableOrDisableBreakpoint', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, primary: undefined, when: EditorContextKeys.editorTextFocus, handler: (accessor) => { @@ -69,7 +72,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'debug.renameWatchExpression', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(5), + weight: KeybindingWeight.WorkbenchContrib + 5, when: CONTEXT_WATCH_EXPRESSIONS_FOCUSED, primary: KeyCode.F2, mac: { primary: KeyCode.Enter }, @@ -90,7 +93,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'debug.setVariable', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(5), + weight: KeybindingWeight.WorkbenchContrib + 5, when: CONTEXT_VARIABLES_FOCUSED, primary: KeyCode.F2, mac: { primary: KeyCode.Enter }, @@ -111,7 +114,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'debug.removeWatchExpression', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_EXPRESSION_SELECTED.toNegated()), primary: KeyCode.Delete, mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace }, @@ -132,7 +135,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'debug.removeBreakpoint', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_BREAKPOINT_SELECTED.toNegated()), primary: KeyCode.Delete, mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace }, @@ -156,7 +159,7 @@ export function registerCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'debug.installAdditionalDebuggers', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: undefined, primary: undefined, handler: (accessor) => { @@ -171,8 +174,8 @@ export function registerCommands(): void { }); KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: 'debug.addConfiguration', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + id: ADD_CONFIGURATION_ID, + weight: KeybindingWeight.WorkbenchContrib, when: undefined, primary: undefined, handler: (accessor, launchUri: string) => { @@ -196,7 +199,6 @@ export function registerCommands(): void { } }); - const INLINE_BREAKPOINT_COMMAND_ID = 'editor.debug.action.toggleInlineBreakpoint'; const inlineBreakpointHandler = (accessor: ServicesAccessor) => { const debugService = accessor.get(IDebugService); const editorService = accessor.get(IEditorService); @@ -218,33 +220,33 @@ export function registerCommands(): void { return TPromise.as(null); }; KeybindingsRegistry.registerCommandAndKeybindingRule({ - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.Shift | KeyCode.F9, when: EditorContextKeys.editorTextFocus, - id: INLINE_BREAKPOINT_COMMAND_ID, + id: TOGGLE_INLINE_BREAKPOINT_ID, handler: inlineBreakpointHandler }); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { - id: INLINE_BREAKPOINT_COMMAND_ID, + id: TOGGLE_INLINE_BREAKPOINT_ID, title: nls.localize('inlineBreakpoint', "Inline Breakpoint"), category: nls.localize('debug', "Debug") } }); MenuRegistry.appendMenuItem(MenuId.EditorContext, { command: { - id: INLINE_BREAKPOINT_COMMAND_ID, + id: TOGGLE_INLINE_BREAKPOINT_ID, title: nls.localize('addInlineBreakpoint', "Add Inline Breakpoint") }, - when: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL, EditorContextKeys.writable), + when: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, EditorContextKeys.writable, EditorContextKeys.editorTextFocus), group: 'debug', order: 1 }); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'debug.openBreakpointToSide', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: CONTEXT_BREAKPOINTS_FOCUSED, primary: KeyMod.CtrlCmd | KeyCode.Enter, secondary: [KeyMod.Alt | KeyCode.Enter], diff --git a/src/vs/workbench/parts/debug/browser/debugEditorActions.ts b/src/vs/workbench/parts/debug/browser/debugEditorActions.ts index 4c871d8342b..d6332e2f2ca 100644 --- a/src/vs/workbench/parts/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugEditorActions.ts @@ -10,23 +10,26 @@ import { Range } from 'vs/editor/common/core/range'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ServicesAccessor, registerEditorAction, EditorAction, IActionOptions } from 'vs/editor/browser/editorExtensions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL, CONTEXT_DEBUG_STATE, State, REPL_ID, VIEWLET_ID, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint } from 'vs/workbench/parts/debug/common/debug'; +import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, REPL_ID, VIEWLET_ID, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint } from 'vs/workbench/parts/debug/common/debug'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { openBreakpointSource } from 'vs/workbench/parts/debug/browser/breakpointsView'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +export const TOGGLE_BREAKPOINT_ID = 'editor.debug.action.toggleBreakpoint'; class ToggleBreakpointAction extends EditorAction { constructor() { super({ - id: 'editor.debug.action.toggleBreakpoint', + id: TOGGLE_BREAKPOINT_ID, label: nls.localize('toggleBreakpointAction', "Debug: Toggle Breakpoint"), alias: 'Debug: Toggle Breakpoint', precondition: null, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyCode.F9 + primary: KeyCode.F9, + weight: KeybindingWeight.EditorContrib } }); } @@ -49,11 +52,12 @@ class ToggleBreakpointAction extends EditorAction { } } +export const TOGGLE_CONDITIONAL_BREAKPOINT_ID = 'editor.debug.action.conditionalBreakpoint'; class ConditionalBreakpointAction extends EditorAction { constructor() { super({ - id: 'editor.debug.action.conditionalBreakpoint', + id: TOGGLE_CONDITIONAL_BREAKPOINT_ID, label: nls.localize('conditionalBreakpointEditorAction', "Debug: Add Conditional Breakpoint..."), alias: 'Debug: Add Conditional Breakpoint...', precondition: null @@ -70,11 +74,12 @@ class ConditionalBreakpointAction extends EditorAction { } } +export const TOGGLE_LOG_POINT_ID = 'editor.debug.action.toggleLogPoint'; class LogPointAction extends EditorAction { constructor() { super({ - id: 'editor.debug.action.toggleLogPoint', + id: TOGGLE_LOG_POINT_ID, label: nls.localize('logPointEditorAction', "Debug: Add Logpoint..."), alias: 'Debug: Add Logpoint...', precondition: null @@ -98,7 +103,7 @@ class RunToCursorAction extends EditorAction { id: 'editor.debug.action.runToCursor', label: nls.localize('runToCursor', "Run to Cursor"), alias: 'Debug: Run to Cursor', - precondition: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL, EditorContextKeys.writable, CONTEXT_DEBUG_STATE.isEqualTo('stopped')), + precondition: ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, EditorContextKeys.writable, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), EditorContextKeys.editorTextFocus), menuOpts: { group: 'debug', order: 2 @@ -141,7 +146,7 @@ class SelectionToReplAction extends EditorAction { id: 'editor.debug.action.selectionToRepl', label: nls.localize('debugEvaluate', "Debug: Evaluate"), alias: 'Debug: Evaluate', - precondition: ContextKeyExpr.and(EditorContextKeys.hasNonEmptySelection, CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL), + precondition: ContextKeyExpr.and(EditorContextKeys.hasNonEmptySelection, CONTEXT_IN_DEBUG_MODE, EditorContextKeys.editorTextFocus), menuOpts: { group: 'debug', order: 0 @@ -167,7 +172,7 @@ class SelectionToWatchExpressionsAction extends EditorAction { id: 'editor.debug.action.selectionToWatch', label: nls.localize('debugAddToWatch', "Debug: Add to Watch"), alias: 'Debug: Add to Watch', - precondition: ContextKeyExpr.and(EditorContextKeys.hasNonEmptySelection, CONTEXT_IN_DEBUG_MODE, CONTEXT_NOT_IN_DEBUG_REPL), + precondition: ContextKeyExpr.and(EditorContextKeys.hasNonEmptySelection, CONTEXT_IN_DEBUG_MODE, EditorContextKeys.editorTextFocus), menuOpts: { group: 'debug', order: 1 @@ -194,7 +199,8 @@ class ShowDebugHoverAction extends EditorAction { precondition: CONTEXT_IN_DEBUG_MODE, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_I) + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_I), + weight: KeybindingWeight.EditorContrib } }); } diff --git a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts new file mode 100644 index 00000000000..d5f424be629 --- /dev/null +++ b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts @@ -0,0 +1,491 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { TreeViewsViewletPanel, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as dom from 'vs/base/browser/dom'; +import * as errors from 'vs/base/common/errors'; +import { normalize, isAbsolute, sep } from 'vs/base/common/paths'; +import { IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { WorkbenchTree, TreeResourceNavigator } from 'vs/platform/list/browser/listService'; +import { renderViewTree, twistiePixels } from 'vs/workbench/parts/debug/browser/baseDebugView'; +import { IAccessibilityProvider, ITree, IRenderer, IDataSource } from 'vs/base/parts/tree/browser/tree'; +import { ISession, IDebugService, IModel, CONTEXT_LOADED_SCRIPTS_ITEM_TYPE } from 'vs/workbench/parts/debug/common/debug'; +import { Source } from 'vs/workbench/parts/debug/common/debugSource'; +import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { tildify } from 'vs/base/common/labels'; +import { isWindows } from 'vs/base/common/platform'; +import URI from 'vs/base/common/uri'; +import { ltrim } from 'vs/base/common/strings'; + +const SMART = true; + +const $ = dom.$; + +const SESSION_TEMPLATE_ID = 'session'; +const SOURCE_TEMPLATE_ID = 'source'; +const ROOT_FOLDER_TEMPLATE_ID = 'node'; + +class BaseTreeItem { + + private _id: string; + private _children: { [key: string]: BaseTreeItem; }; + private _source: Source; + + constructor(private _parent: BaseTreeItem, private _label: string) { + this._id = this._parent ? `${this._parent._id}/${this._label}` : this._label; + this._children = {}; + } + + getLabel() { + const child = this.oneChild(); + if (child) { + const sep = this instanceof RootFolderTreeItem ? ' • ' : '/'; + return `${this._label}${sep}${child.getLabel()}`; + } + return this._label; + } + + getId(): string { + return this._id; + } + + getTemplateId(): string { + return SOURCE_TEMPLATE_ID; + } + + getChildren(): TPromise { + const child = this.oneChild(); + if (child) { + return child.getChildren(); + } + const array = Object.keys(this._children).map(key => this._children[key]); + return TPromise.as(array.sort((a, b) => this.compare(a, b))); + } + + hasChildren(): boolean { + const child = this.oneChild(); + if (child) { + return child.hasChildren(); + } + return Object.keys(this._children).length > 0; + } + + getSource() { + const child = this.oneChild(); + if (child) { + return child.getSource(); + } + return this._source; + } + + setSource(session: ISession, source: Source): void { + this._source = source; + } + + createIfNeeded(key: string, factory: (parent: BaseTreeItem, label: string) => T): T { + let child = this._children[key]; + if (!child) { + child = factory(this, key); + this._children[key] = child; + } + return child; + } + + remove(key: string): void { + delete this._children[key]; + } + + protected compare(a: BaseTreeItem, b: BaseTreeItem): number { + if (a._label && b._label) { + return a._label.localeCompare(b._label); + } + return 0; + } + + private oneChild(): BaseTreeItem { + if (SMART && !(this instanceof RootTreeItem)) { + const keys = Object.keys(this._children); + if (keys.length === 1) { + return this._children[keys[0]]; + } + } + return undefined; + } +} + +class RootFolderTreeItem extends BaseTreeItem { + + constructor(parent: BaseTreeItem, public folder: IWorkspaceFolder) { + super(parent, folder.name); + } + + getTemplateId(): string { + return ROOT_FOLDER_TEMPLATE_ID; + } +} + +class RootTreeItem extends BaseTreeItem { + + private _showedMoreThanOne: boolean; + + constructor(private _debugModel: IModel, private _environmentService: IEnvironmentService, private _contextService: IWorkspaceContextService) { + super(undefined, 'Root'); + this._showedMoreThanOne = false; + this._debugModel.getSessions().forEach(session => { + this.add(session); + }); + } + + hasChildren(): boolean { + return true; + } + + getChildren(): TPromise { + return super.getChildren().then(children => { + const size = children.length; + if (!this._showedMoreThanOne && size === 1) { + // skip session if there is only one + return children[0].getChildren(); + } + this._showedMoreThanOne = size > 1; + return children; + }); + } + + add(session: ISession): SessionTreeItem { + return this.createIfNeeded(session.getId(), () => new SessionTreeItem(this, session, this._environmentService, this._contextService)); + } +} + +class SessionTreeItem extends BaseTreeItem { + + private static URL_REGEXP = /^(https?:\/\/[^/]+)(\/.*)$/; + + private _session: ISession; + private _initialized: boolean; + + constructor(parent: BaseTreeItem, session: ISession, private _environmentService: IEnvironmentService, private rootProvider: IWorkspaceContextService) { + super(parent, session.getName(true)); + this._initialized = false; + this._session = session; + } + + getTemplateId(): string { + return SESSION_TEMPLATE_ID; + } + + hasChildren(): boolean { + return true; + } + + getChildren(): TPromise { + + if (!this._initialized) { + this._initialized = true; + return this._session.getLoadedSources().then(paths => { + paths.forEach(path => this.addPath(path)); + return super.getChildren(); + }); + } + + return super.getChildren(); + } + + protected compare(a: BaseTreeItem, b: BaseTreeItem): number { + const acat = this.category(a); + const bcat = this.category(b); + if (acat !== bcat) { + return acat - bcat; + } + return super.compare(a, b); + } + + /** + * Return an ordinal number for folders + */ + private category(item: BaseTreeItem): number { + + // workspace scripts come at the beginning in "folder" order + if (item instanceof RootFolderTreeItem) { + return item.folder.index; + } + + // <...> come at the very end + const l = item.getLabel(); + if (l && /^<.+>$/.test(l)) { + return 1000; + } + + // everything else in between + return 999; + } + + addPath(source: Source): void { + + let folder: IWorkspaceFolder; + let url: string; + + let path = source.raw.path; + + const match = SessionTreeItem.URL_REGEXP.exec(path); + if (match && match.length === 3) { + url = match[1]; + path = decodeURI(match[2]); + } else { + if (isAbsolute(path)) { + const resource = URI.file(path); + + // return early if we can resolve a relative path label from the root folder + folder = this.rootProvider ? this.rootProvider.getWorkspaceFolder(resource) : null; + if (folder) { + // strip off the root folder path + path = normalize(ltrim(resource.path.substr(folder.uri.path.length), sep), true); + const hasMultipleRoots = this.rootProvider.getWorkspace().folders.length > 1; + if (hasMultipleRoots) { + path = '/' + path; + } else { + // don't show root folder + folder = undefined; + } + } else { + // on unix try to tildify absolute paths + path = normalize(path, true); + if (!isWindows) { + path = tildify(path, this._environmentService.userHome); + } + } + } + } + + let x: BaseTreeItem = this; + path.split(/[\/\\]/).forEach((segment, i) => { + if (segment.length === 0) { // macOS or unix path + segment = '/'; + } + if (i === 0 && folder) { + x = x.createIfNeeded(folder.name, parent => new RootFolderTreeItem(parent, folder)); + } else if (i === 0 && url) { + x = x.createIfNeeded(url, parent => new BaseTreeItem(parent, url)); + } else { + x = x.createIfNeeded(segment, parent => new BaseTreeItem(parent, segment)); + } + }); + + x.setSource(this._session, source); + } +} + +export class LoadedScriptsView extends TreeViewsViewletPanel { + + private static readonly MEMENTO = 'loadedscriptsview.memento'; + + private treeContainer: HTMLElement; + private loadedScriptsItemType: IContextKey; + private settings: any; + + + constructor( + options: IViewletViewOptions, + @IContextMenuService contextMenuService: IContextMenuService, + @IKeybindingService keybindingService: IKeybindingService, + @IInstantiationService private instantiationService: IInstantiationService, + @IConfigurationService configurationService: IConfigurationService, + @IEditorService private editorService: IEditorService, + @IContextKeyService contextKeyService: IContextKeyService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IEnvironmentService private environmentService: IEnvironmentService, + @IDebugService private debugService: IDebugService + ) { + super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('loadedScriptsSection', "Loaded Scripts Section") }, keybindingService, contextMenuService, configurationService); + this.settings = options.viewletSettings; + this.loadedScriptsItemType = CONTEXT_LOADED_SCRIPTS_ITEM_TYPE.bindTo(contextKeyService); + } + + protected renderBody(container: HTMLElement): void { + dom.addClass(container, 'debug-loaded-scripts'); + + this.treeContainer = renderViewTree(container); + + this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, + { + dataSource: new LoadedScriptsDataSource(), + renderer: this.instantiationService.createInstance(LoadedScriptsRenderer), + accessibilityProvider: new LoadedSciptsAccessibilityProvider(), + }, + { + ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'loadedScriptsAriaLabel' }, "Debug Loaded Scripts"), + twistiePixels + } + ); + + const callstackNavigator = new TreeResourceNavigator(this.tree); + this.disposables.push(callstackNavigator); + this.disposables.push(callstackNavigator.openResource(e => { + + const element = e.element; + + if (element instanceof BaseTreeItem) { + const source = element.getSource(); + if (source && source.available) { + const nullRange = { startLineNumber: 0, startColumn: 0, endLineNumber: 0, endColumn: 0 }; + source.openInEditor(this.editorService, nullRange, e.editorOptions.preserveFocus, e.sideBySide, e.editorOptions.pinned).done(undefined, errors.onUnexpectedError); + } + } + })); + + this.disposables.push(this.tree.onDidChangeFocus(() => { + const focus = this.tree.getFocus(); + if (focus instanceof SessionTreeItem) { + this.loadedScriptsItemType.set('session'); + } else { + this.loadedScriptsItemType.reset(); + } + })); + + const root = new RootTreeItem(this.debugService.getModel(), this.environmentService, this.contextService); + this.tree.setInput(root); + + let timeout: number; + + this.disposables.push(this.debugService.onDidLoadedSource(event => { + const sessionRoot = root.add(event.session); + sessionRoot.addPath(event.source); + + clearTimeout(timeout); + timeout = setTimeout(() => { + this.tree.refresh(root, true); + }, 300); + })); + + this.disposables.push(this.debugService.onDidEndSession(session => { + root.remove(session.getId()); + this.tree.refresh(root, false); + })); + } + + layoutBody(size: number): void { + if (this.treeContainer) { + this.treeContainer.style.height = size + 'px'; + } + super.layoutBody(size); + } + + public shutdown(): void { + this.settings[LoadedScriptsView.MEMENTO] = !this.isExpanded(); + super.shutdown(); + } +} + +// A good example of data source, renderers, action providers and accessibilty providers can be found in the callStackView.ts + +class LoadedScriptsDataSource implements IDataSource { + + getId(tree: ITree, element: any): string { + return element.getId(); + } + + hasChildren(tree: ITree, element: any): boolean { + return element.hasChildren(); + } + + getChildren(tree: ITree, element: any): TPromise { + return element.getChildren(); + } + + getParent(tree: ITree, element: any): TPromise { + return TPromise.as(null); + } + + shouldAutoexpand?(tree: ITree, element: any): boolean { + return element instanceof RootTreeItem || element instanceof SessionTreeItem; + } +} + +interface ISessionTemplateData { + session: HTMLElement; +} + +interface ISourceTemplateData { + source: HTMLElement; +} + +interface INodeTemplateData { + node: HTMLElement; +} + +class LoadedScriptsRenderer implements IRenderer { + + getHeight(tree: ITree, element: any): number { + return 22; + } + + getTemplateId(tree: ITree, element: any): string { + return element.getTemplateId(); + } + + renderTemplate(tree: ITree, templateId: string, container: HTMLElement) { + + if (templateId === SESSION_TEMPLATE_ID) { + let data: ISessionTemplateData = Object.create(null); + data.session = dom.append(container, $('.session')); + return data; + } + + if (templateId === SOURCE_TEMPLATE_ID) { + let data: ISourceTemplateData = Object.create(null); + data.source = dom.append(container, $('.source')); + return data; + } + + let data: INodeTemplateData = Object.create(null); + data.node = dom.append(container, $('.node')); + return data; + } + + renderElement(tree: ITree, element: any, templateId: string, templateData: any): void { + if (templateId === SESSION_TEMPLATE_ID) { + this.renderSession(element, templateData); + } else if (templateId === SOURCE_TEMPLATE_ID) { + this.renderSource(element, templateData); + } else if (templateId === ROOT_FOLDER_TEMPLATE_ID) { + this.renderNode(element, templateData); + } + } + + disposeTemplate(tree: ITree, templateId: string, templateData: any): void { + // noop + } + + private renderSession(session: SessionTreeItem, data: ISessionTemplateData): void { + data.session.title = 'session'; + data.session.textContent = session.getLabel(); + } + + private renderSource(source: BaseTreeItem, data: ISourceTemplateData): void { + data.source.title = 'source'; + data.source.textContent = source.getLabel(); + } + + private renderNode(node: BaseTreeItem, data: INodeTemplateData): void { + data.node.title = 'node'; + data.node.textContent = node.getLabel(); + } +} + +class LoadedSciptsAccessibilityProvider implements IAccessibilityProvider { + + public getAriaLabel(tree: ITree, element: any): string { + return nls.localize('implement me', "implement me"); + } +} diff --git a/src/vs/workbench/parts/debug/browser/media/debugViewlet.css b/src/vs/workbench/parts/debug/browser/media/debugViewlet.css index ea156779fae..09cf7033e9a 100644 --- a/src/vs/workbench/parts/debug/browser/media/debugViewlet.css +++ b/src/vs/workbench/parts/debug/browser/media/debugViewlet.css @@ -372,6 +372,7 @@ .debug-viewlet .debug-breakpoints .breakpoint > .icon { width: 19px; height: 19px; + min-width: 19px; } .debug-viewlet .debug-breakpoints .breakpoint > .file-path { @@ -383,6 +384,11 @@ overflow: hidden; } +.debug-viewlet .debug-breakpoints .breakpoint .name { + overflow: hidden; + text-overflow: ellipsis +} + .debug-viewlet .debug-action.remove { background: url('remove.svg') center center no-repeat; } diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts index e0d59a40d6c..6af33b36e60 100644 --- a/src/vs/workbench/parts/debug/common/debug.ts +++ b/src/vs/workbench/parts/debug/common/debug.ts @@ -17,7 +17,7 @@ import { Position } from 'vs/editor/common/core/position'; import { ISuggestion } from 'vs/editor/common/modes'; import { Source } from 'vs/workbench/parts/debug/common/debugSource'; import { Range, IRange } from 'vs/editor/common/core/range'; -import { RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -31,15 +31,15 @@ export const VIEW_CONTAINER: ViewContainer = Registry.as('debugType', undefined); -export const CONTEXT_DEBUG_STATE = new RawContextKey('debugState', undefined); +export const CONTEXT_DEBUG_STATE = new RawContextKey('debugState', 'inactive'); export const CONTEXT_IN_DEBUG_MODE = new RawContextKey('inDebugMode', false); -export const CONTEXT_NOT_IN_DEBUG_MODE: ContextKeyExpr = CONTEXT_IN_DEBUG_MODE.toNegated(); +export const CONTEXT_NOT_IN_DEBUG_MODE = CONTEXT_IN_DEBUG_MODE.toNegated(); export const CONTEXT_IN_DEBUG_REPL = new RawContextKey('inDebugRepl', false); -export const CONTEXT_NOT_IN_DEBUG_REPL: ContextKeyExpr = CONTEXT_IN_DEBUG_REPL.toNegated(); export const CONTEXT_BREAKPOINT_WIDGET_VISIBLE = new RawContextKey('breakpointWidgetVisible', false); export const CONTEXT_IN_BREAKPOINT_WIDGET = new RawContextKey('inBreakpointWidget', false); export const CONTEXT_BREAKPOINTS_FOCUSED = new RawContextKey('breakpointsFocused', true); @@ -48,13 +48,15 @@ export const CONTEXT_VARIABLES_FOCUSED = new RawContextKey('variablesFo export const CONTEXT_EXPRESSION_SELECTED = new RawContextKey('expressionSelected', false); export const CONTEXT_BREAKPOINT_SELECTED = new RawContextKey('breakpointSelected', false); export const CONTEXT_CALLSTACK_ITEM_TYPE = new RawContextKey('callStackItemType', undefined); +export const CONTEXT_LOADED_SCRIPTS_SUPPORTED = new RawContextKey('loadedScriptsSupported', false); +export const CONTEXT_LOADED_SCRIPTS_ITEM_TYPE = new RawContextKey('loadedScriptsItemType', undefined); export const EDITOR_CONTRIBUTION_ID = 'editor.contrib.debug'; export const DEBUG_SCHEME = 'debug'; export const INTERNAL_CONSOLE_OPTIONS_SCHEMA = { enum: ['neverOpen', 'openOnSessionStart', 'openOnFirstSessionStart'], default: 'openOnFirstSessionStart', - description: nls.localize('internalConsoleOptions', "Controls behavior of the internal debug console.") + description: nls.localize('internalConsoleOptions', "Controls when the internal debug console should open.") }; // raw @@ -134,6 +136,8 @@ export interface IRawSession { completions(args: DebugProtocol.CompletionsArguments): TPromise; setVariable(args: DebugProtocol.SetVariableArguments): TPromise; source(args: DebugProtocol.SourceArguments): TPromise; + loadedSources(args: DebugProtocol.LoadedSourcesArguments): TPromise; + } export enum SessionState { @@ -150,6 +154,7 @@ export interface ISession extends ITreeElement { getThread(threadId: number): IThread; getAllThreads(): ReadonlyArray; getSource(raw: DebugProtocol.Source): Source; + getLoadedSources(): TPromise; completions(frameId: number, text: string, position: Position, overwriteBefore: number): TPromise; } @@ -571,6 +576,12 @@ export interface DebugEvent extends DebugProtocol.Event { sessionId?: string; } +export interface LoadedSourceEvent { + session: ISession; + reason: string; + source: Source; +} + export interface IDebugService { _serviceBrand: any; @@ -594,6 +605,11 @@ export interface IDebugService { */ onDidEndSession: Event; + /** + * Allows to register on loaded source events. + */ + onDidLoadedSource: Event; + /** * Allows to register on custom DAP events. */ diff --git a/src/vs/workbench/parts/debug/common/debugModel.ts b/src/vs/workbench/parts/debug/common/debugModel.ts index 6e8edd9f7cf..3a58268ec72 100644 --- a/src/vs/workbench/parts/debug/common/debugModel.ts +++ b/src/vs/workbench/parts/debug/common/debugModel.ts @@ -616,6 +616,14 @@ export class Session implements ISession { return result; } + public getLoadedSources(): TPromise { + return this.raw.loadedSources({}).then(response => { + return response.body.sources.map(src => this.getSource(src)); + }, error => { + return []; + }); + } + public getId(): string { return this.session.getId(); } diff --git a/src/vs/workbench/parts/debug/common/debugViewModel.ts b/src/vs/workbench/parts/debug/common/debugViewModel.ts index 7c945318b01..c5816ddb628 100644 --- a/src/vs/workbench/parts/debug/common/debugViewModel.ts +++ b/src/vs/workbench/parts/debug/common/debugViewModel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Emitter } from 'vs/base/common/event'; -import { CONTEXT_EXPRESSION_SELECTED, IViewModel, IStackFrame, ISession, IThread, IExpression, IFunctionBreakpoint, CONTEXT_BREAKPOINT_SELECTED } from 'vs/workbench/parts/debug/common/debug'; +import { CONTEXT_EXPRESSION_SELECTED, IViewModel, IStackFrame, ISession, IThread, IExpression, IFunctionBreakpoint, CONTEXT_BREAKPOINT_SELECTED, CONTEXT_LOADED_SCRIPTS_SUPPORTED } from 'vs/workbench/parts/debug/common/debug'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; export class ViewModel implements IViewModel { @@ -20,6 +20,7 @@ export class ViewModel implements IViewModel { private multiSessionView: boolean; private expressionSelectedContextKey: IContextKey; private breakpointSelectedContextKey: IContextKey; + private loadedScriptsSupportedContextKey: IContextKey; constructor(contextKeyService: IContextKeyService) { this._onDidFocusSession = new Emitter(); @@ -28,6 +29,7 @@ export class ViewModel implements IViewModel { this.multiSessionView = false; this.expressionSelectedContextKey = CONTEXT_EXPRESSION_SELECTED.bindTo(contextKeyService); this.breakpointSelectedContextKey = CONTEXT_BREAKPOINT_SELECTED.bindTo(contextKeyService); + this.loadedScriptsSupportedContextKey = CONTEXT_LOADED_SCRIPTS_SUPPORTED.bindTo(contextKeyService); } public getId(): string { @@ -66,6 +68,8 @@ export class ViewModel implements IViewModel { this._focusedThread = thread; this._focusedStackFrame = stackFrame; + this.loadedScriptsSupportedContextKey.set(session && session.raw.capabilities.supportsLoadedSourcesRequest); + if (shouldEmit) { this._onDidFocusStackFrame.fire({ stackFrame, explicit }); } diff --git a/src/vs/workbench/parts/debug/electron-browser/breakpointWidget.ts b/src/vs/workbench/parts/debug/electron-browser/breakpointWidget.ts index 5704e16d21e..d9bc400ab10 100644 --- a/src/vs/workbench/parts/debug/electron-browser/breakpointWidget.ts +++ b/src/vs/workbench/parts/debug/electron-browser/breakpointWidget.ts @@ -17,7 +17,6 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView import { IDebugService, IBreakpoint, BreakpointWidgetContext as Context, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, DEBUG_SCHEME, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, CONTEXT_IN_BREAKPOINT_WIDGET } from 'vs/workbench/parts/debug/common/debug'; import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { SimpleDebugEditor } from 'vs/workbench/parts/debug/electron-browser/simpleDebugEditor'; import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ServicesAccessor, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions'; @@ -33,6 +32,8 @@ import { transparent, editorForeground } from 'vs/platform/theme/common/colorReg import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IDecorationOptions } from 'vs/editor/common/editorCommon'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { getSimpleEditorOptions, getSimpleCodeEditorWidgetOptions } from 'vs/workbench/parts/codeEditor/electron-browser/simpleEditorOptions'; const $ = dom.$; const IPrivateBreakpointWidgetService = createDecorator('privateBreakopintWidgetService'); @@ -199,11 +200,11 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi const scopedInstatiationService = this.instantiationService.createChild(new ServiceCollection( [IContextKeyService, scopedContextKeyService], [IPrivateBreakpointWidgetService, this])); - const options = SimpleDebugEditor.getEditorOptions(); - const codeEditorWidgetOptions = SimpleDebugEditor.getCodeEditorWidgetOptions(); + const options = getSimpleEditorOptions(); + const codeEditorWidgetOptions = getSimpleCodeEditorWidgetOptions(); this.input = scopedInstatiationService.createInstance(CodeEditorWidget, container, options, codeEditorWidgetOptions); CONTEXT_IN_BREAKPOINT_WIDGET.bindTo(scopedContextKeyService).set(true); - const model = this.modelService.createModel('', null, uri.parse(`${DEBUG_SCHEME}:breakpointinput`), true); + const model = this.modelService.createModel('', null, uri.parse(`${DEBUG_SCHEME}:${this.editor.getId()}:breakpointinput`), true); this.input.setModel(model); this.toDispose.push(model); const setDecorations = () => { @@ -296,7 +297,8 @@ class AcceptBreakpointWidgetInputAction extends EditorCommand { precondition: CONTEXT_BREAKPOINT_WIDGET_VISIBLE, kbOpts: { kbExpr: CONTEXT_IN_BREAKPOINT_WIDGET, - primary: KeyCode.Enter + primary: KeyCode.Enter, + weight: KeybindingWeight.EditorContrib } }); } @@ -315,7 +317,8 @@ class CloseBreakpointWidgetCommand extends EditorCommand { kbOpts: { kbExpr: EditorContextKeys.textInputFocus, primary: KeyCode.Escape, - secondary: [KeyMod.Shift | KeyCode.Escape] + secondary: [KeyMod.Shift | KeyCode.Escape], + weight: KeybindingWeight.EditorContrib } }); } diff --git a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts index 3868462dbe7..40ddb7702e0 100644 --- a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts @@ -27,8 +27,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; -import { getPathLabel } from 'vs/base/common/labels'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; const $ = dom.$; @@ -399,7 +398,7 @@ class CallStackRenderer implements IRenderer { constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService, - @IEnvironmentService private environmentService: IEnvironmentService + @IUriDisplayService private uriDisplayService: IUriDisplayService ) { // noop } @@ -518,8 +517,7 @@ class CallStackRenderer implements IRenderer { dom.toggleClass(data.stackFrame, 'label', stackFrame.presentationHint === 'label'); dom.toggleClass(data.stackFrame, 'subtle', stackFrame.presentationHint === 'subtle'); - const path = stackFrame.source.raw.path || stackFrame.source.name; - data.file.title = getPathLabel(path, this.environmentService); + data.file.title = stackFrame.source.inMemory ? stackFrame.source.name : this.uriDisplayService.getLabel(stackFrame.source.uri); if (stackFrame.source.raw.origin) { data.file.title += `\n${stackFrame.source.raw.origin}`; } diff --git a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts index a0dd24849eb..c8faf5c0e38 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts @@ -10,7 +10,7 @@ import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { KeybindingsRegistry, IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight, IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionRegistryExtensions } from 'vs/workbench/common/actions'; import { ToggleViewletAction, Extensions as ViewletExtensions, ViewletRegistry, ViewletDescriptor } from 'vs/workbench/browser/viewlet'; @@ -23,21 +23,21 @@ import { CallStackView } from 'vs/workbench/parts/debug/electron-browser/callSta import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { IDebugService, VIEWLET_ID, REPL_ID, CONTEXT_NOT_IN_DEBUG_MODE, CONTEXT_IN_DEBUG_MODE, INTERNAL_CONSOLE_OPTIONS_SCHEMA, - CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, VIEW_CONTAINER + CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, VIEW_CONTAINER, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED } from 'vs/workbench/parts/debug/common/debug'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { DebugEditorModelManager } from 'vs/workbench/parts/debug/browser/debugEditorModelManager'; import { StepOverAction, ClearReplAction, FocusReplAction, StepIntoAction, StepOutAction, StartAction, RestartAction, ContinueAction, StopAction, DisconnectAction, PauseAction, AddFunctionBreakpointAction, - ConfigureAction, DisableAllBreakpointsAction, EnableAllBreakpointsAction, RemoveAllBreakpointsAction, RunAction, ReapplyBreakpointsAction, SelectAndStartAction, TerminateThreadAction + ConfigureAction, DisableAllBreakpointsAction, EnableAllBreakpointsAction, RemoveAllBreakpointsAction, RunAction, ReapplyBreakpointsAction, SelectAndStartAction, TerminateThreadAction, ToggleReplAction } from 'vs/workbench/parts/debug/browser/debugActions'; import { DebugActionsWidget } from 'vs/workbench/parts/debug/browser/debugActionsWidget'; import * as service from 'vs/workbench/parts/debug/electron-browser/debugService'; import { DebugContentProvider } from 'vs/workbench/parts/debug/browser/debugContentProvider'; import 'vs/workbench/parts/debug/electron-browser/debugEditorContribution'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { registerCommands } from 'vs/workbench/parts/debug/browser/debugCommands'; +import { registerCommands, ADD_CONFIGURATION_ID, TOGGLE_INLINE_BREAKPOINT_ID } from 'vs/workbench/parts/debug/browser/debugCommands'; import { IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; import { StatusBarColorProvider } from 'vs/workbench/parts/debug/browser/statusbarColorProvider'; import { ViewsRegistry } from 'vs/workbench/common/views'; @@ -51,6 +51,8 @@ import { DebugStatus } from 'vs/workbench/parts/debug/browser/debugStatus'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; +import { LoadedScriptsView } from 'vs/workbench/parts/debug/browser/loadedScriptsView'; +import { TOGGLE_LOG_POINT_ID, TOGGLE_CONDITIONAL_BREAKPOINT_ID, TOGGLE_BREAKPOINT_ID } from 'vs/workbench/parts/debug/browser/debugEditorActions'; class OpenDebugViewletAction extends ToggleViewletAction { public static readonly ID = VIEWLET_ID; @@ -111,6 +113,7 @@ Registry.as(PanelExtensions.Panels).setDefaultPanelId(REPL_ID); ViewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), ctor: VariablesView, order: 10, weight: 40, container: VIEW_CONTAINER, canToggleVisibility: true }]); ViewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), ctor: WatchExpressionsView, order: 20, weight: 10, container: VIEW_CONTAINER, canToggleVisibility: true }]); ViewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), ctor: CallStackView, order: 30, weight: 30, container: VIEW_CONTAINER, canToggleVisibility: true }]); +ViewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), ctor: LoadedScriptsView, order: 35, weight: 10, container: VIEW_CONTAINER, canToggleVisibility: true, when: CONTEXT_LOADED_SCRIPTS_SUPPORTED }]); ViewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), ctor: BreakpointsView, order: 40, weight: 20, container: VIEW_CONTAINER, canToggleVisibility: true }]); // register action to open viewlet @@ -127,7 +130,7 @@ const debugCategory = nls.localize('debugCategory', "Debug"); registry.registerWorkbenchAction(new SyncActionDescriptor( StartAction, StartAction.ID, StartAction.LABEL, { primary: KeyCode.F5 }, CONTEXT_NOT_IN_DEBUG_MODE), 'Debug: Start Debugging', debugCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(StepOverAction, StepOverAction.ID, StepOverAction.LABEL, { primary: KeyCode.F10 }, CONTEXT_IN_DEBUG_MODE), 'Debug: Step Over', debugCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(StepIntoAction, StepIntoAction.ID, StepIntoAction.LABEL, { primary: KeyCode.F11 }, CONTEXT_IN_DEBUG_MODE, KeybindingsRegistry.WEIGHT.workbenchContrib(1)), 'Debug: Step Into', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(StepIntoAction, StepIntoAction.ID, StepIntoAction.LABEL, { primary: KeyCode.F11 }, CONTEXT_IN_DEBUG_MODE, KeybindingWeight.WorkbenchContrib + 1), 'Debug: Step Into', debugCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(StepOutAction, StepOutAction.ID, StepOutAction.LABEL, { primary: KeyMod.Shift | KeyCode.F11 }, CONTEXT_IN_DEBUG_MODE), 'Debug: Step Out', debugCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(RestartAction, RestartAction.ID, RestartAction.LABEL, { primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.F5 }, CONTEXT_IN_DEBUG_MODE), 'Debug: Restart', debugCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(StopAction, StopAction.ID, StopAction.LABEL, { primary: KeyMod.Shift | KeyCode.F5 }, CONTEXT_IN_DEBUG_MODE), 'Debug: Stop', debugCategory); @@ -175,39 +178,39 @@ configurationRegistry.registerConfiguration({ properties: { 'debug.allowBreakpointsEverywhere': { type: 'boolean', - description: nls.localize({ comment: ['This is the description for a setting'], key: 'allowBreakpointsEverywhere' }, "Allows setting breakpoint in any file"), + description: nls.localize({ comment: ['This is the description for a setting'], key: 'allowBreakpointsEverywhere' }, "Allow setting breakpoints in any file."), default: false }, 'debug.openExplorerOnEnd': { type: 'boolean', - description: nls.localize({ comment: ['This is the description for a setting'], key: 'openExplorerOnEnd' }, "Automatically open explorer view on the end of a debug session"), + description: nls.localize({ comment: ['This is the description for a setting'], key: 'openExplorerOnEnd' }, "Automatically open the explorer view at the end of a debug session"), default: false }, 'debug.inlineValues': { type: 'boolean', - description: nls.localize({ comment: ['This is the description for a setting'], key: 'inlineValues' }, "Show variable values inline in editor while debugging"), + description: nls.localize({ comment: ['This is the description for a setting'], key: 'inlineValues' }, "Show variable values inline in editor while debugging."), default: false }, 'debug.toolBarLocation': { enum: ['floating', 'docked', 'hidden'], - description: nls.localize({ comment: ['This is the description for a setting'], key: 'toolBarLocation' }, "Controls the location of the debug toolbar. Either \"floating\" in all views, \"docked\" in the debug view, or \"hidden\""), + markdownDescription: nls.localize({ comment: ['This is the description for a setting'], key: 'toolBarLocation' }, "Controls the location of the debug toolbar. Either `floating` in all views, `docked` in the debug view, or `hidden`"), default: 'floating' }, 'debug.showInStatusBar': { enum: ['never', 'always', 'onFirstSessionStart'], enumDescriptions: [nls.localize('never', "Never show debug in status bar"), nls.localize('always', "Always show debug in status bar"), nls.localize('onFirstSessionStart', "Show debug in status bar only after debug was started for the first time")], - description: nls.localize({ comment: ['This is the description for a setting'], key: 'showInStatusBar' }, "Controls when the debug status bar should be visible"), + description: nls.localize({ comment: ['This is the description for a setting'], key: 'showInStatusBar' }, "Controls when the debug status bar should be visible."), default: 'onFirstSessionStart' }, 'debug.internalConsoleOptions': INTERNAL_CONSOLE_OPTIONS_SCHEMA, 'debug.openDebug': { enum: ['neverOpen', 'openOnSessionStart', 'openOnFirstSessionStart', 'openOnDebugBreak'], default: 'openOnFirstSessionStart', - description: nls.localize('openDebug', "Controls whether debug view should be open on debugging session start.") + description: nls.localize('openDebug', "Controls when the debug view should open.") }, 'debug.enableAllHovers': { type: 'boolean', - description: nls.localize({ comment: ['This is the description for a setting'], key: 'enableAllHovers' }, "Controls if the non debug hovers should be enabled while debugging. If true the hover providers will be called to provide a hover. Regular hovers will not be shown even if this setting is true."), + description: nls.localize({ comment: ['This is the description for a setting'], key: 'enableAllHovers' }, "Controls whether the non-debug hovers should be enabled while debugging. When enabled the hover providers will be called to provide a hover. Regular hovers will not be shown even if this setting is enabled."), default: false }, 'launch': { @@ -225,6 +228,217 @@ registerCommands(); const statusBar = Registry.as(StatusExtensions.Statusbar); statusBar.registerStatusbarItem(new StatusbarItemDescriptor(DebugStatus, StatusbarAlignment.LEFT, 30 /* Low Priority */)); +// View menu + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '3_views', + command: { + id: VIEWLET_ID, + title: nls.localize({ key: 'miViewDebug', comment: ['&& denotes a mnemonic'] }, "&&Debug") + }, + order: 4 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '4_panels', + command: { + id: ToggleReplAction.ID, + title: nls.localize({ key: 'miToggleDebugConsole', comment: ['&& denotes a mnemonic'] }, "De&&bug Console") + }, + order: 2 +}); + +// Debug menu + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '1_debug', + command: { + id: StartAction.ID, + title: nls.localize({ key: 'miStartDebugging', comment: ['&& denotes a mnemonic'] }, "&&Start Debugging") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '1_debug', + command: { + id: RunAction.ID, + title: nls.localize({ key: 'miStartWithoutDebugging', comment: ['&& denotes a mnemonic'] }, "Start &&Without Debugging") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '1_debug', + command: { + id: StopAction.ID, + title: nls.localize({ key: 'miStopDebugging', comment: ['&& denotes a mnemonic'] }, "&&Stop Debugging"), + precondition: CONTEXT_IN_DEBUG_MODE + }, + order: 3 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '1_debug', + command: { + id: RestartAction.ID, + title: nls.localize({ key: 'miRestart Debugging', comment: ['&& denotes a mnemonic'] }, "&&Restart Debugging"), + precondition: CONTEXT_IN_DEBUG_MODE + }, + order: 4 +}); + +// Configuration +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '2_configuration', + command: { + id: ConfigureAction.ID, + title: nls.localize({ key: 'miOpenConfigurations', comment: ['&& denotes a mnemonic'] }, "Open &&Configurations") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '2_configuration', + command: { + id: ADD_CONFIGURATION_ID, + title: nls.localize({ key: 'miAddConfiguration', comment: ['&& denotes a mnemonic'] }, "Add Configuration...") + }, + order: 2 +}); + +// Step Commands +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '3_step', + command: { + id: StepOverAction.ID, + title: nls.localize({ key: 'miStepOver', comment: ['&& denotes a mnemonic'] }, "Step &&Over"), + precondition: CONTEXT_DEBUG_STATE.isEqualTo('stopped') + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '3_step', + command: { + id: StepIntoAction.ID, + title: nls.localize({ key: 'miStepInto', comment: ['&& denotes a mnemonic'] }, "Step &&Into"), + precondition: CONTEXT_DEBUG_STATE.isEqualTo('stopped') + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '3_step', + command: { + id: StepOutAction.ID, + title: nls.localize({ key: 'miStepOut', comment: ['&& denotes a mnemonic'] }, "Step O&&ut"), + precondition: CONTEXT_DEBUG_STATE.isEqualTo('stopped') + }, + order: 3 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '3_step', + command: { + id: ContinueAction.ID, + title: nls.localize({ key: 'miContinue', comment: ['&& denotes a mnemonic'] }, "&&Continue"), + precondition: CONTEXT_DEBUG_STATE.isEqualTo('stopped') + }, + order: 4 +}); + +// New Breakpoints +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '4_new_breakpoint', + command: { + id: TOGGLE_BREAKPOINT_ID, + title: nls.localize({ key: 'miToggleBreakpoint', comment: ['&& denotes a mnemonic'] }, "Toggle &&Breakpoint") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarNewBreakpointMenu, { + group: '1_breakpoints', + command: { + id: TOGGLE_CONDITIONAL_BREAKPOINT_ID, + title: nls.localize({ key: 'miConditionalBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Conditional Breakpoint...") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarNewBreakpointMenu, { + group: '1_breakpoints', + command: { + id: TOGGLE_INLINE_BREAKPOINT_ID, + title: nls.localize({ key: 'miInlineBreakpoint', comment: ['&& denotes a mnemonic'] }, "Inline Breakp&&oint") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarNewBreakpointMenu, { + group: '1_breakpoints', + command: { + id: AddFunctionBreakpointAction.ID, + title: nls.localize({ key: 'miFunctionBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&Function Breakpoint...") + }, + order: 3 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarNewBreakpointMenu, { + group: '1_breakpoints', + command: { + id: TOGGLE_LOG_POINT_ID, + title: nls.localize({ key: 'miLogPoint', comment: ['&& denotes a mnemonic'] }, "&&Logpoint...") + }, + order: 4 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '4_new_breakpoint', + title: nls.localize({ key: 'miNewBreakpoint', comment: ['&& denotes a mnemonic'] }, "&&New Breakpoint"), + submenu: MenuId.MenubarNewBreakpointMenu, + order: 2 +}); + +// Modify Breakpoints +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '5_breakpoints', + command: { + id: EnableAllBreakpointsAction.ID, + title: nls.localize({ key: 'miEnableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Enable All Breakpoints") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '5_breakpoints', + command: { + id: DisableAllBreakpointsAction.ID, + title: nls.localize({ key: 'miDisableAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Disable A&&ll Breakpoints") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: '5_breakpoints', + command: { + id: RemoveAllBreakpointsAction.ID, + title: nls.localize({ key: 'miRemoveAllBreakpoints', comment: ['&& denotes a mnemonic'] }, "Remove &&All Breakpoints") + }, + order: 3 +}); + +// Install Debuggers +MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, { + group: 'z_install', + command: { + id: 'debug.installAdditionalDebuggers', + title: nls.localize({ key: 'miInstallAdditionalDebuggers', comment: ['&& denotes a mnemonic'] }, "&&Install Additional Debuggers...") + }, + order: 1 +}); + // Touch Bar if (isMacintosh) { diff --git a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts index 3daf1bd37ca..f847d1a6830 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts @@ -10,7 +10,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as strings from 'vs/base/common/strings'; import * as objects from 'vs/base/common/objects'; import uri from 'vs/base/common/uri'; -import * as paths from 'vs/base/common/paths'; +import * as resources from 'vs/base/common/resources'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { ITextModel } from 'vs/editor/common/model'; import { IEditor } from 'vs/workbench/common/editor'; @@ -390,7 +390,7 @@ class Launch implements ILaunch { } public get uri(): uri { - return this.workspace.uri.with({ path: paths.join(this.workspace.uri.path, '/.vscode/launch.json') }); + return resources.joinPath(this.workspace.uri, '/.vscode/launch.json'); } public get name(): string { @@ -442,14 +442,12 @@ class Launch implements ILaunch { } public openConfigFile(sideBySide: boolean, type?: string): TPromise<{ editor: IEditor, created: boolean }> { - return this.configurationManager.activateDebuggers().then(() => { - const resource = this.uri; - let created = false; - - return this.fileService.resolveContent(resource).then(content => content.value, err => { - - // launch.json not found: create one by collecting launch configs from debugConfigProviders + const resource = this.uri; + let created = false; + return this.fileService.resolveContent(resource).then(content => content.value, err => { + // launch.json not found: create one by collecting launch configs from debugConfigProviders + return this.configurationManager.activateDebuggers().then(() => { return this.configurationManager.guessDebugger(type).then(adapter => { if (adapter) { return this.configurationManager.provideDebugConfigurations(this.workspace.uri, adapter.type).then(initialConfigs => { @@ -470,30 +468,30 @@ class Launch implements ILaunch { return content; }); }); - }).then(content => { - if (!content) { - return { editor: undefined, created: false }; - } - const index = content.indexOf(`"${this.configurationManager.selectedConfiguration.name}"`); - let startLineNumber = 1; - for (let i = 0; i < index; i++) { - if (content.charAt(i) === '\n') { - startLineNumber++; - } - } - const selection = startLineNumber > 1 ? { startLineNumber, startColumn: 4 } : undefined; - - return this.editorService.openEditor({ - resource: resource, - options: { - selection, - pinned: created, - revealIfVisible: true - }, - }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => ({ editor, created })); - }, (error) => { - throw new Error(nls.localize('DebugConfig.failed', "Unable to create 'launch.json' file inside the '.vscode' folder ({0}).", error)); }); + }).then(content => { + if (!content) { + return { editor: undefined, created: false }; + } + const index = content.indexOf(`"${this.configurationManager.selectedConfiguration.name}"`); + let startLineNumber = 1; + for (let i = 0; i < index; i++) { + if (content.charAt(i) === '\n') { + startLineNumber++; + } + } + const selection = startLineNumber > 1 ? { startLineNumber, startColumn: 4 } : undefined; + + return this.editorService.openEditor({ + resource, + options: { + selection, + pinned: created, + revealIfVisible: true + }, + }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP).then(editor => ({ editor, created })); + }, (error) => { + throw new Error(nls.localize('DebugConfig.failed', "Unable to create 'launch.json' file inside the '.vscode' folder ({0}).", error)); }); } } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index bc11ac3f751..a5e2dcee7a0 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -69,6 +69,7 @@ export class DebugService implements debug.IDebugService { private readonly _onDidChangeState: Emitter; private readonly _onDidNewSession: Emitter; private readonly _onDidEndSession: Emitter; + private readonly _onDidLoadedSource: Emitter; private readonly _onDidCustomEvent: Emitter; private model: Model; private viewModel: ViewModel; @@ -76,9 +77,9 @@ export class DebugService implements debug.IDebugService { private configurationManager: ConfigurationManager; private toDispose: lifecycle.IDisposable[]; private toDisposeOnSessionEnd: Map; - private inDebugMode: IContextKey; private debugType: IContextKey; private debugState: IContextKey; + private inDebugMode: IContextKey; private breakpointsToSendOnResourceSaved: Set; private firstSessionStart: boolean; private skipRunningTask: boolean; @@ -113,6 +114,7 @@ export class DebugService implements debug.IDebugService { this._onDidChangeState = new Emitter(); this._onDidNewSession = new Emitter(); this._onDidEndSession = new Emitter(); + this._onDidLoadedSource = new Emitter(); this._onDidCustomEvent = new Emitter(); this.sessionStates = new Map(); this.allSessions = new Map(); @@ -120,9 +122,9 @@ export class DebugService implements debug.IDebugService { this.configurationManager = this.instantiationService.createInstance(ConfigurationManager); this.toDispose.push(this.configurationManager); - this.inDebugMode = debug.CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService); this.debugType = debug.CONTEXT_DEBUG_TYPE.bindTo(contextKeyService); this.debugState = debug.CONTEXT_DEBUG_STATE.bindTo(contextKeyService); + this.inDebugMode = debug.CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService); this.model = new Model(this.loadBreakpoints(), this.storageService.getBoolean(DEBUG_BREAKPOINTS_ACTIVATED_KEY, StorageScope.WORKSPACE, true), this.loadFunctionBreakpoints(), this.loadExceptionBreakpoints(), this.loadWatchExpressions()); @@ -168,7 +170,7 @@ export class DebugService implements debug.IDebugService { }); } else { const root = raw.root; - raw.dispose(); + raw.disconnect().done(undefined, errors.onUnexpectedError); this.doCreateSession(root, { resolved: session.configuration, unresolved: session.unresolvedConfiguration }, session.getId()); } @@ -293,7 +295,7 @@ export class DebugService implements debug.IDebugService { return raw.configurationDone().done(null, e => { // Disconnect the debug session on configuration done error #10596 if (raw) { - raw.dispose(); + raw.disconnect().done(undefined, errors.onUnexpectedError); } this.notificationService.error(e.message); }); @@ -343,7 +345,7 @@ export class DebugService implements debug.IDebugService { if (event.body && event.body.restart && session) { this.restartSession(session, event.body.restart).done(null, err => this.notificationService.error(err.message)); } else { - raw.dispose(); + raw.disconnect().done(undefined, errors.onUnexpectedError); } } })); @@ -450,6 +452,14 @@ export class DebugService implements debug.IDebugService { } })); + this.toDisposeOnSessionEnd.get(session.getId()).push(raw.onDidLoadedSource(event => { + this._onDidLoadedSource.fire({ + session: session, + reason: event.body.reason, + source: session.getSource(event.body.source) + }); + })); + this.toDisposeOnSessionEnd.get(session.getId()).push(raw.onDidCustomEvent(event => { this._onDidCustomEvent.fire(event); })); @@ -542,6 +552,10 @@ export class DebugService implements debug.IDebugService { return this._onDidEndSession.event; } + public get onDidLoadedSource(): Event { + return this._onDidLoadedSource.event; + } + public get onDidCustomEvent(): Event { return this._onDidCustomEvent.event; } @@ -560,6 +574,7 @@ export class DebugService implements debug.IDebugService { const stateLabel = debug.State[state]; if (stateLabel) { this.debugState.set(stateLabel.toLowerCase()); + this.inDebugMode.set(state !== debug.State.Inactive); } this.previousState = state; this._onDidChangeState.fire(state); @@ -909,7 +924,6 @@ export class DebugService implements debug.IDebugService { const resolved = configuration.resolved; resolved.__sessionId = sessionId; - this.inDebugMode.set(true); const dbg = this.configurationManager.getDebugger(resolved.type); return this.initializeRawSession(root, configuration, sessionId).then(session => { @@ -976,7 +990,7 @@ export class DebugService implements debug.IDebugService { this.telemetryService.publicLog('debugMisconfiguration', { type: resolved ? resolved.type : undefined, error: errorMessage }); this.updateStateAndEmit(raw.getId(), debug.State.Inactive); if (!raw.disconnected) { - raw.dispose(); + raw.disconnect(); } else if (session) { this.model.removeSession(session.getId()); } @@ -985,9 +999,6 @@ export class DebugService implements debug.IDebugService { if (this.model.getReplElements().length > 0) { this.panelService.openPanel(debug.REPL_ID, false).done(undefined, errors.onUnexpectedError); } - if (this.model.getSessions().length === 0) { - this.inDebugMode.reset(); - } this.showError(errorMessage, errors.isErrorWithActions(error) ? error.actions : []); return undefined; @@ -1108,7 +1119,8 @@ export class DebugService implements debug.IDebugService { const unresolvedConfiguration = (session).unresolvedConfiguration; if (session.raw.capabilities.supportsRestartRequest) { return this.runTask(session.getId(), session.raw.root, session.configuration.postDebugTask, session.configuration, unresolvedConfiguration, - () => session.raw.custom('restart', null)); + () => this.runTask(session.getId(), session.raw.root, session.configuration.preLaunchTask, session.configuration, unresolvedConfiguration, + () => session.raw.custom('restart', null))); } const focusedSession = this.viewModel.focusedSession; @@ -1116,7 +1128,8 @@ export class DebugService implements debug.IDebugService { // Do not run preLaunch and postDebug tasks for automatic restarts this.skipRunningTask = !!restartData; - return session.raw.terminate(true).then(() => { + // If the restart is automatic disconnect, otherwise send the terminate signal #55064 + return (!!restartData ? (session.raw).disconnect(true) : session.raw.terminate(true)).then(() => { if (strings.equalsIgnoreCase(session.configuration.type, 'extensionHost') && session.raw.root) { return this.broadcastService.broadcast({ channel: EXTENSION_RELOAD_BROADCAST_CHANNEL, @@ -1209,7 +1222,6 @@ export class DebugService implements debug.IDebugService { this.updateStateAndEmit(raw.getId(), debug.State.Inactive); if (this.model.getSessions().length === 0) { - this.inDebugMode.reset(); this.debugType.reset(); this.viewModel.setMultiSessionView(false); diff --git a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts index a2bf801d4cc..5e9785e36c8 100644 --- a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts +++ b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts @@ -58,6 +58,7 @@ export class RawDebugSession implements IRawSession { private readonly _onDidThread: Emitter; private readonly _onDidOutput: Emitter; private readonly _onDidBreakpoint: Emitter; + private readonly _onDidLoadedSource: Emitter; private readonly _onDidCustomEvent: Emitter; private readonly _onDidEvent: Emitter; @@ -85,6 +86,7 @@ export class RawDebugSession implements IRawSession { this._onDidThread = new Emitter(); this._onDidOutput = new Emitter(); this._onDidBreakpoint = new Emitter(); + this._onDidLoadedSource = new Emitter(); this._onDidCustomEvent = new Emitter(); this._onDidEvent = new Emitter(); } @@ -129,6 +131,10 @@ export class RawDebugSession implements IRawSession { return this._onDidBreakpoint.event; } + public get onDidLoadedSource(): Event { + return this._onDidLoadedSource.event; + } + public get onDidCustomEvent(): Event { return this._onDidCustomEvent.event; } @@ -234,7 +240,9 @@ export class RawDebugSession implements IRawSession { private onDapEvent(event: DebugEvent): void { event.sessionId = this.id; - if (event.event === 'initialized') { + if (event.event === 'loadedSource') { // most frequent comes first + this._onDidLoadedSource.fire(event); + } else if (event.event === 'initialized') { this.readyForBreakpoints = true; this._onDidInitialize.fire(event); } else if (event.event === 'capabilities' && event.body) { @@ -348,8 +356,7 @@ export class RawDebugSession implements IRawSession { return this.send('terminate', { restart }); } - this.dispose(restart); - return TPromise.as(null); + return this.disconnect(restart); } public setBreakpoints(args: DebugProtocol.SetBreakpointsArguments): TPromise { @@ -388,6 +395,11 @@ export class RawDebugSession implements IRawSession { return this.send('source', args); } + public loadedSources(args: DebugProtocol.LoadedSourcesArguments): TPromise { + return this.send('loadedSources', args); + } + + public threads(): TPromise { return this.send('threads', null); } @@ -475,24 +487,25 @@ export class RawDebugSession implements IRawSession { }); } - public dispose(restart = false): void { + public disconnect(restart = false): TPromise { if (this.disconnected) { - this.stopServer().done(undefined, errors.onUnexpectedError); - } else { - - // Cancel all sent promises on disconnect so debug trees are not left in a broken state #3666. - // Give a 1s timeout to give a chance for some promises to complete. - setTimeout(() => { - this.sentPromises.forEach(p => p && p.cancel()); - this.sentPromises = []; - }, 1000); - - if (this.debugAdapter && !this.disconnected) { - // point of no return: from now on don't report any errors - this.disconnected = true; - this.send('disconnect', { restart }, false).then(() => this.stopServer(), () => this.stopServer()).done(undefined, errors.onUnexpectedError); - } + return this.stopServer(); } + + // Cancel all sent promises on disconnect so debug trees are not left in a broken state #3666. + // Give a 1s timeout to give a chance for some promises to complete. + setTimeout(() => { + this.sentPromises.forEach(p => p && p.cancel()); + this.sentPromises = []; + }, 1000); + + if (this.debugAdapter && !this.disconnected) { + // point of no return: from now on don't report any errors + this.disconnected = true; + return this.send('disconnect', { restart }, false).then(() => this.stopServer(), () => this.stopServer()); + } + + return TPromise.as(null); } private stopServer(): TPromise { diff --git a/src/vs/workbench/parts/debug/electron-browser/repl.ts b/src/vs/workbench/parts/debug/electron-browser/repl.ts index 35b974a511b..453e622d41e 100644 --- a/src/vs/workbench/parts/debug/electron-browser/repl.ts +++ b/src/vs/workbench/parts/debug/electron-browser/repl.ts @@ -11,6 +11,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as errors from 'vs/base/common/errors'; import { IAction } from 'vs/base/common/actions'; import * as dom from 'vs/base/browser/dom'; +import * as aria from 'vs/base/browser/ui/aria/aria'; import { isMacintosh } from 'vs/base/common/platform'; import { CancellationToken } from 'vs/base/common/cancellation'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -29,7 +30,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ReplExpressionsRenderer, ReplExpressionsController, ReplExpressionsDataSource, ReplExpressionsActionProvider, ReplExpressionsAccessibilityProvider } from 'vs/workbench/parts/debug/electron-browser/replViewer'; -import { SimpleDebugEditor } from 'vs/workbench/parts/debug/electron-browser/simpleDebugEditor'; import { ClearReplAction } from 'vs/workbench/parts/debug/browser/debugActions'; import { Panel } from 'vs/workbench/browser/panel'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; @@ -46,6 +46,8 @@ import { IDebugService, REPL_ID, DEBUG_SCHEME, CONTEXT_IN_DEBUG_REPL } from 'vs/ import { HistoryNavigator } from 'vs/base/common/history'; import { IHistoryNavigationWidget } from 'vs/base/browser/history'; import { createAndBindHistoryNavigationWidgetScopedContextKeyService } from 'vs/platform/widget/browser/contextScopedHistoryWidget'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { getSimpleEditorOptions, getSimpleCodeEditorWidgetOptions } from 'vs/workbench/parts/codeEditor/electron-browser/simpleEditorOptions'; const $ = dom.$; @@ -171,7 +173,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati const scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection( [IContextKeyService, scopedContextKeyService], [IPrivateReplService, this])); - this.replInput = scopedInstantiationService.createInstance(CodeEditorWidget, this.replInputContainer, SimpleDebugEditor.getEditorOptions(), SimpleDebugEditor.getCodeEditorWidgetOptions()); + this.replInput = scopedInstantiationService.createInstance(CodeEditorWidget, this.replInputContainer, getSimpleEditorOptions(), getSimpleCodeEditorWidgetOptions()); modes.SuggestRegistry.register({ scheme: DEBUG_SCHEME, pattern: '**/replinput', hasAccessToAllModels: true }, { triggerCharacters: ['.'], @@ -210,6 +212,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati const historyInput = previous ? this.history.previous() : this.history.next(); if (historyInput) { this.replInput.setValue(historyInput); + aria.status(historyInput); // always leave cursor at the end. this.replInput.setPosition({ lineNumber: 1, column: historyInput.length + 1 }); this.historyNavigationEnablement.set(true); @@ -313,7 +316,8 @@ class AcceptReplInputAction extends EditorAction { precondition: CONTEXT_IN_DEBUG_REPL, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, - primary: KeyCode.Enter + primary: KeyCode.Enter, + weight: KeybindingWeight.EditorContrib } }); } diff --git a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts index c76295bd8f7..7ed51ea4b99 100644 --- a/src/vs/workbench/parts/debug/electron-browser/replViewer.ts +++ b/src/vs/workbench/parts/debug/electron-browser/replViewer.ts @@ -24,8 +24,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { LinkDetector } from 'vs/workbench/parts/debug/browser/linkDetector'; import { handleANSIOutput } from 'vs/workbench/parts/debug/browser/debugANSIHandling'; -import { getPathLabel } from 'vs/base/common/labels'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; const $ = dom.$; @@ -98,7 +97,7 @@ export class ReplExpressionsRenderer implements IRenderer { constructor( @IEditorService private editorService: IEditorService, @IInstantiationService private instantiationService: IInstantiationService, - @IEnvironmentService private environmentService: IEnvironmentService + @IUriDisplayService private uriDisplayService: IUriDisplayService ) { this.linkDetector = this.instantiationService.createInstance(LinkDetector); } @@ -261,7 +260,7 @@ export class ReplExpressionsRenderer implements IRenderer { dom.addClass(templateData.value, (element.severity === severity.Warning) ? 'warn' : (element.severity === severity.Error) ? 'error' : (element.severity === severity.Ignore) ? 'ignore' : 'info'); templateData.source.textContent = element.sourceData ? `${element.sourceData.source.name}:${element.sourceData.lineNumber}` : ''; - templateData.source.title = element.sourceData ? getPathLabel(element.sourceData.source.uri, this.environmentService) : ''; + templateData.source.title = element.sourceData ? this.uriDisplayService.getLabel(element.sourceData.source.uri) : ''; templateData.getReplElementSource = () => element.sourceData; } diff --git a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts index 5a517047652..0963de50db7 100644 --- a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts @@ -177,7 +177,7 @@ class WatchExpressionsActionProvider implements IActionProvider { if (element instanceof Variable) { const variable = element; if (!variable.hasChildren) { - actions.push(new CopyValueAction(CopyValueAction.ID, CopyValueAction.LABEL, variable.value, this.debugService)); + actions.push(new CopyValueAction(CopyValueAction.ID, CopyValueAction.LABEL, variable, this.debugService)); } actions.push(new Separator()); } @@ -300,16 +300,17 @@ class WatchExpressionsRenderer implements IRenderer { } data.name.textContent = watchExpression.name; - if (watchExpression.value) { + renderExpressionValue(watchExpression, data.value, { + showChanged: true, + maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET, + preserveWhitespace: false, + showHover: true, + colorize: true + }); + data.name.title = watchExpression.type ? watchExpression.type : watchExpression.value; + + if (typeof watchExpression.value === 'string') { data.name.textContent += ':'; - renderExpressionValue(watchExpression, data.value, { - showChanged: true, - maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET, - preserveWhitespace: false, - showHover: true, - colorize: true - }); - data.name.title = watchExpression.type ? watchExpression.type : watchExpression.value; } } diff --git a/src/vs/workbench/parts/debug/node/debugger.ts b/src/vs/workbench/parts/debug/node/debugger.ts index cde530adaef..a341ff73572 100644 --- a/src/vs/workbench/parts/debug/node/debugger.ts +++ b/src/vs/workbench/parts/debug/node/debugger.ts @@ -19,10 +19,10 @@ import { IOutputService } from 'vs/workbench/parts/output/common/output'; import { DebugAdapter, SocketDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; -import uri from 'vs/base/common/uri'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { memoize } from 'vs/base/common/decorators'; import { TaskDefinitionRegistry } from 'vs/workbench/parts/tasks/common/taskDefinitionRegistry'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; export class Debugger { @@ -173,7 +173,7 @@ export class Debugger { return telemetryInfo; }).then(data => { const client = new TelemetryClient( - uri.parse(require.toUrl('bootstrap')).fsPath, + getPathFromAmdModule(require, 'bootstrap'), { serverName: 'Debug Telemetry', timeout: 1000 * 60 * 5, diff --git a/src/vs/workbench/parts/debug/node/terminals.ts b/src/vs/workbench/parts/debug/node/terminals.ts index a46ea642248..00ff6960cb4 100644 --- a/src/vs/workbench/parts/debug/node/terminals.ts +++ b/src/vs/workbench/parts/debug/node/terminals.ts @@ -11,8 +11,8 @@ import * as env from 'vs/base/common/platform'; import * as pfs from 'vs/base/node/pfs'; import { assign } from 'vs/base/common/objects'; import { TPromise } from 'vs/base/common/winjs.base'; -import uri from 'vs/base/common/uri'; import { ITerminalLauncher, ITerminalSettings } from 'vs/workbench/parts/debug/common/debug'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; const TERMINAL_TITLE = nls.localize('console.title', "VS Code Console"); @@ -132,7 +132,7 @@ class MacTerminalService extends TerminalLauncher { // and then launches the program inside that window. const script = terminalApp === MacTerminalService.DEFAULT_TERMINAL_OSX ? 'TerminalHelper' : 'iTermHelper'; - const scriptpath = uri.parse(require.toUrl(`vs/workbench/parts/execution/electron-browser/${script}.scpt`)).fsPath; + const scriptpath = getPathFromAmdModule(require, `vs/workbench/parts/execution/electron-browser/${script}.scpt`); const osaArgs = [ scriptpath, @@ -415,4 +415,4 @@ export function prepareCommand(args: DebugProtocol.RunInTerminalRequestArguments } return command; -} \ No newline at end of file +} diff --git a/src/vs/workbench/parts/debug/test/common/mockDebug.ts b/src/vs/workbench/parts/debug/test/common/mockDebug.ts index ceb4cc25a49..f92a322a3a0 100644 --- a/src/vs/workbench/parts/debug/test/common/mockDebug.ts +++ b/src/vs/workbench/parts/debug/test/common/mockDebug.ts @@ -7,7 +7,7 @@ import uri from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; import { TPromise } from 'vs/base/common/winjs.base'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { ILaunch, IDebugService, State, DebugEvent, ISession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IModel, IViewModel, IRawSession, IBreakpoint } from 'vs/workbench/parts/debug/common/debug'; +import { ILaunch, IDebugService, State, DebugEvent, ISession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IModel, IViewModel, IRawSession, IBreakpoint, LoadedSourceEvent } from 'vs/workbench/parts/debug/common/debug'; export class MockDebugService implements IDebugService { public _serviceBrand: any; @@ -32,6 +32,10 @@ export class MockDebugService implements IDebugService { return null; } + public get onDidLoadedSource(): Event { + return null; + } + public getConfigurationManager(): IConfigurationManager { return null; } @@ -218,7 +222,7 @@ export class MockSession implements IRawSession { return TPromise.as(null); } - public terminateThreads(args: DebugProtocol.TerminateThreadsArguments): TPromise { + public terminateThreads(args: DebugProtocol.TerminateThreadsArguments): TPromise { return TPromise.as(null); } @@ -242,6 +246,10 @@ export class MockSession implements IRawSession { return TPromise.as(null); } + public loadedSources(args: DebugProtocol.LoadedSourcesArguments): TPromise { + return TPromise.as(null); + } + public setBreakpoints(args: DebugProtocol.SetBreakpointsArguments): TPromise { return TPromise.as(null); } diff --git a/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.ts b/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.ts index ea18fc77506..c144579d4aa 100644 --- a/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.ts +++ b/src/vs/workbench/parts/emmet/browser/actions/showEmmetCommands.ts @@ -12,6 +12,7 @@ import { registerEditorAction, EditorAction, ServicesAccessor } from 'vs/editor/ import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { MenuId } from 'vs/platform/actions/common/actions'; const EMMET_COMMANDS_PREFIX = '>Emmet: '; @@ -23,6 +24,12 @@ class ShowEmmetCommandsAction extends EditorAction { label: nls.localize('showEmmetCommands', "Show Emmet Commands"), alias: 'Show Emmet Commands', precondition: EditorContextKeys.writable, + menubarOpts: { + menuId: MenuId.MenubarEditMenu, + group: '5_insert', + title: nls.localize({ key: 'miShowEmmetCommands', comment: ['&& denotes a mnemonic'] }, "E&&mmet..."), + order: 4 + } }); } diff --git a/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.ts b/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.ts index 5a9512f8999..42968c4b846 100644 --- a/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.ts +++ b/src/vs/workbench/parts/emmet/electron-browser/actions/expandAbbreviation.ts @@ -10,6 +10,8 @@ import { registerEditorAction } from 'vs/editor/browser/editorExtensions'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { KeyCode } from 'vs/base/common/keyCodes'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { MenuId } from 'vs/platform/actions/common/actions'; class ExpandAbbreviationAction extends EmmetEditorAction { @@ -26,7 +28,14 @@ class ExpandAbbreviationAction extends EmmetEditorAction { EditorContextKeys.editorTextFocus, EditorContextKeys.tabDoesNotMoveFocus, ContextKeyExpr.has('config.emmet.triggerExpansionOnTab') - ) + ), + weight: KeybindingWeight.EditorContrib + }, + menubarOpts: { + menuId: MenuId.MenubarEditMenu, + group: '5_insert', + title: nls.localize({ key: 'miEmmetExpandAbbreviation', comment: ['&& denotes a mnemonic'] }, "Emmet: E&&xpand Abbreviation"), + order: 3 } }); diff --git a/src/vs/workbench/parts/execution/electron-browser/execution.contribution.ts b/src/vs/workbench/parts/execution/electron-browser/execution.contribution.ts index 00f631a566e..8687e81f62d 100644 --- a/src/vs/workbench/parts/execution/electron-browser/execution.contribution.ts +++ b/src/vs/workbench/parts/execution/electron-browser/execution.contribution.ts @@ -20,7 +20,7 @@ import { getDefaultTerminalWindows, getDefaultTerminalLinuxReady, DEFAULT_TERMIN import { WinTerminalService, MacTerminalService, LinuxTerminalService } from 'vs/workbench/parts/execution/electron-browser/terminalService'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { ResourceContextKey } from 'vs/workbench/common/resources'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IFileService } from 'vs/platform/files/common/files'; import { IListService } from 'vs/platform/list/browser/listService'; import { getMultiSelectedResources } from 'vs/workbench/parts/files/browser/files'; @@ -40,37 +40,41 @@ if (env.isWindows) { getDefaultTerminalLinuxReady().then(defaultTerminalLinux => { let configurationRegistry = Registry.as(Extensions.Configuration); configurationRegistry.registerConfiguration({ - 'id': 'externalTerminal', - 'order': 100, - 'title': nls.localize('terminalConfigurationTitle', "External Terminal"), - 'type': 'object', - 'properties': { + id: 'externalTerminal', + order: 100, + title: nls.localize('terminalConfigurationTitle', "External Terminal"), + type: 'object', + properties: { 'terminal.explorerKind': { - 'type': 'string', - 'enum': [ + type: 'string', + enum: [ 'integrated', 'external' ], - 'description': nls.localize('explorer.openInTerminalKind', "Customizes what kind of terminal to launch."), - 'default': 'integrated' + enumDescriptions: [ + nls.localize('terminal.explorerKind.integrated', "Use VS Code's integrated terminal."), + nls.localize('terminal.explorerKind.external', "Use the configured external terminal.") + ], + description: nls.localize('explorer.openInTerminalKind', "Customizes what kind of terminal to launch."), + default: 'integrated' }, 'terminal.external.windowsExec': { - 'type': 'string', - 'description': nls.localize('terminal.external.windowsExec', "Customizes which terminal to run on Windows."), - 'default': getDefaultTerminalWindows(), - 'scope': ConfigurationScope.APPLICATION + type: 'string', + description: nls.localize('terminal.external.windowsExec', "Customizes which terminal to run on Windows."), + default: getDefaultTerminalWindows(), + scope: ConfigurationScope.APPLICATION }, 'terminal.external.osxExec': { - 'type': 'string', - 'description': nls.localize('terminal.external.osxExec', "Customizes which terminal application to run on OS X."), - 'default': DEFAULT_TERMINAL_OSX, - 'scope': ConfigurationScope.APPLICATION + type: 'string', + description: nls.localize('terminal.external.osxExec', "Customizes which terminal application to run on macOS."), + default: DEFAULT_TERMINAL_OSX, + scope: ConfigurationScope.APPLICATION }, 'terminal.external.linuxExec': { - 'type': 'string', - 'description': nls.localize('terminal.external.linuxExec', "Customizes which terminal to run on Linux."), - 'default': defaultTerminalLinux, - 'scope': ConfigurationScope.APPLICATION + type: 'string', + description: nls.localize('terminal.external.linuxExec', "Customizes which terminal to run on Linux."), + default: defaultTerminalLinux, + scope: ConfigurationScope.APPLICATION } } }); @@ -109,7 +113,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: OPEN_NATIVE_CONSOLE_COMMAND_ID, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C, when: KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, handler: (accessor) => { const historyService = accessor.get(IHistoryService); const terminalService = accessor.get(ITerminalService); diff --git a/src/vs/workbench/parts/execution/electron-browser/terminalService.ts b/src/vs/workbench/parts/execution/electron-browser/terminalService.ts index 275f11a662e..b1835830812 100644 --- a/src/vs/workbench/parts/execution/electron-browser/terminalService.ts +++ b/src/vs/workbench/parts/execution/electron-browser/terminalService.ts @@ -15,8 +15,8 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { ITerminalService } from 'vs/workbench/parts/execution/common/execution'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITerminalConfiguration, getDefaultTerminalWindows, getDefaultTerminalLinuxReady, DEFAULT_TERMINAL_OSX } from 'vs/workbench/parts/execution/electron-browser/terminal'; -import uri from 'vs/base/common/uri'; import { IProcessEnvironment } from 'vs/base/common/platform'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; const TERMINAL_TITLE = nls.localize('console.title', "VS Code Console"); @@ -143,7 +143,7 @@ export class MacTerminalService implements ITerminalService { // and then launches the program inside that window. const script = terminalApp === DEFAULT_TERMINAL_OSX ? 'TerminalHelper' : 'iTermHelper'; - const scriptpath = uri.parse(require.toUrl(`vs/workbench/parts/execution/electron-browser/${script}.scpt`)).fsPath; + const scriptpath = getPathFromAmdModule(require, `vs/workbench/parts/execution/electron-browser/${script}.scpt`); const osaArgs = [ scriptpath, diff --git a/src/vs/workbench/parts/experiments/node/experimentService.ts b/src/vs/workbench/parts/experiments/node/experimentService.ts index 061f06a5e6d..96777a2416c 100644 --- a/src/vs/workbench/parts/experiments/node/experimentService.ts +++ b/src/vs/workbench/parts/experiments/node/experimentService.ts @@ -10,20 +10,17 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; - +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IExtensionManagementService, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IRequestService } from 'vs/platform/request/node/request'; - import { TPromise } from 'vs/base/common/winjs.base'; import { language } from 'vs/base/common/platform'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { match } from 'vs/base/common/glob'; import { asJson } from 'vs/base/node/request'; - +import { Emitter, Event } from 'vs/base/common/event'; import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/common/textfiles'; import { WorkspaceStats } from 'vs/workbench/parts/stats/node/workspaceStats'; -import { Emitter, Event } from 'vs/base/common/event'; - interface IExperimentStorageState { enabled: boolean; @@ -123,7 +120,8 @@ export class ExperimentService extends Disposable implements IExperimentService @IEnvironmentService private environmentService: IEnvironmentService, @ITelemetryService private telemetryService: ITelemetryService, @ILifecycleService private lifecycleService: ILifecycleService, - @IRequestService private requestService: IRequestService + @IRequestService private requestService: IRequestService, + @IConfigurationService private configurationService: IConfigurationService ) { super(); @@ -167,7 +165,7 @@ export class ExperimentService extends Disposable implements IExperimentService } protected getExperiments(): TPromise { - if (!product.experimentsUrl) { + if (!product.experimentsUrl || this.configurationService.getValue('workbench.enableExperiments') === false) { return TPromise.as([]); } return this.requestService.request({ type: 'GET', url: product.experimentsUrl }).then(context => { @@ -262,7 +260,12 @@ export class ExperimentService extends Disposable implements IExperimentService }); return TPromise.join(promises).then(() => { - this.telemetryService.publicLog('experiments', this._experiments); + /* __GDPR__ + "experiments" : { + "experiments" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog('experiments', { experiments: this._experiments }); }); }); } diff --git a/src/vs/workbench/parts/extensions/common/extensionQuery.ts b/src/vs/workbench/parts/extensions/common/extensionQuery.ts index 8fb85a6d48e..b966d438028 100644 --- a/src/vs/workbench/parts/extensions/common/extensionQuery.ts +++ b/src/vs/workbench/parts/extensions/common/extensionQuery.ts @@ -3,12 +3,43 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + +import { flatten } from 'vs/base/common/arrays'; + export class Query { constructor(public value: string, public sortBy: string, public groupBy: string) { this.value = value.trim(); } + static autocompletions(query: string): string[] { + const commands = ['installed', 'outdated', 'enabled', 'disabled', 'builtin', 'recommended', 'sort', 'category', 'tag', 'ext']; + const subcommands = { + 'sort': ['installs', 'rating', 'name'], + 'category': ['"programming languages"', 'snippets', 'linters', 'themes', 'debuggers', 'formatters', 'keymaps', '"scm providers"', 'other', '"extension packs"', '"language packs"'], + 'tag': [''], + 'ext': [''] + }; + + let queryContains = (substr: string) => query.indexOf(substr) > -1; + let hasSort = subcommands.sort.some(subcommand => queryContains(`@sort:${subcommand}`)); + let hasCategory = subcommands.category.some(subcommand => queryContains(`@category:${subcommand}`)); + + return flatten( + commands.map(command => { + if (hasSort && command === 'sort' || hasCategory && command === 'category') { + return []; + } + if (subcommands[command]) { + return subcommands[command].map(subcommand => `@${command}:${subcommand}${subcommand === '' ? '' : ' '}`); + } + else { + return [`@${command} `]; + } + })); + + } + static parse(value: string): Query { let sortBy = ''; value = value.replace(/@sort:(\w+)(-\w*)?/g, (match, by: string, order: string) => { diff --git a/src/vs/workbench/parts/extensions/common/extensions.ts b/src/vs/workbench/parts/extensions/common/extensions.ts index bf50d9d44ae..4a08d9122f7 100644 --- a/src/vs/workbench/parts/extensions/common/extensions.ts +++ b/src/vs/workbench/parts/extensions/common/extensions.ts @@ -96,11 +96,13 @@ export interface IExtensionsWorkbenchService { export const ConfigurationKey = 'extensions'; export const AutoUpdateConfigurationKey = 'extensions.autoUpdate'; +export const AutoCheckUpdatesConfigurationKey = 'extensions.autoCheckUpdates'; export const ShowRecommendationsOnlyOnDemandKey = 'extensions.showRecommendationsOnlyOnDemand'; export const CloseExtensionDetailsOnViewChangeKey = 'extensions.closeExtensionDetailsOnViewChange'; export interface IExtensionsConfiguration { autoUpdate: boolean; + autoCheckUpdates: boolean; ignoreRecommendations: boolean; showRecommendationsOnlyOnDemand: boolean; closeExtensionDetailsOnViewChange: boolean; diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts index 997b386f29b..4f850fe16ea 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts @@ -43,12 +43,13 @@ import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLa import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { Command } from 'vs/editor/browser/editorExtensions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { Color } from 'vs/base/common/color'; import { assign } from 'vs/base/common/objects'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionsTree, IExtensionData } from 'vs/workbench/parts/extensions/browser/extensionsViewer'; +import { ShowCurrentReleaseNotesAction } from 'vs/workbench/parts/update/electron-browser/update'; /** A context key that is set when an extension editor webview has focus. */ export const KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_FOCUS = new RawContextKey('extensionEditorWebviewFocus', undefined); @@ -493,8 +494,11 @@ export class ExtensionEditor extends BaseEditor { this.activeWebview.contents = body; this.activeWebview.onDidClickLink(link => { + if (!link) { + return; + } // Whitelist supported schemes for links - if (link && ['http', 'https', 'mailto'].indexOf(link.scheme) >= 0) { + if (['http', 'https', 'mailto'].indexOf(link.scheme) >= 0 || (link.scheme === 'command' && link.path === ShowCurrentReleaseNotesAction.ID)) { this.openerService.open(link); } }, null, this.contentDisposables); @@ -1117,7 +1121,8 @@ const showCommand = new ShowExtensionEditorFindCommand({ id: 'editor.action.extensioneditor.showfind', precondition: KEYBINDING_CONTEXT_EXTENSIONEDITOR_WEBVIEW_FOCUS, kbOpts: { - primary: KeyMod.CtrlCmd | KeyCode.KEY_F + primary: KeyMod.CtrlCmd | KeyCode.KEY_F, + weight: KeybindingWeight.EditorContrib } }); -KeybindingsRegistry.registerCommandAndKeybindingRule(showCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); +showCommand.register(); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts index 163cf655cf8..79af9338ba2 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts @@ -43,7 +43,6 @@ import { assign } from 'vs/base/common/objects'; import URI from 'vs/base/common/uri'; import { areSameExtensions, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IExperimentService, ExperimentActionType, ExperimentState } from 'vs/workbench/parts/experiments/node/experimentService'; -import { Schemas } from 'vs/base/common/network'; const milliSecondsInADay = 1000 * 60 * 60 * 24; const choiceNever = localize('neverShowAgain', "Don't Show Again"); @@ -312,7 +311,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe .then(content => json.parse(content.value), err => null); } - private validateExtensions(contents: IExtensionsConfigContent[]): TPromise<{ invalidExtensions: string[], message: string }> { + private async validateExtensions(contents: IExtensionsConfigContent[]): TPromise<{ invalidExtensions: string[], message: string }> { const extensionsContent: IExtensionsConfigContent = { recommendations: distinct(flatten(contents.map(content => content.recommendations || []))), unwantedRecommendations: distinct(flatten(contents.map(content => content.unwantedRecommendations || []))) @@ -340,27 +339,24 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe const filteredWanted = regexFilter(extensionsContent.recommendations || []).map(x => x.toLowerCase()); - if (!filteredWanted.length) { - return TPromise.as({ invalidExtensions, message }); - } + if (filteredWanted.length) { + try { + let validRecommendations = (await this._galleryService.query({ names: filteredWanted })).firstPage + .map(extension => extension.identifier.id.toLowerCase()); - return this._galleryService.query({ names: filteredWanted }).then(pager => { - let page = pager.firstPage; - let validRecommendations = page.map(extension => { - return extension.identifier.id.toLowerCase(); - }); - - if (validRecommendations.length !== filteredWanted.length) { - filteredWanted.forEach(element => { - if (validRecommendations.indexOf(element.toLowerCase()) === -1) { - invalidExtensions.push(element.toLowerCase()); - message += `${element} (not found in marketplace)\n`; - } - }); + if (validRecommendations.length !== filteredWanted.length) { + filteredWanted.forEach(element => { + if (validRecommendations.indexOf(element.toLowerCase()) === -1) { + invalidExtensions.push(element.toLowerCase()); + message += `${element} (not found in marketplace)\n`; + } + }); + } + } catch (e) { + console.warn('Error querying extensions gallery', e); } - - return TPromise.as({ invalidExtensions, message }); - }); + } + return { invalidExtensions, message }; } private isExtensionAllowedToBeRecommended(id: string): boolean { @@ -500,7 +496,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe let hasSuggestion = false; const uri = model.uri; - if (!uri || uri.scheme !== Schemas.file) { + if (!uri || !this.fileService.canHandleResource(uri)) { return; } @@ -526,6 +522,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe recommendationsToSuggest.push(id); } const filedBasedRecommendation = this._fileBasedRecommendations[id.toLowerCase()] || { recommendedTime: now, sources: [] }; + filedBasedRecommendation.recommendedTime = now; if (!filedBasedRecommendation.sources.some(s => s instanceof URI && s.toString() === uri.toString())) { filedBasedRecommendation.sources.push(uri); } @@ -676,8 +673,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe }); } }, { - label: choiceNever, - isSecondary: true, + label: localize('dontShowAgainExtension', "Don't Show Again for '.{0}' files", fileExtension), run: () => { fileExtensionSuggestionIgnoreList.push(fileExtension); this.storageService.store( @@ -891,6 +887,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe private fetchDynamicWorkspaceRecommendations(): TPromise { if (this.contextService.getWorkbenchState() !== WorkbenchState.FOLDER + || !this.fileService.canHandleResource(this.contextService.getWorkspace().folders[0].uri) || this._dynamicWorkspaceRecommendations.length || !this._extensionsRecommendationsUrl) { return TPromise.as(null); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts index ea4c3874785..d14b8666b9d 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensions.contribution.ts @@ -8,7 +8,7 @@ import { localize } from 'vs/nls'; import * as errors from 'vs/base/common/errors'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IExtensionGalleryService, IExtensionTipsService, ExtensionsLabel, ExtensionsChannelId, PreferencesLabel } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; @@ -204,23 +204,32 @@ Registry.as(ConfigurationExtensions.Configuration) properties: { 'extensions.autoUpdate': { type: 'boolean', - description: localize('extensionsAutoUpdate', "Automatically update extensions"), + description: localize('extensionsAutoUpdate', "When enabled, automatically installs updates for extensions. The updates are fetched from an online service."), default: true, - scope: ConfigurationScope.APPLICATION + scope: ConfigurationScope.APPLICATION, + tags: ['usesOnlineServices'] + }, + 'extensions.autoCheckUpdates': { + type: 'boolean', + description: localize('extensionsCheckUpdates', "When enabled, automatically checks extensions for updates. If an extension has an update, it is marked as outdated in the Extensions view. The updates are fetched from an online service."), + default: true, + scope: ConfigurationScope.APPLICATION, + tags: ['usesOnlineServices'] }, 'extensions.ignoreRecommendations': { type: 'boolean', - description: localize('extensionsIgnoreRecommendations', "If set to true, the notifications for extension recommendations will stop showing up."), + description: localize('extensionsIgnoreRecommendations', "When enabled, the notifications for extension recommendations will not be shown."), default: false }, 'extensions.showRecommendationsOnlyOnDemand': { type: 'boolean', - description: localize('extensionsShowRecommendationsOnlyOnDemand', "If set to true, recommendations will not be fetched or shown unless specifically requested by the user."), - default: false + description: localize('extensionsShowRecommendationsOnlyOnDemand', "When enabled, recommendations will not be fetched or shown unless specifically requested by the user. Some recommendations are fetched from an online service."), + default: false, + tags: ['usesOnlineServices'] }, 'extensions.closeExtensionDetailsOnViewChange': { type: 'boolean', - description: localize('extensionsCloseExtensionDetailsOnViewChange', "If set to true, editors with extension details will be automatically closed upon navigating away from the Extensions View."), + description: localize('extensionsCloseExtensionDetailsOnViewChange', "When enabled, editors with extension details will be automatically closed upon navigating away from the Extensions View."), default: false } } @@ -236,4 +245,26 @@ CommandsRegistry.registerCommand('_extensions.manage', (accessor: ServicesAccess if (extension.length === 1) { extensionService.open(extension[0]).done(null, errors.onUnexpectedError); } -}); \ No newline at end of file +}); + +// File menu registration + +MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { + group: '2_keybindings', + command: { + id: ShowRecommendedKeymapExtensionsAction.ID, + title: localize({ key: 'miOpenKeymapExtensions', comment: ['&& denotes a mnemonic'] }, "&&Keymap Extensions") + }, + order: 2 +}); + +// View menu + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '3_views', + command: { + id: VIEWLET_ID, + title: localize({ key: 'miViewExtensions', comment: ['&& denotes a mnemonic'] }, "E&&xtensions") + }, + order: 5 +}); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index ce4e5210290..0d74ff218bd 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -46,12 +46,12 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IQuickOpenService, IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput'; import product from 'vs/platform/node/product'; import { ContextSubMenu } from 'vs/base/browser/contextmenu'; +import { IQuickPickItem, IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; const promptDownloadManually = (extension: IGalleryExtension, message: string, instantiationService: IInstantiationService, notificationService: INotificationService, openerService: IOpenerService) => { const downloadUrl = `${product.extensionsGallery.serviceUrl}/publishers/${extension.publisher}/vsextensions/${extension.name}/${extension.version}/vspackage`; @@ -2694,7 +2694,7 @@ export class ReinstallAction extends Action { constructor( id: string = ReinstallAction.ID, label: string = ReinstallAction.LABEL, @IExtensionsWorkbenchService private extensionsWorkbenchService: IExtensionsWorkbenchService, - @IQuickOpenService private quickOpenService: IQuickOpenService, + @IQuickInputService private quickInputService: IQuickInputService, @INotificationService private notificationService: INotificationService, @IWindowService private windowService: IWindowService ) { @@ -2706,21 +2706,22 @@ export class ReinstallAction extends Action { } run(): TPromise { - return this.quickOpenService.pick(this.getEntries(), { placeHolder: localize('selectExtension', "Select Extension to Reinstall") }); + return this.quickInputService.pick(this.getEntries(), { placeHolder: localize('selectExtension', "Select Extension to Reinstall") }) + .then(pick => pick && this.reinstallExtension(pick.extension)); } - private getEntries(): TPromise { + private getEntries() { return this.extensionsWorkbenchService.queryLocal() .then(local => { - const entries: IPickOpenEntry[] = local + const entries = local .filter(extension => extension.type === LocalExtensionType.User) .map(extension => { - return { + return { id: extension.id, label: extension.displayName, description: extension.id, - run: () => this.reinstallExtension(extension), - }; + extension, + } as (IQuickPickItem & { extension: IExtension }); }); return entries; }); @@ -2752,13 +2753,16 @@ CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsForL }); }); -CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsWithId', function (accessor: ServicesAccessor, extensionId: string) { +CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsWithIds', function (accessor: ServicesAccessor, extensionIds: string[]) { const viewletService = accessor.get(IViewletService); return viewletService.openViewlet(VIEWLET_ID, true) .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => { - viewlet.search(`@id:${extensionId}`); + const query = extensionIds + .map(id => `@id:${id}`) + .join(' '); + viewlet.search(query); viewlet.focus(); }); }); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts index 1153fd57fb8..846d0185cd5 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts @@ -191,7 +191,7 @@ export class Renderer implements IPagedRenderer { extension.getManifest().then(manifest => { const name = manifest && manifest.contributes && manifest.contributes.localizations && manifest.contributes.localizations.length > 0 && manifest.contributes.localizations[0].localizedLanguageName; if (name) { data.description.textContent = name[0].toLocaleUpperCase() + name.slice(1); } - }); + }, () => { }); } disposeElement(): void { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index 507bf50decd..334c02d10d7 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -6,21 +6,21 @@ 'use strict'; import 'vs/css!./media/extensionsViewlet'; +import uri from 'vs/base/common/uri'; +import * as modes from 'vs/editor/common/modes'; import { localize } from 'vs/nls'; import { ThrottledDelayer, always } from 'vs/base/common/async'; import { TPromise } from 'vs/base/common/winjs.base'; import { isPromiseCanceledError, onUnexpectedError, create as createError } from 'vs/base/common/errors'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { Event as EventOf, mapEvent, chain } from 'vs/base/common/event'; +import { Event as EventOf, Emitter, chain } from 'vs/base/common/event'; import { IAction } from 'vs/base/common/actions'; -import { domEvent } from 'vs/base/browser/event'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { append, $, addStandardDisposableListener, EventType, addClass, removeClass, toggleClass, Dimension } from 'vs/base/browser/dom'; +import { append, $, addClass, removeClass, toggleClass, Dimension } from 'vs/base/browser/dom'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -39,7 +39,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorG import Severity from 'vs/base/common/severity'; import { IActivityService, ProgressBadge, NumberBadge } from 'vs/workbench/services/activity/common/activity'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { inputForeground, inputBackground, inputBorder } from 'vs/platform/theme/common/colorRegistry'; +import { inputForeground, inputBackground, inputBorder, inputPlaceholderForeground } from 'vs/platform/theme/common/colorRegistry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ViewsRegistry, IViewDescriptor } from 'vs/workbench/common/views'; import { ViewContainerViewlet, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; @@ -58,6 +58,19 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { SingleServerExtensionManagementServerService } from 'vs/workbench/services/extensions/node/extensionManagementServerService'; +import { Query } from 'vs/workbench/parts/extensions/common/extensionQuery'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { Range } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; +import { ITextModel } from 'vs/editor/common/model'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { getSimpleEditorOptions } from 'vs/workbench/parts/codeEditor/electron-browser/simpleEditorOptions'; +import { SuggestController } from 'vs/editor/contrib/suggest/suggestController'; +import { ContextMenuController } from 'vs/editor/contrib/contextmenu/contextmenu'; +import { MenuPreventer } from 'vs/workbench/parts/codeEditor/electron-browser/menuPreventer'; +import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; +import { isMacintosh } from 'vs/base/common/platform'; interface SearchInputEvent extends Event { target: HTMLInputElement; @@ -120,7 +133,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio container: VIEW_CONTAINER, ctor: EnabledExtensionsView, when: ContextKeyExpr.not('searchExtensions'), - weight: 30, + weight: 40, canToggleVisibility: true, order: 1 }; @@ -169,7 +182,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio container: VIEW_CONTAINER, ctor: DefaultRecommendedExtensionsView, when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions'), ContextKeyExpr.has('defaultRecommendedExtensions')), - weight: 70, + weight: 60, order: 2, canToggleVisibility: true }; @@ -252,12 +265,14 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio private searchDelayer: ThrottledDelayer; private root: HTMLElement; - private searchBox: HTMLInputElement; + private searchBox: CodeEditorWidget; private extensionsBox: HTMLElement; private primaryActions: IAction[]; private secondaryActions: IAction[]; private groupByServerAction: IAction; private disposables: IDisposable[] = []; + private monacoStyleContainer: HTMLDivElement; + private placeholderText: HTMLDivElement; constructor( @IPartService partService: IPartService, @@ -275,7 +290,8 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio @IContextKeyService contextKeyService: IContextKeyService, @IContextMenuService contextMenuService: IContextMenuService, @IExtensionService extensionService: IExtensionService, - @IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService + @IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService, + @IModelService private modelService: IModelService, ) { super(VIEWLET_ID, `${VIEWLET_ID}.state`, true, partService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); @@ -299,6 +315,28 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio this.defaultRecommendedExtensionsContextKey.set(!this.configurationService.getValue(ShowRecommendationsOnlyOnDemandKey)); } }, this, this.disposables); + + modes.SuggestRegistry.register({ scheme: 'extensions', pattern: '**/searchinput', hasAccessToAllModels: true }, { + triggerCharacters: ['@'], + provideCompletionItems: (model: ITextModel, position: Position, _context: modes.SuggestContext) => { + const sortKey = (item: string) => { + if (item.indexOf(':') === -1) { return 'a'; } + else if (/ext:/.test(item) || /tag:/.test(item)) { return 'b'; } + else if (/sort:/.test(item)) { return 'c'; } + else { return 'd'; } + }; + return { + suggestions: this.autoComplete(model.getValue(), position.column).map(item => ( + { + label: item.fullText, + insertText: item.fullText, + overwriteBefore: item.overwrite, + sortText: sortKey(item.fullText), + type: 'keyword' + })) + }; + } + }); } create(parent: HTMLElement): TPromise { @@ -306,31 +344,56 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio this.root = parent; const header = append(this.root, $('.header')); + this.monacoStyleContainer = append(header, $('.monaco-container')); + this.searchBox = this.instantiationService.createInstance(CodeEditorWidget, this.monacoStyleContainer, + mixinHTMLInputStyleOptions(getSimpleEditorOptions(), localize('searchExtensions', "Search Extensions in Marketplace")), + { + isSimpleWidget: true, contributions: [ + SuggestController, + SnippetController2, + ContextMenuController, + MenuPreventer + ] + }); - this.searchBox = append(header, $('input.search-box')); - this.searchBox.placeholder = localize('searchExtensions', "Search Extensions in Marketplace"); - this.disposables.push(addStandardDisposableListener(this.searchBox, EventType.FOCUS, () => addClass(this.searchBox, 'synthetic-focus'))); - this.disposables.push(addStandardDisposableListener(this.searchBox, EventType.BLUR, () => removeClass(this.searchBox, 'synthetic-focus'))); + this.placeholderText = append(this.monacoStyleContainer, $('.search-placeholder', null, localize('searchExtensions', "Search Extensions in Marketplace"))); this.extensionsBox = append(this.root, $('.extensions')); - const onKeyDown = chain(domEvent(this.searchBox, 'keydown')) - .map(e => new StandardKeyboardEvent(e)); - onKeyDown.filter(e => e.keyCode === KeyCode.Escape).on(this.onEscape, this, this.disposables); + this.searchBox.setModel(this.modelService.createModel('', null, uri.parse('extensions:searchinput'), true)); - const onKeyDownForList = onKeyDown.filter(() => this.count() > 0); - onKeyDownForList.filter(e => e.keyCode === KeyCode.Enter).on(this.onEnter, this, this.disposables); + this.disposables.push(this.searchBox.onDidPaste(() => { + let trimmed = this.searchBox.getValue().replace(/\s+/g, ' '); + this.searchBox.setValue(trimmed); + this.searchBox.setScrollTop(0); + this.searchBox.setPosition(new Position(1, trimmed.length + 1)); + })); - const onSearchInput = domEvent(this.searchBox, 'input') as EventOf; - onSearchInput(e => this.triggerSearch(e.immediate), null, this.disposables); + this.disposables.push(this.searchBox.onDidFocusEditorText(() => addClass(this.monacoStyleContainer, 'synthetic-focus'))); + this.disposables.push(this.searchBox.onDidBlurEditorText(() => removeClass(this.monacoStyleContainer, 'synthetic-focus'))); - this.onSearchChange = mapEvent(onSearchInput, e => e.target.value); + const onKeyDownMonaco = chain(this.searchBox.onKeyDown); + onKeyDownMonaco.filter(e => e.keyCode === KeyCode.Enter).on(e => e.preventDefault(), this, this.disposables); + onKeyDownMonaco.filter(e => e.keyCode === KeyCode.DownArrow && (isMacintosh ? e.metaKey : e.ctrlKey)).on(() => this.focusListView(), this, this.disposables); + + const searchChangeEvent = new Emitter(); + this.onSearchChange = searchChangeEvent.event; + + let existingContent = this.searchBox.getValue().trim(); + this.disposables.push(this.searchBox.getModel().onDidChangeContent(() => { + this.placeholderText.style.visibility = this.searchBox.getValue() ? 'hidden' : 'visible'; + let content = this.searchBox.getValue().trim(); + if (existingContent === content) { return; } + this.triggerSearch(); + searchChangeEvent.fire(content); + existingContent = content; + })); return super.create(this.extensionsBox) .then(() => this.extensionManagementService.getInstalled(LocalExtensionType.User)) .then(installed => { if (installed.length === 0) { - this.searchBox.value = '@sort:installs'; + this.searchBox.setValue('@sort:installs'); this.searchExtensionsContextKey.set(true); } }); @@ -339,13 +402,19 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio public updateStyles(): void { super.updateStyles(); - this.searchBox.style.backgroundColor = this.getColor(inputBackground); - this.searchBox.style.color = this.getColor(inputForeground); + this.monacoStyleContainer.style.backgroundColor = this.getColor(inputBackground); + this.monacoStyleContainer.style.color = this.getColor(inputForeground); + this.placeholderText.style.color = this.getColor(inputPlaceholderForeground); const inputBorderColor = this.getColor(inputBorder); - this.searchBox.style.borderWidth = inputBorderColor ? '1px' : null; - this.searchBox.style.borderStyle = inputBorderColor ? 'solid' : null; - this.searchBox.style.borderColor = inputBorderColor; + this.monacoStyleContainer.style.borderWidth = inputBorderColor ? '1px' : null; + this.monacoStyleContainer.style.borderStyle = inputBorderColor ? 'solid' : null; + this.monacoStyleContainer.style.borderColor = inputBorderColor; + + let cursor = this.monacoStyleContainer.getElementsByClassName('cursor')[0] as HTMLDivElement; + if (cursor) { + cursor.style.backgroundColor = this.getColor(inputForeground); + } } setVisible(visible: boolean): TPromise { @@ -354,7 +423,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio if (isVisibilityChanged) { if (visible) { this.searchBox.focus(); - this.searchBox.setSelectionRange(0, this.searchBox.value.length); + this.searchBox.setSelection(new Range(1, 1, 1, this.searchBox.getValue().length + 1)); } } }); @@ -366,6 +435,9 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio layout(dimension: Dimension): void { toggleClass(this.root, 'narrow', dimension.width <= 300); + this.searchBox.layout({ height: 20, width: dimension.width - 34 }); + this.placeholderText.style.width = '' + (dimension.width - 30) + 'px'; + super.layout(new Dimension(dimension.width, dimension.height - 38)); } @@ -420,17 +492,20 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio const event = new Event('input', { bubbles: true }) as SearchInputEvent; event.immediate = true; - this.searchBox.value = value; - this.searchBox.dispatchEvent(event); + this.searchBox.setValue(value); + this.searchBox.setPosition(new Position(1, value.length + 1)); } private triggerSearch(immediate = false): void { - this.searchDelayer.trigger(() => this.doSearch(), immediate || !this.searchBox.value ? 0 : 500) - .done(null, err => this.onError(err)); + this.searchDelayer.trigger(() => this.doSearch(), immediate || !this.searchBox.getValue() ? 0 : 500).done(null, err => this.onError(err)); + } + + private normalizedQuery(): string { + return (this.searchBox.getValue() || '').replace(/@category/g, 'category').replace(/@tag:/g, 'tag:').replace(/@ext:/g, 'ext:'); } private doSearch(): TPromise { - const value = this.searchBox.value || ''; + const value = this.normalizedQuery(); this.searchExtensionsContextKey.set(!!value); this.searchInstalledExtensionsContextKey.set(InstalledExtensionsView.isInstalledExtensionsQuery(value)); this.searchBuiltInExtensionsContextKey.set(ExtensionsListView.isBuiltInExtensionsQuery(value)); @@ -439,14 +514,14 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio this.nonEmptyWorkspaceContextKey.set(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY); if (value) { - return this.progress(TPromise.join(this.panels.map(view => (view).show(this.searchBox.value)))); + return this.progress(TPromise.join(this.panels.map(view => (view).show(this.normalizedQuery())))); } return TPromise.as(null); } protected onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPanel[] { const addedViews = super.onDidAddViews(added); - this.progress(TPromise.join(addedViews.map(addedView => (addedView).show(this.searchBox.value)))); + this.progress(TPromise.join(addedViews.map(addedView => (addedView).show(this.normalizedQuery())))); return addedViews; } @@ -464,16 +539,24 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio return this.instantiationService.createInstance(viewDescriptor.ctor, options) as ViewletPanel; } + private autoComplete(query: string, position: number): { fullText: string, overwrite: number }[] { + let wordStart = query.lastIndexOf(' ', position - 1) + 1; + let alreadyTypedCount = position - wordStart - 1; + + // dont show autosuggestions if the user has typed something, but hasn't used the trigger character + if (alreadyTypedCount > 0 && query[wordStart] !== '@') { return []; } + + return Query.autocompletions(query).map(replacement => ({ fullText: replacement, overwrite: alreadyTypedCount })); + } + private count(): number { return this.panels.reduce((count, view) => (view).count() + count, 0); } - private onEscape(): void { - this.search(''); - } - - private onEnter(): void { - (this.panels[0]).select(); + private focusListView(): void { + if (this.count() > 0) { + this.panels[0].focus(); + } } private onViewletOpen(viewlet: IViewlet): void { @@ -607,4 +690,18 @@ export class MaliciousExtensionChecker implements IWorkbenchContribution { dispose(): void { this.disposables = dispose(this.disposables); } -} \ No newline at end of file +} + +function mixinHTMLInputStyleOptions(config: IEditorOptions, ariaLabel?: string): IEditorOptions { + config.fontSize = 13; + config.lineHeight = 22; + config.wordWrap = 'off'; + config.scrollbar.vertical = 'hidden'; + config.ariaLabel = ariaLabel || ''; + config.renderIndentGuides = false; + config.cursorWidth = 1; + config.snippetSuggestions = 'none'; + config.suggest = { filterGraceful: false }; + config.fontFamily = ' -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "HelveticaNeue-Light", "Ubuntu", "Droid Sans", sans-serif'; + return config; +} diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index 6e18d8391ed..70edc1d5fab 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -71,6 +71,10 @@ export class ExtensionsListView extends ViewletPanel { super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService); } + protected renderHeader(container: HTMLElement): void { + this.renderHeaderTitle(container); + } + renderHeaderTitle(container: HTMLElement): void { super.renderHeaderTitle(container, this.options.title); @@ -107,7 +111,10 @@ export class ExtensionsListView extends ViewletPanel { } async show(query: string): Promise> { - const model = await this.query(query); + const model = await this.query(query).catch(e => { + console.warn('Error querying extensions gallery', e); + return new PagedModel([]); + }); this.setModel(model); return model; } @@ -140,6 +147,12 @@ export class ExtensionsListView extends ViewletPanel { return this.list.length; } + protected showEmptyModel(): TPromise> { + const emptyModel = new PagedModel([]); + this.setModel(emptyModel); + return TPromise.as(emptyModel); + } + private async query(value: string): Promise> { const query = Query.parse(value); @@ -217,12 +230,16 @@ export class ExtensionsListView extends ViewletPanel { return new PagedModel(this.sortExtensions(result, options)); } - const idMatch = /@id:(([a-z0-9A-Z][a-z0-9\-A-Z]*)\.([a-z0-9A-Z][a-z0-9\-A-Z]*))/.exec(value); - - if (idMatch) { + const idRegex = /@id:(([a-z0-9A-Z][a-z0-9\-A-Z]*)\.([a-z0-9A-Z][a-z0-9\-A-Z]*))/g; + let idMatch; + const names: string[] = []; + while ((idMatch = idRegex.exec(value)) !== null) { const name = idMatch[1]; + names.push(name); + } - return this.extensionsWorkbenchService.queryGallery({ names: [name], source: 'queryById' }) + if (names.length) { + return this.extensionsWorkbenchService.queryGallery({ names, source: 'queryById' }) .then(pager => new PagedModel(pager)); } @@ -640,6 +657,14 @@ export class ExtensionsListView extends ViewletPanel { static isKeymapsRecommendedExtensionsQuery(query: string): boolean { return /@recommended:keymaps/i.test(query); } + + focus(): void { + super.focus(); + if (!(this.list.getFocus().length || this.list.getSelection().length)) { + this.list.focusNext(); + } + this.list.domFocus(); + } } export class InstalledExtensionsView extends ExtensionsListView { @@ -674,42 +699,41 @@ export class GroupByServerExtensionsView extends ExtensionsListView { } export class EnabledExtensionsView extends ExtensionsListView { + private readonly enabledExtensionsQuery = '@enabled'; async show(query: string): Promise> { - return super.show('@enabled'); + return (query && query.trim() !== this.enabledExtensionsQuery) ? this.showEmptyModel() : super.show(this.enabledExtensionsQuery); } } export class DisabledExtensionsView extends ExtensionsListView { + private readonly disabledExtensionsQuery = '@disabled'; async show(query: string): Promise> { - return super.show('@disabled'); + return (query && query.trim() !== this.disabledExtensionsQuery) ? this.showEmptyModel() : super.show(this.disabledExtensionsQuery); } } export class BuiltInExtensionsView extends ExtensionsListView { - async show(query: string): Promise> { - return super.show(query.replace('@builtin', '@builtin:features')); + return (query && query.trim() !== '@builtin') ? this.showEmptyModel() : super.show('@builtin:features'); } - } export class BuiltInThemesExtensionsView extends ExtensionsListView { - async show(query: string): Promise> { - return super.show(query.replace('@builtin', '@builtin:themes')); + return (query && query.trim() !== '@builtin') ? this.showEmptyModel() : super.show('@builtin:themes'); } } export class BuiltInBasicsExtensionsView extends ExtensionsListView { - async show(query: string): Promise> { - return super.show(query.replace('@builtin', '@builtin:basics')); + return (query && query.trim() !== '@builtin') ? this.showEmptyModel() : super.show('@builtin:basics'); } } export class DefaultRecommendedExtensionsView extends ExtensionsListView { + private readonly recommendedExtensionsQuery = '@recommended:all'; renderBody(container: HTMLElement): void { super.renderBody(container); @@ -720,13 +744,13 @@ export class DefaultRecommendedExtensionsView extends ExtensionsListView { } async show(query: string): Promise> { - return super.show('@recommended:all'); + return (query && query.trim() !== this.recommendedExtensionsQuery) ? this.showEmptyModel() : super.show(this.recommendedExtensionsQuery); } } export class RecommendedExtensionsView extends ExtensionsListView { - + private readonly recommendedExtensionsQuery = '@recommended'; renderBody(container: HTMLElement): void { super.renderBody(container); @@ -737,12 +761,12 @@ export class RecommendedExtensionsView extends ExtensionsListView { } async show(query: string): Promise> { - return super.show('@recommended'); + return (query && query.trim() !== this.recommendedExtensionsQuery) ? this.showEmptyModel() : super.show(this.recommendedExtensionsQuery); } } export class WorkspaceRecommendedExtensionsView extends ExtensionsListView { - + private readonly recommendedExtensionsQuery = '@recommended:workspace'; private installAllAction: InstallWorkspaceRecommendedExtensionsAction; renderBody(container: HTMLElement): void { @@ -776,14 +800,15 @@ export class WorkspaceRecommendedExtensionsView extends ExtensionsListView { this.disposables.push(...[this.installAllAction, configureWorkspaceFolderAction, actionbar]); } - async show(): Promise> { - let model = await super.show('@recommended:workspace'); + async show(query: string): Promise> { + let shouldShowEmptyView = query && query.trim() !== '@recommended' && query.trim() !== '@recommended:workspace'; + let model = await (shouldShowEmptyView ? this.showEmptyModel() : super.show(this.recommendedExtensionsQuery)); this.setExpanded(model.length > 0); return model; } private update(): void { - this.show(); + this.show(this.recommendedExtensionsQuery); this.setRecommendationsToInstall(); } diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css index a83f6a0c066..98e419e22e2 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css @@ -44,7 +44,7 @@ } .extensions-viewlet > .extensions .panel-header { - padding-right: 12px; + padding-right: 28px; } .extensions-viewlet > .extensions .panel-header > .title { @@ -179,10 +179,15 @@ flex: 1; font-size: 90%; padding-right: 6px; - opacity: 0.6; + opacity: 0.9; font-weight: 600; } +.extensions-viewlet > .extensions .selected .extension > .details > .footer > .author, +.extensions-viewlet > .extensions .selected.focused .extension > .details > .footer > .author { + opacity: 1; +} + .extensions-viewlet > .extensions .extension > .details > .footer > .monaco-action-bar > .actions-container { flex-wrap: wrap-reverse; } @@ -197,6 +202,32 @@ opacity: 0.9; } +.extensions-viewlet .header .monaco-container { + padding: 3px 4px 5px; +} + +.extensions-viewlet .header .monaco-container .suggest-widget { + width: 275px; +} + +.extensions-viewlet .header .monaco-container .monaco-editor-background, +.extensions-viewlet .header .monaco-container .monaco-editor, +.extensions-viewlet .header .monaco-container .mtk1 { + /* allow the embedded monaco to be styled from the outer context */ + background-color: inherit; + color: inherit; +} + +.extensions-viewlet .header .search-placeholder { + position: absolute; + z-index: 1; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + pointer-events: none; + margin-top: 2px; +} + .vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .bookmark, .vs-dark .extensions-viewlet > .extensions .monaco-list-row.disabled > .bookmark, .vs .extensions-viewlet > .extensions .monaco-list-row.disabled > .extension > .icon, @@ -220,4 +251,4 @@ background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNCIgaGVpZ2h0PSIxNCIgdmlld0JveD0iMiAyIDE0IDE0IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDIgMiAxNCAxNCI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTkgMTZjLTMuODYgMC03LTMuMTQtNy03czMuMTQtNyA3LTdjMy44NTkgMCA3IDMuMTQxIDcgN3MtMy4xNDEgNy03IDd6bTAtMTIuNmMtMy4wODggMC01LjYgMi41MTMtNS42IDUuNnMyLjUxMiA1LjYgNS42IDUuNiA1LjYtMi41MTIgNS42LTUuNi0yLjUxMi01LjYtNS42LTUuNnptMy44NiA3LjFsLTMuMTYtMS44OTZ2LTMuODA0aC0xLjR2NC41OTZsMy44NCAyLjMwNS43Mi0xLjIwMXoiLz48L3N2Zz4="); background-position: center center; background-repeat: no-repeat; -} \ No newline at end of file +} diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index 236dd7e4e70..eaf82c0f793 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -26,7 +26,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IWindowService } from 'vs/platform/windows/common/windows'; import Severity from 'vs/base/common/severity'; import URI from 'vs/base/common/uri'; -import { IExtension, IExtensionDependencies, ExtensionState, IExtensionsWorkbenchService, AutoUpdateConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions'; +import { IExtension, IExtensionDependencies, ExtensionState, IExtensionsWorkbenchService, AutoUpdateConfigurationKey, AutoCheckUpdatesConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput'; @@ -38,7 +38,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { groupBy } from 'vs/base/common/collections'; import { Schemas } from 'vs/base/common/network'; -import { posix } from 'path'; +import * as resources from 'vs/base/common/resources'; interface IExtensionStateProvider { (extension: Extension): T; @@ -131,7 +131,7 @@ class Extension implements IExtension { private get localIconUrl(): string { if (this.local && this.local.manifest.icon) { - return this.local.location.with({ path: posix.join(this.local.location.path, this.local.manifest.icon) }).toString(); + return resources.joinPath(this.local.location, this.local.manifest.icon).toString(); } return null; } @@ -264,7 +264,7 @@ ${this.description} return uri.scheme === 'file'; } - return false; + return this.type === LocalExtensionType.System; } getChangelog(): TPromise { @@ -275,6 +275,10 @@ ${this.description} const changelogUrl = this.local && this.local.changelogUrl; if (!changelogUrl) { + if (this.type === LocalExtensionType.System) { + return TPromise.as('Please check the [VS Code Release Notes](command:update.showCurrentReleaseNotes) for changes to the built-in extensions.'); + } + return TPromise.wrapError(new Error('not available')); } @@ -411,6 +415,11 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, this.checkForUpdates(); } } + if (e.affectsConfiguration(AutoCheckUpdatesConfigurationKey)) { + if (this.isAutoCheckUpdatesEnabled()) { + this.checkForUpdates(); + } + } }, this, this.disposables); this.queryLocal().done(() => this.eventuallySyncWithGallery(true)); @@ -606,8 +615,13 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, return this.configurationService.getValue(AutoUpdateConfigurationKey); } + private isAutoCheckUpdatesEnabled(): boolean { + return this.configurationService.getValue(AutoCheckUpdatesConfigurationKey); + } + private eventuallySyncWithGallery(immediate = false): void { - const loop = () => this.syncWithGallery().then(() => this.eventuallySyncWithGallery()); + const shouldSync = this.isAutoUpdateEnabled() || this.isAutoCheckUpdatesEnabled(); + const loop = () => (shouldSync ? this.syncWithGallery() : TPromise.as(null)).then(() => this.eventuallySyncWithGallery()); const delay = immediate ? 0 : ExtensionsWorkbenchService.SyncPeriod; this.syncDelayer.trigger(loop, delay) @@ -738,38 +752,45 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, } private promptAndSetEnablement(extensions: IExtension[], enablementState: EnablementState): TPromise { - const allDependenciesAndPackedExtensions = this.getDependenciesAndPackedExtensionsRecursively(extensions, this.local, enablementState); - if (allDependenciesAndPackedExtensions.length > 0) { - if (extensions.length === 1 && (enablementState === EnablementState.Disabled || enablementState === EnablementState.WorkspaceDisabled)) { - return this.promptForDependenciesAndDisable(extensions[0], allDependenciesAndPackedExtensions, enablementState); + const enable = enablementState === EnablementState.Enabled || enablementState === EnablementState.WorkspaceEnabled; + if (enable) { + const allDependenciesAndPackedExtensions = this.getExtensionsRecursively(extensions, this.local, enablementState, { dependencies: true, pack: true }); + return this.checkAndSetEnablement(extensions, allDependenciesAndPackedExtensions, enablementState); + } else { + const packedExtensions = this.getExtensionsRecursively(extensions, this.local, enablementState, { dependencies: false, pack: true }); + if (packedExtensions.length) { + return this.checkAndSetEnablement(extensions, packedExtensions, enablementState); + } + const dependencies = this.getExtensionsRecursively(extensions, this.local, enablementState, { dependencies: true, pack: false }); + if (dependencies.length) { + return this.promptForDependenciesAndDisable(extensions, dependencies, enablementState); } else { - return this.checkAndSetEnablement(extensions, allDependenciesAndPackedExtensions, enablementState); + return this.checkAndSetEnablement(extensions, [], enablementState); } } - return this.checkAndSetEnablement(extensions, [], enablementState); } - private promptForDependenciesAndDisable(extension: IExtension, dependencies: IExtension[], enablementState: EnablementState): TPromise { - const message = nls.localize('disableExtensionPackConfirmation', "Would you like to disable '{0}' only or as a pack?", extension.displayName); + private promptForDependenciesAndDisable(extensions: IExtension[], dependencies: IExtension[], enablementState: EnablementState): TPromise { + const message = extensions.length > 1 ? nls.localize('disableDependeciesConfirmation', "Also disable the dependencies of the extensions?") : nls.localize('disableDependeciesSingleExtensionConfirmation', "Also disable the dependencies of the extension '{0}'?", extensions[0].displayName); const buttons = [ - nls.localize('disablePack', "Disable Extension Pack"), - nls.localize('disableOnly', "Disable Extension Only"), - nls.localize('cancel', "Cancel") + nls.localize('yes', "Yes"), + nls.localize('cancel', "Cancel"), + nls.localize('no', "No"), ]; - return this.dialogService.show(Severity.Info, message, buttons) + return this.dialogService.show(Severity.Info, message, buttons, { cancelId: 2 }) .then(value => { if (value === 0) { - return this.checkAndSetEnablement([extension], dependencies, enablementState); + return this.checkAndSetEnablement(extensions, dependencies, enablementState); } - if (value === 1) { - return this.checkAndSetEnablement([extension], [], enablementState); + if (value === 2) { + return this.checkAndSetEnablement(extensions, [], enablementState); } return TPromise.as(null); }); } - private checkAndSetEnablement(extensions: IExtension[], dependencies: IExtension[], enablementState: EnablementState): TPromise { - const allExtensions = [...extensions, ...dependencies]; + private checkAndSetEnablement(extensions: IExtension[], otherExtensions: IExtension[], enablementState: EnablementState): TPromise { + const allExtensions = [...extensions, ...otherExtensions]; const enable = enablementState === EnablementState.Enabled || enablementState === EnablementState.WorkspaceEnabled; if (!enable) { for (const extension of extensions) { @@ -782,7 +803,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, return TPromise.join(allExtensions.map(e => this.doSetEnablement(e, enablementState))); } - private getDependenciesAndPackedExtensionsRecursively(extensions: IExtension[], installed: IExtension[], enablementState: EnablementState, checked: IExtension[] = []): IExtension[] { + private getExtensionsRecursively(extensions: IExtension[], installed: IExtension[], enablementState: EnablementState, options: { dependencies: boolean, pack: boolean }, checked: IExtension[] = []): IExtension[] { const toCheck = extensions.filter(e => checked.indexOf(e) === -1); if (toCheck.length) { for (const extension of toCheck) { @@ -795,11 +816,14 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, if (i.enablementState === enablementState) { return false; } - return i.type === LocalExtensionType.User && - extensions.some(extension => extension.dependencies.some(id => areSameExtensions({ id }, i)) || extension.extensionPack.some(id => areSameExtensions({ id }, i))); + return (options.dependencies || options.pack) + && extensions.some(extension => + (options.dependencies && extension.dependencies.some(id => areSameExtensions({ id }, i))) + || (options.pack && extension.extensionPack.some(id => areSameExtensions({ id }, i))) + ); }); if (extensionsToDisable.length) { - extensionsToDisable.push(...this.getDependenciesAndPackedExtensionsRecursively(extensionsToDisable, installed, enablementState, checked)); + extensionsToDisable.push(...this.getExtensionsRecursively(extensionsToDisable, installed, enablementState, options, checked)); } return extensionsToDisable; } diff --git a/src/vs/workbench/parts/extensions/test/common/extensionQuery.test.ts b/src/vs/workbench/parts/extensions/test/common/extensionQuery.test.ts index 1d1031053eb..a3ba946041f 100644 --- a/src/vs/workbench/parts/extensions/test/common/extensionQuery.test.ts +++ b/src/vs/workbench/parts/extensions/test/common/extensionQuery.test.ts @@ -140,4 +140,12 @@ suite('Extension query', () => { query2 = new Query('hello', 'installs', ''); assert(!query1.equals(query2)); }); + + test('autocomplete', () => { + Query.autocompletions('@sort:in').some(x => x === '@sort:installs '); + Query.autocompletions('@sort:installs').every(x => x !== '@sort:rating '); + + Query.autocompletions('@category:blah').some(x => x === '@category:"extension packs" '); + Query.autocompletions('@category:"extension packs"').every(x => x !== '@category:formatters '); + }); }); \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts index 926f041931e..1c34aeeffa7 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsActions.test.ts @@ -16,7 +16,7 @@ import { IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, LocalExtensionType, IGalleryExtension, DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, EnablementState, InstallOperation, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { getGalleryExtensionId, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionManagementService, getLocalExtensionIdFromGallery, getLocalExtensionIdFromManifest } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService'; import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/common/extensionEnablementService.test'; @@ -79,12 +79,12 @@ suite('ExtensionsActions Test', () => { instantiationService.stub(IURLService, URLService); }); - setup(() => { + setup(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', []); instantiationService.stubPromise(IExtensionManagementService, 'getExtensionsReport', []); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage()); instantiationService.stub(IExtensionService, { getExtensions: () => TPromise.wrap([]) }); - (instantiationService.get(IExtensionEnablementService)).reset(); + await (instantiationService.get(IExtensionEnablementService)).reset(); instantiationService.set(IExtensionsWorkbenchService, instantiationService.createInstance(ExtensionsWorkbenchService)); }); @@ -1207,6 +1207,7 @@ suite('ExtensionsActions Test', () => { assign(localExtension.manifest, { name, publisher: 'pub', version: '1.0.0' }, manifest); localExtension.identifier = { id: getLocalExtensionIdFromManifest(localExtension.manifest) }; localExtension.metadata = { id: localExtension.identifier.id, publisherId: localExtension.manifest.publisher, publisherDisplayName: 'somename' }; + localExtension.galleryIdentifier = { id: getGalleryExtensionIdFromLocal(localExtension), uuid: void 0 }; return localExtension; } diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index f3f0e333ec4..7da387ac880 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -11,13 +11,13 @@ import * as fs from 'fs'; import { assign } from 'vs/base/common/objects'; import { TPromise } from 'vs/base/common/winjs.base'; import { generateUuid } from 'vs/base/common/uuid'; -import { IExtensionsWorkbenchService, ExtensionState } from 'vs/workbench/parts/extensions/common/extensions'; +import { IExtensionsWorkbenchService, ExtensionState, AutoCheckUpdatesConfigurationKey, AutoUpdateConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions'; import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService'; import { IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, LocalExtensionType, IGalleryExtension, DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IGalleryExtensionAssets, IExtensionIdentifier, EnablementState, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { getGalleryExtensionId, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionManagementService, getLocalExtensionIdFromGallery, getLocalExtensionIdFromManifest } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService'; import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/common/extensionEnablementService.test'; @@ -66,7 +66,14 @@ suite('ExtensionsWorkbenchServiceTest', () => { instantiationService.stub(IURLService, URLService); instantiationService.stub(IWorkspaceContextService, new TestContextService()); - instantiationService.stub(IConfigurationService, { onDidUpdateConfiguration: () => { }, onDidChangeConfiguration: () => { }, getConfiguration: () => ({}) }); + instantiationService.stub(IConfigurationService, { + onDidUpdateConfiguration: () => { }, + onDidChangeConfiguration: () => { }, + getConfiguration: () => ({}), + getValue: (key) => { + return (key === AutoCheckUpdatesConfigurationKey || key === AutoUpdateConfigurationKey) ? true : undefined; + } + }); instantiationService.stub(IExtensionManagementService, ExtensionManagementService); instantiationService.stub(IExtensionManagementService, 'onInstallExtension', installEvent.event); @@ -82,13 +89,13 @@ suite('ExtensionsWorkbenchServiceTest', () => { instantiationService.stub(IDialogService, { show: () => TPromise.as(0) }); }); - setup(() => { + setup(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', []); instantiationService.stubPromise(IExtensionManagementService, 'getExtensionsReport', []); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage()); instantiationService.stub(IDialogService, { show: () => TPromise.as(0) }); instantiationService.stubPromise(INotificationService, 'prompt', 0); - (instantiationService.get(IExtensionEnablementService)).reset(); + await (instantiationService.get(IExtensionEnablementService)).reset(); }); teardown(() => { @@ -211,7 +218,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { assert.equal('1.2.0', actual.version); assert.equal('1.2.0', actual.latestVersion); assert.equal('localDescription2', actual.description); - assert.ok(fs.existsSync(actual.iconUrl)); + assert.ok(fs.existsSync(URI.parse(actual.iconUrl).fsPath)); assert.equal(null, actual.licenseUrl); assert.equal(ExtensionState.Installed, actual.state); assert.equal(null, actual.installCount); @@ -304,7 +311,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { assert.equal('1.2.0', actual.version); assert.equal('1.2.0', actual.latestVersion); assert.equal('localDescription2', actual.description); - assert.ok(fs.existsSync(actual.iconUrl)); + assert.ok(fs.existsSync(URI.parse(actual.iconUrl).fsPath)); assert.equal(null, actual.licenseUrl); assert.equal(ExtensionState.Installed, actual.state); assert.equal(null, actual.installCount); @@ -840,7 +847,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Enabled)) .then(() => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]); - instantiationService.stubPromise(IDialogService, 'show', 1); + instantiationService.stubPromise(IDialogService, 'show', 2); testObject = instantiationService.createInstance(ExtensionsWorkbenchService); return testObject.setEnablement(testObject.local[0], EnablementState.Disabled) @@ -851,7 +858,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); }); - test('test disable extension pack disable only itself', () => { + test('test disable extension pack disables the pack', () => { const extensionA = aLocalExtension('a', { extensionPack: ['pub.b'] }); const extensionB = aLocalExtension('b'); const extensionC = aLocalExtension('c'); @@ -861,13 +868,12 @@ suite('ExtensionsWorkbenchServiceTest', () => { .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Enabled)) .then(() => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]); - instantiationService.stubPromise(IDialogService, 'show', 1); testObject = instantiationService.createInstance(ExtensionsWorkbenchService); return testObject.setEnablement(testObject.local[0], EnablementState.Disabled) .then(() => { assert.equal(testObject.local[0].enablementState, EnablementState.Disabled); - assert.equal(testObject.local[1].enablementState, EnablementState.Enabled); + assert.equal(testObject.local[1].enablementState, EnablementState.Disabled); }); }); }); @@ -1003,7 +1009,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Enabled)) .then(() => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]); - instantiationService.stubPromise(IDialogService, 'show', 1); + instantiationService.stubPromise(IDialogService, 'show', 2); testObject = instantiationService.createInstance(ExtensionsWorkbenchService); return testObject.setEnablement(testObject.local[0], EnablementState.Disabled) @@ -1238,6 +1244,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { assign(localExtension.manifest, { name, publisher: 'pub', version: '1.0.0' }, manifest); localExtension.identifier = { id: getLocalExtensionIdFromManifest(localExtension.manifest) }; localExtension.metadata = { id: localExtension.identifier.id, publisherId: localExtension.manifest.publisher, publisherDisplayName: 'somename' }; + localExtension.galleryIdentifier = { id: getGalleryExtensionIdFromLocal(localExtension), uuid: void 0 }; return localExtension; } @@ -1275,4 +1282,4 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); }); } -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/parts/files/browser/editors/fileEditorTracker.ts b/src/vs/workbench/parts/files/browser/editors/fileEditorTracker.ts index 8b16509b50c..9f6bd55fff9 100644 --- a/src/vs/workbench/parts/files/browser/editors/fileEditorTracker.ts +++ b/src/vs/workbench/parts/files/browser/editors/fileEditorTracker.ts @@ -7,7 +7,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import URI from 'vs/base/common/uri'; -import * as paths from 'vs/base/common/paths'; +import * as resources from 'vs/base/common/resources'; import { IEditorViewState } from 'vs/editor/common/editorCommon'; import { toResource, SideBySideEditorInput, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor'; import { ITextFileService, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; @@ -149,7 +149,7 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut // Do NOT close any opened editor that matches the resource path (either equal or being parent) of the // resource we move to (movedTo). Otherwise we would close a resource that has been renamed to the same // path but different casing. - if (movedTo && paths.isEqualOrParent(resource.fsPath, movedTo.fsPath, !isLinux /* ignorecase */) && resource.fsPath.indexOf(movedTo.fsPath) === 0) { + if (movedTo && resources.isEqualOrParent(resource, movedTo, resources.hasToIgnoreCase(resource)) && resource.path.indexOf(movedTo.path) === 0) { return; } @@ -157,7 +157,7 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut if (arg1 instanceof FileChangesEvent) { matches = arg1.contains(resource, FileChangeType.DELETED); } else { - matches = paths.isEqualOrParent(resource.fsPath, arg1.fsPath, !isLinux /* ignorecase */); + matches = resources.isEqualOrParent(resource, arg1, resources.hasToIgnoreCase(resource)); } if (!matches) { @@ -224,13 +224,13 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut const resource = input.getResource(); // Update Editor if file (or any parent of the input) got renamed or moved - if (paths.isEqualOrParent(resource.fsPath, oldResource.fsPath, !isLinux /* ignorecase */)) { + if (resources.isEqualOrParent(resource, oldResource, resources.hasToIgnoreCase(resource))) { let reopenFileResource: URI; if (oldResource.toString() === resource.toString()) { reopenFileResource = newResource; // file got moved } else { const index = this.getIndexOfPath(resource.path, oldResource.path); - reopenFileResource = newResource.with({ path: paths.join(newResource.path, resource.path.substr(index + oldResource.path.length + 1)) }); // parent folder got moved + reopenFileResource = resources.joinPath(newResource, resource.path.substr(index + oldResource.path.length + 1)); // parent folder got moved } // Reopen diff --git a/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts b/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts index 8ec1880198a..5eea47892d2 100644 --- a/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/parts/files/common/editors/fileEditorInput.ts @@ -9,22 +9,19 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { memoize } from 'vs/base/common/decorators'; import * as paths from 'vs/base/common/paths'; import * as resources from 'vs/base/common/resources'; -import * as labels from 'vs/base/common/labels'; import URI from 'vs/base/common/uri'; import { EncodingMode, ConfirmResult, EditorInput, IFileEditorInput, ITextEditorModel, Verbosity, IRevertOptions } from 'vs/workbench/common/editor'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { ITextFileService, AutoSaveMode, ModelState, TextFileModelChangeEvent, LoadReason } from 'vs/workbench/services/textfile/common/textfiles'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IReference } from 'vs/base/common/lifecycle'; import { telemetryURIDescriptor } from 'vs/platform/telemetry/common/telemetryUtils'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IHashService } from 'vs/workbench/services/hash/common/hashService'; import { FILE_EDITOR_INPUT_ID, TEXT_FILE_EDITOR_ID, BINARY_FILE_EDITOR_ID } from 'vs/workbench/parts/files/common/files'; -import { Schemas } from 'vs/base/common/network'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; /** * A file editor input is the input type for the file editor of file system resources. @@ -43,11 +40,10 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { private resource: URI, preferredEncoding: string, @IInstantiationService private instantiationService: IInstantiationService, - @IWorkspaceContextService private contextService: IWorkspaceContextService, @ITextFileService private textFileService: ITextFileService, - @IEnvironmentService private environmentService: IEnvironmentService, @ITextModelService private textModelResolverService: ITextModelService, - @IHashService private hashService: IHashService + @IHashService private hashService: IHashService, + @IUriDisplayService private uriDisplayService: IUriDisplayService ) { super(); @@ -131,23 +127,22 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { this.name = resources.basenameOrAuthority(this.resource); } - return this.decorateOrphanedFiles(this.name); + return this.decorateLabel(this.name); } @memoize private get shortDescription(): string { - return paths.basename(labels.getPathLabel(resources.dirname(this.resource), this.environmentService)); + return paths.basename(this.uriDisplayService.getLabel(resources.dirname(this.resource))); } @memoize private get mediumDescription(): string { - return labels.getPathLabel(resources.dirname(this.resource), this.environmentService, this.contextService); + return this.uriDisplayService.getLabel(resources.dirname(this.resource), true); } @memoize private get longDescription(): string { - const rootProvider = this.resource.scheme !== Schemas.file ? this.contextService : undefined; - return labels.getPathLabel(resources.dirname(this.resource), this.environmentService, rootProvider); + return this.uriDisplayService.getLabel(resources.dirname(this.resource), true); } getDescription(verbosity: Verbosity = Verbosity.MEDIUM): string { @@ -175,13 +170,12 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { @memoize private get mediumTitle(): string { - return labels.getPathLabel(this.resource, this.environmentService, this.contextService); + return this.uriDisplayService.getLabel(this.resource, true); } @memoize private get longTitle(): string { - const rootProvider = this.resource.scheme !== Schemas.file ? this.contextService : undefined; - return labels.getPathLabel(this.resource, this.environmentService, rootProvider); + return this.uriDisplayService.getLabel(this.resource, true); } getTitle(verbosity: Verbosity): string { @@ -198,14 +192,17 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { break; } - return this.decorateOrphanedFiles(title); + return this.decorateLabel(title); } - private decorateOrphanedFiles(label: string): string { + private decorateLabel(label: string): string { const model = this.textFileService.models.get(this.resource); if (model && model.hasState(ModelState.ORPHAN)) { return localize('orphanedFile', "{0} (deleted from disk)", label); } + if (model && model.isReadonly()) { + return localize('readonlyFile', "{0} (read-only)", label); + } return label; } diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerModel.ts index 92490a50246..49d64d0a561 100644 --- a/src/vs/workbench/parts/files/common/explorerModel.ts +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -15,7 +15,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { toResource, IEditorIdentifier, IEditorInput } from 'vs/workbench/common/editor'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; -import { startsWith, startsWithIgnoreCase, rtrim } from 'vs/base/common/strings'; +import { rtrim } from 'vs/base/common/strings'; import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; export class Model { @@ -157,7 +157,7 @@ export class ExplorerItem { // the folder is fully resolved if either it has a list of children or the client requested this by using the resolveTo // array of resource path to resolve. stat.isDirectoryResolved = !!raw.children || (!!resolveTo && resolveTo.some((r) => { - return resources.isEqualOrParent(r, stat.resource, !isLinux /* ignorecase */); + return resources.isEqualOrParent(r, stat.resource, resources.hasToIgnoreCase(r)); })); // Recurse into children @@ -311,7 +311,7 @@ export class ExplorerItem { } private updateResource(recursive: boolean): void { - this.resource = this.parent.resource.with({ path: paths.join(this.parent.resource.path, this.name) }); + this.resource = resources.joinPath(this.parent.resource, this.name); if (recursive) { if (this.isDirectory && this.children) { @@ -342,9 +342,7 @@ export class ExplorerItem { */ public find(resource: URI): ExplorerItem { // Return if path found - if (resource && this.resource.scheme === resource.scheme && this.resource.authority === resource.authority && - (isLinux ? startsWith(resource.path, this.resource.path) : startsWithIgnoreCase(resource.path, this.resource.path)) - ) { + if (resource && resources.isEqualOrParent(resource, this.resource)) { return this.findByPath(rtrim(resource.path, paths.sep), this.resource.path.length); } diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts index 9f88aa03a21..1fd97d3e912 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts @@ -14,7 +14,7 @@ import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { openWindowCommand, REVEAL_IN_OS_COMMAND_ID, COPY_PATH_COMMAND_ID, REVEAL_IN_EXPLORER_COMMAND_ID, OPEN_TO_SIDE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_COMMAND_ID, SAVE_FILE_LABEL, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID, OpenEditorsGroupContext, COMPARE_WITH_SAVED_COMMAND_ID, COMPARE_RESOURCE_COMMAND_ID, SELECT_FOR_COMPARE_COMMAND_ID, ResourceSelectedForCompareContext, REVEAL_IN_OS_LABEL, DirtyEditorContext, COMPARE_SELECTED_COMMAND_ID, REMOVE_ROOT_FOLDER_COMMAND_ID, REMOVE_ROOT_FOLDER_LABEL, SAVE_FILES_COMMAND_ID, COPY_RELATIVE_PATH_COMMAND_ID } from 'vs/workbench/parts/files/electron-browser/fileCommands'; import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { isWindows, isMacintosh } from 'vs/base/common/platform'; import { FilesExplorerFocusCondition, ExplorerRootContext, ExplorerFolderContext, ExplorerResourceNotReadonlyContext } from 'vs/workbench/parts/files/common/files'; import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL } from 'vs/workbench/browser/actions/workspaceCommands'; @@ -50,7 +50,7 @@ const explorerCommandsWeightBonus = 10; // give our commands a little bit more w const RENAME_ID = 'renameFile'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: RENAME_ID, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(explorerCommandsWeightBonus), + weight: KeybindingWeight.WorkbenchContrib + explorerCommandsWeightBonus, when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerRootContext.toNegated(), ExplorerResourceNotReadonlyContext), primary: KeyCode.F2, mac: { @@ -62,7 +62,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const MOVE_FILE_TO_TRASH_ID = 'moveFileToTrash'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: MOVE_FILE_TO_TRASH_ID, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(explorerCommandsWeightBonus), + weight: KeybindingWeight.WorkbenchContrib + explorerCommandsWeightBonus, when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerRootContext.toNegated(), ExplorerResourceNotReadonlyContext), primary: KeyCode.Delete, mac: { @@ -74,7 +74,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const DELETE_FILE_ID = 'deleteFile'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: DELETE_FILE_ID, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(explorerCommandsWeightBonus), + weight: KeybindingWeight.WorkbenchContrib + explorerCommandsWeightBonus, when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerRootContext.toNegated(), ExplorerResourceNotReadonlyContext), primary: KeyMod.Shift | KeyCode.Delete, mac: { @@ -86,7 +86,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const COPY_FILE_ID = 'filesExplorer.copy'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: COPY_FILE_ID, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(explorerCommandsWeightBonus), + weight: KeybindingWeight.WorkbenchContrib + explorerCommandsWeightBonus, when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerRootContext.toNegated()), primary: KeyMod.CtrlCmd | KeyCode.KEY_C, handler: copyFileHandler, @@ -96,7 +96,7 @@ const PASTE_FILE_ID = 'filesExplorer.paste'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: PASTE_FILE_ID, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(explorerCommandsWeightBonus), + weight: KeybindingWeight.WorkbenchContrib + explorerCommandsWeightBonus, when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerResourceNotReadonlyContext), primary: KeyMod.CtrlCmd | KeyCode.KEY_V, handler: pasteFileHandler @@ -114,8 +114,8 @@ const copyRelativePathCommand = { // Editor Title Context Menu appendEditorTitleContextMenuItem(REVEAL_IN_OS_COMMAND_ID, REVEAL_IN_OS_LABEL, ResourceContextKey.Scheme.isEqualTo(Schemas.file)); -appendEditorTitleContextMenuItem(COPY_PATH_COMMAND_ID, copyPathCommand.title, ResourceContextKey.IsFile, copyRelativePathCommand); -appendEditorTitleContextMenuItem(REVEAL_IN_EXPLORER_COMMAND_ID, nls.localize('revealInSideBar', "Reveal in Side Bar"), ResourceContextKey.IsFile); +appendEditorTitleContextMenuItem(COPY_PATH_COMMAND_ID, copyPathCommand.title, ResourceContextKey.HasResource, copyRelativePathCommand); +appendEditorTitleContextMenuItem(REVEAL_IN_EXPLORER_COMMAND_ID, nls.localize('revealInSideBar', "Reveal in Side Bar"), ResourceContextKey.HasResource); function appendEditorTitleContextMenuItem(id: string, title: string, when: ContextKeyExpr, alt?: { id: string, title: string }): void { @@ -186,7 +186,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { group: 'navigation', order: 10, command: openToSideCommand, - when: ResourceContextKey.IsFile + when: ResourceContextKey.HasResource }); const revealInOsCommand = { @@ -205,7 +205,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { order: 40, command: copyPathCommand, alt: copyRelativePathCommand, - when: ResourceContextKey.IsFile + when: ResourceContextKey.HasResource }); MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { @@ -216,7 +216,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { title: SAVE_FILE_LABEL, precondition: DirtyEditorContext }, - when: ContextKeyExpr.and(ResourceContextKey.IsFile, AutoSaveContext.notEqualsTo('afterDelay') && AutoSaveContext.notEqualsTo('')) + when: ContextKeyExpr.and(ResourceContextKey.HasResource, AutoSaveContext.notEqualsTo('afterDelay') && AutoSaveContext.notEqualsTo('')) }); MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { @@ -227,7 +227,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { title: nls.localize('revert', "Revert File"), precondition: DirtyEditorContext }, - when: ContextKeyExpr.and(ResourceContextKey.IsFile, AutoSaveContext.notEqualsTo('afterDelay') && AutoSaveContext.notEqualsTo('')) + when: ContextKeyExpr.and(ResourceContextKey.HasResource, AutoSaveContext.notEqualsTo('afterDelay') && AutoSaveContext.notEqualsTo('')) }); MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { @@ -256,7 +256,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { title: nls.localize('compareWithSaved', "Compare with Saved"), precondition: DirtyEditorContext }, - when: ContextKeyExpr.and(ResourceContextKey.IsFile, AutoSaveContext.notEqualsTo('afterDelay') && AutoSaveContext.notEqualsTo(''), WorkbenchListDoubleSelection.toNegated()) + when: ContextKeyExpr.and(ResourceContextKey.HasResource, AutoSaveContext.notEqualsTo('afterDelay') && AutoSaveContext.notEqualsTo(''), WorkbenchListDoubleSelection.toNegated()) }); const compareResourceCommand = { @@ -372,21 +372,21 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { group: '3_compare', order: 20, command: compareResourceCommand, - when: ContextKeyExpr.and(ExplorerFolderContext.toNegated(), ResourceContextKey.IsFile, ResourceSelectedForCompareContext, WorkbenchListDoubleSelection.toNegated()) + when: ContextKeyExpr.and(ExplorerFolderContext.toNegated(), ResourceContextKey.HasResource, ResourceSelectedForCompareContext, WorkbenchListDoubleSelection.toNegated()) }); MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { group: '3_compare', order: 30, command: selectForCompareCommand, - when: ContextKeyExpr.and(ExplorerFolderContext.toNegated(), ResourceContextKey.IsFile, WorkbenchListDoubleSelection.toNegated()) + when: ContextKeyExpr.and(ExplorerFolderContext.toNegated(), ResourceContextKey.HasResource, WorkbenchListDoubleSelection.toNegated()) }); MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { group: '3_compare', order: 30, command: compareSelectedCommand, - when: ContextKeyExpr.and(ExplorerFolderContext.toNegated(), ResourceContextKey.IsFile, WorkbenchListDoubleSelection) + when: ContextKeyExpr.and(ExplorerFolderContext.toNegated(), ResourceContextKey.HasResource, WorkbenchListDoubleSelection) }); MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { @@ -415,7 +415,7 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { order: 30, command: copyPathCommand, alt: copyRelativePathCommand, - when: ResourceContextKey.IsFile + when: ResourceContextKey.HasResource }); MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { @@ -476,5 +476,82 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { }); // Empty Editor Group Context Menu -MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: 'workbench.action.files.newUntitledFile', title: nls.localize('newFile', "New File") }, group: '1_file', order: 10 }); +MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: GlobalNewUntitledFileAction.ID, title: nls.localize('newFile', "New File") }, group: '1_file', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: 'workbench.action.quickOpen', title: nls.localize('openFile', "Open File...") }, group: '1_file', order: 20 }); + +// File menu + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '1_new', + command: { + id: GlobalNewUntitledFileAction.ID, + title: nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New File") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '4_save', + command: { + id: SAVE_FILE_COMMAND_ID, + title: nls.localize({ key: 'miSave', comment: ['&& denotes a mnemonic'] }, "&&Save") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '4_save', + command: { + id: SAVE_FILE_AS_COMMAND_ID, + title: nls.localize({ key: 'miSaveAs', comment: ['&& denotes a mnemonic'] }, "Save &&As...") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '4_save', + command: { + id: SaveAllAction.ID, + title: nls.localize({ key: 'miSaveAll', comment: ['&& denotes a mnemonic'] }, "Save A&&ll") + }, + order: 3 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '5_autosave', + command: { + id: ToggleAutoSaveAction.ID, + title: nls.localize('miAutoSave', "Auto Save") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '6_close', + command: { + id: REVERT_FILE_COMMAND_ID, + title: nls.localize({ key: 'miRevert', comment: ['&& denotes a mnemonic'] }, "Re&&vert File"), + precondition: DirtyEditorContext + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '6_close', + command: { + id: CLOSE_EDITOR_COMMAND_ID, + title: nls.localize({ key: 'miCloseEditor', comment: ['&& denotes a mnemonic'] }, "&&Close Editor") + }, + order: 2 +}); + +// Go to menu + +MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { + group: 'z_go_to', + command: { + id: 'workbench.action.quickOpen', + title: nls.localize({ key: 'miGotoFile', comment: ['&& denotes a mnemonic'] }, "Go to &&File...") + }, + order: 1 +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 75e5b8c2d8c..ba7e84bb2f5 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -14,11 +14,9 @@ import { sequence, ITask, always } from 'vs/base/common/async'; import * as paths from 'vs/base/common/paths'; import * as resources from 'vs/base/common/resources'; import URI from 'vs/base/common/uri'; -import { posix } from 'path'; import * as errors from 'vs/base/common/errors'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import * as strings from 'vs/base/common/strings'; -import * as diagnostics from 'vs/base/common/diagnostics'; import { Action, IAction } from 'vs/base/common/actions'; import { MessageType, IInputValidator } from 'vs/base/browser/ui/inputbox/inputBox'; import { ITree, IHighlightEvent } from 'vs/base/parts/tree/browser/tree'; @@ -301,7 +299,7 @@ class RenameFileAction extends BaseRenameAction { public runAction(newName: string): TPromise { const parentResource = this.element.parent.resource; - const targetResource = parentResource.with({ path: paths.join(parentResource.path, newName) }); + const targetResource = resources.joinPath(parentResource, newName); return this.textFileService.move(this.element.resource, targetResource); } @@ -497,7 +495,7 @@ class CreateFileAction extends BaseCreateAction { public runAction(fileName: string): TPromise { const resource = this.element.parent.resource; - return this.fileService.createFile(resource.with({ path: paths.join(resource.path, fileName) })).then(stat => { + return this.fileService.createFile(resources.joinPath(resource, fileName)).then(stat => { return this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }); }, (error) => { this.onErrorWithRetry(error, () => this.runAction(fileName)); @@ -524,7 +522,7 @@ class CreateFolderAction extends BaseCreateAction { public runAction(fileName: string): TPromise { const resource = this.element.parent.resource; - return this.fileService.createFolder(resource.with({ path: paths.join(resource.path, fileName) })).then(null, (error) => { + return this.fileService.createFolder(resources.joinPath(resource, fileName)).then(null, (error) => { this.onErrorWithRetry(error, () => this.runAction(fileName)); }); } @@ -785,9 +783,9 @@ export class AddFilesAction extends BaseFileAction { this._updateEnablement(); } - public run(resources: URI[]): TPromise { + public run(resourcesToAdd: URI[]): TPromise { const addPromise = TPromise.as(null).then(() => { - if (resources && resources.length > 0) { + if (resourcesToAdd && resourcesToAdd.length > 0) { // Find parent to add to let targetElement: ExplorerItem; @@ -812,8 +810,8 @@ export class AddFilesAction extends BaseFileAction { }); let overwritePromise: TPromise = TPromise.as({ confirmed: true }); - if (resources.some(resource => { - return targetNames.has(isLinux ? paths.basename(resource.fsPath) : paths.basename(resource.fsPath).toLowerCase()); + if (resourcesToAdd.some(resource => { + return targetNames.has(!resources.hasToIgnoreCase(resource) ? resources.basename(resource) : resources.basename(resource).toLowerCase()); })) { const confirm: IConfirmation = { message: nls.localize('confirmOverwrite', "A file or folder with the same name already exists in the destination folder. Do you want to replace it?"), @@ -832,10 +830,10 @@ export class AddFilesAction extends BaseFileAction { // Run add in sequence const addPromisesFactory: ITask>[] = []; - resources.forEach(resource => { + resourcesToAdd.forEach(resource => { addPromisesFactory.push(() => { const sourceFile = resource; - const targetFile = targetElement.resource.with({ path: paths.join(targetElement.resource.path, paths.basename(sourceFile.path)) }); + const targetFile = resources.joinPath(targetElement.resource, resources.basename(sourceFile)); // if the target exists and is dirty, make sure to revert it. otherwise the dirty contents // of the target file would replace the contents of the added file. since we already @@ -846,11 +844,11 @@ export class AddFilesAction extends BaseFileAction { } return revertPromise.then(() => { - const target = targetElement.resource.with({ path: posix.join(targetElement.resource.path, posix.basename(sourceFile.path)) }); + const target = resources.joinPath(targetElement.resource, resources.basename(sourceFile)); return this.fileService.copyFile(sourceFile, target, true).then(stat => { // if we only add one file, just open it directly - if (resources.length === 1) { + if (resourcesToAdd.length === 1) { this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }); } }, error => this.onError(error)); @@ -1021,14 +1019,14 @@ export class DuplicateFileAction extends BaseFileAction { function findValidPasteFileTarget(targetFolder: ExplorerItem, fileToPaste: { resource: URI, isDirectory?: boolean }): URI { let name = resources.basenameOrAuthority(fileToPaste.resource); - let candidate = targetFolder.resource.with({ path: paths.join(targetFolder.resource.path, name) }); + let candidate = resources.joinPath(targetFolder.resource, name); while (true) { if (!targetFolder.root.find(candidate)) { break; } name = incrementFileName(name, fileToPaste.isDirectory); - candidate = targetFolder.resource.with({ path: paths.join(targetFolder.resource.path, name) }); + candidate = resources.joinPath(targetFolder.resource, name); } return candidate; @@ -1444,7 +1442,7 @@ export class ShowOpenedFileInNewWindow extends Action { public run(): TPromise { const fileResource = toResource(this.editorService.activeEditor, { supportSideBySide: true, filter: Schemas.file /* todo@remote */ }); if (fileResource) { - this.windowService.openWindow([fileResource.fsPath], { forceNewWindow: true, forceOpenWorkspaceAsFile: true }); + this.windowService.openWindow([fileResource], { forceNewWindow: true, forceOpenWorkspaceAsFile: true }); } else { this.notificationService.info(nls.localize('openFileToShowInNewWindow', "Open a file first to open in new window")); } @@ -1546,7 +1544,7 @@ export class CompareWithClipboardAction extends Action { this.registrationDisposal = this.textModelService.registerTextModelContentProvider(CompareWithClipboardAction.SCHEME, provider); } - const name = paths.basename(resource.fsPath); + const name = resources.basename(resource); const editorLabel = nls.localize('clipboardComparisonLabel', "Clipboard ↔ {0}", name); const cleanUp = () => { @@ -1580,14 +1578,6 @@ class ClipboardContentProvider implements ITextModelContentProvider { } } -// Diagnostics support -let diag: (...args: any[]) => void; -if (!diag) { - diag = diagnostics.register('FileActionsDiagnostics', function (...args: any[]) { - console.log(args[1] + ' - ' + args[0] + ' (time: ' + args[2].getTime() + ' [' + args[2].toUTCString() + '])'); - }); -} - interface IExplorerContext { viewletState: IFileViewletState; stat: ExplorerItem; diff --git a/src/vs/workbench/parts/files/electron-browser/fileCommands.ts b/src/vs/workbench/parts/files/electron-browser/fileCommands.ts index f6991efd4c5..5be4ed15f03 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileCommands.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileCommands.ts @@ -29,7 +29,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IEditorViewState } from 'vs/editor/common/editorCommon'; import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; import { isWindows, isMacintosh, isLinux } from 'vs/base/common/platform'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; @@ -79,10 +79,10 @@ export const ResourceSelectedForCompareContext = new RawContextKey('res export const REMOVE_ROOT_FOLDER_COMMAND_ID = 'removeRootFolder'; export const REMOVE_ROOT_FOLDER_LABEL = nls.localize('removeFolderFromWorkspace', "Remove Folder from Workspace"); -export const openWindowCommand = (accessor: ServicesAccessor, paths: string[], forceNewWindow: boolean) => { +//TODO #54483 support string paths for backward compatibility. check with @bpasero and remove if not necessary +export const openWindowCommand = (accessor: ServicesAccessor, paths: (string | URI)[], forceNewWindow: boolean) => { const windowService = accessor.get(IWindowService); - - windowService.openWindow(paths, { forceNewWindow }); + windowService.openWindow(paths.map(p => typeof p === 'string' ? URI.file(p) : p), { forceNewWindow }); }; function save( @@ -234,7 +234,7 @@ CommandsRegistry.registerCommand({ }); KeybindingsRegistry.registerCommandAndKeybindingRule({ - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ExplorerFocusCondition, primary: KeyMod.CtrlCmd | KeyCode.Enter, mac: { @@ -272,7 +272,7 @@ let provider: FileOnDiskContentProvider; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: COMPARE_WITH_SAVED_COMMAND_ID, when: undefined, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_D), handler: (accessor, resource: URI | object) => { if (!provider) { @@ -365,7 +365,7 @@ function revealResourcesInOS(resources: URI[], windowsService: IWindowsService, KeybindingsRegistry.registerCommandAndKeybindingRule({ id: REVEAL_IN_OS_COMMAND_ID, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: EditorContextKeys.focus.toNegated(), primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_R, win: { @@ -378,7 +378,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ }); KeybindingsRegistry.registerCommandAndKeybindingRule({ - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: undefined, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_R), id: 'workbench.action.files.revealActiveFileInWindows', @@ -419,7 +419,7 @@ function resourcesToClipboard(resources: URI[], clipboardService: IClipboardServ } KeybindingsRegistry.registerCommandAndKeybindingRule({ - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: EditorContextKeys.focus.toNegated(), primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C, win: { @@ -433,7 +433,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ }); KeybindingsRegistry.registerCommandAndKeybindingRule({ - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: EditorContextKeys.focus.toNegated(), primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_C, win: { @@ -447,7 +447,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ }); KeybindingsRegistry.registerCommandAndKeybindingRule({ - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: undefined, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_P), id: 'workbench.action.files.copyPathOfActiveFile', @@ -486,7 +486,7 @@ CommandsRegistry.registerCommand({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: SAVE_FILE_AS_COMMAND_ID, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: undefined, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_S, handler: (accessor, resourceOrObject: URI | object | { from: string }) => { @@ -504,7 +504,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ when: undefined, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.CtrlCmd | KeyCode.KEY_S, id: SAVE_FILE_COMMAND_ID, handler: (accessor, resource: URI | object) => { diff --git a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts index c152f150f4d..a34d3ded080 100644 --- a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts @@ -8,11 +8,11 @@ import URI from 'vs/base/common/uri'; import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, ToggleViewletAction } from 'vs/workbench/browser/viewlet'; import * as nls from 'vs/nls'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; -import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IEditorInputFactory, EditorInput, IFileEditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions } from 'vs/workbench/common/editor'; import { AutoSaveConfiguration, HotExitConfiguration, SUPPORTED_ENCODINGS } from 'vs/platform/files/common/files'; import { VIEWLET_ID, SortOrderConfiguration, FILE_EDITOR_INPUT_ID } from 'vs/workbench/parts/files/common/files'; @@ -34,6 +34,9 @@ import { DataUriEditorInput } from 'vs/workbench/common/editor/dataUriEditorInpu import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; +import { Schemas } from 'vs/base/common/network'; +import { nativeSep } from 'vs/base/common/paths'; // Viewlet Action export class OpenExplorerViewletAction extends ToggleViewletAction { @@ -50,6 +53,18 @@ export class OpenExplorerViewletAction extends ToggleViewletAction { } } +class FileUriDisplayContribution implements IWorkbenchContribution { + + constructor(@IUriDisplayService uriDisplayService: IUriDisplayService) { + uriDisplayService.registerFormater(Schemas.file, { + label: '${path}', + separator: nativeSep, + tildify: !platform.isWindows, + normalizeDriveLetter: platform.isWindows + }); + } +} + // Register Viewlet Registry.as(ViewletExtensions.Viewlets).registerViewlet(new ViewletDescriptor( ExplorerViewlet, @@ -156,6 +171,10 @@ Registry.as(WorkbenchExtensions.Workbench).regi // Register Dirty Files Tracker Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DirtyFilesTracker, LifecyclePhase.Starting); +// Register uri display for file uris +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(FileUriDisplayContribution, LifecyclePhase.Starting); + + // Configuration const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); @@ -167,7 +186,7 @@ configurationRegistry.registerConfiguration({ 'properties': { 'files.exclude': { 'type': 'object', - 'description': nls.localize('exclude', "Configure glob patterns for excluding files and folders. For example, the files explorer decides which files and folders to show or hide based on this setting."), + 'markdownDescription': nls.localize('exclude', "Configure glob patterns for excluding files and folders. For example, the files explorer decides which files and folders to show or hide based on this setting. Read more about glob patterns [here](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options)."), 'default': { '**/.git': true, '**/.svn': true, '**/.hg': true, '**/CVS': true, '**/.DS_Store': true }, 'scope': ConfigurationScope.RESOURCE, 'additionalProperties': { @@ -192,14 +211,14 @@ configurationRegistry.registerConfiguration({ }, 'files.associations': { 'type': 'object', - 'description': nls.localize('associations', "Configure file associations to languages (e.g. \"*.extension\": \"html\"). These have precedence over the default associations of the languages installed."), + 'markdownDescription': nls.localize('associations', "Configure file associations to languages (e.g. `\"*.extension\": \"html\"`). These have precedence over the default associations of the languages installed."), }, 'files.encoding': { 'type': 'string', 'overridable': true, 'enum': Object.keys(SUPPORTED_ENCODINGS), 'default': 'utf8', - 'description': nls.localize('encoding', "The default character set encoding to use when reading and writing files. This setting can be configured per language too."), + 'description': nls.localize('encoding', "The default character set encoding to use when reading and writing files. This setting can also be configured per language."), 'scope': ConfigurationScope.RESOURCE, 'enumDescriptions': Object.keys(SUPPORTED_ENCODINGS).map(key => SUPPORTED_ENCODINGS[key].labelLong) }, @@ -207,7 +226,7 @@ configurationRegistry.registerConfiguration({ 'type': 'boolean', 'overridable': true, 'default': false, - 'description': nls.localize('autoGuessEncoding', "When enabled, will attempt to guess the character set encoding when opening files. This setting can be configured per language too."), + 'description': nls.localize('autoGuessEncoding', "When enabled, the editor will attempt to guess the character set encoding when opening files. This setting can also be configured per language."), 'scope': ConfigurationScope.RESOURCE }, 'files.eol': { @@ -216,8 +235,12 @@ configurationRegistry.registerConfiguration({ '\n', '\r\n' ], + 'enumDescriptions': [ + nls.localize('eol.LF', "LF"), + nls.localize('eol.CRLF', "CRLF") + ], 'default': (platform.isLinux || platform.isMacintosh) ? '\n' : '\r\n', - 'description': nls.localize('eol', "The default end of line character. Use \\n for LF and \\r\\n for CRLF."), + 'description': nls.localize('eol', "The default end of line character."), 'scope': ConfigurationScope.RESOURCE }, 'files.trimTrailingWhitespace': { @@ -244,19 +267,19 @@ configurationRegistry.registerConfiguration({ 'files.autoSave': { 'type': 'string', 'enum': [AutoSaveConfiguration.OFF, AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, AutoSaveConfiguration.ON_WINDOW_CHANGE], - 'enumDescriptions': [ + 'markdownEnumDescriptions': [ nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'files.autoSave.off' }, "A dirty file is never automatically saved."), - nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'files.autoSave.afterDelay' }, "A dirty file is automatically saved after the configured 'files.autoSaveDelay'."), + nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'files.autoSave.afterDelay' }, "A dirty file is automatically saved after the configured `#files.autoSaveDelay#`."), nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'files.autoSave.onFocusChange' }, "A dirty file is automatically saved when the editor loses focus."), nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'files.autoSave.onWindowChange' }, "A dirty file is automatically saved when the window loses focus.") ], 'default': AutoSaveConfiguration.OFF, - 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSave' }, "Controls auto save of dirty files. Accepted values: '{0}', '{1}', '{2}' (editor loses focus), '{3}' (window loses focus). If set to '{4}', you can configure the delay in [`files.autoSaveDelay`](#files.autoSaveDelay). Read more about autosave [here](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save)", AutoSaveConfiguration.OFF, AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, AutoSaveConfiguration.ON_WINDOW_CHANGE, AutoSaveConfiguration.AFTER_DELAY) + 'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSave' }, "Controls auto save of dirty files. Read more about autosave [here](https://code.visualstudio.com/docs/editor/codebasics#_save-auto-save).", AutoSaveConfiguration.OFF, AutoSaveConfiguration.AFTER_DELAY, AutoSaveConfiguration.ON_FOCUS_CHANGE, AutoSaveConfiguration.ON_WINDOW_CHANGE, AutoSaveConfiguration.AFTER_DELAY) }, 'files.autoSaveDelay': { 'type': 'number', 'default': 1000, - 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSaveDelay' }, "Controls the delay in ms after which a dirty file is saved automatically. Only applies when [`files.autoSave`](#files.autoSave) is set to '{0}'", AutoSaveConfiguration.AFTER_DELAY) + 'markdownDescription': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'autoSaveDelay' }, "Controls the delay in ms after which a dirty file is saved automatically. Only applies when `#files.autoSave#` is set to `{0}`.", AutoSaveConfiguration.AFTER_DELAY) }, 'files.watcherExclude': { 'type': 'object', @@ -268,10 +291,10 @@ configurationRegistry.registerConfiguration({ 'type': 'string', 'enum': [HotExitConfiguration.OFF, HotExitConfiguration.ON_EXIT, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE], 'default': HotExitConfiguration.ON_EXIT, - 'enumDescriptions': [ + 'markdownEnumDescriptions': [ nls.localize('hotExit.off', 'Disable hot exit.'), - nls.localize('hotExit.onExit', 'Hot exit will be triggered when the application is closed, that is when the last window is closed on Windows/Linux or when the workbench.action.quit command is triggered (command palette, keybinding, menu). All windows with backups will be restored upon next launch.'), - nls.localize('hotExit.onExitAndWindowClose', 'Hot exit will be triggered when the application is closed, that is when the last window is closed on Windows/Linux or when the workbench.action.quit command is triggered (command palette, keybinding, menu), and also for any window with a folder opened regardless of whether it\'s the last window. All windows without folders opened will be restored upon next launch. To restore folder windows as they were before shutdown set "window.restoreWindows" to "all".') + nls.localize('hotExit.onExit', 'Hot exit will be triggered when the last window is closed on Windows/Linux or when the `workbench.action.quit` command is triggered (command palette, keybinding, menu). All windows with backups will be restored upon next launch.'), + nls.localize('hotExit.onExitAndWindowClose', 'Hot exit will be triggered when the last window is closed on Windows/Linux or when the `workbench.action.quit` command is triggered (command palette, keybinding, menu), and also for any window with a folder opened regardless of whether it\'s the last window. All windows without folders opened will be restored upon next launch. To restore folder windows as they were before shutdown set `#window.restoreWindows#` to `all`.') ], 'description': nls.localize('hotExit', "Controls whether unsaved files are remembered between sessions, allowing the save prompt when exiting the editor to be skipped.", HotExitConfiguration.ON_EXIT, HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) }, @@ -287,7 +310,7 @@ configurationRegistry.registerConfiguration({ 'files.maxMemoryForLargeFilesMB': { 'type': 'number', 'default': 4096, - 'description': nls.localize('maxMemoryForLargeFilesMB', "Controls the memory available to VS Code after restart when trying to open large files. Same effect as specifying --max-memory=NEWSIZE on the command line.") + 'markdownDescription': nls.localize('maxMemoryForLargeFilesMB', "Controls the memory available to VS Code after restart when trying to open large files. Same effect as specifying `--max-memory=NEWSIZE` on the command line.") } } }); @@ -308,7 +331,7 @@ configurationRegistry.registerConfiguration({ 'editor.formatOnSaveTimeout': { 'type': 'number', 'default': 750, - 'description': nls.localize('formatOnSaveTimeout', "Format on save timeout. Specifies a time limit in milliseconds for formatOnSave-commands. Commands taking longer than the specified timeout will be cancelled."), + 'description': nls.localize('formatOnSaveTimeout', "Timeout in milliseconds after which the formatting that is run on file save is cancelled."), 'overridable': true, 'scope': ConfigurationScope.RESOURCE } @@ -328,22 +351,22 @@ configurationRegistry.registerConfiguration({ }, 'explorer.autoReveal': { 'type': 'boolean', - 'description': nls.localize('autoReveal', "Controls if the explorer should automatically reveal and select files when opening them."), + 'description': nls.localize('autoReveal', "Controls whether the explorer should automatically reveal and select files when opening them."), 'default': true }, 'explorer.enableDragAndDrop': { 'type': 'boolean', - 'description': nls.localize('enableDragAndDrop', "Controls if the explorer should allow to move files and folders via drag and drop."), + 'description': nls.localize('enableDragAndDrop', "Controls whether the explorer should allow to move files and folders via drag and drop."), 'default': true }, 'explorer.confirmDragAndDrop': { 'type': 'boolean', - 'description': nls.localize('confirmDragAndDrop', "Controls if the explorer should ask for confirmation to move files and folders via drag and drop."), + 'description': nls.localize('confirmDragAndDrop', "Controls whether the explorer should ask for confirmation to move files and folders via drag and drop."), 'default': true }, 'explorer.confirmDelete': { 'type': 'boolean', - 'description': nls.localize('confirmDelete', "Controls if the explorer should ask for confirmation when deleting a file via the trash."), + 'description': nls.localize('confirmDelete', "Controls whether the explorer should ask for confirmation when deleting a file via the trash."), 'default': true }, 'explorer.sortOrder': { @@ -357,17 +380,27 @@ configurationRegistry.registerConfiguration({ nls.localize('sortOrder.type', 'Files and folders are sorted by their extensions, in alphabetical order. Folders are displayed before files.'), nls.localize('sortOrder.modified', 'Files and folders are sorted by last modified date, in descending order. Folders are displayed before files.') ], - 'description': nls.localize({ key: 'sortOrder', comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'] }, "Controls sorting order of files and folders in the explorer. In addition to the default sorting, you can set the order to 'mixed' (files and folders sorted combined), 'type' (by file type), 'modified' (by last modified date) or 'filesFirst' (sort files before folders).") + 'description': nls.localize('sortOrder', "Controls sorting order of files and folders in the explorer.") }, 'explorer.decorations.colors': { type: 'boolean', - description: nls.localize('explorer.decorations.colors', "Controls if file decorations should use colors."), + description: nls.localize('explorer.decorations.colors', "Controls whether file decorations should use colors."), default: true }, 'explorer.decorations.badges': { type: 'boolean', - description: nls.localize('explorer.decorations.badges', "Controls if file decorations should use badges."), + description: nls.localize('explorer.decorations.badges', "Controls whether file decorations should use badges."), default: true }, } }); + +// View menu +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '3_views', + command: { + id: VIEWLET_ID, + title: nls.localize({ key: 'miViewExplorer', comment: ['&& denotes a mnemonic'] }, "&&Explorer") + }, + order: 1 +}); diff --git a/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css b/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css index eb657380a67..c3fc2504f70 100644 --- a/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css +++ b/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css @@ -59,7 +59,7 @@ } .explorer-viewlet .explorer-open-editors .monaco-list .monaco-list-row:hover > .monaco-action-bar, -.explorer-viewlet .explorer-open-editors .monaco-list.focused .monaco-list-row.focused > .monaco-action-bar, +.explorer-viewlet .explorer-open-editors .monaco-list .monaco-list-row.focused > .monaco-action-bar, .explorer-viewlet .explorer-open-editors .monaco-list .monaco-list-row.dirty > .monaco-action-bar { visibility: visible; } diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts index cadc88c5131..40850fe8f72 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -196,6 +196,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView this.disposables.push(this.contextService.onDidChangeWorkspaceFolders(e => this.refreshFromEvent(e.added))); this.disposables.push(this.contextService.onDidChangeWorkbenchState(e => this.refreshFromEvent())); + this.disposables.push(this.fileService.onDidChangeFileSystemProviderRegistrations(() => this.refreshFromEvent())); } layoutBody(size: number): void { diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts index a59212c44df..af257952938 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -279,7 +279,7 @@ export class FileRenderer implements IRenderer { const parent = stat.name ? resources.dirname(stat.resource) : stat.resource; const value = stat.name || ''; - label.setFile(parent.with({ path: paths.join(parent.path, value || ' ') }), labelOptions); // Use icon for ' ' if name is empty. + label.setFile(resources.joinPath(parent, value || ' '), labelOptions); // Use icon for ' ' if name is empty. // Input field for name const inputBox = new InputBox(label.element, this.contextViewService, { @@ -291,7 +291,7 @@ export class FileRenderer implements IRenderer { const styler = attachInputBoxStyler(inputBox, this.themeService); inputBox.onDidChange(value => { - label.setFile(parent.with({ path: paths.join(parent.path, value || ' ') }), labelOptions); // update label icon while typing! + label.setFile(resources.joinPath(parent, value || ' '), labelOptions); // update label icon while typing! }); const lastDot = value.lastIndexOf('.'); @@ -390,7 +390,7 @@ export class FileRenderer implements IRenderer { export class FileAccessibilityProvider implements IAccessibilityProvider { public getAriaLabel(tree: ITree, stat: ExplorerItem): string { - return nls.localize('filesExplorerViewerAriaLabel', "{0}, Files Explorer", stat.name); + return stat.name; } } @@ -1058,7 +1058,7 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { } // Otherwise move - const targetResource = target.resource.with({ path: paths.join(target.resource.path, source.name) }); + const targetResource = resources.joinPath(target.resource, source.name); return this.textFileService.move(source.resource, targetResource).then(null, error => { diff --git a/src/vs/workbench/parts/files/test/electron-browser/explorerModel.test.ts b/src/vs/workbench/parts/files/test/electron-browser/explorerModel.test.ts index 210ff1bdb78..e88d9dadf02 100644 --- a/src/vs/workbench/parts/files/test/electron-browser/explorerModel.test.ts +++ b/src/vs/workbench/parts/files/test/electron-browser/explorerModel.test.ts @@ -18,7 +18,12 @@ function createStat(path: string, name: string, isFolder: boolean, hasChildren: } function toResource(path) { - return URI.file(join('C:\\', path)); + if (isWindows) { + return URI.file(join('C:\\', path)); + } else { + return URI.file(join('/home/john', path)); + } + } suite('Files - View Model', () => { diff --git a/src/vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts b/src/vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts index 340f821e0f4..ef0e09b3c44 100644 --- a/src/vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts +++ b/src/vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts @@ -141,19 +141,15 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo return; } - const extensionIdPostfix = this.getPossibleChineseMapping(locale); - const ceintlExtensionSearch = this.galleryService.query({ names: [`MS-CEINTL.vscode-language-pack-${extensionIdPostfix}`], pageSize: 1 }); - const tagSearch = this.galleryService.query({ text: `tag:lp-${locale}`, pageSize: 1 }); - - TPromise.join([ceintlExtensionSearch, tagSearch]).then(([ceintlResult, tagResult]) => { - if (ceintlResult.total === 0 && tagResult.total === 0) { + this.galleryService.query({ text: `tag:lp-${locale}` }).then(tagResult => { + if (tagResult.total === 0) { return; } - const extensionToInstall = ceintlResult.total === 1 ? ceintlResult.firstPage[0] : tagResult.total === 1 ? tagResult.firstPage[0] : null; - const extensionToFetchTranslationsFrom = extensionToInstall || (tagResult.total > 0 ? tagResult.firstPage[0] : null); + const extensionToInstall = tagResult.total === 1 ? tagResult.firstPage[0] : tagResult.firstPage.filter(e => e.publisher === 'MS-CEINTL' && e.name.indexOf('vscode-language-pack') === 0)[0]; + const extensionToFetchTranslationsFrom = extensionToInstall || tagResult.firstPage[0]; - if (!extensionToFetchTranslationsFrom || !extensionToFetchTranslationsFrom.assets.manifest) { + if (!extensionToFetchTranslationsFrom.assets.manifest) { return; } @@ -236,11 +232,6 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo } - private getPossibleChineseMapping(locale: string): string { - locale = locale.toLowerCase(); - return locale === 'zh-cn' ? 'zh-hans' : locale === 'zh-tw' ? 'zh-hant' : locale; - } - private getLanguagePackExtension(language: string): TPromise { return this.localizationService.getLanguageIds(LanguageType.Core) .then(coreLanguages => { diff --git a/src/vs/workbench/parts/localizations/electron-browser/localizationsActions.ts b/src/vs/workbench/parts/localizations/electron-browser/localizationsActions.ts index 7d22c8fc3a6..2290be2e5d0 100644 --- a/src/vs/workbench/parts/localizations/electron-browser/localizationsActions.ts +++ b/src/vs/workbench/parts/localizations/electron-browser/localizationsActions.ts @@ -6,15 +6,14 @@ import { localize } from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { IFileService } from 'vs/platform/files/common/files'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { TPromise } from 'vs/base/common/winjs.base'; import { IEditor } from 'vs/workbench/common/editor'; import { join } from 'vs/base/common/paths'; import URI from 'vs/base/common/uri'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { getPathLabel } from 'vs/base/common/labels'; import { language } from 'vs/base/common/platform'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; export class ConfigureLocaleAction extends Action { public static readonly ID = 'workbench.action.configureLocale'; @@ -31,9 +30,9 @@ export class ConfigureLocaleAction extends Action { constructor(id: string, label: string, @IFileService private fileService: IFileService, - @IWorkspaceContextService private contextService: IWorkspaceContextService, @IEnvironmentService private environmentService: IEnvironmentService, - @IEditorService private editorService: IEditorService + @IEditorService private editorService: IEditorService, + @IUriDisplayService private uriDisplayService: IUriDisplayService ) { super(id, label); } @@ -50,7 +49,7 @@ export class ConfigureLocaleAction extends Action { resource: stat.resource }); }, (error) => { - throw new Error(localize('fail.createSettings', "Unable to create '{0}' ({1}).", getPathLabel(file, this.environmentService, this.contextService), error)); + throw new Error(localize('fail.createSettings', "Unable to create '{0}' ({1}).", this.uriDisplayService.getLabel(file, true), error)); }); } } diff --git a/src/vs/workbench/parts/logs/electron-browser/logsActions.ts b/src/vs/workbench/parts/logs/electron-browser/logsActions.ts index 2fcdc413e3d..e9695b3cde0 100644 --- a/src/vs/workbench/parts/logs/electron-browser/logsActions.ts +++ b/src/vs/workbench/parts/logs/electron-browser/logsActions.ts @@ -9,13 +9,13 @@ import * as paths from 'vs/base/common/paths'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IQuickOpenService, IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; import { ILogService, LogLevel, DEFAULT_LOG_LEVEL } from 'vs/platform/log/common/log'; import { IOutputService, COMMAND_OPEN_LOG_VIEWER } from 'vs/workbench/parts/output/common/output'; import * as Constants from 'vs/workbench/parts/logs/common/logConstants'; import { ICommandService } from 'vs/platform/commands/common/commands'; import URI from 'vs/base/common/uri'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IQuickPickItem, IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; export class OpenLogsFolderAction extends Action { @@ -40,7 +40,7 @@ export class ShowLogsAction extends Action { static LABEL = nls.localize('showLogs', "Show Logs..."); constructor(id: string, label: string, - @IQuickOpenService private quickOpenService: IQuickOpenService, + @IQuickInputService private quickInputService: IQuickInputService, @IOutputService private outputService: IOutputService, @IWorkspaceContextService private contextService: IWorkspaceContextService ) { @@ -48,14 +48,14 @@ export class ShowLogsAction extends Action { } run(): TPromise { - const entries: IPickOpenEntry[] = [ + const entries: IQuickPickItem[] = [ { id: Constants.rendererLogChannelId, label: this.contextService.getWorkspace().name ? nls.localize('rendererProcess', "Window ({0})", this.contextService.getWorkspace().name) : nls.localize('emptyWindow', "Window") }, { id: Constants.extHostLogChannelId, label: nls.localize('extensionHost', "Extension Host") }, { id: Constants.sharedLogChannelId, label: nls.localize('sharedProcess', "Shared") }, { id: Constants.mainLogChannelId, label: nls.localize('mainProcess', "Main") } ]; - return this.quickOpenService.pick(entries, { placeHolder: nls.localize('selectProcess', "Select Log for Process") }) + return this.quickInputService.pick(entries, { placeHolder: nls.localize('selectProcess', "Select Log for Process") }) .then(entry => { if (entry) { return this.outputService.showChannel(entry.id); @@ -71,7 +71,7 @@ export class OpenLogFileAction extends Action { static LABEL = nls.localize('openLogFile', "Open Log File..."); constructor(id: string, label: string, - @IQuickOpenService private quickOpenService: IQuickOpenService, + @IQuickInputService private quickInputService: IQuickInputService, @IEnvironmentService private environmentService: IEnvironmentService, @ICommandService private commandService: ICommandService, @IWindowService private windowService: IWindowService, @@ -81,7 +81,7 @@ export class OpenLogFileAction extends Action { } run(): TPromise { - const entries: IPickOpenEntry[] = [ + const entries: IQuickPickItem[] = [ { id: URI.file(paths.join(this.environmentService.logsPath, `renderer${this.windowService.getCurrentWindowId()}.log`)).fsPath, label: this.contextService.getWorkspace().name ? nls.localize('rendererProcess', "Window ({0})", this.contextService.getWorkspace().name) : nls.localize('emptyWindow', "Window") }, { id: URI.file(paths.join(this.environmentService.logsPath, `exthost${this.windowService.getCurrentWindowId()}.log`)).fsPath, label: nls.localize('extensionHost', "Extension Host") }, { id: URI.file(paths.join(this.environmentService.logsPath, `sharedprocess.log`)).fsPath, label: nls.localize('sharedProcess', "Shared") }, @@ -89,7 +89,7 @@ export class OpenLogFileAction extends Action { { id: URI.file(paths.join(this.environmentService.logsPath, `telemetry.log`)).fsPath, label: nls.localize('telemetry', "Telemetry") } ]; - return this.quickOpenService.pick(entries, { placeHolder: nls.localize('selectProcess', "Select Log for Process") }) + return this.quickInputService.pick(entries, { placeHolder: nls.localize('selectProcess', "Select Log for Process") }) .then(entry => { if (entry) { return this.commandService.executeCommand(COMMAND_OPEN_LOG_VIEWER, URI.file(entry.id)); @@ -105,7 +105,7 @@ export class SetLogLevelAction extends Action { static LABEL = nls.localize('setLogLevel', "Set Log Level..."); constructor(id: string, label: string, - @IQuickOpenService private quickOpenService: IQuickOpenService, + @IQuickInputService private quickInputService: IQuickInputService, @ILogService private logService: ILogService ) { super(id, label); @@ -123,7 +123,7 @@ export class SetLogLevelAction extends Action { { label: nls.localize('off', "Off"), level: LogLevel.Off, description: this.getDescription(LogLevel.Off, current) }, ]; - return this.quickOpenService.pick(entries, { placeHolder: nls.localize('selectLogLevel', "Select log level"), autoFocus: { autoFocusIndex: this.logService.getLevel() } }).then(entry => { + return this.quickInputService.pick(entries, { placeHolder: nls.localize('selectLogLevel', "Select log level"), activeItem: entries[this.logService.getLevel()] }).then(entry => { if (entry) { this.logService.setLevel(entry.level); } diff --git a/src/vs/workbench/parts/markers/electron-browser/markers.contribution.ts b/src/vs/workbench/parts/markers/electron-browser/markers.contribution.ts index 789c79ff77b..8ed5e8f4b35 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markers.contribution.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markers.contribution.ts @@ -10,7 +10,7 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; -import { KeybindingsRegistry, IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight, IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { localize } from 'vs/nls'; import { Marker, RelatedInformation } from 'vs/workbench/parts/markers/electron-browser/markersModel'; @@ -27,7 +27,7 @@ import './markersFileDecorations'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Constants.MARKER_OPEN_SIDE_ACTION_ID, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(Constants.MarkerFocusContextKey), primary: KeyMod.CtrlCmd | KeyCode.Enter, mac: { @@ -41,7 +41,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Constants.MARKER_SHOW_PANEL_ID, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: undefined, primary: undefined, handler: (accessor, args: any) => { @@ -212,3 +212,12 @@ function registerAction(desc: IActionDescriptor) { }); } } + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '4_panels', + command: { + id: ToggleMarkersPanelAction.ID, + title: localize({ key: 'miMarker', comment: ['&& denotes a mnemonic'] }, "&&Problems") + }, + order: 4 +}); diff --git a/src/vs/workbench/parts/markers/electron-browser/markersFileDecorations.ts b/src/vs/workbench/parts/markers/electron-browser/markersFileDecorations.ts index 5f5b3ba56df..5338f344e90 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersFileDecorations.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersFileDecorations.ts @@ -49,7 +49,7 @@ class MarkersDecorationsProvider implements IDecorationsProvider { weight: 100 * first.severity, bubble: true, tooltip: markers.length === 1 ? localize('tooltip.1', "1 problem in this file") : localize('tooltip.N', "{0} problems in this file", markers.length), - letter: markers.length < 10 ? markers.length.toString() : '+9', + letter: markers.length < 10 ? markers.length.toString() : '9+', color: first.severity === MarkerSeverity.Error ? listErrorForeground : listWarningForeground, }; } diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts index dde2adc6b77..c89d699c248 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts @@ -19,7 +19,7 @@ import { Marker, ResourceMarkers, RelatedInformation } from 'vs/workbench/parts/ import { Controller } from 'vs/workbench/parts/markers/electron-browser/markersTreeController'; import * as Viewer from 'vs/workbench/parts/markers/electron-browser/markersTreeViewer'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { CollapseAllAction, MarkersFilterActionItem, MarkersFilterAction } from 'vs/workbench/parts/markers/electron-browser/markersPanelActions'; +import { CollapseAllAction, MarkersFilterActionItem, MarkersFilterAction, QuickFixAction, QuickFixActionItem } from 'vs/workbench/parts/markers/electron-browser/markersPanelActions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import Messages from 'vs/workbench/parts/markers/electron-browser/messages'; import { RangeHighlightDecorations } from 'vs/workbench/browser/parts/editor/rangeDecorations'; @@ -68,6 +68,7 @@ export class MarkersPanel extends Panel { this.delayedRefresh = new Delayer(500); this.autoExpanded = new Set(); this.panelSettings = this.getMemento(storageService, Scope.WORKSPACE); + this.setCurrentActiveEditor(); } public create(parent: HTMLElement): TPromise { @@ -196,7 +197,7 @@ export class MarkersPanel extends Panel { private createTree(parent: HTMLElement): void { this.treeContainer = dom.append(parent, dom.$('.tree-container.show-file-icons')); - const renderer = this.instantiationService.createInstance(Viewer.Renderer); + const renderer = this.instantiationService.createInstance(Viewer.Renderer, (action) => this.getActionItem(action)); const dnd = this.instantiationService.createInstance(SimpleFileResourceDragAndDrop, obj => obj instanceof ResourceMarkers ? obj.uri : void 0); const controller = this.instantiationService.createInstance(Controller); this.tree = this.instantiationService.createInstance(WorkbenchTree, this.treeContainer, { @@ -269,9 +270,13 @@ export class MarkersPanel extends Panel { } private onActiveEditorChanged(): void { + this.setCurrentActiveEditor(); + this.autoReveal(); + } + + private setCurrentActiveEditor(): void { const activeEditor = this.editorService.activeEditor; this.currentActiveResource = activeEditor ? activeEditor.getResource() : void 0; - this.autoReveal(); } private onSelected(): void { @@ -433,6 +438,9 @@ export class MarkersPanel extends Panel { if (action.id === MarkersFilterAction.ID) { return this.filterInputActionItem; } + if (action.id === QuickFixAction.ID) { + return this.instantiationService.createInstance(QuickFixActionItem, action); + } return super.getActionItem(action); } diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts index fd918657fe9..56c76acfaf4 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts @@ -10,7 +10,7 @@ import { Action, IAction } from 'vs/base/common/actions'; import { HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { TogglePanelAction } from 'vs/workbench/browser/panel'; import Messages from 'vs/workbench/parts/markers/electron-browser/messages'; import Constants from 'vs/workbench/parts/markers/electron-browser/constants'; @@ -24,12 +24,17 @@ import { attachInputBoxStyler, attachStylerCallback, attachCheckboxStyler } from import { IMarkersWorkbenchService } from 'vs/workbench/parts/markers/electron-browser/markers'; import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { BaseActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { BaseActionItem, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { badgeBackground, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { localize } from 'vs/nls'; import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ContextScopedHistoryInputBox } from 'vs/platform/widget/browser/contextScopedHistoryWidget'; +import { Marker, ResourceMarkers } from 'vs/workbench/parts/markers/electron-browser/markersModel'; +import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands'; +import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; export class ToggleMarkersPanelAction extends TogglePanelAction { @@ -269,4 +274,65 @@ export class MarkersFilterActionItem extends BaseActionItem { this._toDispose.push(t); return t; } +} + +export class QuickFixAction extends Action { + + public static readonly ID: string = 'workbench.actions.problems.quickfix'; + + constructor( + readonly marker: Marker, + readonly resourceMarkers: ResourceMarkers, + @IBulkEditService private bulkEditService: IBulkEditService, + @ICommandService private commandService: ICommandService, + @IEditorService private editorService: IEditorService + ) { + super(QuickFixAction.ID, Messages.MARKERS_PANEL_ACTION_TOOLTIP_QUICKFIX, 'markers-panel-action-quickfix', false); + resourceMarkers.hasFixes(marker).then(hasFixes => this.enabled = hasFixes); + } + + async getQuickFixActions(): Promise { + const codeActions = await this.resourceMarkers.getFixes(this.marker); + return codeActions.map(codeAction => new Action( + codeAction.command ? codeAction.command.id : codeAction.title, + codeAction.title, + void 0, + true, + () => { + return this.openFileAtMarker(this.marker) + .then(() => applyCodeAction(codeAction, this.bulkEditService, this.commandService)); + })); + } + + public openFileAtMarker(element: Marker): TPromise { + const { resource, selection } = { resource: element.resource, selection: element.range }; + return this.editorService.openEditor({ + resource, + options: { + selection, + preserveFocus: true, + pinned: false, + revealIfVisible: true + }, + }, ACTIVE_GROUP).then(() => null); + } +} + +export class QuickFixActionItem extends ActionItem { + + constructor(action: QuickFixAction, + @IContextMenuService private contextMenuService: IContextMenuService + ) { + super(null, action, { icon: true, label: false }); + } + + public onClick(event: DOM.EventLike): void { + DOM.EventHelper.stop(event, true); + const elementPosition = DOM.getDomNodePagePosition(this.builder.getHTMLElement()); + this.contextMenuService.showContextMenu({ + getAnchor: () => ({ x: elementPosition.left + 10, y: elementPosition.top + elementPosition.height }), + getActions: () => TPromise.wrap((this.getAction()).getQuickFixActions()), + }); + } + } \ No newline at end of file diff --git a/src/vs/workbench/parts/markers/electron-browser/markersTreeController.ts b/src/vs/workbench/parts/markers/electron-browser/markersTreeController.ts index 8fe527107df..142f95c65a2 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersTreeController.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersTreeController.ts @@ -10,16 +10,13 @@ import * as tree from 'vs/base/parts/tree/browser/tree'; import { MarkersModel, Marker, ResourceMarkers } from 'vs/workbench/parts/markers/electron-browser/markersModel'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; -import { IAction, Action } from 'vs/base/common/actions'; +import { IAction } from 'vs/base/common/actions'; import { ActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { WorkbenchTree, WorkbenchTreeController } from 'vs/platform/list/browser/listService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; -import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands'; -import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { localize } from 'vs/nls'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { QuickFixAction } from 'vs/workbench/parts/markers/electron-browser/markersPanelActions'; export class Controller extends WorkbenchTreeController { @@ -28,9 +25,7 @@ export class Controller extends WorkbenchTreeController { @IMenuService private menuService: IMenuService, @IKeybindingService private readonly _keybindingService: IKeybindingService, @IConfigurationService configurationService: IConfigurationService, - @IBulkEditService private bulkEditService: IBulkEditService, - @ICommandService private commandService: ICommandService, - @IEditorService private editorService: IEditorService + @IInstantiationService private instantiationService: IInstantiationService ) { super({}, configurationService); } @@ -80,15 +75,16 @@ export class Controller extends WorkbenchTreeController { private async _getMenuActions(tree: WorkbenchTree, element: any): Promise { const result: IAction[] = []; - if (element instanceof Marker) { - const quickFixActions = await this._getQuickFixActions(tree, element); - if (quickFixActions.length) { - result.push(...quickFixActions); - } else { - result.push(new Action('problems.no.fixes', localize('no fixes available', "No fixes available"), void 0, false)); + const parent = tree.getNavigator(element).parent(); + if (parent instanceof ResourceMarkers) { + const quickFixAction = this.instantiationService.createInstance(QuickFixAction, element, parent); + const quickFixActions = await quickFixAction.getQuickFixActions(); + if (quickFixActions.length) { + result.push(...quickFixActions); + result.push(new Separator()); + } } - result.push(new Separator()); } const menu = this.menuService.createMenu(MenuId.ProblemsPanelContext, tree.contextKeyService); @@ -104,34 +100,4 @@ export class Controller extends WorkbenchTreeController { result.pop(); // remove last separator return result; } - - private async _getQuickFixActions(tree: WorkbenchTree, element: Marker): Promise { - const parent = tree.getNavigator(element).parent(); - if (parent instanceof ResourceMarkers) { - const codeActions = await parent.getFixes(element); - return codeActions.map(codeAction => new Action( - codeAction.command ? codeAction.command.id : codeAction.title, - codeAction.title, - void 0, - true, - () => { - return this.openFileAtMarker(element) - .then(() => applyCodeAction(codeAction, this.bulkEditService, this.commandService)); - })); - } - return []; - } - - public openFileAtMarker(element: Marker): TPromise { - const { resource, selection } = { resource: element.resource, selection: element.range }; - return this.editorService.openEditor({ - resource, - options: { - selection, - preserveFocus: true, - pinned: false, - revealIfVisible: true - }, - }, ACTIVE_GROUP).then(() => null); - } } diff --git a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts index 0436d143f3b..133884c00dd 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts @@ -19,9 +19,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { getPathLabel } from 'vs/base/common/labels'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { ActionBar, IActionItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; +import { QuickFixAction } from 'vs/workbench/parts/markers/electron-browser/markersPanelActions'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; interface IResourceMarkersTemplateData { resourceLabel: ResourceLabel; @@ -31,7 +31,7 @@ interface IResourceMarkersTemplateData { interface IMarkerTemplateData { icon: HTMLElement; - lightbulb: HTMLElement; + actionBar: ActionBar; source: HighlightedLabel; description: HighlightedLabel; lnCol: HTMLElement; @@ -96,9 +96,10 @@ export class Renderer implements IRenderer { private static readonly RELATED_INFO_TEMPLATE_ID = 'related-info-template'; constructor( + private actionItemProvider: IActionItemProvider, @IInstantiationService private instantiationService: IInstantiationService, @IThemeService private themeService: IThemeService, - @IEnvironmentService private environmentService: IEnvironmentService + @IUriDisplayService private uriDisplayService: IUriDisplayService ) { } @@ -166,6 +167,9 @@ export class Renderer implements IRenderer { private renderRelatedInfoTemplate(container: HTMLElement): IRelatedInformationTemplateData { const data: IRelatedInformationTemplateData = Object.create(null); + dom.append(container, dom.$('.actions')); + dom.append(container, dom.$('.icon')); + data.resourceLabel = new HighlightedLabel(dom.append(container, dom.$('.related-info-resource'))); data.lnCol = dom.append(container, dom.$('span.marker-line')); @@ -179,8 +183,9 @@ export class Renderer implements IRenderer { private renderMarkerTemplate(container: HTMLElement): IMarkerTemplateData { const data: IMarkerTemplateData = Object.create(null); - data.icon = dom.append(container, dom.$('.marker-icon')); - data.lightbulb = dom.append(container, dom.$('.icon.lightbulb')); + const actionsContainer = dom.append(container, dom.$('.actions')); + data.actionBar = new ActionBar(actionsContainer, { actionItemProvider: this.actionItemProvider }); + data.icon = dom.append(container, dom.$('.icon')); data.source = new HighlightedLabel(dom.append(container, dom.$(''))); data.description = new HighlightedLabel(dom.append(container, dom.$('.marker-description'))); data.lnCol = dom.append(container, dom.$('span.marker-line')); @@ -203,7 +208,7 @@ export class Renderer implements IRenderer { if (templateData.resourceLabel instanceof FileLabel) { templateData.resourceLabel.setFile(element.uri, { matches: element.uriMatches }); } else { - templateData.resourceLabel.setLabel({ name: element.name, description: getPathLabel(element.uri, this.environmentService), resource: element.uri }, { matches: element.uriMatches }); + templateData.resourceLabel.setLabel({ name: element.name, description: this.uriDisplayService.getLabel(element.uri, true), resource: element.uri }, { matches: element.uriMatches }); } (templateData).count.setCount(element.filteredCount); } @@ -212,25 +217,25 @@ export class Renderer implements IRenderer { let marker = element.raw; templateData.icon.className = 'icon ' + Renderer.iconClassNameFor(marker); - dom.removeClass(templateData.lightbulb, 'quick-fixes'); templateData.source.set(marker.source, element.sourceMatches); dom.toggleClass(templateData.source.element, 'marker-source', !!marker.source); + templateData.actionBar.clear(); + const resourceMarkers: ResourceMarkers = tree.getNavigator(element).parent(); + const quickFixAction = this.instantiationService.createInstance(QuickFixAction, element, resourceMarkers); + templateData.actionBar.push([quickFixAction], { icon: true, label: false }); + templateData.description.set(marker.message, element.messageMatches); templateData.description.element.title = marker.message; templateData.lnCol.textContent = Messages.MARKERS_PANEL_AT_LINE_COL_NUMBER(marker.startLineNumber, marker.startColumn); - const parent = tree.getNavigator(element).parent(); - if (parent instanceof ResourceMarkers) { - parent.hasFixes(element).then(hasFixes => dom.toggleClass(templateData.lightbulb, 'quick-fixes', hasFixes)); - } } private renderRelatedInfoElement(tree: ITree, element: RelatedInformation, templateData: IRelatedInformationTemplateData) { templateData.resourceLabel.set(paths.basename(element.raw.resource.fsPath), element.uriMatches); - templateData.resourceLabel.element.title = getPathLabel(element.raw.resource, this.environmentService); + templateData.resourceLabel.element.title = this.uriDisplayService.getLabel(element.raw.resource, true); templateData.lnCol.textContent = Messages.MARKERS_PANEL_AT_LINE_COL_NUMBER(element.raw.startLineNumber, element.raw.startColumn); templateData.description.set(element.raw.message, element.messageMatches); templateData.description.element.title = element.raw.message; @@ -267,14 +272,13 @@ export class Renderer implements IRenderer { export class MarkersTreeAccessibilityProvider implements IAccessibilityProvider { constructor( - @IWorkspaceContextService private contextService: IWorkspaceContextService, - @IEnvironmentService private environmentService: IEnvironmentService + @IUriDisplayService private uriDisplayServie: IUriDisplayService ) { } public getAriaLabel(tree: ITree, element: any): string { if (element instanceof ResourceMarkers) { - const path = getPathLabel(element.uri, this.environmentService, this.contextService) || element.uri.fsPath; + const path = this.uriDisplayServie.getLabel(element.uri, true) || element.uri.fsPath; return Messages.MARKERS_TREE_ARIA_LABEL_RESOURCE(element.filteredCount, element.name, paths.dirname(path)); } if (element instanceof Marker) { diff --git a/src/vs/workbench/parts/markers/electron-browser/media/markers.css b/src/vs/workbench/parts/markers/electron-browser/media/markers.css index b351563aa6c..373b5718b2e 100644 --- a/src/vs/workbench/parts/markers/electron-browser/media/markers.css +++ b/src/vs/workbench/parts/markers/electron-browser/media/markers.css @@ -136,7 +136,7 @@ font-weight: bold; } -.markers-panel .icon { +.markers-panel .monaco-tree .markers-panel-tree-entry > .icon { height: 22px; margin-right: 6px; flex: 0 0 16px; @@ -166,17 +166,36 @@ background: url('status-info-inverse.svg') center center no-repeat; } -.markers-panel .icon.lightbulb { - background: url('lightbulb.svg') center/40% no-repeat; - position: absolute; - width: 24px; - height: 30px; +.markers-panel .monaco-tree .markers-panel-tree-entry .actions .action-label.icon.markers-panel-action-quickfix { + background: url('lightbulb.svg') center/80% no-repeat; + margin-right: 0px; } -.vs-dark .markers-panel .icon.lightbulb { - background: url('lightbulb-dark.svg') center/40% no-repeat; +.vs-dark .markers-panel .monaco-tree .markers-panel-tree-entry .actions .action-label.icon.markers-panel-action-quickfix { + background: url('lightbulb-dark.svg') center/80% no-repeat; } -.markers-panel .icon.lightbulb:not(.quick-fixes) { +.markers-panel .monaco-tree .monaco-tree-row .markers-panel-tree-entry > .actions { + width: 16px; +} + +.markers-panel .monaco-tree .monaco-tree-row .markers-panel-tree-entry > .actions .monaco-action-bar { + display: none; +} + +.markers-panel .monaco-tree .monaco-tree-row:hover .markers-panel-tree-entry > .actions .monaco-action-bar, +.markers-panel .monaco-tree .monaco-tree-row.selected .markers-panel-tree-entry > .actions .monaco-action-bar, +.markers-panel .monaco-tree .monaco-tree-row.focused .markers-panel-tree-entry > .actions .monaco-action-bar { + display: block; +} + +.markers-panel .monaco-tree .markers-panel-tree-entry .actions .action-label { + width: 16px; + height: 100%; + background-position: 50% 50%; + background-repeat: no-repeat; +} + +.markers-panel .monaco-tree .markers-panel-tree-entry .actions .action-item.disabled { display: none; } \ No newline at end of file diff --git a/src/vs/workbench/parts/markers/electron-browser/messages.ts b/src/vs/workbench/parts/markers/electron-browser/messages.ts index f54ddaea508..09e3eddbb5d 100644 --- a/src/vs/workbench/parts/markers/electron-browser/messages.ts +++ b/src/vs/workbench/parts/markers/electron-browser/messages.ts @@ -16,7 +16,7 @@ export default class Messages { public static MARKERS_PANEL_SHOW_LABEL: string = nls.localize('problems.view.focus.label', "Focus Problems (Errors, Warnings, Infos)"); public static PROBLEMS_PANEL_CONFIGURATION_TITLE: string = nls.localize('problems.panel.configuration.title', "Problems View"); - public static PROBLEMS_PANEL_CONFIGURATION_AUTO_REVEAL: string = nls.localize('problems.panel.configuration.autoreveal', "Controls if Problems view should automatically reveal files when opening them"); + public static PROBLEMS_PANEL_CONFIGURATION_AUTO_REVEAL: string = nls.localize('problems.panel.configuration.autoreveal', "Controls whether Problems view should automatically reveal files when opening them."); public static MARKERS_PANEL_TITLE_PROBLEMS: string = nls.localize('markers.panel.title.problems', "Problems"); public static MARKERS_PANEL_ARIA_LABEL_PROBLEMS_TREE: string = nls.localize('markers.panel.aria.label.problems.tree', "Problems grouped by files"); @@ -28,6 +28,7 @@ export default class Messages { public static MARKERS_PANEL_ACTION_TOOLTIP_USE_FILES_EXCLUDE: string = nls.localize('markers.panel.action.useFilesExclude', "Filter using Files Exclude Setting"); public static MARKERS_PANEL_ACTION_TOOLTIP_DO_NOT_USE_FILES_EXCLUDE: string = nls.localize('markers.panel.action.donotUseFilesExclude', "Do not use Files Exclude Setting"); public static MARKERS_PANEL_ACTION_TOOLTIP_FILTER: string = nls.localize('markers.panel.action.filter', "Filter Problems"); + public static MARKERS_PANEL_ACTION_TOOLTIP_QUICKFIX: string = nls.localize('markers.panel.action.quickfix', "Show fixes"); public static MARKERS_PANEL_FILTER_ARIA_LABEL: string = nls.localize('markers.panel.filter.ariaLabel', "Filter Problems"); public static MARKERS_PANEL_FILTER_PLACEHOLDER: string = nls.localize('markers.panel.filter.placeholder', "Filter. Eg: text, **/*.ts, !**/node_modules/**"); public static MARKERS_PANEL_FILTER_ERRORS: string = nls.localize('markers.panel.filter.errors', "errors"); diff --git a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.css b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.css index 858ba8e3266..23afd397253 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.css +++ b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.css @@ -71,11 +71,17 @@ display: none; } -.monaco-tree.focused .selected .outline-element-label, .monaco-tree.focused .selected .outline-element-decoration { +.monaco-tree.focused .selected .outline-element-label, .monaco-tree.focused .selected .outline-element-decoration{ /* make sure selection color wins when a label is being selected */ color: inherit !important; } +.monaco-tree.focused .selected .outline-element-label .monaco-highlighted-label .highlight, +.monaco-tree.focused .selected .monaco-icon-label .monaco-highlighted-label .highlight{ + /* allows text color to use the default when selected */ + color: inherit !important; +} + .monaco-workbench .outline-panel.no-icons .outline-element .outline-element-icon { display: none; } diff --git a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts index 8eda7d0263c..4123498f29d 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts +++ b/src/vs/workbench/parts/outline/electron-browser/outlinePanel.ts @@ -18,7 +18,7 @@ import { onUnexpectedError, isPromiseCanceledError } from 'vs/base/common/errors import { Emitter } from 'vs/base/common/event'; import { defaultGenerator } from 'vs/base/common/idGenerator'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { LRUCache } from 'vs/base/common/map'; import { escape } from 'vs/base/common/strings'; import URI from 'vs/base/common/uri'; @@ -39,7 +39,7 @@ import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/con import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { WorkbenchTree } from 'vs/platform/list/browser/listService'; import { IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -50,7 +50,6 @@ import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewl import { CollapseAction } from 'vs/workbench/browser/viewlet'; import { IViewsService } from 'vs/workbench/common/views'; import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/keybindingService'; import { OutlineConfigKeys, OutlineViewFiltered, OutlineViewFocused, OutlineViewId } from './outline'; import { OutlineController, OutlineDataSource, OutlineItemComparator, OutlineItemCompareType, OutlineItemFilter, OutlineRenderer, OutlineTreeState } from '../../../../editor/contrib/documentSymbols/outlineTree'; import { IResourceInput } from 'vs/platform/editor/common/editor'; @@ -256,12 +255,12 @@ export class OutlinePanel extends ViewletPanel { @IEditorService private readonly _editorService: IEditorService, @IMarkerService private readonly _markerService: IMarkerService, @IConfigurationService private readonly _configurationService: IConfigurationService, + @IKeybindingService private readonly _keybindingService: IKeybindingService, @IConfigurationService configurationService: IConfigurationService, - @IKeybindingService keybindingService: IKeybindingService, @IContextKeyService contextKeyService: IContextKeyService, @IContextMenuService contextMenuService: IContextMenuService, ) { - super(options, keybindingService, contextMenuService, configurationService); + super(options, _keybindingService, contextMenuService, configurationService); this._outlineViewState.restore(this._storageService); this._contextKeyFocused = OutlineViewFocused.bindTo(contextKeyService); this._contextKeyFiltered = OutlineViewFiltered.bindTo(contextKeyService); @@ -326,8 +325,6 @@ export class OutlinePanel extends ViewletPanel { const $this = this; const controller = new class extends OutlineController { - private readonly _mapper = KeyboardMapperFactory.INSTANCE; - constructor() { super({}, $this.configurationService); } @@ -340,22 +337,10 @@ export class OutlinePanel extends ViewletPanel { if (this.upKeyBindingDispatcher.has(event.keyCode)) { return false; } - if (event.ctrlKey || event.metaKey) { - // ignore ctrl/cmd-combination but not shift/alt-combinatios - return false; - } // crazy -> during keydown focus moves to the input box // and because of that the keyup event is handled by the // input field - const mapping = this._mapper.getRawKeyboardMapping(); - if (!mapping) { - return false; - } - const keyInfo = mapping[event.code]; - if (!keyInfo) { - return false; - } - if (keyInfo.value) { + if ($this._keybindingService.mightProducePrintableCharacter(event)) { $this._input.focus(); return true; } @@ -489,6 +474,10 @@ export class OutlinePanel extends ViewletPanel { return; } + if (TreeElement.empty(model)) { + return this._showMessage(localize('no-symbols', "No symbols found in document '{0}'", posix.basename(textModel.uri.path))); + } + let newSize = TreeElement.size(model); if (newSize > 7500) { // this is a workaround for performance issues with the tree: https://github.com/Microsoft/vscode/issues/18180 @@ -567,9 +556,7 @@ export class OutlinePanel extends ViewletPanel { } this._editorDisposables.push(this._input.onDidChange(onInputValueChanged)); - this._editorDisposables.push({ - dispose: () => this._contextKeyFiltered.reset() - }); + this._editorDisposables.push(toDisposable(() => this._contextKeyFiltered.reset())); // feature: reveal outline selection in editor // on change -> reveal/select defining range @@ -719,7 +706,7 @@ async function goUpOrDownToHighligthedElement(accessor: ServicesAccessor, prev: KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'outline.focusDownHighlighted', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, primary: KeyCode.DownArrow, when: ContextKeyExpr.and(OutlineViewFiltered, OutlineViewFocused), handler: accessor => goUpOrDownToHighligthedElement(accessor, false) @@ -727,7 +714,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'outline.focusUpHighlighted', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, primary: KeyCode.UpArrow, when: ContextKeyExpr.and(OutlineViewFiltered, OutlineViewFocused), handler: accessor => goUpOrDownToHighligthedElement(accessor, true) diff --git a/src/vs/workbench/parts/output/browser/outputActions.ts b/src/vs/workbench/parts/output/browser/outputActions.ts index 8543c9bb2b0..4334f2886d6 100644 --- a/src/vs/workbench/parts/output/browser/outputActions.ts +++ b/src/vs/workbench/parts/output/browser/outputActions.ts @@ -6,6 +6,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; +import * as aria from 'vs/base/browser/ui/aria/aria'; import { IAction, Action } from 'vs/base/common/actions'; import { IOutputService, OUTPUT_PANEL_ID, IOutputChannelRegistry, Extensions as OutputExt, IOutputChannelIdentifier, COMMAND_OPEN_LOG_VIEWER } from 'vs/workbench/parts/output/common/output'; import { SelectActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -49,6 +50,7 @@ export class ClearOutputAction extends Action { public run(): TPromise { this.outputService.getActiveChannel().clear(); + aria.status(nls.localize('outputCleared', "Output was cleared")); return TPromise.as(true); } diff --git a/src/vs/workbench/parts/output/common/outputLinkComputer.ts b/src/vs/workbench/parts/output/common/outputLinkComputer.ts index 54334478c76..71cba0a3ce8 100644 --- a/src/vs/workbench/parts/output/common/outputLinkComputer.ts +++ b/src/vs/workbench/parts/output/common/outputLinkComputer.ts @@ -9,6 +9,7 @@ import { ILink } from 'vs/editor/common/modes'; import { TPromise } from 'vs/base/common/winjs.base'; import URI from 'vs/base/common/uri'; import * as paths from 'vs/base/common/paths'; +import * as resources from 'vs/base/common/resources'; import * as strings from 'vs/base/common/strings'; import * as arrays from 'vs/base/common/arrays'; import { Range } from 'vs/editor/common/core/range'; @@ -70,7 +71,7 @@ export class OutputLinkComputer { const resourceCreator: IResourceCreator = { toResource: (folderRelativePath: string): URI => { if (typeof folderRelativePath === 'string') { - return folderUri.with({ path: paths.join(folderUri.path, folderRelativePath) }); + return resources.joinPath(folderUri, folderRelativePath); } return null; diff --git a/src/vs/workbench/parts/output/electron-browser/output.contribution.ts b/src/vs/workbench/parts/output/electron-browser/output.contribution.ts index 63be0b0f1c2..4442ede409c 100644 --- a/src/vs/workbench/parts/output/electron-browser/output.contribution.ts +++ b/src/vs/workbench/parts/output/electron-browser/output.contribution.ts @@ -187,3 +187,12 @@ CommandsRegistry.registerCommand(COMMAND_OPEN_LOG_VIEWER, function (accessor: Se } return null; }); + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '4_panels', + command: { + id: ToggleOutputAction.ID, + title: nls.localize({ key: 'miToggleOutput', comment: ['&& denotes a mnemonic'] }, "&&Output") + }, + order: 1 +}); diff --git a/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.ts b/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.ts index 2a3fab73b51..c72f997106d 100644 --- a/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.ts +++ b/src/vs/workbench/parts/preferences/browser/keybindingsEditorContribution.ts @@ -29,6 +29,7 @@ import { WindowsNativeResolvedKeybinding } from 'vs/workbench/services/keybindin import { themeColorFromId, ThemeColor } from 'vs/platform/theme/common/themeService'; import { overviewRulerInfo, overviewRulerError } from 'vs/editor/common/view/editorColorRegistry'; import { IModelDeltaDecoration, ITextModel, TrackedRangeStickiness, OverviewRulerLane } from 'vs/editor/common/model'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; const NLS_LAUNCH_MESSAGE = nls.localize('defineKeybinding.start', "Define Keybinding"); const NLS_KB_LAYOUT_ERROR_MESSAGE = nls.localize('defineKeybinding.kbLayoutErrorMessage', "You won't be able to produce this key combination under your current keyboard layout."); @@ -365,7 +366,8 @@ class DefineKeybindingCommand extends EditorCommand { precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.languageId.isEqualTo('jsonc')), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K) + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K), + weight: KeybindingWeight.EditorContrib } }); } diff --git a/src/vs/workbench/parts/preferences/browser/media/action-remove-dark.svg b/src/vs/workbench/parts/preferences/browser/media/action-remove-dark.svg new file mode 100644 index 00000000000..751e89b3b02 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/media/action-remove-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/media/action-remove.svg b/src/vs/workbench/parts/preferences/browser/media/action-remove.svg new file mode 100644 index 00000000000..fde34404d4e --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/media/action-remove.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/media/ellipsis-inverse.svg b/src/vs/workbench/parts/preferences/browser/media/ellipsis-inverse.svg new file mode 100644 index 00000000000..e3337557a23 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/media/ellipsis-inverse.svg @@ -0,0 +1 @@ +Ellipsis_bold_16x \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/media/ellipsis.svg b/src/vs/workbench/parts/preferences/browser/media/ellipsis.svg new file mode 100644 index 00000000000..e3f85623356 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/media/ellipsis.svg @@ -0,0 +1 @@ +Ellipsis_bold_16x \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css index 075de22d1aa..30a6e934f54 100644 --- a/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css @@ -8,18 +8,16 @@ } .settings-editor { - padding-top: 11px; - padding-left: 5px; - max-width: 1100px; + padding: 11px 24px 0px; + max-width: 1000px; margin: auto; } /* header styling */ .settings-editor > .settings-header { - padding-left: 17px; - padding-right: 11px; box-sizing: border-box; margin: auto; + overflow: hidden; } .settings-editor > .settings-header > .settings-preview-header { @@ -28,25 +26,13 @@ .settings-editor > .settings-header > .settings-preview-header .settings-preview-label { opacity: .7; -} - -.settings-editor > .settings-header > .settings-advanced-customization .open-settings-button, -.settings-editor > .settings-header > .settings-advanced-customization .open-settings-button:hover, -.settings-editor > .settings-header > .settings-advanced-customization .open-settings-button:active { - padding: 0; - text-decoration: underline; - display: inline; -} - -.settings-editor > .settings-header > .settings-advanced-customization { - opacity: .7; - margin-top: 8px; + white-space: nowrap; } .settings-editor > .settings-header > .settings-preview-header > .settings-preview-warning { text-align: right; text-transform: uppercase; - background: rgba(136, 136, 136, 0.3); + background: rgba(136, 136, 136, 0.2); border-radius: 2px; font-size: 0.8em; padding: 0 3px; @@ -66,19 +52,41 @@ width: 100%; } -.settings-editor > .settings-header .search-container > .settings-search-input > .monaco-inputbox .input { +.settings-editor > .settings-header .search-container .settings-search-input > .monaco-inputbox .input { font-size: 14px; - padding-left: 10px; + padding-left: 7px; } .settings-editor > .settings-header > .settings-header-controls { - margin-top: 8px; - height: 30px; + height: 32px; display: flex; + border-bottom: solid 1px; + margin-top: 10px; } -.settings-editor > .settings-header .settings-tabs-widget > .monaco-action-bar .action-item:not(:first-child) .action-label { - margin-left: 14px; +.settings-editor > .settings-header > .settings-header-controls .settings-tabs-widget .action-label { + opacity: 0.9; +} + +.settings-editor > .settings-header > .settings-header-controls .settings-tabs-widget .action-label:hover { + opacity: 1; +} + +.settings-editor > .settings-header > .settings-header-controls .settings-tabs-widget .action-label.checked { + font-weight: 500; + opacity: 1; +} + +.vs .settings-editor > .settings-header > .settings-header-controls { + border-color: #cccccc; +} + +.vs-dark .settings-editor > .settings-header > .settings-header-controls { + border-color: #3c3c3c; +} + +.settings-editor > .settings-header .settings-tabs-widget > .monaco-action-bar .action-item .action-label { + margin-right: 0px; } .settings-editor > .settings-header .settings-tabs-widget .monaco-action-bar .action-item .dropdown-icon { @@ -88,42 +96,71 @@ .settings-editor > .settings-header > .settings-header-controls .settings-header-controls-right { margin-left: auto; - padding-top: 3px; + padding-top: 4px; display: flex; } -.settings-editor > .settings-header > .settings-header-controls .settings-header-controls-right #configured-only-checkbox { - flex-shrink: 0; +.settings-editor > .settings-header > .settings-header-controls .settings-header-controls-right .toolbar-toggle-more { + display: block; + width: 22px; + height: 22px; + background-position: center; + background-repeat: no-repeat; + background-size: 16px; } -.settings-editor > .settings-header > .settings-header-controls .settings-header-controls-right .configured-only-label { - white-space: nowrap; - margin-right: 10px; - margin-left: 2px; - opacity: 0.7; +.vs .settings-editor > .settings-header > .settings-header-controls .settings-header-controls-right .toolbar-toggle-more { + background-image: url('ellipsis.svg'); +} + +.vs-dark .settings-editor > .settings-header > .settings-header-controls .settings-header-controls-right .toolbar-toggle-more { + background-image: url('ellipsis-inverse.svg'); +} + +.settings-editor > .settings-header > .settings-header-controls .settings-tabs-widget > .monaco-action-bar .action-item { + padding: 0px; /* padding must be on action-label because it has the bottom-border, because that's where the .checked class is */ +} + +.settings-editor > .settings-header > .settings-header-controls .settings-tabs-widget > .monaco-action-bar .action-item .action-label { + text-transform: none; + font-size: 13px; + + padding-bottom: 7px; + padding-top: 7px; + padding-left: 8px; + padding-right: 8px; } .settings-editor > .settings-body { display: flex; margin: auto; - max-width: 1100px; - justify-content: space-between; + max-width: 1000px; } -.settings-editor > .settings-body .settings-tree-container .monaco-tree::before { - outline: none !important; +.settings-editor.search-mode > .settings-body .settings-tree-container .monaco-tree-wrapper, +.settings-editor.search-mode > .settings-body > .settings-tree-container .setting-measure-container { + width: calc(100% - 11px); + margin-left: 0px; +} + +.settings-editor.narrow > .settings-body .settings-tree-container .monaco-tree-wrapper, +.settings-editor.narrow > .settings-body > .settings-tree-container .setting-measure-container { + width: calc(100% - 11px); + margin-left: 0px; } .settings-editor > .settings-body .settings-tree-container .monaco-tree-wrapper, .settings-editor > .settings-body > .settings-tree-container .setting-measure-container { - /** Allocate space for the scrollbar */ - width: calc(100% - 11px) + /** 11px for scrollbar + 208px for TOC margin */ + width: calc(100% - 219px); + margin-left: 208px; } - .settings-editor > .settings-body .settings-toc-container { - width: 175px; - margin-right: 5px; + position: absolute; + width: 160px; + margin-top: 16px; + padding-left: 5px; } .settings-editor > .settings-body .settings-toc-container.hidden { @@ -134,36 +171,61 @@ display: none; } -.settings-editor.search-mode > .settings-body .settings-tree-container { - max-width: 1100px; -} - .settings-editor.narrow > .settings-body .settings-toc-container { display: none; } +.settings-editor > .settings-body .settings-toc-container .monaco-scrollable-element > .shadow { + display: none; +} + +.settings-editor > .settings-body .settings-toc-container .monaco-tree-row .content { + display: flex; +} + .settings-editor > .settings-body .settings-toc-container .monaco-tree-row .settings-toc-entry { overflow: hidden; text-overflow: ellipsis; line-height: 22px; + opacity: 0.9; + flex-shrink: 1; +} + +.settings-editor > .settings-body .settings-toc-container .monaco-tree-row .settings-toc-count { + line-height: 22px; + opacity: 0.7; + margin-left: 3px; +} + +.settings-editor > .settings-body .settings-toc-container .monaco-tree-row.has-children > .content:before { + opacity: 0.9; +} + +.settings-editor > .settings-body .settings-toc-container .monaco-tree-row.has-children.selected > .content:before { + opacity: 1; } .settings-editor > .settings-body .settings-toc-container .monaco-tree-row .settings-toc-entry.no-results { opacity: 0.5; } +.settings-editor > .settings-body .settings-toc-container .monaco-tree-row.selected .settings-toc-entry { + font-weight: bold; + opacity: 1; +} + .settings-editor > .settings-body .settings-tree-container { flex: 1; - max-width: 875px; margin-right: 1px; /* So the item doesn't blend into the edge of the view container */ + margin-top: 14px; border-spacing: 0; border-collapse: separate; position: relative; } .settings-editor > .settings-body > .settings-tree-container .setting-item { - padding-top: 11px; - padding-bottom: 15px; + padding-top: 12px; + padding-bottom: 18px; box-sizing: border-box; cursor: default; white-space: normal; @@ -193,11 +255,11 @@ .settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-label, .settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-category { - font-weight: bold; + font-weight: 600; } .settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-category { - opacity: 0.7; + opacity: 0.9; } .settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description { @@ -205,14 +267,24 @@ overflow: hidden; text-overflow: ellipsis; height: 18px; + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + transform: translate3d(0px, 0px, 0px); } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description * { +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown * { margin: 0px; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description code { +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown a:focus { + outline: 1px solid -webkit-focus-ring-color; + outline-offset: -1px; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown code { line-height: 15px; /** For some reason, this is needed, otherwise will take up 20px height */ + font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; } .settings-editor > .settings-body > .settings-tree-container .setting-measure-container.monaco-tree-row { @@ -223,6 +295,16 @@ .settings-editor > .settings-body > .settings-tree-container .setting-item.is-expanded .setting-item-description, .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-measure-helper .setting-item-description { height: initial; + -webkit-line-clamp: initial; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-enumDescription { + display: none; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.is-expanded .setting-item-enumDescription, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-measure-helper .setting-item-enumDescription { + display: block; } .settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-item-value-description { @@ -230,15 +312,15 @@ } .settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-value-checkbox { - height: 16px; - width: 16px; + height: 18px; + width: 18px; border: 1px solid transparent; border-radius: 3px; - margin-right: 4px; + margin-right: 9px; margin-left: 0px; margin-top: 4px; padding: 0px; - background-size: 14px !important; + background-size: 16px !important; } .vs .settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-value-checkbox.checked { @@ -250,7 +332,7 @@ } .settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value { - margin-top: 7px; + margin-top: 9px; display: flex; } @@ -258,14 +340,17 @@ min-width: 200px; } +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-text .setting-item-control { + width: 500px; +} + .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-enum .setting-item-value > .setting-item-control, .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-text .setting-item-value > .setting-item-control { - flex: 1; min-width: initial; } .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-enum .setting-item-value > .setting-item-control > select { - width: 100%; + width: 320px; } .settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value .edit-in-settings-button, @@ -282,10 +367,20 @@ height: 26px; } +.settings-editor > .settings-body > .settings-tree-container .setting-item-new-extensions { + display: flex; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item-new-extensions .settings-new-extensions-button { + margin: auto; + width: initial; + padding: 4px 10px; +} + .settings-editor > .settings-body > .settings-tree-container .group-title, .settings-editor > .settings-body > .settings-tree-container .setting-item { - padding-left: 10px; - padding-right: 10px; + padding-left: 9px; + padding-right: 9px; } .settings-editor > .settings-body > .settings-tree-container .group-title { @@ -294,7 +389,7 @@ .settings-editor > .settings-body > .settings-tree-container .settings-group-title-label { margin: 0px; - font-weight: bold; + font-weight: 600; } .settings-editor > .settings-body > .settings-tree-container .settings-group-level-1 { @@ -303,12 +398,12 @@ } .settings-editor > .settings-body > .settings-tree-container .settings-group-level-2 { - padding-top: 27px; + padding-top: 32px; font-size: 20px; } .settings-editor > .settings-body > .settings-tree-container .settings-group-level-1.settings-group-first { - padding-top: 4px; + padding-top: 7px; } .settings-editor > .settings-body .settings-feedback-button { diff --git a/src/vs/workbench/parts/preferences/browser/media/settingsWidgets.css b/src/vs/workbench/parts/preferences/browser/media/settingsWidgets.css new file mode 100644 index 00000000000..b34fee0b67b --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/media/settingsWidgets.css @@ -0,0 +1,103 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-item-value > .setting-item-control { + width: 100%; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-pattern { + margin-right: 3px; + margin-left: 2px; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-pattern, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-sibling { + display: inline-block; + line-height: 22px; + font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-sibling { + opacity: 0.7; + margin-left: 0.5em; + font-size: 0.9em; + white-space: pre; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-action-bar { + display: none; + position: absolute; + right: 0px; + margin-top: 1px; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row { + position: relative; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row:focus { + outline: none; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row:hover .monaco-action-bar, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row.selected .monaco-action-bar { + display: block; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .action-label { + width: 16px; + height: 16px; + padding: 2px; + margin-right: 2px; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .setting-excludeAction-edit { + margin-right: 4px; +} + +.vs .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .setting-excludeAction-edit { + background: url(edit.svg) center center no-repeat; +} + +.vs-dark .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .setting-excludeAction-edit { + background: url(edit_inverse.svg) center center no-repeat; +} + +.vs .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .setting-excludeAction-remove { + background: url(action-remove.svg) center center no-repeat; +} + +.vs-dark .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .setting-excludeAction-remove { + background: url(action-remove-dark.svg) center center no-repeat; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-text-button { + width: initial; + padding: 2px 14px; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-item-control.setting-exclude-new-mode .setting-exclude-new-row { + display: none; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-text-button.setting-exclude-addButton { + margin-right: 10px; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-patternInput, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-siblingInput { + height: 22px; + max-width: 300px; + display: inline-block; + margin-right: 10px; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-okButton { + margin-right: 10px; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-widget { + margin-bottom: 1px; +} diff --git a/src/vs/workbench/parts/preferences/browser/preferencesActions.ts b/src/vs/workbench/parts/preferences/browser/preferencesActions.ts index 4c19890f0f3..95acd99a978 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesActions.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesActions.ts @@ -10,11 +10,13 @@ import URI from 'vs/base/common/uri'; import { Action } from 'vs/base/common/actions'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IModeService } from 'vs/editor/common/services/modeService'; -import { IQuickOpenService, IPickOpenEntry, IFilePickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; +import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; +import { getIconClasses } from 'vs/workbench/browser/labels'; +import { IModelService } from 'vs/editor/common/services/modelService'; export class OpenRawDefaultSettingsAction extends Action { @@ -125,6 +127,24 @@ export class OpenGlobalKeybindingsFileAction extends Action { } } +export class OpenDefaultKeybindingsFileAction extends Action { + + public static readonly ID = 'workbench.action.openDefaultKeybindingsFile'; + public static readonly LABEL = nls.localize('openDefaultKeybindingsFile', "Open Default Keyboard Shortcuts File"); + + constructor( + id: string, + label: string, + @IPreferencesService private preferencesService: IPreferencesService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + return this.preferencesService.openDefaultKeybindingsFile(); + } +} + export class OpenWorkspaceSettingsAction extends Action { public static readonly ID = 'workbench.action.openWorkspaceSettings'; @@ -207,8 +227,9 @@ export class ConfigureLanguageBasedSettingsAction extends Action { constructor( id: string, label: string, + @IModelService private modelService: IModelService, @IModeService private modeService: IModeService, - @IQuickOpenService private quickOpenService: IQuickOpenService, + @IQuickInputService private quickInputService: IQuickInputService, @IPreferencesService private preferencesService: IPreferencesService ) { super(id, label); @@ -216,7 +237,7 @@ export class ConfigureLanguageBasedSettingsAction extends Action { public run(): TPromise { const languages = this.modeService.getRegisteredLanguageNames(); - const picks: IPickOpenEntry[] = languages.sort().map((lang, index) => { + const picks: IQuickPickItem[] = languages.sort().map((lang, index) => { let description: string = nls.localize('languageDescriptionConfigured', "({0})", this.modeService.getModeIdForLanguageName(lang.toLowerCase())); // construct a fake resource to be able to show nice icons if any let fakeResource: URI; @@ -229,14 +250,14 @@ export class ConfigureLanguageBasedSettingsAction extends Action { fakeResource = URI.file(filenames[0]); } } - return { + return { label: lang, - resource: fakeResource, + iconClasses: getIconClasses(this.modelService, this.modeService, fakeResource), description - }; + } as IQuickPickItem; }); - return this.quickOpenService.pick(picks, { placeHolder: nls.localize('pickLanguage', "Select Language") }) + return this.quickInputService.pick(picks, { placeHolder: nls.localize('pickLanguage', "Select Language") }) .then(pick => { if (pick) { return this.modeService.getOrCreateModeByLanguageName(pick.label) diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index 1875e5070fc..3f1ba5f6ebd 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -242,7 +242,7 @@ export class PreferencesEditor extends BaseEditor { private triggerSearch(query: string): TPromise { if (query) { return TPromise.join([ - this.localSearchDelayer.trigger(() => this.preferencesRenderers.localFilterPreferences(query)), + this.localSearchDelayer.trigger(() => this.preferencesRenderers.localFilterPreferences(query).then(() => { })), this.remoteSearchThrottle.trigger(() => TPromise.wrap(this.progressService.showWhile(this.preferencesRenderers.remoteSearchPreferences(query), 500))) ]) as TPromise; } else { @@ -436,8 +436,10 @@ class PreferencesRenderersController extends Disposable { } private async _onEditableContentDidChange(): Promise { - await this.localFilterPreferences(this._lastQuery, true); - await this.remoteSearchPreferences(this._lastQuery, true); + const foundExactMatch = await this.localFilterPreferences(this._lastQuery, true); + if (!foundExactMatch) { + await this.remoteSearchPreferences(this._lastQuery, true); + } } onHidden(): void { @@ -446,6 +448,11 @@ class PreferencesRenderersController extends Disposable { } remoteSearchPreferences(query: string, updateCurrentResults?: boolean): TPromise { + if (this.lastFilterResult && this.lastFilterResult.exactMatch) { + // Skip and clear remote search + query = ''; + } + if (this._remoteFilterInProgress && this._remoteFilterInProgress.cancel) { // Resolved/rejected promises have no .cancel() this._remoteFilterInProgress.cancel(); @@ -466,7 +473,7 @@ class PreferencesRenderersController extends Disposable { }); } - localFilterPreferences(query: string, updateCurrentResults?: boolean): TPromise { + localFilterPreferences(query: string, updateCurrentResults?: boolean): TPromise { if (this._settingsNavigator) { this._settingsNavigator.reset(); } @@ -475,14 +482,15 @@ class PreferencesRenderersController extends Disposable { return this.filterOrSearchPreferences(query, this._currentLocalSearchProvider, 'filterResult', nls.localize('filterResult', "Filtered Results"), 0, updateCurrentResults); } - private filterOrSearchPreferences(query: string, searchProvider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, editableContentOnly?: boolean): TPromise { + private filterOrSearchPreferences(query: string, searchProvider: ISearchProvider, groupId: string, groupLabel: string, groupOrder: number, editableContentOnly?: boolean): TPromise { this._lastQuery = query; - const filterPs: TPromise[] = [this._filterOrSearchPreferences(query, this.editablePreferencesRenderer, searchProvider, groupId, groupLabel, groupOrder)]; + const filterPs: TPromise[] = [this._filterOrSearchPreferences(query, this.editablePreferencesRenderer, searchProvider, groupId, groupLabel, groupOrder)]; if (!editableContentOnly) { filterPs.push( this._filterOrSearchPreferences(query, this.defaultPreferencesRenderer, searchProvider, groupId, groupLabel, groupOrder)); - filterPs.push(this.searchAllSettingsTargets(query, searchProvider, groupId, groupLabel, groupOrder)); + filterPs.push( + this.searchAllSettingsTargets(query, searchProvider, groupId, groupLabel, groupOrder).then(() => null)); } return TPromise.join(filterPs).then(results => { @@ -494,6 +502,8 @@ class PreferencesRenderersController extends Disposable { this.consolidateAndUpdate(defaultFilterResult, editableFilterResult); this._lastFilterResult = defaultFilterResult; + + return defaultFilterResult && defaultFilterResult.exactMatch; }); } @@ -624,8 +634,10 @@ class PreferencesRenderersController extends Disposable { if (filterResult) { filterResult.query = filter; + filterResult.exactMatch = searchResult && searchResult.exactMatch; } + return filterResult; }); } diff --git a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts index 395c614d1d2..fc9229b091f 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts @@ -3,43 +3,41 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; -import * as nls from 'vs/nls'; -import { Delayer } from 'vs/base/common/async'; -import * as arrays from 'vs/base/common/arrays'; -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { Position } from 'vs/editor/common/core/position'; -import { IAction } from 'vs/base/common/actions'; -import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import { Event, Emitter } from 'vs/base/common/event'; -import { Registry } from 'vs/platform/registry/common/platform'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { Range, IRange } from 'vs/editor/common/core/range'; -import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IPreferencesService, ISettingsGroup, ISetting, IPreferencesEditorModel, IFilterResult, ISettingsEditorModel, IExtensionSetting, IScoredResults } from 'vs/workbench/services/preferences/common/preferences'; -import { SettingsEditorModel, DefaultSettingsEditorModel, WorkspaceConfigurationEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; -import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { SettingsGroupTitleWidget, EditPreferenceWidget, SettingsHeaderWidget, DefaultSettingsHeaderWidget, FloatingClickWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { RangeHighlightDecorations } from 'vs/workbench/browser/parts/editor/rangeDecorations'; -import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; -import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { overrideIdentifierFromKey, IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ITextModel, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model'; -import { CodeLensProviderRegistry, CodeLensProvider, ICodeLensSymbol } from 'vs/editor/common/modes'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { getDomNodePagePosition } from 'vs/base/browser/dom'; -import { IssueType, ISettingsSearchIssueReporterData, ISettingSearchResult } from 'vs/platform/issue/common/issue'; -import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; -import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { INotificationService } from 'vs/platform/notification/common/notification'; import { ContextSubMenu } from 'vs/base/browser/contextmenu'; +import { getDomNodePagePosition } from 'vs/base/browser/dom'; +import { IAction } from 'vs/base/common/actions'; +import * as arrays from 'vs/base/common/arrays'; +import { Delayer } from 'vs/base/common/async'; +import { Emitter, Event } from 'vs/base/common/event'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; +import { Position } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IModelDeltaDecoration, ITextModel, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; +import * as nls from 'vs/nls'; +import { ConfigurationTarget, IConfigurationService, overrideIdentifierFromKey } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationScope, Extensions as ConfigurationExtensions, IConfigurationPropertySchema, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ISettingSearchResult, ISettingsSearchIssueReporterData, IssueType } from 'vs/platform/issue/common/issue'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { RangeHighlightDecorations } from 'vs/workbench/browser/parts/editor/rangeDecorations'; +import { DefaultSettingsHeaderWidget, EditPreferenceWidget, FloatingClickWidget, SettingsGroupTitleWidget, SettingsHeaderWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { IWorkbenchSettingsConfiguration } from 'vs/workbench/parts/preferences/common/preferences'; +import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; +import { IFilterResult, IPreferencesEditorModel, IPreferencesService, IScoredResults, ISetting, ISettingsEditorModel, ISettingsGroup } from 'vs/workbench/services/preferences/common/preferences'; +import { DefaultSettingsEditorModel, SettingsEditorModel, WorkspaceConfigurationEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; export interface IPreferencesRenderer extends IDisposable { readonly preferencesModel: IPreferencesEditorModel; @@ -90,7 +88,6 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend this._register(this.editSettingActionRenderer.onUpdateSetting(({ key, value, source }) => this._updatePreference(key, value, source))); this._register(this.editor.getModel().onDidChangeContent(() => this.modelChangeDelayer.trigger(() => this.onModelChanged()))); - this.createHeader(); } public getAssociatedPreferencesModel(): IPreferencesEditorModel { @@ -100,6 +97,9 @@ export class UserSettingsRenderer extends Disposable implements IPreferencesRend public setAssociatedPreferencesModel(associatedPreferencesModel: IPreferencesEditorModel): void { this.associatedPreferencesModel = associatedPreferencesModel; this.editSettingActionRenderer.associatedPreferencesModel = associatedPreferencesModel; + + // Create header only in Settings editor mode + this.createHeader(); } protected createHeader(): void { @@ -242,7 +242,6 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR private issueWidgetRenderer: IssueWidgetRenderer; private feedbackWidgetRenderer: FeedbackWidgetRenderer; private bracesHidingRenderer: BracesHidingRenderer; - private extensionCodelensRenderer: ExtensionCodelensRenderer; private filterResult: IFilterResult; private readonly _onUpdatePreference: Emitter<{ key: string, value: any, source: IIndexedSetting }> = new Emitter<{ key: string, value: any, source: IIndexedSetting }>(); @@ -269,7 +268,6 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR this.feedbackWidgetRenderer = this._register(instantiationService.createInstance(FeedbackWidgetRenderer, editor)); this.bracesHidingRenderer = this._register(instantiationService.createInstance(BracesHidingRenderer, editor, preferencesModel)); this.hiddenAreasRenderer = this._register(instantiationService.createInstance(HiddenAreasRenderer, editor, [this.settingsGroupTitleRenderer, this.filteredMatchesRenderer, this.bracesHidingRenderer])); - this.extensionCodelensRenderer = this._register(instantiationService.createInstance(ExtensionCodelensRenderer, editor)); this._register(this.editSettingActionRenderer.onUpdateSetting(e => this._onUpdatePreference.fire(e))); this._register(this.settingsGroupTitleRenderer.onHiddenAreasChanged(() => this.hiddenAreasRenderer.render())); @@ -307,7 +305,6 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR this.settingHighlighter.clear(true); this.bracesHidingRenderer.render(filterResult, this.preferencesModel.settingsGroups); this.editSettingActionRenderer.render(filterResult.filteredGroups, this._associatedPreferencesModel); - this.extensionCodelensRenderer.render(filterResult); } else { this.settingHighlighter.clear(true); this.filteredMatchesRenderer.render(null, this.preferencesModel.settingsGroups); @@ -317,7 +314,6 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR this.settingsGroupTitleRenderer.showGroup(0); this.bracesHidingRenderer.render(null, this.preferencesModel.settingsGroups); this.editSettingActionRenderer.render(this.preferencesModel.settingsGroups, this._associatedPreferencesModel); - this.extensionCodelensRenderer.render(null); } this.hiddenAreasRenderer.render(); @@ -946,51 +942,6 @@ export class HighlightMatchesRenderer extends Disposable { } } -export class ExtensionCodelensRenderer extends Disposable implements CodeLensProvider { - private filterResult: IFilterResult; - - constructor() { - super(); - this._register(CodeLensProviderRegistry.register({ pattern: '**/settings.json' }, this)); - } - - public render(filterResult: IFilterResult): void { - this.filterResult = filterResult; - } - - public provideCodeLenses(model: ITextModel, token: CancellationToken): ICodeLensSymbol[] { - if (!this.filterResult || !this.filterResult.filteredGroups) { - return []; - } - - const newExtensionGroup = arrays.first(this.filterResult.filteredGroups, g => g.id === 'newExtensionsResult'); - if (!newExtensionGroup) { - return []; - } - - return newExtensionGroup.sections[0].settings - .filter((s: IExtensionSetting) => { - // Skip any non IExtensionSettings that somehow got in here - return s.extensionName && s.extensionPublisher; - }) - .map((s: IExtensionSetting) => { - const extId = s.extensionPublisher + '.' + s.extensionName; - return { - command: { - title: nls.localize('newExtensionLabel', "Show Extension \"{0}\"", extId), - id: 'workbench.extensions.action.showExtensionsWithId', - arguments: [extId.toLowerCase()] - }, - range: new Range(s.keyRange.startLineNumber, 1, s.keyRange.startLineNumber, 1) - }; - }); - } - - public resolveCodeLens(model: ITextModel, codeLens: ICodeLensSymbol, token: CancellationToken): ICodeLensSymbol { - return codeLens; - } -} - export interface IIndexedSetting extends ISetting { index: number; groupId: string; diff --git a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts index e3c77593bb1..4f94c79cb9e 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts @@ -4,48 +4,45 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; +import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { Button } from 'vs/base/browser/ui/button/button'; +import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; +import { Action } from 'vs/base/common/actions'; import * as arrays from 'vs/base/common/arrays'; import { Delayer, ThrottledDelayer } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import * as collections from 'vs/base/common/collections'; -import { Color, RGBA } from 'vs/base/common/color'; import { getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; -import { ITree, ITreeConfiguration } from 'vs/base/parts/tree/browser/tree'; -import { DefaultTreestyler, OpenMode } from 'vs/base/parts/tree/browser/treeDefaults'; +import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; +import { collapseAll, expandAll } from 'vs/base/parts/tree/browser/treeUtils'; import 'vs/css!./media/settingsEditor2'; import { localize } from 'vs/nls'; import { ConfigurationTarget, IConfigurationOverrides, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { WorkbenchTree, WorkbenchTreeController } from 'vs/platform/list/browser/listService'; +import { WorkbenchTree } from 'vs/platform/list/browser/listService'; import { ILogService } from 'vs/platform/log/common/log'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { editorBackground, focusBorder, foreground, registerColor } from 'vs/platform/theme/common/colorRegistry'; -import { attachButtonStyler, attachStyler } from 'vs/platform/theme/common/styler'; -import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { attachButtonStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { EditorOptions, IEditor } from 'vs/workbench/common/editor'; +import { PreferencesEditor } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; import { SearchWidget, SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { commonlyUsedData, tocData } from 'vs/workbench/parts/preferences/browser/settingsLayout'; -import { ISettingsEditorViewState, NonExpandableTree, resolveExtensionsSettings, resolveSettingsTree, SearchResultIdx, SearchResultModel, SettingsAccessibilityProvider, SettingsDataSource, SettingsRenderer, SettingsTreeController, SettingsTreeElement, SettingsTreeFilter, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/parts/preferences/browser/settingsTree'; -import { TOCDataSource, TOCRenderer, TOCTreeModel } from 'vs/workbench/parts/preferences/browser/tocTree'; -import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_FIRST_ROW_FOCUS, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, IPreferencesSearchService, ISearchProvider } from 'vs/workbench/parts/preferences/common/preferences'; +import { ISettingsEditorViewState, MODIFIED_SETTING_TAG, ONLINE_SERVICES_SETTING_TAG, resolveExtensionsSettings, resolveSettingsTree, SearchResultIdx, SearchResultModel, SettingsRenderer, SettingsTree, SettingsTreeElement, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/parts/preferences/browser/settingsTree'; +import { TOCRenderer, TOCTree, TOCTreeModel } from 'vs/workbench/parts/preferences/browser/tocTree'; +import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, IPreferencesSearchService, ISearchProvider } from 'vs/workbench/parts/preferences/common/preferences'; import { IPreferencesService, ISearchResult, ISettingsEditorModel } from 'vs/workbench/services/preferences/common/preferences'; import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { DefaultSettingsEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; const $ = DOM.$; -export const settingItemInactiveSelectionBorder = registerColor('settings.inactiveSelectedItemBorder', { - dark: '#3F3F46', - light: '#CCCEDB', - hc: null -}, localize('settingItemInactiveSelectionBorder', "The color of the selected setting row border, when the settings list does not have focus.")); - export class SettingsEditor2 extends BaseEditor { public static readonly ID: string = 'workbench.editor.settings2'; @@ -56,12 +53,11 @@ export class SettingsEditor2 extends BaseEditor { private headerContainer: HTMLElement; private searchWidget: SearchWidget; private settingsTargetsWidget: SettingsTargetsWidget; - - private showConfiguredSettingsOnlyCheckbox: HTMLInputElement; + private toolbar: ToolBar; private settingsTreeContainer: HTMLElement; - private settingsTree: WorkbenchTree; - private treeDataSource: SettingsDataSource; + private settingsTree: Tree; + private settingsTreeRenderer: SettingsRenderer; private tocTreeModel: TOCTreeModel; private settingsTreeModel: SettingsTreeModel; @@ -72,21 +68,23 @@ export class SettingsEditor2 extends BaseEditor { private localSearchDelayer: Delayer; private remoteSearchThrottle: ThrottledDelayer; private searchInProgress: TPromise; + private delayRefreshOnLayout: Delayer; private settingUpdateDelayer: Delayer; private pendingSettingUpdate: { key: string, value: any }; - private selectedElement: SettingsTreeElement; - private viewState: ISettingsEditorViewState; private searchResultModel: SearchResultModel; - private firstRowFocused: IContextKey; - private rowFocused: IContextKey; private tocRowFocused: IContextKey; private inSettingsEditorContextKey: IContextKey; private searchFocusContextKey: IContextKey; + private tagRegex = /(^|\s)@tag:("([^"]*)"|[^"]\S*)/g; + + /** Don't spam warnings */ + private hasWarnedMissingSettings: boolean; + constructor( @ITelemetryService telemetryService: ITelemetryService, @IConfigurationService private configurationService: IConfigurationService, @@ -96,20 +94,20 @@ export class SettingsEditor2 extends BaseEditor { @IPreferencesSearchService private preferencesSearchService: IPreferencesSearchService, @ILogService private logService: ILogService, @IEnvironmentService private environmentService: IEnvironmentService, - @IContextKeyService contextKeyService: IContextKeyService + @IContextKeyService contextKeyService: IContextKeyService, + @IContextMenuService private contextMenuService: IContextMenuService ) { super(SettingsEditor2.ID, telemetryService, themeService); this.delayedFilterLogging = new Delayer(1000); this.localSearchDelayer = new Delayer(100); this.remoteSearchThrottle = new ThrottledDelayer(200); this.viewState = { settingsTarget: ConfigurationTarget.USER }; + this.delayRefreshOnLayout = new Delayer(100); this.settingUpdateDelayer = new Delayer(500); this.inSettingsEditorContextKey = CONTEXT_SETTINGS_EDITOR.bindTo(contextKeyService); this.searchFocusContextKey = CONTEXT_SETTINGS_SEARCH_FOCUS.bindTo(contextKeyService); - this.firstRowFocused = CONTEXT_SETTINGS_FIRST_ROW_FOCUS.bindTo(contextKeyService); - this.rowFocused = CONTEXT_SETTINGS_ROW_FOCUS.bindTo(contextKeyService); this.tocRowFocused = CONTEXT_TOC_ROW_FOCUS.bindTo(contextKeyService); this._register(configurationService.onDidChangeConfiguration(e => { @@ -132,8 +130,9 @@ export class SettingsEditor2 extends BaseEditor { setInput(input: SettingsEditor2Input, options: EditorOptions, token: CancellationToken): Thenable { this.inSettingsEditorContextKey.set(true); return super.setInput(input, options, token) + .then(() => new Promise(process.nextTick)) // Force setInput to be async .then(() => { - this.render(token); + return this.render(token); }); } @@ -147,6 +146,8 @@ export class SettingsEditor2 extends BaseEditor { this.layoutTrees(dimension); DOM.toggleClass(this.rootElement, 'narrow', dimension.width < 600); + + this.delayRefreshOnLayout.trigger(() => this.refreshTreeAndMaintainFocus()); } focus(): void { @@ -154,32 +155,27 @@ export class SettingsEditor2 extends BaseEditor { } focusSettings(): void { - const selection = this.settingsTree.getSelection(); - if (selection && selection[0]) { - this.settingsTree.setFocus(selection[0]); - } else { - this.settingsTree.focusFirst(); + const firstFocusable = this.settingsTree.getHTMLElement().querySelector(SettingsRenderer.CONTROL_SELECTOR); + if (firstFocusable) { + (firstFocusable).focus(); } - - this.settingsTree.domFocus(); } focusSearch(): void { this.searchWidget.focus(); } - editSelectedSetting(): void { - const focus = this.settingsTree.getFocus(); - if (focus instanceof SettingsTreeSettingElement) { - const itemId = focus.id.replace(/\./g, '_'); - this.focusEditControlForRow(itemId); - } - } - clearSearchResults(): void { this.searchWidget.clear(); } + search(text: string): void { + if (this.searchWidget) { + this.searchWidget.focus(); + this.searchWidget.setValue(text); + } + } + private createHeader(parent: HTMLElement): void { this.headerContainer = DOM.append(parent, $('.settings-header')); @@ -200,26 +196,13 @@ export class SettingsEditor2 extends BaseEditor { })); this._register(this.searchWidget.onDidChange(() => this.onSearchInputChanged())); - const advancedCustomization = DOM.append(this.headerContainer, $('.settings-advanced-customization')); - const advancedCustomizationLabel = DOM.append(advancedCustomization, $('span.settings-advanced-customization-label')); - advancedCustomizationLabel.textContent = localize('advancedCustomizationLabel', "For advanced customizations open and edit") + ' '; - const openSettingsButton = this._register(new Button(advancedCustomization, { title: true, buttonBackground: null, buttonHoverBackground: null })); - this._register(attachButtonStyler(openSettingsButton, this.themeService, { - buttonBackground: Color.transparent.toString(), - buttonHoverBackground: Color.transparent.toString(), - buttonForeground: foreground - })); - openSettingsButton.label = localize('openSettingsLabel', "settings.json"); - openSettingsButton.element.classList.add('open-settings-button'); - - this._register(openSettingsButton.onDidClick(() => this.openSettingsFile())); - const headerControlsContainer = DOM.append(this.headerContainer, $('.settings-header-controls')); const targetWidgetContainer = DOM.append(headerControlsContainer, $('.settings-target-container')); this.settingsTargetsWidget = this._register(this.instantiationService.createInstance(SettingsTargetsWidget, targetWidgetContainer)); this.settingsTargetsWidget.settingsTarget = ConfigurationTarget.USER; this.settingsTargetsWidget.onDidTargetChange(() => { this.viewState.settingsTarget = this.settingsTargetsWidget.settingsTarget; + this.toolbar.context = { target: this.settingsTargetsWidget.settingsTarget }; this.settingsTreeModel.update(); this.refreshTreeAndMaintainFocus(); @@ -231,25 +214,48 @@ export class SettingsEditor2 extends BaseEditor { private createHeaderControls(parent: HTMLElement): void { const headerControlsContainerRight = DOM.append(parent, $('.settings-header-controls-right')); - this.showConfiguredSettingsOnlyCheckbox = DOM.append(headerControlsContainerRight, $('input#configured-only-checkbox')); - this.showConfiguredSettingsOnlyCheckbox.type = 'checkbox'; - const showConfiguredSettingsOnlyLabel = DOM.append(headerControlsContainerRight, $('label.configured-only-label')); - showConfiguredSettingsOnlyLabel.textContent = localize('showOverriddenOnly', "Show modified only"); - showConfiguredSettingsOnlyLabel.htmlFor = 'configured-only-checkbox'; + this.toolbar = this._register(new ToolBar(headerControlsContainerRight, this.contextMenuService, { + ariaLabel: localize('settingsToolbarLabel', "Settings Editor Actions"), + actionRunner: this.actionRunner + })); - this._register(DOM.addDisposableListener(this.showConfiguredSettingsOnlyCheckbox, 'change', e => this.onShowConfiguredOnlyClicked())); + const actions: Action[] = [ + this.instantiationService.createInstance(FilterByTagAction, + localize('filterModifiedLabel', "Show modified settings"), + MODIFIED_SETTING_TAG, + this) + ]; + if (this.environmentService.appQuality !== 'stable') { + actions.push( + this.instantiationService.createInstance( + FilterByTagAction, + localize('filterOnlineServicesLabel', "Show settings for online services"), + ONLINE_SERVICES_SETTING_TAG, + this)); + actions.push(new Separator()); + } + actions.push(this.instantiationService.createInstance(OpenSettingsAction)); + + this.toolbar.setActions([], actions)(); + this.toolbar.context = { target: this.settingsTargetsWidget.settingsTarget }; } private revealSetting(settingName: string): void { const element = this.settingsTreeModel.getElementByName(settingName); if (element) { - this.settingsTree.setSelection([element]); - this.settingsTree.setFocus(element); - this.settingsTree.reveal(element, 0); - this.settingsTree.domFocus(); + this.settingsTree.reveal(element, .1); } } + private revealSettingElement(element: SettingsTreeElement): void { + const top = this.settingsTree.getRelativeTop(element); + const clampedTop = Math.max( + Math.min(top, .9), + .1); + + this.settingsTree.reveal(element, clampedTop); + } + private openSettingsFile(): TPromise { const currentSettingsTarget = this.settingsTargetsWidget.settingsTarget; @@ -265,46 +271,92 @@ export class SettingsEditor2 extends BaseEditor { private createBody(parent: HTMLElement): void { const bodyContainer = DOM.append(parent, $('.settings-body')); - this.createTOC(bodyContainer); + this.createFocusSink( + bodyContainer, + e => { + if (DOM.findParentWithClass(e.relatedTarget, 'settings-editor-tree')) { + if (this.settingsTree.getScrollPosition() > 0) { + const firstElement = this.settingsTree.getFirstVisibleElement(); + this.settingsTree.reveal(firstElement, 0.1); + return true; + } + } else { + const firstControl = this.settingsTree.getHTMLElement().querySelector(SettingsRenderer.CONTROL_SELECTOR); + if (firstControl) { + (firstControl).focus(); + } + } + + return false; + }, + 'settings list focus helper'); + this.createSettingsTree(bodyContainer); + this.createFocusSink( + bodyContainer, + e => { + if (DOM.findParentWithClass(e.relatedTarget, 'settings-editor-tree')) { + if (this.settingsTree.getScrollPosition() < 1) { + const lastElement = this.settingsTree.getLastVisibleElement(); + this.settingsTree.reveal(lastElement, 0.9); + return true; + } + } else { + const controls = this.settingsTree.getHTMLElement().querySelectorAll(SettingsRenderer.CONTROL_SELECTOR); + const lastControl = controls && controls[controls.length]; + if (lastControl) { + (lastControl).focus(); + } + } + + return false; + }, + 'settings list focus helper' + ); + + this.createTOC(bodyContainer); + if (this.environmentService.appQuality !== 'stable') { this.createFeedbackButton(bodyContainer); } } + private createFocusSink(container: HTMLElement, callback: (e: any) => boolean, label: string): HTMLElement { + const listFocusSink = DOM.append(container, $('.settings-tree-focus-sink')); + listFocusSink.setAttribute('aria-label', label); + listFocusSink.tabIndex = 0; + this._register(DOM.addDisposableListener(listFocusSink, 'focus', (e: any) => { + if (e.relatedTarget && callback(e)) { + e.relatedTarget.focus(); + } + })); + + return listFocusSink; + } + private createTOC(parent: HTMLElement): void { + this.tocTreeModel = new TOCTreeModel(); this.tocTreeContainer = DOM.append(parent, $('.settings-toc-container')); - const tocDataSource = this.instantiationService.createInstance(TOCDataSource); const tocRenderer = this.instantiationService.createInstance(TOCRenderer); - this.tocTreeModel = new TOCTreeModel(); - this.tocTree = this.instantiationService.createInstance(WorkbenchTree, this.tocTreeContainer, - { - dataSource: tocDataSource, - renderer: tocRenderer, - controller: this.instantiationService.createInstance(WorkbenchTreeController, { openMode: OpenMode.DOUBLE_CLICK }), - filter: this.instantiationService.createInstance(SettingsTreeFilter, this.viewState) - }, + this.tocTree = this._register(this.instantiationService.createInstance(TOCTree, this.tocTreeContainer, + this.viewState, { - showLoading: false, - twistiePixels: 15 - }); + renderer: tocRenderer + })); this._register(this.tocTree.onDidChangeFocus(e => { const element = e.focus; if (this.searchResultModel) { this.viewState.filterToCategory = element; this.refreshTreeAndMaintainFocus(); - } else if (this.settingsTreeModel) { - if (element && !e.payload.fromScroll) { - this.settingsTree.reveal(element, 0); - this.settingsTree.setSelection([element]); - this.settingsTree.setFocus(element); - } } + if (element && (!e.payload || !e.payload.fromScroll)) { + this.settingsTree.reveal(element, 0); + } })); this._register(this.tocTree.onDidFocus(() => { @@ -326,103 +378,25 @@ export class SettingsEditor2 extends BaseEditor { private createSettingsTree(parent: HTMLElement): void { this.settingsTreeContainer = DOM.append(parent, $('.settings-tree-container')); - this.treeDataSource = this.instantiationService.createInstance(SettingsDataSource, this.viewState); - const renderer = this.instantiationService.createInstance(SettingsRenderer, this.settingsTreeContainer); - this._register(renderer.onDidChangeSetting(e => this.onDidChangeSetting(e.key, e.value))); - this._register(renderer.onDidOpenSettings(() => this.openSettingsFile())); - this._register(renderer.onDidClickSettingLink(settingName => this.revealSetting(settingName))); - - const treeClass = 'settings-editor-tree'; - this.settingsTree = this.instantiationService.createInstance(NonExpandableTree, this.settingsTreeContainer, - { - dataSource: this.treeDataSource, - renderer, - controller: this.instantiationService.createInstance(SettingsTreeController), - accessibilityProvider: this.instantiationService.createInstance(SettingsAccessibilityProvider), - filter: this.instantiationService.createInstance(SettingsTreeFilter, this.viewState), - styler: new DefaultTreestyler(DOM.createStyleSheet(), treeClass) - }, - { - ariaLabel: localize('treeAriaLabel', "Settings"), - showLoading: false, - indentPixels: 0, - twistiePixels: 0, - }); - - this._register(registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { - const activeBorderColor = theme.getColor(focusBorder); - if (activeBorderColor) { - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-tree:focus .monaco-tree-row.focused {outline: solid 1px ${activeBorderColor}; outline-offset: -1px; }`); - } - - const inactiveBorderColor = theme.getColor(settingItemInactiveSelectionBorder); - if (inactiveBorderColor) { - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .monaco-tree .monaco-tree-row.focused {outline: solid 1px ${inactiveBorderColor}; outline-offset: -1px; }`); - } - - const foregroundColor = theme.getColor(foreground); - if (foregroundColor) { - // Links appear inside other elements in markdown. CSS opacity acts like a mask. So we have to dynamically compute the description color to avoid - // applying an opacity to the link color. - const fgWithOpacity = new Color(new RGBA(foregroundColor.rgba.r, foregroundColor.rgba.g, foregroundColor.rgba.b, .7)); - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description { color: ${fgWithOpacity}; }`); - } - })); - - this.settingsTree.getHTMLElement().classList.add(treeClass); - - this._register(attachStyler(this.themeService, { - listActiveSelectionBackground: editorBackground, - listActiveSelectionForeground: foreground, - listFocusAndSelectionBackground: editorBackground, - listFocusAndSelectionForeground: foreground, - listFocusBackground: editorBackground, - listFocusForeground: foreground, - listHoverForeground: foreground, - listHoverBackground: editorBackground, - listInactiveSelectionBackground: editorBackground, - listInactiveSelectionForeground: foreground - }, colors => { - this.settingsTree.style(colors); - })); - - this._register(this.settingsTree.onDidChangeFocus(e => { - this.settingsTree.setSelection([e.focus]); - if (this.selectedElement) { - this.settingsTree.refresh(this.selectedElement); - } - - if (e.focus) { - this.settingsTree.refresh(e.focus); - } - - this.selectedElement = e.focus; - })); - - this._register(this.settingsTree.onDidBlur(() => { - this.rowFocused.set(false); - this.firstRowFocused.set(false); - })); - - this._register(this.settingsTree.onDidChangeSelection(e => { - this.updateTreeScrollSync(); - - let firstRowFocused = false; - let rowFocused = false; - const selection: SettingsTreeElement = e.selection[0]; - if (selection) { - rowFocused = true; - if (this.searchResultModel) { - firstRowFocused = selection.id === this.searchResultModel.getChildren()[0].id; - } else { - const firstRowId = this.settingsTreeModel.root.children[0] && this.settingsTreeModel.root.children[0].id; - firstRowFocused = selection.id === firstRowId; + this.settingsTreeRenderer = this.instantiationService.createInstance(SettingsRenderer, this.settingsTreeContainer); + this._register(this.settingsTreeRenderer.onDidChangeSetting(e => this.onDidChangeSetting(e.key, e.value))); + this._register(this.settingsTreeRenderer.onDidOpenSettings(settingKey => { + this.openSettingsFile().then(editor => { + if (editor instanceof PreferencesEditor && settingKey) { + editor.focusSearch(settingKey); } - } - - this.rowFocused.set(rowFocused); - this.firstRowFocused.set(firstRowFocused); + }); })); + this._register(this.settingsTreeRenderer.onDidClickSettingLink(settingName => this.revealSetting(settingName))); + this._register(this.settingsTreeRenderer.onDidFocusSetting(element => this.revealSettingElement(element))); + + this.settingsTree = this._register(this.instantiationService.createInstance(SettingsTree, + this.settingsTreeContainer, + this.viewState, + { + renderer: this.settingsTreeRenderer + })); + this.settingsTree.getHTMLElement().attributes.removeNamedItem('tabindex'); this._register(this.settingsTree.onDidScroll(() => { this.updateTreeScrollSync(); @@ -441,14 +415,6 @@ export class SettingsEditor2 extends BaseEditor { })); } - private onShowConfiguredOnlyClicked(): void { - this.viewState.showConfiguredOnly = this.showConfiguredSettingsOnlyCheckbox.checked; - this.refreshTreeAndMaintainFocus(); - this.tocTree.refresh(); - this.settingsTree.setScrollPosition(0); - this.expandAll(this.settingsTree); - } - private onDidChangeSetting(key: string, value: any): void { if (this.pendingSettingUpdate && this.pendingSettingUpdate.key !== key) { this.updateChangedSetting(key, value); @@ -467,25 +433,19 @@ export class SettingsEditor2 extends BaseEditor { return; } - let elementToSync = this.settingsTree.getFirstVisibleElement(); - const selection = this.settingsTree.getSelection()[0]; - if (selection) { - const selectionPos = this.settingsTree.getRelativeTop(selection); - if (selectionPos >= 0 && selectionPos <= 1) { - elementToSync = selection; - } - } - + const elementToSync = this.settingsTree.getFirstVisibleElement(); const element = elementToSync instanceof SettingsTreeSettingElement ? elementToSync.parent : elementToSync instanceof SettingsTreeGroupElement ? elementToSync : null; if (element && this.tocTree.getSelection()[0] !== element) { + this.tocTree.reveal(element); const elementTop = this.tocTree.getRelativeTop(element); - if (elementTop < 0) { - this.tocTree.reveal(element, 0); - } else if (elementTop > 1) { - this.tocTree.reveal(element, 1); + collapseAll(this.tocTree, element); + if (elementTop < 0 || elementTop > 1) { + this.tocTree.reveal(element); + } else { + this.tocTree.reveal(element, elementTop); } this.tocTree.setSelection([element]); @@ -515,7 +475,7 @@ export class SettingsEditor2 extends BaseEditor { query: this.searchWidget.getValue(), searchResults: this.searchResultModel && this.searchResultModel.getUniqueResults(), rawResults: this.searchResultModel && this.searchResultModel.getRawResults(), - showConfiguredOnly: this.viewState.showConfiguredOnly, + showConfiguredOnly: this.viewState.tagFilters && this.viewState.tagFilters.has(MODIFIED_SETTING_TAG), isReset: typeof value === 'undefined', settingsTarget: this.settingsTargetsWidget.settingsTarget as SettingsTarget }; @@ -590,8 +550,9 @@ export class SettingsEditor2 extends BaseEditor { return void 0; } + this._register(model.onDidChangeGroups(() => this.onConfigUpdate())); this.defaultSettingsEditorModel = model; - this.onConfigUpdate(); + return this.onConfigUpdate(); }); } return TPromise.as(null); @@ -607,9 +568,22 @@ export class SettingsEditor2 extends BaseEditor { private onConfigUpdate(): TPromise { const groups = this.defaultSettingsEditorModel.settingsGroups.slice(1); // Without commonlyUsed const dividedGroups = collections.groupBy(groups, g => g.contributedByExtension ? 'extension' : 'core'); - const resolvedSettingsRoot = resolveSettingsTree(tocData, dividedGroups.core); + const settingsResult = resolveSettingsTree(tocData, dividedGroups.core); + const resolvedSettingsRoot = settingsResult.tree; + + // Warn for settings not included in layout + if (settingsResult.leftoverSettings.size && !this.hasWarnedMissingSettings) { + let settingKeyList = []; + settingsResult.leftoverSettings.forEach(s => { + settingKeyList.push(s.key); + }); + + this.logService.warn(`SettingsEditor2: Settings not included in settingsLayout.ts: ${settingKeyList.join(', ')}`); + this.hasWarnedMissingSettings = true; + } + const commonlyUsed = resolveSettingsTree(commonlyUsedData, dividedGroups.core); - resolvedSettingsRoot.children.unshift(commonlyUsed); + resolvedSettingsRoot.children.unshift(commonlyUsed.tree); resolvedSettingsRoot.children.push(resolveExtensionsSettings(dividedGroups.extension || [])); @@ -619,6 +593,7 @@ export class SettingsEditor2 extends BaseEditor { if (this.settingsTreeModel) { this.settingsTreeModel.update(resolvedSettingsRoot); + return this.refreshTreeAndMaintainFocus(); } else { this.settingsTreeModel = this.instantiationService.createInstance(SettingsTreeModel, this.viewState, resolvedSettingsRoot); this.settingsTree.setInput(this.settingsTreeModel.root); @@ -631,7 +606,7 @@ export class SettingsEditor2 extends BaseEditor { } } - return this.refreshTreeAndMaintainFocus(); + return TPromise.wrap(null); } private refreshTreeAndMaintainFocus(): TPromise { @@ -649,13 +624,16 @@ export class SettingsEditor2 extends BaseEditor { } }) .then(() => { + // TODO@roblou - hack + this.tocTreeModel.update(); + return this.tocTree.refresh(); }); } private focusEditControlForRow(id: string, selection?: number): void { const rowSelector = `.setting-item#${id}`; - const inputElementToFocus: HTMLElement = this.settingsTreeContainer.querySelector(`${rowSelector} input, ${rowSelector} select, ${rowSelector} a, ${rowSelector} .monaco-custom-checkbox`); + const inputElementToFocus: HTMLElement = this.settingsTreeContainer.querySelector(`${rowSelector} input, ${rowSelector} select, ${rowSelector} .monaco-custom-checkbox`); if (inputElementToFocus) { inputElementToFocus.focus(); if (typeof selection === 'number') { @@ -675,6 +653,18 @@ export class SettingsEditor2 extends BaseEditor { } private triggerSearch(query: string): TPromise { + this.viewState.tagFilters = new Set(); + if (query) { + query = query.replace(this.tagRegex, (_, __, quotedTag, tag) => { + this.viewState.tagFilters.add(tag || quotedTag); + return ''; + }); + query = query.replace(`@${MODIFIED_SETTING_TAG}`, () => { + this.viewState.tagFilters.add(MODIFIED_SETTING_TAG); + return ''; + }); + } + query = query.trim(); if (query) { return this.searchInProgress = TPromise.join([ this.localSearchDelayer.trigger(() => this.localFilterPreferences(query)), @@ -694,17 +684,8 @@ export class SettingsEditor2 extends BaseEditor { this.viewState.filterToCategory = null; this.tocTree.refresh(); this.toggleSearchMode(); - this.settingsTree.setInput(this.settingsTreeModel.root); - - return TPromise.wrap(null); - } - } - - private expandAll(tree: ITree): void { - const nav = tree.getNavigator(); - let cur; - while (cur = nav.next()) { - tree.expand(cur); + collapseAll(this.tocTree); + return this.settingsTree.setInput(this.settingsTreeModel.root); } } @@ -758,17 +739,14 @@ export class SettingsEditor2 extends BaseEditor { } private filterOrSearchPreferences(query: string, type: SearchResultIdx, searchProvider: ISearchProvider): TPromise { - const filterPs: TPromise[] = [this._filterOrSearchPreferencesModel(query, this.defaultSettingsEditorModel, searchProvider)]; - let isCanceled = false; return new TPromise(resolve => { - return TPromise.join(filterPs).then(results => { + return this._filterOrSearchPreferencesModel(query, this.defaultSettingsEditorModel, searchProvider).then(result => { if (isCanceled) { // Handle cancellation like this because cancellation is lost inside the search provider due to async/await return null; } - const [result] = results; if (!this.searchResultModel) { this.searchResultModel = this.instantiationService.createInstance(SearchResultModel, this.viewState); this.searchResultModel.setResult(type, result); @@ -780,6 +758,8 @@ export class SettingsEditor2 extends BaseEditor { } this.tocTreeModel.update(); + expandAll(this.tocTree); + resolve(this.refreshTreeAndMaintainFocus()); }); }, () => { @@ -812,17 +792,66 @@ export class SettingsEditor2 extends BaseEditor { } private layoutTrees(dimension: DOM.Dimension): void { - const listHeight = dimension.height - (DOM.getDomNodePagePosition(this.headerContainer).height + 12 /*padding*/); - this.settingsTreeContainer.style.height = `${listHeight}px`; - this.settingsTree.layout(listHeight, 800); + const listHeight = dimension.height - (DOM.getDomNodePagePosition(this.headerContainer).height + 11 /*padding*/); + const settingsTreeHeight = listHeight - 14; + this.settingsTreeContainer.style.height = `${settingsTreeHeight}px`; + this.settingsTree.layout(settingsTreeHeight, 800); - const selectedSetting = this.settingsTree.getSelection()[0]; - if (selectedSetting) { - this.settingsTree.refresh(selectedSetting); - } + const tocTreeHeight = listHeight - 16; + this.tocTreeContainer.style.height = `${tocTreeHeight}px`; + this.tocTree.layout(tocTreeHeight, 175); - const tocHeight = listHeight - 5; // padding - this.tocTreeContainer.style.height = `${tocHeight}px`; - this.tocTree.layout(tocHeight, 175); + this.settingsTreeRenderer.updateWidth(dimension.width); + } +} + +interface ISettingsToolbarContext { + target: SettingsTarget; +} + +class OpenSettingsAction extends Action { + static readonly ID = 'settings.openSettingsJson'; + static readonly LABEL = localize('openSettingsJsonLabel', "Open settings.json"); + + constructor( + @IPreferencesService private readonly preferencesService: IPreferencesService, + ) { + super(OpenSettingsAction.ID, OpenSettingsAction.LABEL, 'open-settings-json'); + } + + + run(context?: ISettingsToolbarContext): TPromise { + return this._run(context) + .then(() => { }); + } + + private _run(context?: ISettingsToolbarContext): TPromise { + const target = context && context.target; + if (target === ConfigurationTarget.USER) { + return this.preferencesService.openGlobalSettings(); + } else if (target === ConfigurationTarget.WORKSPACE) { + return this.preferencesService.openWorkspaceSettings(); + } else if (URI.isUri(target)) { + return this.preferencesService.openFolderSettings(target); + } + + return TPromise.wrap(null); + } +} + +class FilterByTagAction extends Action { + static readonly ID = 'settings.filterByTag'; + + constructor( + label: string, + private tag: string, + private settingsEditor: SettingsEditor2 + ) { + super(FilterByTagAction.ID, label, 'toggle-filter-tag'); + } + + run(): TPromise { + this.settingsEditor.search(this.tag === MODIFIED_SETTING_TAG ? `@${this.tag} ` : `@tag:${this.tag} `); + return TPromise.as(null); } } diff --git a/src/vs/workbench/parts/preferences/browser/settingsLayout.ts b/src/vs/workbench/parts/preferences/browser/settingsLayout.ts index db7fd6c80ac..ef3cccf1611 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsLayout.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsLayout.ts @@ -84,6 +84,11 @@ export const tocData: ITOCEntry = { label: localize('appearance', "Appearance"), settings: ['workbench.activityBar.*', 'workbench.*color*', 'workbench.fontAliasing', 'workbench.iconTheme', 'workbench.sidebar.location', 'workbench.*.visible', 'workbench.tips.enabled', 'workbench.tree.*', 'workbench.view.*'] }, + { + id: 'workbench/breadcrumbs', + label: localize('breadcrumbs', "Breadcrumbs"), + settings: ['breadcrumbs.*'] + }, { id: 'workbench/editor', label: localize('editorManagement', "Editor Management"), @@ -128,7 +133,7 @@ export const tocData: ITOCEntry = { children: [ { id: 'features/explorer', - label: localize('fileExplorer', "File Explorer"), + label: localize('fileExplorer', "Explorer"), settings: ['explorer.*', 'outline.*'] }, { diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index e9e02fd07a4..59737f81ccb 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -12,6 +12,7 @@ import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox'; import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox'; import * as arrays from 'vs/base/common/arrays'; +import { Color, RGBA } from 'vs/base/common/color'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -20,69 +21,29 @@ import * as objects from 'vs/base/common/objects'; import { escapeRegExpCharacters, startsWith } from 'vs/base/common/strings'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IAccessibilityProvider, IDataSource, IFilter, IRenderer, ITree } from 'vs/base/parts/tree/browser/tree'; +import { IAccessibilityProvider, IDataSource, IFilter, IRenderer as ITreeRenderer, ITree, ITreeConfiguration } from 'vs/base/parts/tree/browser/tree'; +import { DefaultTreestyler } from 'vs/base/parts/tree/browser/treeDefaults'; +import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; import { localize } from 'vs/nls'; +import { ICommandService } from 'vs/platform/commands/common/commands'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { WorkbenchTree, WorkbenchTreeController } from 'vs/platform/list/browser/listService'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IListService, WorkbenchTreeController } from 'vs/platform/list/browser/listService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { inputBackground, inputBorder, inputForeground, registerColor, selectBackground, selectBorder, selectForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; -import { attachInputBoxStyler, attachSelectBoxStyler } from 'vs/platform/theme/common/styler'; +import { editorBackground, focusBorder, foreground } from 'vs/platform/theme/common/colorRegistry'; +import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler, attachStyler } from 'vs/platform/theme/common/styler'; import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { SettingsTarget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { ITOCEntry } from 'vs/workbench/parts/preferences/browser/settingsLayout'; -import { ISearchResult, ISetting, ISettingsGroup } from 'vs/workbench/services/preferences/common/preferences'; +import { ExcludeSettingWidget, IExcludeDataItem, settingsHeaderForeground, settingsNumberInputBackground, settingsNumberInputBorder, settingsNumberInputForeground, settingsSelectBackground, settingsSelectBorder, settingsSelectForeground, settingsTextInputBackground, settingsTextInputBorder, settingsTextInputForeground } from 'vs/workbench/parts/preferences/browser/settingsWidgets'; +import { IExtensionSetting, ISearchResult, ISetting, ISettingsGroup } from 'vs/workbench/services/preferences/common/preferences'; const $ = DOM.$; -export const modifiedItemForeground = registerColor('settings.modifiedItemForeground', { - light: '#019001', - dark: '#73C991', - hc: '#73C991' -}, localize('modifiedItemForeground', "(For settings editor preview) The foreground color for a modified setting.")); - -// Enum control colors -export const settingsSelectBackground = registerColor('settings.dropdownBackground', { dark: selectBackground, light: selectBackground, hc: selectBackground }, localize('settingsDropdownBackground', "Settings editor dropdown background.")); -export const settingsSelectForeground = registerColor('settings.dropdownForeground', { dark: selectForeground, light: selectForeground, hc: selectForeground }, localize('settingsDropdownForeground', "Settings editor dropdown foreground.")); -export const settingsSelectBorder = registerColor('settings.dropdownBorder', { dark: selectBorder, light: selectBorder, hc: selectBorder }, localize('settingsDropdownBorder', "Settings editor dropdown border.")); - -// Bool control colors -export const settingsCheckboxBackground = registerColor('settings.checkboxBackground', { dark: selectBackground, light: selectBackground, hc: selectBackground }, localize('settingsCheckboxBackground', "Settings editor checkbox background.")); -export const settingsCheckboxForeground = registerColor('settings.checkboxForeground', { dark: selectForeground, light: selectForeground, hc: selectForeground }, localize('settingsCheckboxForeground', "Settings editor checkbox foreground.")); -export const settingsCheckboxBorder = registerColor('settings.checkboxBorder', { dark: selectBorder, light: selectBorder, hc: selectBorder }, localize('settingsCheckboxBorder', "Settings editor checkbox border.")); - -// Text control colors -export const settingsTextInputBackground = registerColor('settings.textInputBackground', { dark: inputBackground, light: inputBackground, hc: inputBackground }, localize('textInputBoxBackground', "Settings editor text input box background.")); -export const settingsTextInputForeground = registerColor('settings.textInputForeground', { dark: inputForeground, light: inputForeground, hc: inputForeground }, localize('textInputBoxForeground', "Settings editor text input box foreground.")); -export const settingsTextInputBorder = registerColor('settings.textInputBorder', { dark: inputBorder, light: inputBorder, hc: inputBorder }, localize('textInputBoxBorder', "Settings editor text input box border.")); - -// Number control colors -export const settingsNumberInputBackground = registerColor('settings.numberInputBackground', { dark: inputBackground, light: inputBackground, hc: inputBackground }, localize('numberInputBoxBackground', "Settings editor number input box background.")); -export const settingsNumberInputForeground = registerColor('settings.numberInputForeground', { dark: inputForeground, light: inputForeground, hc: inputForeground }, localize('numberInputBoxForeground', "Settings editor number input box foreground.")); -export const settingsNumberInputBorder = registerColor('settings.numberInputBorder', { dark: inputBorder, light: inputBorder, hc: inputBorder }, localize('numberInputBoxBorder', "Settings editor number input box border.")); - -registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { - const modifiedItemForegroundColor = theme.getColor(modifiedItemForeground); - if (modifiedItemForegroundColor) { - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item.is-configured .setting-item-is-configured-label { color: ${modifiedItemForegroundColor}; }`); - } - - const checkboxBackgroundColor = theme.getColor(settingsCheckboxBackground); - if (checkboxBackgroundColor) { - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-value-checkbox { background-color: ${checkboxBackgroundColor} !important; }`); - } - - const checkboxBorderColor = theme.getColor(settingsCheckboxBorder); - if (checkboxBorderColor) { - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-value-checkbox { border-color: ${checkboxBorderColor} !important; }`); - } - - const link = theme.getColor(textLinkForeground); - if (link) { - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description a { color: ${link}; }`); - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description a > code { color: ${link}; }`); - } -}); +export const MODIFIED_SETTING_TAG = 'modified'; +export const ONLINE_SERVICES_SETTING_TAG = 'usesOnlineServices'; export abstract class SettingsTreeElement { id: string; @@ -96,16 +57,40 @@ export class SettingsTreeGroupElement extends SettingsTreeElement { isFirstGroup: boolean; } +export class SettingsTreeNewExtensionsElement extends SettingsTreeElement { + extensionIds: string[]; +} + export class SettingsTreeSettingElement extends SettingsTreeElement { setting: ISetting; displayCategory: string; displayLabel: string; + + /** + * scopeValue || defaultValue, for rendering convenience. + */ value: any; + + /** + * The value in the current settings scope. + */ + scopeValue: any; + + /** + * The default value + */ + defaultValue?: any; + + /** + * Whether the setting is configured in the selected scope. + */ isConfigured: boolean; + + tags?: Set; overriddenScopeList: string[]; description: string; - valueType: 'enum' | 'string' | 'integer' | 'number' | 'boolean' | 'complex'; + valueType: 'enum' | 'string' | 'integer' | 'number' | 'boolean' | 'exclude' | 'complex'; } export interface ITOCEntry { @@ -161,7 +146,8 @@ export class SettingsTreeModel { if (tocEntry.children) { element.children = tocEntry.children.map(child => this.createSettingsTreeGroupElement(child, element)); } else if (tocEntry.settings) { - element.children = tocEntry.settings.map(s => this.createSettingsTreeSettingElement(s, element)); + element.children = tocEntry.settings.map(s => this.createSettingsTreeSettingElement(s, element)) + .filter(el => el.setting.deprecationMessage ? el.isConfigured : true); } this._treeElementsById.set(element.id, element); @@ -193,7 +179,8 @@ function createSettingsTreeSettingElement(setting: ISetting, parent: any, settin element.id = sanitizeId(parent.id + '_' + setting.key); element.parent = parent; - const { isConfigured, inspected, targetSelector } = inspectSetting(setting.key, settingsTarget, configurationService); + const inspectResult = inspectSetting(setting.key, settingsTarget, configurationService); + const { isConfigured, inspected, targetSelector } = inspectResult; const displayValue = isConfigured ? inspected[targetSelector] : inspected.default; const overriddenScopeList = []; @@ -211,21 +198,69 @@ function createSettingsTreeSettingElement(setting: ISetting, parent: any, settin element.displayCategory = displayKeyFormat.category; element.value = displayValue; + element.scopeValue = isConfigured && inspected[targetSelector]; + element.defaultValue = inspected.default; + element.isConfigured = isConfigured; + if (isConfigured || setting.tags) { + element.tags = new Set(); + if (isConfigured) { + element.tags.add(MODIFIED_SETTING_TAG); + } + + if (setting.tags) { + setting.tags.forEach(tag => element.tags.add(tag)); + } + } + element.overriddenScopeList = overriddenScopeList; element.description = setting.description.join('\n'); - element.valueType = (setting.enum && (setting.type === 'string' || !setting.type)) ? 'enum' : - setting.type === 'string' ? 'string' : - setting.type === 'integer' ? 'integer' : - setting.type === 'number' ? 'number' : - setting.type === 'boolean' ? 'boolean' : - 'complex'; + if (setting.enum && (setting.type === 'string' || !setting.type)) { + element.valueType = 'enum'; + } else if (setting.type === 'string') { + element.valueType = 'string'; + } else if (isExcludeSetting(setting)) { + element.valueType = 'exclude'; + } else if (setting.type === 'integer') { + element.valueType = 'integer'; + } else if (setting.type === 'number') { + element.valueType = 'number'; + } else if (setting.type === 'boolean') { + element.valueType = 'boolean'; + } else { + element.valueType = 'complex'; + } return element; } -function inspectSetting(key: string, target: SettingsTarget, configurationService: IConfigurationService): { isConfigured: boolean, inspected: any, targetSelector: string } { +function getExcludeDisplayValue(element: SettingsTreeSettingElement): IExcludeDataItem[] { + const data = element.isConfigured ? + objects.mixin({ ...element.scopeValue }, element.defaultValue, false) : + element.defaultValue; + + return Object.keys(data) + .filter(key => !!data[key]) + .map(key => { + const value = data[key]; + const sibling = typeof value === 'boolean' ? undefined : value.when; + + return { + id: key, + pattern: key, + sibling + }; + }); +} + +interface IInspectResult { + isConfigured: boolean; + inspected: any; + targetSelector: string; +} + +function inspectSetting(key: string, target: SettingsTarget, configurationService: IConfigurationService): IInspectResult { const inspectOverrides = URI.isUri(target) ? { resource: target } : undefined; const inspected = configurationService.inspect(key, inspectOverrides); const targetSelector = target === ConfigurationTarget.USER ? 'user' : @@ -236,8 +271,12 @@ function inspectSetting(key: string, target: SettingsTarget, configurationServic return { isConfigured, inspected, targetSelector }; } -export function resolveSettingsTree(tocData: ITOCEntry, coreSettingsGroups: ISettingsGroup[]): ITOCEntry { - return _resolveSettingsTree(tocData, getFlatSettings(coreSettingsGroups)); +export function resolveSettingsTree(tocData: ITOCEntry, coreSettingsGroups: ISettingsGroup[]): { tree: ITOCEntry, leftoverSettings: Set } { + const allSettings = getFlatSettings(coreSettingsGroups); + return { + tree: _resolveSettingsTree(tocData, allSettings), + leftoverSettings: allSettings + }; } export function resolveExtensionsSettings(groups: ISettingsGroup[]): ITOCEntry { @@ -311,7 +350,9 @@ function getFlatSettings(settingsGroups: ISettingsGroup[]) { for (let group of settingsGroups) { for (let section of group.sections) { for (let s of section.settings) { - result.add(s); + if (!s.overrides || !s.overrides.length) { + result.add(s); + } } } } @@ -338,7 +379,7 @@ export class SettingsDataSource implements IDataSource { return false; } - getChildren(tree: ITree, element: SettingsTreeElement): TPromise { + getChildren(tree: ITree, element: SettingsTreeElement): TPromise { return TPromise.as(this._getChildren(element)); } @@ -353,8 +394,8 @@ export class SettingsDataSource implements IDataSource { } } - getParent(tree: ITree, element: SettingsTreeElement): TPromise { - return TPromise.wrap(element.parent); + getParent(tree: ITree, element: SettingsTreeElement): TPromise { + return TPromise.wrap(element && element.parent); } shouldAutoexpand(): boolean { @@ -363,17 +404,18 @@ export class SettingsDataSource implements IDataSource { } export function settingKeyToDisplayFormat(key: string, groupId = ''): { category: string, label: string } { - let label = wordifyKey(key); - const lastDotIdx = label.lastIndexOf('.'); + const lastDotIdx = key.lastIndexOf('.'); let category = ''; if (lastDotIdx >= 0) { - category = label.substr(0, lastDotIdx); - label = label.substr(lastDotIdx + 1); + category = key.substr(0, lastDotIdx); + key = key.substr(lastDotIdx + 1); } - groupId = wordifyKey(groupId.replace(/\//g, '.')); + groupId = groupId.replace(/\//g, '.'); category = trimCategoryForGroup(category, groupId); + category = wordifyKey(category); + const label = wordifyKey(key); return { category, label }; } @@ -417,7 +459,7 @@ function trimCategoryForGroup(category: string, groupId: string): string { export interface ISettingsEditorViewState { settingsTarget: SettingsTarget; - showConfiguredOnly?: boolean; + tagFilters?: Set; filterToCategory?: SettingsTreeGroupElement; } @@ -428,6 +470,7 @@ interface IDisposableTemplate { interface ISettingItemTemplate extends IDisposableTemplate { onChange?: (value: T) => void; + context?: SettingsTreeSettingElement; containerElement: HTMLElement; categoryElement: HTMLElement; labelElement: HTMLElement; @@ -449,12 +492,27 @@ type ISettingNumberItemTemplate = ISettingTextItemTemplate; interface ISettingEnumItemTemplate extends ISettingItemTemplate { selectBox: SelectBox; + enumDescriptionElement: HTMLElement; } interface ISettingComplexItemTemplate extends ISettingItemTemplate { button: Button; } +interface ISettingExcludeItemTemplate extends ISettingItemTemplate { + excludeWidget: ExcludeSettingWidget; +} + +interface ISettingNewExtensionsTemplate extends IDisposableTemplate { + button: Button; + context?: SettingsTreeNewExtensionsElement; +} + +function isExcludeSetting(setting: ISetting): boolean { + return setting.key === 'files.exclude' || + setting.key === 'search.exclude'; +} + interface IGroupTitleTemplate extends IDisposableTemplate { context?: SettingsTreeGroupElement; parent: HTMLElement; @@ -464,7 +522,9 @@ const SETTINGS_TEXT_TEMPLATE_ID = 'settings.text.template'; const SETTINGS_NUMBER_TEMPLATE_ID = 'settings.number.template'; const SETTINGS_ENUM_TEMPLATE_ID = 'settings.enum.template'; const SETTINGS_BOOL_TEMPLATE_ID = 'settings.bool.template'; +const SETTINGS_EXCLUDE_TEMPLATE_ID = 'settings.exclude.template'; const SETTINGS_COMPLEX_TEMPLATE_ID = 'settings.complex.template'; +const SETTINGS_NEW_EXTENSIONS_TEMPLATE_ID = 'settings.newExtensions.template'; const SETTINGS_GROUP_ELEMENT_TEMPLATE_ID = 'settings.group.template'; export interface ISettingChangeEvent { @@ -472,70 +532,97 @@ export interface ISettingChangeEvent { value: any; // undefined => reset/unconfigure } -export class SettingsRenderer implements IRenderer { +export class SettingsRenderer implements ITreeRenderer { - private static readonly SETTING_ROW_HEIGHT = 98; - private static readonly SETTING_BOOL_ROW_HEIGHT = 65; + public static readonly MAX_ENUM_DESCRIPTIONS = 10; + + private static readonly CONTROL_CLASS = 'setting-control-focus-target'; + public static readonly CONTROL_SELECTOR = '.' + SettingsRenderer.CONTROL_CLASS; private readonly _onDidChangeSetting: Emitter = new Emitter(); public readonly onDidChangeSetting: Event = this._onDidChangeSetting.event; - private readonly _onDidOpenSettings: Emitter = new Emitter(); - public readonly onDidOpenSettings: Event = this._onDidOpenSettings.event; + private readonly _onDidOpenSettings: Emitter = new Emitter(); + public readonly onDidOpenSettings: Event = this._onDidOpenSettings.event; private readonly _onDidClickSettingLink: Emitter = new Emitter(); public readonly onDidClickSettingLink: Event = this._onDidClickSettingLink.event; + private readonly _onDidFocusSetting: Emitter = new Emitter(); + public readonly onDidFocusSetting: Event = this._onDidFocusSetting.event; + private measureContainer: HTMLElement; + private measureTemplatesPool = new Map(); + private rowHeightCache = new Map(); + private lastRenderedWidth: number; constructor( _measureContainer: HTMLElement, @IThemeService private themeService: IThemeService, @IContextViewService private contextViewService: IContextViewService, @IOpenerService private readonly openerService: IOpenerService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ICommandService private readonly commandService: ICommandService, ) { this.measureContainer = DOM.append(_measureContainer, $('.setting-measure-container.monaco-tree-row')); } + updateWidth(width: number): void { + if (this.lastRenderedWidth !== width) { + this.rowHeightCache = new Map(); + } + + this.lastRenderedWidth = width; + } + getHeight(tree: ITree, element: SettingsTreeElement): number { + if (this.rowHeightCache.has(element.id)) { + return this.rowHeightCache.get(element.id); + } + + const h = this._getHeight(tree, element); + this.rowHeightCache.set(element.id, h); + return h; + } + + _getHeight(tree: ITree, element: SettingsTreeElement): number { if (element instanceof SettingsTreeGroupElement) { if (element.isFirstGroup) { - return 28; + return 31; } return 40 + (7 * element.level); } if (element instanceof SettingsTreeSettingElement) { - const isSelected = this.elementIsSelected(tree, element); - if (isSelected) { - return this.measureSettingElementHeight(tree, element); + if (isExcludeSetting(element.setting)) { + return this._getExcludeSettingHeight(element); } else { - return this._getUnexpandedSettingHeight(element); + return this.measureSettingElementHeight(tree, element); } } + if (element instanceof SettingsTreeNewExtensionsElement) { + return 40; + } + return 0; } - _getUnexpandedSettingHeight(element: SettingsTreeSettingElement): number { - if (element.valueType === 'boolean') { - return SettingsRenderer.SETTING_BOOL_ROW_HEIGHT; - } else { - return SettingsRenderer.SETTING_ROW_HEIGHT; - } + _getExcludeSettingHeight(element: SettingsTreeSettingElement): number { + const displayValue = getExcludeDisplayValue(element); + return (displayValue.length + 1) * 22 + 80; } private measureSettingElementHeight(tree: ITree, element: SettingsTreeSettingElement): number { - const measureHelper = DOM.append(this.measureContainer, $('.setting-measure-helper')); - const templateId = this.getTemplateId(tree, element); - const template = this.renderTemplate(tree, templateId, measureHelper); + const template: ISettingItemTemplate = this.measureTemplatesPool.get(templateId) || this.renderTemplate(tree, templateId, $('.setting-measure-helper')) as ISettingItemTemplate; this.renderElement(tree, element, templateId, template); + this.measureContainer.appendChild(template.containerElement); const height = this.measureContainer.offsetHeight; this.measureContainer.removeChild(this.measureContainer.firstChild); - return Math.max(height, this._getUnexpandedSettingHeight(element)); + return height; } getTemplateId(tree: ITree, element: SettingsTreeElement): string { @@ -561,9 +648,17 @@ export class SettingsRenderer implements IRenderer { return SETTINGS_ENUM_TEMPLATE_ID; } + if (element.valueType === 'exclude') { + return SETTINGS_EXCLUDE_TEMPLATE_ID; + } + return SETTINGS_COMPLEX_TEMPLATE_ID; } + if (element instanceof SettingsTreeNewExtensionsElement) { + return SETTINGS_NEW_EXTENSIONS_TEMPLATE_ID; + } + return ''; } @@ -588,10 +683,18 @@ export class SettingsRenderer implements IRenderer { return this.renderSettingEnumTemplate(tree, container); } + if (templateId === SETTINGS_EXCLUDE_TEMPLATE_ID) { + return this.renderSettingExcludeTemplate(tree, container); + } + if (templateId === SETTINGS_COMPLEX_TEMPLATE_ID) { return this.renderSettingComplexTemplate(tree, container); } + if (templateId === SETTINGS_NEW_EXTENSIONS_TEMPLATE_ID) { + return this.renderNewExtensionsTemplate(container); + } + return null; } @@ -610,7 +713,6 @@ export class SettingsRenderer implements IRenderer { private renderCommonTemplate(tree: ITree, container: HTMLElement, typeClass: string): ISettingItemTemplate { DOM.addClass(container, 'setting-item'); DOM.addClass(container, 'setting-item-' + typeClass); - const titleElement = DOM.append(container, $('.setting-item-title')); const categoryElement = DOM.append(titleElement, $('span.setting-item-category')); const labelElement = DOM.append(titleElement, $('span.setting-item-label')); @@ -620,7 +722,6 @@ export class SettingsRenderer implements IRenderer { const valueElement = DOM.append(container, $('.setting-item-value')); const controlElement = DOM.append(valueElement, $('div.setting-item-control')); - const resetButtonElement = DOM.append(valueElement, $('.reset-button-container')); const toDispose = []; const template: ISettingItemTemplate = { @@ -637,7 +738,6 @@ export class SettingsRenderer implements IRenderer { // Prevent clicks from being handled by list toDispose.push(DOM.addDisposableListener(controlElement, 'mousedown', (e: IMouseEvent) => e.stopPropagation())); - toDispose.push(DOM.addDisposableListener(resetButtonElement, 'mousedown', (e: IMouseEvent) => e.stopPropagation())); toDispose.push(DOM.addStandardDisposableListener(valueElement, 'keydown', (e: StandardKeyboardEvent) => { if (e.keyCode === KeyCode.Escape) { @@ -649,6 +749,14 @@ export class SettingsRenderer implements IRenderer { return template; } + private addSettingElementFocusHandler(template: ISettingItemTemplate): void { + template.toDispose.push(DOM.addDisposableListener(template.containerElement, 'focus', e => { + if (template.context) { + this._onDidFocusSetting.fire(template.context); + } + }, true)); + } + private renderSettingTextTemplate(tree: ITree, container: HTMLElement, type = 'text'): ISettingTextItemTemplate { const common = this.renderCommonTemplate(tree, container, 'text'); @@ -666,12 +774,15 @@ export class SettingsRenderer implements IRenderer { } })); common.toDispose.push(inputBox); + inputBox.inputElement.classList.add(SettingsRenderer.CONTROL_CLASS); const template: ISettingTextItemTemplate = { ...common, inputBox }; + this.addSettingElementFocusHandler(template); + return template; } @@ -692,12 +803,15 @@ export class SettingsRenderer implements IRenderer { } })); common.toDispose.push(inputBox); + inputBox.inputElement.classList.add(SettingsRenderer.CONTROL_CLASS); const template: ISettingNumberItemTemplate = { ...common, inputBox }; + this.addSettingElementFocusHandler(template); + return template; } @@ -724,6 +838,7 @@ export class SettingsRenderer implements IRenderer { template.onChange(checkbox.checked); } })); + checkbox.domNode.classList.add(SettingsRenderer.CONTROL_CLASS); const template: ISettingBoolItemTemplate = { toDispose, @@ -738,6 +853,8 @@ export class SettingsRenderer implements IRenderer { otherOverridesElement }; + this.addSettingElementFocusHandler(template); + // Prevent clicks from being handled by list toDispose.push(DOM.addDisposableListener(controlElement, 'mousedown', (e: IMouseEvent) => e.stopPropagation())); @@ -762,6 +879,10 @@ export class SettingsRenderer implements IRenderer { selectBorder: settingsSelectBorder })); selectBox.render(common.controlElement); + const selectElement = common.controlElement.querySelector('select'); + if (selectElement) { + selectElement.classList.add(SettingsRenderer.CONTROL_CLASS); + } common.toDispose.push( selectBox.onDidSelect(e => { @@ -770,11 +891,69 @@ export class SettingsRenderer implements IRenderer { } })); + const enumDescriptionElement = common.containerElement.insertBefore($('.setting-item-enumDescription'), common.descriptionElement.nextSibling); + const template: ISettingEnumItemTemplate = { ...common, - selectBox + selectBox, + enumDescriptionElement }; + this.addSettingElementFocusHandler(template); + + return template; + } + + private renderSettingExcludeTemplate(tree: ITree, container: HTMLElement): ISettingExcludeItemTemplate { + const common = this.renderCommonTemplate(tree, container, 'exclude'); + + const excludeWidget = this.instantiationService.createInstance(ExcludeSettingWidget, common.controlElement); + excludeWidget.domNode.classList.add(SettingsRenderer.CONTROL_CLASS); + common.toDispose.push(excludeWidget); + + const template: ISettingExcludeItemTemplate = { + ...common, + excludeWidget + }; + + this.addSettingElementFocusHandler(template); + + common.toDispose.push(excludeWidget.onDidChangeExclude(e => { + if (template.context) { + const newValue = { + ...template.context.scopeValue + }; + + if (e.pattern) { + if (e.originalPattern in newValue) { + // editing something present in the value + newValue[e.pattern] = newValue[e.originalPattern]; + delete newValue[e.originalPattern]; + } else if (e.originalPattern) { + // editing a default + newValue[e.originalPattern] = false; + newValue[e.pattern] = template.context.defaultValue[e.originalPattern]; + } else { + // adding a new pattern + newValue[e.pattern] = true; + } + } else { + if (e.originalPattern in newValue) { + // deleting a configured pattern + delete newValue[e.originalPattern]; + } else if (e.originalPattern) { + // "deleting" a default by overriding it + newValue[e.originalPattern] = false; + } + } + + this._onDidChangeSetting.fire({ + key: template.context.setting.key, + value: newValue + }); + } + })); + return template; } @@ -782,15 +961,50 @@ export class SettingsRenderer implements IRenderer { const common = this.renderCommonTemplate(tree, container, 'complex'); const openSettingsButton = new Button(common.controlElement, { title: true, buttonBackground: null, buttonHoverBackground: null }); - openSettingsButton.onDidClick(() => this._onDidOpenSettings.fire()); + common.toDispose.push(openSettingsButton); + common.toDispose.push(openSettingsButton.onDidClick(() => template.onChange(null))); openSettingsButton.label = localize('editInSettingsJson', "Edit in settings.json"); openSettingsButton.element.classList.add('edit-in-settings-button'); + common.toDispose.push(attachButtonStyler(openSettingsButton, this.themeService, { + buttonBackground: Color.transparent.toString(), + buttonHoverBackground: Color.transparent.toString(), + buttonForeground: 'foreground' + })); + const template: ISettingComplexItemTemplate = { ...common, button: openSettingsButton }; + this.addSettingElementFocusHandler(template); + + return template; + } + + private renderNewExtensionsTemplate(container: HTMLElement): ISettingNewExtensionsTemplate { + const toDispose = []; + + container.classList.add('setting-item-new-extensions'); + + const button = new Button(container, { title: true, buttonBackground: null, buttonHoverBackground: null }); + toDispose.push(button); + toDispose.push(button.onDidClick(() => { + if (template.context) { + this.commandService.executeCommand('workbench.extensions.action.showExtensionsWithIds', template.context.extensionIds); + } + })); + button.label = localize('newExtensionsButtonLabel', "Show other matching extensions"); + button.element.classList.add('settings-new-extensions-button'); + toDispose.push(attachButtonStyler(button, this.themeService)); + + const template: ISettingNewExtensionsTemplate = { + button, + toDispose + }; + + // this.addSettingElementFocusHandler(template); + return template; } @@ -799,6 +1013,10 @@ export class SettingsRenderer implements IRenderer { return this.renderGroupElement(element, template); } + if (templateId === SETTINGS_NEW_EXTENSIONS_TEMPLATE_ID) { + return this.renderNewExtensionsElement(element, template); + } + return this.renderSettingElement(tree, element, templateId, template); } @@ -813,18 +1031,17 @@ export class SettingsRenderer implements IRenderer { } } - private elementIsSelected(tree: ITree, element: SettingsTreeElement): boolean { - const selection = tree.getSelection(); - const selectedElement: SettingsTreeElement = selection && selection[0]; - return selectedElement && selectedElement.id === element.id; + private renderNewExtensionsElement(element: SettingsTreeNewExtensionsElement, template: ISettingNewExtensionsTemplate): void { + template.context = element; } private renderSettingElement(tree: ITree, element: SettingsTreeSettingElement, templateId: string, template: ISettingItemTemplate | ISettingBoolItemTemplate): void { - const isSelected = !!this.elementIsSelected(tree, element); + template.context = element; + const setting = element.setting; DOM.toggleClass(template.containerElement, 'is-configured', element.isConfigured); - DOM.toggleClass(template.containerElement, 'is-expanded', isSelected); + DOM.toggleClass(template.containerElement, 'is-expanded', true); template.containerElement.id = element.id.replace(/\./g, '_'); const titleTooltip = setting.key; @@ -834,32 +1051,14 @@ export class SettingsRenderer implements IRenderer { template.labelElement.textContent = element.displayLabel; template.labelElement.title = titleTooltip; - const enumDescriptionText = element.setting.enumDescriptions ? - '\n' + element.setting.enumDescriptions - .map((desc, i) => ` - \`${element.setting.enum[i]}\`: ${desc}`) - .join('\n') : - ''; - const descriptionText = element.description + enumDescriptionText; - const renderedDescription = renderMarkdown({ value: descriptionText }, { - actionHandler: { - callback: (content: string) => { - if (startsWith(content, '#')) { - this._onDidClickSettingLink.fire(content.substr(1)); - } else { - this.openerService.open(URI.parse(content)).then(void 0, onUnexpectedError); - } - }, - disposeables: template.toDispose - } - }); - renderedDescription.classList.add('setting-item-description-markdown'); + this.renderValue(element, templateId, template); template.descriptionElement.innerHTML = ''; - template.descriptionElement.appendChild(renderedDescription); - (renderedDescription.querySelectorAll('a')).forEach(aElement => { - aElement.tabIndex = isSelected ? 0 : -1; - }); - - this.renderValue(element, isSelected, templateId, template); + if (element.setting.descriptionIsMarkdown) { + const renderedDescription = this.renderDescriptionMarkdown(element.description, template.toDispose); + template.descriptionElement.appendChild(renderedDescription); + } else { + template.descriptionElement.innerText = element.description; + } template.isConfiguredElement.textContent = element.isConfigured ? localize('configured', "Modified") : ''; @@ -874,65 +1073,168 @@ export class SettingsRenderer implements IRenderer { } } - private renderValue(element: SettingsTreeSettingElement, isSelected: boolean, templateId: string, template: ISettingItemTemplate | ISettingBoolItemTemplate): void { + private renderDescriptionMarkdown(text: string, disposeables: IDisposable[]): HTMLElement { + // Rewrite `#editor.fontSize#` to link format + text = fixSettingLinks(text); + + const renderedMarkdown = renderMarkdown({ value: text }, { + actionHandler: { + callback: (content: string) => { + if (startsWith(content, '#')) { + this._onDidClickSettingLink.fire(content.substr(1)); + } else { + this.openerService.open(URI.parse(content)).then(void 0, onUnexpectedError); + } + }, + disposeables + } + }); + + renderedMarkdown.classList.add('setting-item-description-markdown'); + cleanRenderedMarkdown(renderedMarkdown); + return renderedMarkdown; + } + + private renderValue(element: SettingsTreeSettingElement, templateId: string, template: ISettingItemTemplate | ISettingBoolItemTemplate): void { const onChange = value => this._onDidChangeSetting.fire({ key: element.setting.key, value }); if (templateId === SETTINGS_ENUM_TEMPLATE_ID) { - this.renderEnum(element, isSelected, template, onChange); + this.renderEnum(element, template, onChange); } else if (templateId === SETTINGS_TEXT_TEMPLATE_ID) { - this.renderText(element, isSelected, template, onChange); + this.renderText(element, template, onChange); } else if (templateId === SETTINGS_NUMBER_TEMPLATE_ID) { - this.renderNumber(element, isSelected, template, onChange); + this.renderNumber(element, template, onChange); } else if (templateId === SETTINGS_BOOL_TEMPLATE_ID) { - this.renderBool(element, isSelected, template, onChange); + this.renderBool(element, template, onChange); + } else if (templateId === SETTINGS_EXCLUDE_TEMPLATE_ID) { + this.renderExcludeSetting(element, template); } else if (templateId === SETTINGS_COMPLEX_TEMPLATE_ID) { - this.renderEditInSettingsJson(element, isSelected, template); + this.renderComplexSetting(element, template); } } - private renderBool(dataElement: SettingsTreeSettingElement, isSelected: boolean, template: ISettingBoolItemTemplate, onChange: (value: boolean) => void): void { + private renderBool(dataElement: SettingsTreeSettingElement, template: ISettingBoolItemTemplate, onChange: (value: boolean) => void): void { template.onChange = null; template.checkbox.checked = dataElement.value; template.onChange = onChange; - template.checkbox.domNode.tabIndex = isSelected ? 0 : -1; + // Setup and add ARIA attributes + // Create id and label for control/input element - parent is wrapper div + const id = (dataElement.displayCategory + '_' + dataElement.displayLabel).replace(/ /g, '_'); + const label = ' ' + dataElement.displayCategory + ' ' + dataElement.displayLabel + ' checkbox ' + (dataElement.value ? 'checked ' : 'unchecked ') + template.isConfiguredElement.textContent; + + // We use the parent control div for the aria-labelledby target + // Does not appear you can use the direct label on the element itself within a tree + template.checkbox.domNode.parentElement.setAttribute('id', id); + template.checkbox.domNode.parentElement.setAttribute('aria-label', label); + + // Labels will not be read on descendent input elements of the parent treeitem + // unless defined as role=treeitem and indirect aria-labelledby approach + // TODO: Determine method to normally label input items with value read last + template.checkbox.domNode.setAttribute('id', id + 'item'); + template.checkbox.domNode.setAttribute('role', 'treeitem'); + template.checkbox.domNode.setAttribute('aria-labelledby', id + 'item ' + id); + } - private renderEnum(dataElement: SettingsTreeSettingElement, isSelected: boolean, template: ISettingEnumItemTemplate, onChange: (value: string) => void): void { - const displayOptions = dataElement.setting.enum.map(escapeInvisibleChars); + private renderEnum(dataElement: SettingsTreeSettingElement, template: ISettingEnumItemTemplate, onChange: (value: string) => void): void { + const displayOptions = getDisplayEnumOptions(dataElement.setting); template.selectBox.setOptions(displayOptions); + const label = ' ' + dataElement.displayCategory + ' ' + dataElement.displayLabel + ' combobox ' + template.isConfiguredElement.textContent; + + template.selectBox.setAriaLabel(label); + const idx = dataElement.setting.enum.indexOf(dataElement.value); template.onChange = null; template.selectBox.select(idx); template.onChange = idx => onChange(dataElement.setting.enum[idx]); if (template.controlElement.firstElementChild) { - template.controlElement.firstElementChild.setAttribute('tabindex', isSelected ? '0' : '-1'); + // SelectBox needs to be treeitem to read correctly within tree + template.controlElement.firstElementChild.setAttribute('role', 'treeitem'); } + template.enumDescriptionElement.innerHTML = ''; + // if (dataElement.setting.enumDescriptions && dataElement.setting.enum && dataElement.setting.enum.length < SettingsRenderer.MAX_ENUM_DESCRIPTIONS) { + // if (isSelected) { + // let enumDescriptionText = '\n' + dataElement.setting.enumDescriptions + // .map((desc, i) => { + // const displayEnum = escapeInvisibleChars(dataElement.setting.enum[i]); + // return desc ? + // ` - \`${displayEnum}\`: ${desc}` : + // ` - \`${dataElement.setting.enum[i]}\``; + // }) + // .filter(desc => !!desc) + // .join('\n'); + + // const renderedMarkdown = this.renderDescriptionMarkdown(fixSettingLinks(enumDescriptionText), template.toDispose); + // template.enumDescriptionElement.appendChild(renderedMarkdown); + // } + + // return { overflows: true }; + // } } - private renderText(dataElement: SettingsTreeSettingElement, isSelected: boolean, template: ISettingTextItemTemplate, onChange: (value: string) => void): void { + private renderText(dataElement: SettingsTreeSettingElement, template: ISettingTextItemTemplate, onChange: (value: string) => void): void { template.onChange = null; template.inputBox.value = dataElement.value; template.onChange = value => onChange(value); - template.inputBox.inputElement.tabIndex = isSelected ? 0 : -1; + + // Setup and add ARIA attributes + // Create id and label for control/input element - parent is wrapper div + const id = (dataElement.displayCategory + '_' + dataElement.displayLabel).replace(/ /g, '_'); + const label = ' ' + dataElement.displayCategory + ' ' + dataElement.displayLabel + ' ' + template.isConfiguredElement.textContent; + + // We use the parent control div for the aria-labelledby target + // Does not appear you can use the direct label on the element itself within a tree + template.inputBox.inputElement.parentElement.setAttribute('id', id); + template.inputBox.inputElement.parentElement.setAttribute('aria-label', label); + + // Labels will not be read on descendent input elements of the parent treeitem + // unless defined as role=treeitem and indirect aria-labelledby approach + // TODO: Determine method to normally label input items with value read last + template.inputBox.inputElement.setAttribute('id', id + 'item'); + template.inputBox.inputElement.setAttribute('role', 'treeitem'); + template.inputBox.inputElement.setAttribute('aria-labelledby', id + 'item ' + id); + } - private renderNumber(dataElement: SettingsTreeSettingElement, isSelected: boolean, template: ISettingTextItemTemplate, onChange: (value: number) => void): void { + + private renderNumber(dataElement: SettingsTreeSettingElement, template: ISettingTextItemTemplate, onChange: (value: number) => void): void { template.onChange = null; template.inputBox.value = dataElement.value; template.onChange = value => onChange(parseFn(value)); - template.inputBox.inputElement.tabIndex = isSelected ? 0 : -1; const parseFn = dataElement.valueType === 'integer' ? parseInt : parseFloat; + + // Setup and add ARIA attributes + // Create id and label for control/input element - parent is wrapper div + const id = (dataElement.displayCategory + '_' + dataElement.displayLabel).replace(/ /g, '_'); + const label = ' ' + dataElement.displayCategory + ' ' + dataElement.displayLabel + ' number ' + template.isConfiguredElement.textContent; + + // We use the parent control div for the aria-labelledby target + // Does not appear you can use the direct label on the element itself within a tree + template.inputBox.inputElement.parentElement.setAttribute('id', id); + template.inputBox.inputElement.parentElement.setAttribute('aria-label', label); + + // Labels will not be read on descendent input elements of the parent treeitem + // unless defined as role=treeitem and indirect aria-labelledby approach + // TODO: Determine method to normally label input items with value read last + template.inputBox.inputElement.setAttribute('id', id + 'item'); + template.inputBox.inputElement.setAttribute('role', 'treeitem'); + template.inputBox.inputElement.setAttribute('aria-labelledby', id + 'item ' + id); + } - private renderEditInSettingsJson(dataElement: SettingsTreeSettingElement, isSelected: boolean, template: ISettingComplexItemTemplate): void { - template.button.element.tabIndex = isSelected ? 0 : -1; + private renderExcludeSetting(dataElement: SettingsTreeSettingElement, template: ISettingExcludeItemTemplate): void { + const value = getExcludeDisplayValue(dataElement); + template.excludeWidget.setValue(value); + template.context = dataElement; + } - template.onChange = () => this._onDidOpenSettings.fire(); + private renderComplexSetting(dataElement: SettingsTreeSettingElement, template: ISettingComplexItemTemplate): void { + template.onChange = () => this._onDidOpenSettings.fire(dataElement.setting.key); } disposeTemplate(tree: ITree, templateId: string, template: IDisposableTemplate): void { @@ -940,6 +1242,39 @@ export class SettingsRenderer implements IRenderer { } } +function cleanRenderedMarkdown(element: Node): void { + for (let i = 0; i < element.childNodes.length; i++) { + const child = element.childNodes.item(i); + + const tagName = (child).tagName && (child).tagName.toLowerCase(); + if (tagName === 'img') { + element.removeChild(child); + } else { + cleanRenderedMarkdown(child); + } + } +} + +function fixSettingLinks(text: string): string { + return text.replace(/`#([^#]*)#`/g, (match, settingName) => `[\`${settingName}\`](#${settingName})`); +} + +function getDisplayEnumOptions(setting: ISetting): string[] { + if (setting.enum.length > SettingsRenderer.MAX_ENUM_DESCRIPTIONS && setting.enumDescriptions) { + return setting.enum + .map(escapeInvisibleChars) + .map((value, i) => { + return setting.enumDescriptions[i] ? + `${value}: ${setting.enumDescriptions[i]}` : + value; + }); + } + + return setting.enum + .map(String) + .map(escapeInvisibleChars); +} + function escapeInvisibleChars(enumValue: string): string { return enumValue && enumValue .replace(/\n/g, '\\n') @@ -958,12 +1293,20 @@ export class SettingsTreeFilter implements IFilter { } } - if (element instanceof SettingsTreeSettingElement && this.viewState.showConfiguredOnly) { - return element.isConfigured; + if (element instanceof SettingsTreeSettingElement && this.viewState.tagFilters && this.viewState.tagFilters.size) { + if (element.tags) { + let hasFilteredTag = true; + this.viewState.tagFilters.forEach(tag => { + hasFilteredTag = hasFilteredTag && element.tags.has(tag); + }); + return hasFilteredTag; + } else { + return false; + } } - if (element instanceof SettingsTreeGroupElement && this.viewState.showConfiguredOnly) { - return this.groupHasConfiguredSetting(element); + if (element instanceof SettingsTreeGroupElement && this.viewState.tagFilters && this.viewState.tagFilters.size) { + return element.children.some(child => this.isVisible(tree, child)); } return true; @@ -980,22 +1323,6 @@ export class SettingsTreeFilter implements IFilter { } }); } - - private groupHasConfiguredSetting(element: SettingsTreeGroupElement): boolean { - for (let child of element.children) { - if (child instanceof SettingsTreeSettingElement) { - if (child.isConfigured) { - return true; - } - } else if (child instanceof SettingsTreeGroupElement) { - if (this.groupHasConfiguredSetting(child)) { - return true; - } - } - } - - return false; - } } export class SettingsTreeController extends WorkbenchTreeController { @@ -1041,13 +1368,15 @@ export class SettingsAccessibilityProvider implements IAccessibilityProvider { export enum SearchResultIdx { Local = 0, - Remote = 1 + Remote = 1, + NewExtensions = 2 } export class SearchResultModel { private rawSearchResults: ISearchResult[]; private cachedUniqueSearchResults: ISearchResult[]; - private children: SettingsTreeSettingElement[]; + private newExtensionSearchResults: ISearchResult; + private children: (SettingsTreeSettingElement | SettingsTreeNewExtensionsElement)[]; readonly id = 'searchResultModel'; @@ -1056,7 +1385,7 @@ export class SearchResultModel { @IConfigurationService private _configurationService: IConfigurationService ) { } - getChildren(): SettingsTreeSettingElement[] { + getChildren(): (SettingsTreeSettingElement | SettingsTreeNewExtensionsElement)[] { return this.children; } @@ -1080,6 +1409,8 @@ export class SearchResultModel { remoteResult.filterMatches = remoteResult.filterMatches.filter(m => !localMatchKeys.has(m.setting.key)); } + this.newExtensionSearchResults = objects.deepClone(this.rawSearchResults[SearchResultIdx.NewExtensions]); + this.cachedUniqueSearchResults = [localResult, remoteResult]; return this.cachedUniqueSearchResults; } @@ -1102,7 +1433,20 @@ export class SearchResultModel { updateChildren(): void { this.children = this.getFlatSettings() - .map(s => createSettingsTreeSettingElement(s, this, this._viewState.settingsTarget, this._configurationService)); + .map(s => createSettingsTreeSettingElement(s, this, this._viewState.settingsTarget, this._configurationService)) + .filter(el => el.setting.deprecationMessage ? el.isConfigured : true); + + if (this.newExtensionSearchResults) { + const newExtElement = new SettingsTreeNewExtensionsElement(); + newExtElement.parent = this; + newExtElement.id = 'newExtensions'; + const resultExtensionIds = this.newExtensionSearchResults.filterMatches + .map(result => (result.setting)) + .filter(setting => setting.extensionName && setting.extensionPublisher) + .map(setting => `${setting.extensionPublisher}.${setting.extensionName}`); + newExtElement.extensionIds = arrays.distinct(resultExtensionIds); + this.children.push(newExtElement); + } } private getFlatSettings(): ISetting[] { @@ -1118,12 +1462,104 @@ export class SearchResultModel { } } -export class NonExpandableTree extends WorkbenchTree { - expand(): TPromise { +class NonExpandableOrSelectableTree extends Tree { + expand(): TPromise { return TPromise.wrap(null); } - collapse(): TPromise { + collapse(): TPromise { return TPromise.wrap(null); } + + public setFocus(element?: any, eventPayload?: any): void { + return; + } + + public focusNext(count?: number, eventPayload?: any): void { + return; + } + + public focusPrevious(count?: number, eventPayload?: any): void { + return; + } +} + +export class SettingsTree extends NonExpandableOrSelectableTree { + protected disposables: IDisposable[]; + + constructor( + container: HTMLElement, + viewState: ISettingsEditorViewState, + configuration: Partial, + @IContextKeyService contextKeyService: IContextKeyService, + @IListService listService: IListService, + @IThemeService themeService: IThemeService, + @IInstantiationService instantiationService: IInstantiationService, + @IConfigurationService configurationService: IConfigurationService + ) { + const treeClass = 'settings-editor-tree'; + + const controller = instantiationService.createInstance(SettingsTreeController); + const fullConfiguration = { + dataSource: instantiationService.createInstance(SettingsDataSource, viewState), + controller, + accessibilityProvider: instantiationService.createInstance(SettingsAccessibilityProvider), + filter: instantiationService.createInstance(SettingsTreeFilter, viewState), + styler: new DefaultTreestyler(DOM.createStyleSheet(container), treeClass), + + ...configuration + }; + + const options = { + ariaLabel: localize('treeAriaLabel', "Settings"), + showLoading: false, + indentPixels: 0, + twistiePixels: 0, + }; + + super(container, + fullConfiguration, + options); + + this.disposables = []; + this.disposables.push(controller); + + this.disposables.push(registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { + const activeBorderColor = theme.getColor(focusBorder); + if (activeBorderColor) { + // TODO@rob - why isn't this applied when added to the stylesheet from tocTree.ts? Seems like a chromium glitch. + collector.addRule(`.settings-editor > .settings-body > .settings-toc-container .monaco-tree:focus .monaco-tree-row.focused {outline: solid 1px ${activeBorderColor}; outline-offset: -1px; }`); + } + + const foregroundColor = theme.getColor(foreground); + if (foregroundColor) { + // Links appear inside other elements in markdown. CSS opacity acts like a mask. So we have to dynamically compute the description color to avoid + // applying an opacity to the link color. + const fgWithOpacity = new Color(new RGBA(foregroundColor.rgba.r, foregroundColor.rgba.g, foregroundColor.rgba.b, .9)); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description { color: ${fgWithOpacity}; }`); + } + + const headerForegroundColor = theme.getColor(settingsHeaderForeground); + if (headerForegroundColor) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .settings-group-title-label { color: ${headerForegroundColor} };`); + } + })); + + this.getHTMLElement().classList.add(treeClass); + + this.disposables.push(attachStyler(themeService, { + listActiveSelectionBackground: editorBackground, + listActiveSelectionForeground: foreground, + listFocusAndSelectionBackground: editorBackground, + listFocusAndSelectionForeground: foreground, + listFocusBackground: editorBackground, + listFocusForeground: foreground, + listHoverForeground: foreground, + listHoverBackground: editorBackground, + listInactiveSelectionBackground: editorBackground, + listInactiveSelectionForeground: foreground + }, colors => { + this.style(colors); + })); + } } diff --git a/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts b/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts new file mode 100644 index 00000000000..862ec4dde3b --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts @@ -0,0 +1,439 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as DOM from 'vs/base/browser/dom'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Button } from 'vs/base/browser/ui/button/button'; +import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; +import { IAction } from 'vs/base/common/actions'; +import { Emitter, Event } from 'vs/base/common/event'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import 'vs/css!./media/settingsWidgets'; +import { localize } from 'vs/nls'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { foreground, inputBackground, inputBorder, inputForeground, listActiveSelectionBackground, listActiveSelectionForeground, listHoverBackground, listHoverForeground, listInactiveSelectionBackground, listInactiveSelectionForeground, registerColor, selectBackground, selectBorder, selectForeground, textLinkForeground, textPreformatForeground } from 'vs/platform/theme/common/colorRegistry'; +import { attachButtonStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler'; +import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; + +const $ = DOM.$; +export const settingsHeaderForeground = registerColor('settings.headerForeground', { light: '#444444', dark: '#e7e7e7', hc: '#ffffff' }, localize('headerForeground', "(For settings editor preview) The foreground color for a section header or active title.")); +export const modifiedItemForeground = registerColor('settings.modifiedItemForeground', { light: '#018101', dark: '#73C991', hc: '#73C991' }, localize('modifiedItemForeground', "(For settings editor preview) The foreground color for a the modified setting indicator.")); + +// Enum control colors +export const settingsSelectBackground = registerColor('settings.dropdownBackground', { dark: selectBackground, light: selectBackground, hc: selectBackground }, localize('settingsDropdownBackground', "(For settings editor preview) Settings editor dropdown background.")); +export const settingsSelectForeground = registerColor('settings.dropdownForeground', { dark: selectForeground, light: selectForeground, hc: selectForeground }, localize('settingsDropdownForeground', "(For settings editor preview) Settings editor dropdown foreground.")); +export const settingsSelectBorder = registerColor('settings.dropdownBorder', { dark: selectBorder, light: selectBorder, hc: selectBorder }, localize('settingsDropdownBorder', "(For settings editor preview) Settings editor dropdown border.")); + +// Bool control colors +export const settingsCheckboxBackground = registerColor('settings.checkboxBackground', { dark: selectBackground, light: selectBackground, hc: selectBackground }, localize('settingsCheckboxBackground', "(For settings editor preview) Settings editor checkbox background.")); +export const settingsCheckboxForeground = registerColor('settings.checkboxForeground', { dark: selectForeground, light: selectForeground, hc: selectForeground }, localize('settingsCheckboxForeground', "(For settings editor preview) Settings editor checkbox foreground.")); +export const settingsCheckboxBorder = registerColor('settings.checkboxBorder', { dark: selectBorder, light: selectBorder, hc: selectBorder }, localize('settingsCheckboxBorder', "(For settings editor preview) Settings editor checkbox border.")); + +// Text control colors +export const settingsTextInputBackground = registerColor('settings.textInputBackground', { dark: inputBackground, light: inputBackground, hc: inputBackground }, localize('textInputBoxBackground', "(For settings editor preview) Settings editor text input box background.")); +export const settingsTextInputForeground = registerColor('settings.textInputForeground', { dark: inputForeground, light: inputForeground, hc: inputForeground }, localize('textInputBoxForeground', "(For settings editor preview) Settings editor text input box foreground.")); +export const settingsTextInputBorder = registerColor('settings.textInputBorder', { dark: inputBorder, light: inputBorder, hc: inputBorder }, localize('textInputBoxBorder', "(For settings editor preview) Settings editor text input box border.")); + +// Number control colors +export const settingsNumberInputBackground = registerColor('settings.numberInputBackground', { dark: inputBackground, light: inputBackground, hc: inputBackground }, localize('numberInputBoxBackground', "(For settings editor preview) Settings editor number input box background.")); +export const settingsNumberInputForeground = registerColor('settings.numberInputForeground', { dark: inputForeground, light: inputForeground, hc: inputForeground }, localize('numberInputBoxForeground', "(For settings editor preview) Settings editor number input box foreground.")); +export const settingsNumberInputBorder = registerColor('settings.numberInputBorder', { dark: inputBorder, light: inputBorder, hc: inputBorder }, localize('numberInputBoxBorder', "(For settings editor preview) Settings editor number input box border.")); + +registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { + const modifiedItemForegroundColor = theme.getColor(modifiedItemForeground); + if (modifiedItemForegroundColor) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item.is-configured .setting-item-is-configured-label { color: ${modifiedItemForegroundColor}; }`); + } + + const checkboxBackgroundColor = theme.getColor(settingsCheckboxBackground); + if (checkboxBackgroundColor) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-value-checkbox { background-color: ${checkboxBackgroundColor} !important; }`); + } + + const checkboxBorderColor = theme.getColor(settingsCheckboxBorder); + if (checkboxBorderColor) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-value-checkbox { border-color: ${checkboxBorderColor} !important; }`); + } + + const link = theme.getColor(textLinkForeground); + if (link) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown a { color: ${link}; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown a > code { color: ${link}; }`); + } + + const headerForegroundColor = theme.getColor(settingsHeaderForeground); + if (headerForegroundColor) { + collector.addRule(`.settings-editor > .settings-header > .settings-header-controls .settings-tabs-widget .action-label.checked { color: ${headerForegroundColor}; border-bottom-color: ${headerForegroundColor}; }`); + } + + const foregroundColor = theme.getColor(foreground); + if (foregroundColor) { + collector.addRule(`.settings-editor > .settings-header > .settings-header-controls .settings-tabs-widget .action-label { color: ${foregroundColor}; }`); + } + + // Exclude control + const listHoverBackgroundColor = theme.getColor(listHoverBackground); + if (listHoverBackgroundColor) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row:hover { background-color: ${listHoverBackgroundColor}; }`); + } + + const listHoverForegroundColor = theme.getColor(listHoverForeground); + if (listHoverForegroundColor) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row:hover { color: ${listHoverForegroundColor}; }`); + } + + const listSelectBackgroundColor = theme.getColor(listActiveSelectionBackground); + if (listSelectBackgroundColor) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row.selected:focus { background-color: ${listSelectBackgroundColor}; }`); + } + + const listInactiveSelectionBackgroundColor = theme.getColor(listInactiveSelectionBackground); + if (listInactiveSelectionBackgroundColor) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row.selected:not(:focus) { background-color: ${listInactiveSelectionBackgroundColor}; }`); + } + + const listInactiveSelectionForegroundColor = theme.getColor(listInactiveSelectionForeground); + if (listInactiveSelectionForegroundColor) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row.selected:not(:focus) { color: ${listInactiveSelectionForegroundColor}; }`); + } + + const listSelectForegroundColor = theme.getColor(listActiveSelectionForeground); + if (listSelectForegroundColor) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row.selected:focus { color: ${listSelectForegroundColor}; }`); + } + + const codeTextForegroundColor = theme.getColor(textPreformatForeground); + if (codeTextForegroundColor) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown code { color: ${codeTextForegroundColor} }`); + } +}); + +export class ExcludeSettingListModel { + private _dataItems: IExcludeDataItem[] = []; + private _editKey: string | null; + private _selectedIdx: number | null; + + get items(): IExcludeViewItem[] { + const items = this._dataItems.map((item, i) => { + const editing = item.pattern === this._editKey; + return { + ...item, + editing, + selected: i === this._selectedIdx || editing + }; + }); + + if (this._editKey === '') { + items.push({ + editing: true, + selected: true, + pattern: '', + sibling: '' + }); + } + + return items; + } + + setEditKey(key: string): void { + this._editKey = key; + } + + setValue(excludeData: IExcludeDataItem[]): void { + this._dataItems = excludeData; + } + + select(idx: number): void { + this._selectedIdx = idx; + } + + getSelected(): number { + return this._selectedIdx; + } + + selectNext(): void { + if (typeof this._selectedIdx === 'number') { + this._selectedIdx = Math.min(this._selectedIdx + 1, this._dataItems.length - 1); + } else { + this._selectedIdx = 0; + } + } + + selectPrevious(): void { + if (typeof this._selectedIdx === 'number') { + this._selectedIdx = Math.max(this._selectedIdx - 1, 0); + } else { + this._selectedIdx = 0; + } + } +} + +interface IExcludeChangeEvent { + originalPattern: string; + pattern: string; + sibling?: string; +} + +export class ExcludeSettingWidget extends Disposable { + private listElement: HTMLElement; + private listDisposables: IDisposable[] = []; + + private model = new ExcludeSettingListModel(); + + private readonly _onDidChangeExclude: Emitter = new Emitter(); + public readonly onDidChangeExclude: Event = this._onDidChangeExclude.event; + + get domNode(): HTMLElement { + return this.listElement; + } + + constructor( + private container: HTMLElement, + @IThemeService private themeService: IThemeService, + @IContextViewService private contextViewService: IContextViewService + ) { + super(); + + this.listElement = DOM.append(container, $('.setting-exclude-widget')); + this.listElement.setAttribute('tabindex', '0'); + DOM.append(container, this.renderAddButton()); + this.renderList(); + + this._register(DOM.addDisposableListener(this.listElement, 'click', (e: MouseEvent) => { + if (!e.target) { + return; + } + + const element = DOM.findParentWithClass((e.target), 'setting-exclude-row'); + if (!element) { + return; + } + + const targetIdxStr = element.getAttribute('data-index'); + if (!targetIdxStr) { + return; + } + + const targetIdx = parseInt(targetIdxStr); + + if (this.model.getSelected() === targetIdx) { + return; + } + + this.model.select(targetIdx); + this.renderList(); + e.preventDefault(); + e.stopPropagation(); + })); + + + this._register(DOM.addStandardDisposableListener(this.listElement, 'keydown', (e: KeyboardEvent) => { + if (e.keyCode === KeyCode.UpArrow) { + this.model.selectPrevious(); + this.renderList(); + e.preventDefault(); + e.stopPropagation(); + } else if (e.keyCode === KeyCode.DownArrow) { + this.model.selectNext(); + this.renderList(); + e.preventDefault(); + e.stopPropagation(); + } + })); + } + + setValue(excludeData: IExcludeDataItem[]): void { + this.model.setValue(excludeData); + this.renderList(); + } + + private renderList(): void { + const focused = DOM.isAncestor(document.activeElement, this.listElement); + + DOM.clearNode(this.listElement); + this.listDisposables = dispose(this.listDisposables); + + const newMode = this.model.items.some(item => item.editing && !item.pattern); + DOM.toggleClass(this.container, 'setting-exclude-new-mode', newMode); + + this.model.items + .map((item, i) => this.renderItem(item, i, focused)) + .forEach(itemElement => this.listElement.appendChild(itemElement)); + + const listHeight = 22 * this.model.items.length; + this.listElement.style.height = listHeight + 'px'; + } + + private createDeleteAction(key: string): IAction { + return { + class: 'setting-excludeAction-remove', + enabled: true, + id: 'workbench.action.removeExcludeItem', + tooltip: localize('removeExcludeItem', "Remove Exclude Item"), + run: () => this._onDidChangeExclude.fire({ originalPattern: key, pattern: undefined }) + }; + } + + private createEditAction(key: string): IAction { + return { + class: 'setting-excludeAction-edit', + enabled: true, + id: 'workbench.action.editExcludeItem', + tooltip: localize('editExcludeItem', "Edit Exclude Item"), + run: () => { + this.model.setEditKey(key); + this.renderList(); + } + }; + } + + private renderItem(item: IExcludeViewItem, idx: number, listFocused: boolean): HTMLElement { + return item.editing ? + this.renderEditItem(item) : + this.renderDataItem(item, idx, listFocused); + } + + private renderDataItem(item: IExcludeViewItem, idx: number, listFocused: boolean): HTMLElement { + const rowElement = $('.setting-exclude-row'); + rowElement.setAttribute('data-index', idx + ''); + rowElement.setAttribute('tabindex', item.selected ? '0' : '-1'); + DOM.toggleClass(rowElement, 'selected', item.selected); + + const actionBar = new ActionBar(rowElement); + this.listDisposables.push(actionBar); + + const patternElement = DOM.append(rowElement, $('.setting-exclude-pattern')); + const siblingElement = DOM.append(rowElement, $('.setting-exclude-sibling')); + patternElement.textContent = item.pattern; + siblingElement.textContent = item.sibling && ('when: ' + item.sibling); + + actionBar.push([ + this.createEditAction(item.pattern), + this.createDeleteAction(item.pattern) + ], { icon: true, label: false }); + + rowElement.title = item.sibling ? + localize('excludeSiblingHintLabel', "Exclude files matching `{0}`, only when a file matching `{1}` is present", item.pattern, item.sibling) : + localize('excludePatternHintLabel', "Exclude files matching `{0}`", item.pattern); + + if (item.selected) { + if (listFocused) { + setTimeout(() => { + rowElement.focus(); + }, 10); + } + } + + return rowElement; + } + + private renderAddButton(): HTMLElement { + const rowElement = $('.setting-exclude-new-row'); + + const startAddButton = this._register(new Button(rowElement)); + startAddButton.label = localize('addPattern', "Add Pattern"); + startAddButton.element.classList.add('setting-exclude-addButton'); + this._register(attachButtonStyler(startAddButton, this.themeService)); + + this._register(startAddButton.onDidClick(() => { + this.model.setEditKey(''); + this.renderList(); + })); + + return rowElement; + } + + private renderEditItem(item: IExcludeViewItem): HTMLElement { + const rowElement = $('.setting-exclude-edit-row'); + + const onSubmit = edited => { + this.model.setEditKey(null); + const pattern = patternInput.value.trim(); + if (edited && pattern) { + this._onDidChangeExclude.fire({ + originalPattern: item.pattern, + pattern, + sibling: siblingInput && siblingInput.value.trim() + }); + } else { + this.renderList(); + } + }; + + const onKeydown = (e: StandardKeyboardEvent) => { + if (e.equals(KeyCode.Enter)) { + onSubmit(true); + } else if (e.equals(KeyCode.Escape)) { + onSubmit(false); + e.preventDefault(); + } + }; + + const patternInput = new InputBox(rowElement, this.contextViewService, { + placeholder: localize('excludePatternInputPlaceholder', "Exclude Pattern...") + }); + patternInput.element.classList.add('setting-exclude-patternInput'); + this.listDisposables.push(attachInputBoxStyler(patternInput, this.themeService, { + inputBackground: settingsTextInputBackground, + inputForeground: settingsTextInputForeground, + inputBorder: settingsTextInputBorder + })); + this.listDisposables.push(patternInput); + patternInput.value = item.pattern; + this.listDisposables.push(DOM.addStandardDisposableListener(patternInput.inputElement, DOM.EventType.KEY_DOWN, onKeydown)); + + let siblingInput: InputBox; + if (item.sibling) { + siblingInput = new InputBox(rowElement, this.contextViewService, { + placeholder: localize('excludeSiblingInputPlaceholder', "When Pattern Is Present...") + }); + siblingInput.element.classList.add('setting-exclude-siblingInput'); + this.listDisposables.push(siblingInput); + this.listDisposables.push(attachInputBoxStyler(siblingInput, this.themeService, { + inputBackground: settingsTextInputBackground, + inputForeground: settingsTextInputForeground, + inputBorder: settingsTextInputBorder + })); + siblingInput.value = item.sibling; + this.listDisposables.push(DOM.addStandardDisposableListener(siblingInput.inputElement, DOM.EventType.KEY_DOWN, onKeydown)); + } + + const okButton = this._register(new Button(rowElement)); + okButton.label = localize('okButton', "OK"); + okButton.element.classList.add('setting-exclude-okButton'); + this.listDisposables.push(attachButtonStyler(okButton, this.themeService)); + this.listDisposables.push(okButton.onDidClick(() => onSubmit(true))); + + const cancelButton = this._register(new Button(rowElement)); + cancelButton.label = localize('cancelButton', "Cancel"); + cancelButton.element.classList.add('setting-exclude-cancelButton'); + this.listDisposables.push(attachButtonStyler(cancelButton, this.themeService)); + this.listDisposables.push(cancelButton.onDidClick(() => onSubmit(false))); + + setTimeout(() => { + patternInput.focus(); + patternInput.select(); + }, 0); + + return rowElement; + } + + dispose() { + super.dispose(); + this.listDisposables = dispose(this.listDisposables); + } +} + +export interface IExcludeDataItem { + pattern: string; + sibling?: string; +} + +interface IExcludeViewItem extends IExcludeDataItem { + editing?: boolean; + selected?: boolean; +} diff --git a/src/vs/workbench/parts/preferences/browser/tocTree.ts b/src/vs/workbench/parts/preferences/browser/tocTree.ts index 54c4d4297e8..1b71bcdb72b 100644 --- a/src/vs/workbench/parts/preferences/browser/tocTree.ts +++ b/src/vs/workbench/parts/preferences/browser/tocTree.ts @@ -5,10 +5,18 @@ import * as DOM from 'vs/base/browser/dom'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IDataSource, IRenderer, ITree } from 'vs/base/parts/tree/browser/tree'; -import { SearchResultModel, SettingsTreeElement, SettingsTreeGroupElement, SettingsTreeSettingElement } from 'vs/workbench/parts/preferences/browser/settingsTree'; -import { ISetting } from 'vs/workbench/services/preferences/common/preferences'; +import { IDataSource, IRenderer, ITree, ITreeConfiguration } from 'vs/base/parts/tree/browser/tree'; +import { DefaultTreestyler, OpenMode } from 'vs/base/parts/tree/browser/treeDefaults'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IListService, WorkbenchTree, WorkbenchTreeController } from 'vs/platform/list/browser/listService'; +import { editorBackground, focusBorder } from 'vs/platform/theme/common/colorRegistry'; +import { attachStyler } from 'vs/platform/theme/common/styler'; +import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { ISettingsEditorViewState, SearchResultModel, SettingsAccessibilityProvider, SettingsTreeElement, SettingsTreeFilter, SettingsTreeGroupElement, SettingsTreeSettingElement } from 'vs/workbench/parts/preferences/browser/settingsTree'; +import { settingsHeaderForeground } from 'vs/workbench/parts/preferences/browser/settingsWidgets'; +import { ISetting } from 'vs/workbench/services/preferences/common/preferences'; const $ = DOM.$; @@ -49,7 +57,7 @@ export class TOCTreeModel { private getSearchResultChildrenCount(group: SettingsTreeGroupElement): number { return this._currentSearchModel.getChildren().filter(child => { - return this.groupContainsSetting(group, child.setting); + return child instanceof SettingsTreeSettingElement && this.groupContainsSetting(group, child.setting); }).length; } @@ -83,7 +91,7 @@ export class TOCDataSource implements IDataSource { (element instanceof SettingsTreeGroupElement && element.children && element.children.every(child => child instanceof SettingsTreeGroupElement)); } - getChildren(tree: ITree, element: TOCTreeElement): TPromise { + getChildren(tree: ITree, element: TOCTreeElement): TPromise { return TPromise.as(this._getChildren(element)); } @@ -99,19 +107,16 @@ export class TOCDataSource implements IDataSource { return element.children; } - getParent(tree: ITree, element: TOCTreeElement): TPromise { + getParent(tree: ITree, element: TOCTreeElement): TPromise { return TPromise.wrap(element instanceof SettingsTreeGroupElement && element.parent); } - - shouldAutoexpand() { - return true; - } } const TOC_ENTRY_TEMPLATE_ID = 'settings.toc.entry'; interface ITOCEntryTemplate { - element: HTMLElement; + labelElement: HTMLElement; + countElement: HTMLElement; } export class TOCRenderer implements IRenderer { @@ -125,19 +130,88 @@ export class TOCRenderer implements IRenderer { renderTemplate(tree: ITree, templateId: string, container: HTMLElement): ITOCEntryTemplate { return { - element: DOM.append(container, $('.settings-toc-entry')) + labelElement: DOM.append(container, $('.settings-toc-entry')), + countElement: DOM.append(container, $('.settings-toc-count')) }; } renderElement(tree: ITree, element: SettingsTreeGroupElement, templateId: string, template: ITOCEntryTemplate): void { - const label = (element).count ? - `${element.label} (${(element).count})` : - element.label; + const count = (element).count; + const label = element.label; - DOM.toggleClass(template.element, 'no-results', (element).count === 0); - template.element.textContent = label; + DOM.toggleClass(template.labelElement, 'no-results', count === 0); + template.labelElement.textContent = label; + + if (count) { + template.countElement.textContent = ` (${count})`; + } else { + template.countElement.textContent = ''; + } } disposeTemplate(tree: ITree, templateId: string, templateData: any): void { } } + +export class TOCTree extends WorkbenchTree { + constructor( + container: HTMLElement, + viewState: ISettingsEditorViewState, + configuration: Partial, + @IContextKeyService contextKeyService: IContextKeyService, + @IListService listService: IListService, + @IThemeService themeService: IThemeService, + @IInstantiationService instantiationService: IInstantiationService, + @IConfigurationService configurationService: IConfigurationService + ) { + const treeClass = 'settings-toc-tree'; + + const fullConfiguration = { + controller: instantiationService.createInstance(WorkbenchTreeController, { openMode: OpenMode.DOUBLE_CLICK }), + filter: instantiationService.createInstance(SettingsTreeFilter, viewState), + styler: new DefaultTreestyler(DOM.createStyleSheet(container), treeClass), + dataSource: instantiationService.createInstance(TOCDataSource), + accessibilityProvider: instantiationService.createInstance(SettingsAccessibilityProvider), + + ...configuration + }; + + const options = { + showLoading: false, + twistiePixels: 15 + }; + + super(container, + fullConfiguration, + options, + contextKeyService, + listService, + themeService, + instantiationService, + configurationService); + + this.disposables.push(registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { + const activeBorderColor = theme.getColor(focusBorder); + if (activeBorderColor) { + collector.addRule(`.settings-editor > .settings-body > .settings-toc-container .monaco-tree:focus .monaco-tree-row.focused { outline-color: ${activeBorderColor}; }`); + } + })); + + this.getHTMLElement().classList.add(treeClass); + + this.disposables.push(attachStyler(themeService, { + listActiveSelectionBackground: editorBackground, + listActiveSelectionForeground: settingsHeaderForeground, + listFocusAndSelectionBackground: editorBackground, + listFocusAndSelectionForeground: settingsHeaderForeground, + listFocusBackground: editorBackground, + listFocusForeground: settingsHeaderForeground, + listHoverForeground: settingsHeaderForeground, + listHoverBackground: editorBackground, + listInactiveSelectionBackground: editorBackground, + listInactiveSelectionForeground: settingsHeaderForeground, + }, colors => { + this.style(colors); + })); + } +} diff --git a/src/vs/workbench/parts/preferences/common/preferences.ts b/src/vs/workbench/parts/preferences/common/preferences.ts index efbbd723b00..f77fc18a455 100644 --- a/src/vs/workbench/parts/preferences/common/preferences.ts +++ b/src/vs/workbench/parts/preferences/common/preferences.ts @@ -61,8 +61,6 @@ export interface IKeybindingsEditor extends IEditor { export const CONTEXT_SETTINGS_EDITOR = new RawContextKey('inSettingsEditor', false); export const CONTEXT_SETTINGS_SEARCH_FOCUS = new RawContextKey('inSettingsSearch', false); -export const CONTEXT_SETTINGS_FIRST_ROW_FOCUS = new RawContextKey('firstSettingRowFocused', false); -export const CONTEXT_SETTINGS_ROW_FOCUS = new RawContextKey('settingRowFocused', false); export const CONTEXT_TOC_ROW_FOCUS = new RawContextKey('settingsTocRowFocus', false); export const CONTEXT_KEYBINDINGS_EDITOR = new RawContextKey('inKeybindings', false); export const CONTEXT_KEYBINDINGS_SEARCH_FOCUS = new RawContextKey('inKeybindingsSearch', false); @@ -74,7 +72,6 @@ export const SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING = 'settings.action.focus export const SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING = 'settings.action.focusPreviousSetting'; export const SETTINGS_EDITOR_COMMAND_FOCUS_FILE = 'settings.action.focusSettingsFile'; export const SETTINGS_EDITOR_COMMAND_EDIT_FOCUSED_SETTING = 'settings.action.editFocusedSetting'; -export const SETTINGS_EDITOR_COMMAND_FOCUS_SEARCH_FROM_SETTINGS = 'settings.action.focusSearchFromSettings'; export const SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH = 'settings.action.focusSettingsFromSearch'; export const SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_LIST = 'settings.action.focusSettingsList'; diff --git a/src/vs/workbench/parts/preferences/common/preferencesContribution.ts b/src/vs/workbench/parts/preferences/common/preferencesContribution.ts index a5adc6ab4b7..f1c464e54c5 100644 --- a/src/vs/workbench/parts/preferences/common/preferencesContribution.ts +++ b/src/vs/workbench/parts/preferences/common/preferencesContribution.ts @@ -23,8 +23,8 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IEditorInput } from 'vs/workbench/common/editor'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { isEqual } from 'vs/base/common/paths'; import { isLinux } from 'vs/base/common/platform'; +import { isEqual, hasToIgnoreCase } from 'vs/base/common/resources'; const schemaRegistry = Registry.as(JSONContributionRegistry.Extensions.JSONContribution); @@ -66,14 +66,14 @@ export class PreferencesContribution implements IWorkbenchContribution { private onEditorOpening(editor: IEditorInput, options: IEditorOptions | ITextEditorOptions, group: IEditorGroup): IOpenEditorOverride { const resource = editor.getResource(); if ( - !resource || resource.scheme !== 'file' || // require a file path opening - !endsWith(resource.fsPath, 'settings.json') || // file must end in settings.json + !resource || + !endsWith(resource.path, 'settings.json') || // resource must end in settings.json !this.configurationService.getValue(DEFAULT_SETTINGS_EDITOR_SETTING) // user has not disabled default settings editor ) { return void 0; } - // If the file resource was already opened before in the group, do not prevent + // If the resource was already opened before in the group, do not prevent // the opening of that resource. Otherwise we would have the same settings // opened twice (https://github.com/Microsoft/vscode/issues/36447) if (group.isOpened(editor)) { @@ -81,7 +81,7 @@ export class PreferencesContribution implements IWorkbenchContribution { } // Global User Settings File - if (isEqual(resource.fsPath, this.environmentService.appSettingsPath, !isLinux)) { + if (isEqual(resource, URI.file(this.environmentService.appSettingsPath), !isLinux)) { return { override: this.preferencesService.openGlobalSettings(options, group) }; } @@ -89,7 +89,7 @@ export class PreferencesContribution implements IWorkbenchContribution { const state = this.workspaceService.getWorkbenchState(); if (state === WorkbenchState.FOLDER) { const folders = this.workspaceService.getWorkspace().folders; - if (resource.fsPath === folders[0].toResource(FOLDER_SETTINGS_PATH).fsPath) { + if (isEqual(resource, folders[0].toResource(FOLDER_SETTINGS_PATH), hasToIgnoreCase(resource))) { return { override: this.preferencesService.openWorkspaceSettings(options, group) }; } } @@ -98,7 +98,7 @@ export class PreferencesContribution implements IWorkbenchContribution { else if (state === WorkbenchState.WORKSPACE) { const folders = this.workspaceService.getWorkspace().folders; for (let i = 0; i < folders.length; i++) { - if (resource.fsPath === folders[i].toResource(FOLDER_SETTINGS_PATH).fsPath) { + if (isEqual(resource, folders[i].toResource(FOLDER_SETTINGS_PATH), hasToIgnoreCase(resource))) { return { override: this.preferencesService.openFolderSettings(folders[i].uri, options, group) }; } } diff --git a/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts b/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts index b0ea7b33c62..12f455b6224 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts @@ -8,7 +8,7 @@ import 'vs/css!../browser/media/preferences'; import * as nls from 'vs/nls'; import URI from 'vs/base/common/uri'; import { Registry } from 'vs/platform/registry/common/platform'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { EditorInput, IEditorInputFactory, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions } from 'vs/workbench/common/editor'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; @@ -19,10 +19,10 @@ import { PreferencesEditor } from 'vs/workbench/parts/preferences/browser/prefer import { SettingsEditor2 } from 'vs/workbench/parts/preferences/browser/settingsEditor2'; import { DefaultPreferencesEditorInput, PreferencesEditorInput, KeybindingsEditorInput, SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { KeybindingsEditor } from 'vs/workbench/parts/preferences/browser/keybindingsEditor'; -import { OpenRawDefaultSettingsAction, OpenSettingsAction, OpenGlobalSettingsAction, OpenGlobalKeybindingsFileAction, OpenWorkspaceSettingsAction, OpenFolderSettingsAction, ConfigureLanguageBasedSettingsAction, OPEN_FOLDER_SETTINGS_COMMAND, OpenGlobalKeybindingsAction, OpenSettings2Action } from 'vs/workbench/parts/preferences/browser/preferencesActions'; +import { OpenDefaultKeybindingsFileAction, OpenRawDefaultSettingsAction, OpenSettingsAction, OpenGlobalSettingsAction, OpenGlobalKeybindingsFileAction, OpenWorkspaceSettingsAction, OpenFolderSettingsAction, ConfigureLanguageBasedSettingsAction, OPEN_FOLDER_SETTINGS_COMMAND, OpenGlobalKeybindingsAction, OpenSettings2Action } from 'vs/workbench/parts/preferences/browser/preferencesActions'; import { IKeybindingsEditor, IPreferencesSearchService, CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_SEARCH, - KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SEARCH, CONTEXT_SETTINGS_EDITOR, SETTINGS_EDITOR_COMMAND_FOCUS_FILE, CONTEXT_SETTINGS_SEARCH_FOCUS, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING, SETTINGS_EDITOR_COMMAND_EDIT_FOCUSED_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_SEARCH_FROM_SETTINGS, SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH, CONTEXT_SETTINGS_FIRST_ROW_FOCUS, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_TOC_ROW_FOCUS, SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_LIST + KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SEARCH, CONTEXT_SETTINGS_EDITOR, SETTINGS_EDITOR_COMMAND_FOCUS_FILE, CONTEXT_SETTINGS_SEARCH_FOCUS, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING, SETTINGS_EDITOR_COMMAND_EDIT_FOCUSED_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH, CONTEXT_TOC_ROW_FOCUS, SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_LIST } from 'vs/workbench/parts/preferences/common/preferences'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; @@ -191,16 +191,17 @@ Registry.as(EditorInputExtensions.EditorInputFactor const category = nls.localize('preferences', "Preferences"); const registry = Registry.as(Extensions.WorkbenchActions); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenRawDefaultSettingsAction, OpenRawDefaultSettingsAction.ID, OpenRawDefaultSettingsAction.LABEL), 'Preferences: Open Raw Default Settings', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenSettingsAction, OpenSettingsAction.ID, OpenSettingsAction.LABEL), 'Preferences: Open Settings', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenSettings2Action, OpenSettings2Action.ID, OpenSettings2Action.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_COMMA }), 'Preferences: Open Settings (Preview)', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(OpenSettingsAction, OpenSettingsAction.ID, OpenSettingsAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_COMMA }), 'Preferences: Open Settings', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(OpenSettings2Action, OpenSettings2Action.ID, OpenSettings2Action.LABEL), 'Preferences: Open Settings (Preview)', category); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenGlobalSettingsAction, OpenGlobalSettingsAction.ID, OpenGlobalSettingsAction.LABEL), 'Preferences: Open User Settings', category); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenGlobalKeybindingsAction, OpenGlobalKeybindingsAction.ID, OpenGlobalKeybindingsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_S) }), 'Preferences: Open Keyboard Shortcuts', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(OpenDefaultKeybindingsFileAction, OpenDefaultKeybindingsFileAction.ID, OpenDefaultKeybindingsFileAction.LABEL), 'Preferences: Open Default Keyboard Shortcuts File', category); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenGlobalKeybindingsFileAction, OpenGlobalKeybindingsFileAction.ID, OpenGlobalKeybindingsFileAction.LABEL, { primary: null }), 'Preferences: Open Keyboard Shortcuts File', category); registry.registerWorkbenchAction(new SyncActionDescriptor(ConfigureLanguageBasedSettingsAction, ConfigureLanguageBasedSettingsAction.ID, ConfigureLanguageBasedSettingsAction.LABEL), 'Preferences: Configure Language Specific Settings...', category); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: KEYBINDINGS_EDITOR_COMMAND_DEFINE, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS), primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_K), handler: (accessor, args: any) => { @@ -211,7 +212,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: KEYBINDINGS_EDITOR_COMMAND_REMOVE, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS), primary: KeyCode.Delete, mac: { @@ -225,7 +226,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: KEYBINDINGS_EDITOR_COMMAND_RESET, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS), primary: null, handler: (accessor, args: any) => { @@ -236,7 +237,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: KEYBINDINGS_EDITOR_COMMAND_SEARCH, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS), primary: KeyMod.CtrlCmd | KeyCode.KEY_F, handler: (accessor, args: any) => (accessor.get(IEditorService).activeControl as IKeybindingsEditor).focusSearch() @@ -244,7 +245,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS), primary: null, handler: (accessor, args: any) => { @@ -255,7 +256,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: KEYBINDINGS_EDITOR_COMMAND_COPY, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS), primary: KeyMod.CtrlCmd | KeyCode.KEY_C, handler: (accessor, args: any) => { @@ -266,7 +267,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDING_FOCUS), primary: null, handler: (accessor, args: any) => { @@ -277,7 +278,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS), primary: KeyCode.DownArrow, handler: (accessor, args: any) => { @@ -288,7 +289,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS), primary: KeyCode.Escape, handler: (accessor, args: any) => { @@ -350,26 +351,9 @@ class StartSearchDefaultSettingsCommand extends SettingsCommand { const startSearchCommand = new StartSearchDefaultSettingsCommand({ id: SETTINGS_EDITOR_COMMAND_SEARCH, precondition: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR), - kbOpts: { primary: KeyMod.CtrlCmd | KeyCode.KEY_F } + kbOpts: { primary: KeyMod.CtrlCmd | KeyCode.KEY_F, weight: KeybindingWeight.EditorContrib } }); -KeybindingsRegistry.registerCommandAndKeybindingRule(startSearchCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); - -class FocusSearchFromSettingsCommand extends SettingsCommand { - - public runCommand(accessor: ServicesAccessor, args: any): void { - const preferencesEditor = this.getPreferencesEditor(accessor); - if (preferencesEditor) { - preferencesEditor.focusSearch(); - } - } -} -const focusSearchFromSettingsCommand = new FocusSearchFromSettingsCommand({ - id: SETTINGS_EDITOR_COMMAND_FOCUS_SEARCH_FROM_SETTINGS, - precondition: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_FIRST_ROW_FOCUS), - kbOpts: { primary: KeyCode.UpArrow } -}); -KeybindingsRegistry.registerCommandAndKeybindingRule(focusSearchFromSettingsCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.workbenchContrib())); - +startSearchCommand.register(); class ClearSearchResultsCommand extends SettingsCommand { @@ -383,9 +367,9 @@ class ClearSearchResultsCommand extends SettingsCommand { const clearSearchResultsCommand = new ClearSearchResultsCommand({ id: SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, precondition: CONTEXT_SETTINGS_SEARCH_FOCUS, - kbOpts: { primary: KeyCode.Escape } + kbOpts: { primary: KeyCode.Escape, weight: KeybindingWeight.EditorContrib } }); -KeybindingsRegistry.registerCommandAndKeybindingRule(clearSearchResultsCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); +clearSearchResultsCommand.register(); class FocusSettingsFileEditorCommand extends SettingsCommand { @@ -401,16 +385,16 @@ class FocusSettingsFileEditorCommand extends SettingsCommand { const focusSettingsFileEditorCommand = new FocusSettingsFileEditorCommand({ id: SETTINGS_EDITOR_COMMAND_FOCUS_FILE, precondition: CONTEXT_SETTINGS_SEARCH_FOCUS, - kbOpts: { primary: KeyCode.DownArrow } + kbOpts: { primary: KeyCode.DownArrow, weight: KeybindingWeight.EditorContrib } }); -KeybindingsRegistry.registerCommandAndKeybindingRule(focusSettingsFileEditorCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); +focusSettingsFileEditorCommand.register(); const focusSettingsFromSearchCommand = new FocusSettingsFileEditorCommand({ id: SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH, precondition: CONTEXT_SETTINGS_SEARCH_FOCUS, - kbOpts: { primary: KeyCode.DownArrow } + kbOpts: { primary: KeyCode.DownArrow, weight: KeybindingWeight.WorkbenchContrib } }); -KeybindingsRegistry.registerCommandAndKeybindingRule(focusSettingsFromSearchCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.workbenchContrib())); +focusSettingsFromSearchCommand.register(); class FocusNextSearchResultCommand extends SettingsCommand { @@ -424,9 +408,9 @@ class FocusNextSearchResultCommand extends SettingsCommand { const focusNextSearchResultCommand = new FocusNextSearchResultCommand({ id: SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING, precondition: CONTEXT_SETTINGS_SEARCH_FOCUS, - kbOpts: { primary: KeyCode.Enter } + kbOpts: { primary: KeyCode.Enter, weight: KeybindingWeight.EditorContrib } }); -KeybindingsRegistry.registerCommandAndKeybindingRule(focusNextSearchResultCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); +focusNextSearchResultCommand.register(); class FocusPreviousSearchResultCommand extends SettingsCommand { @@ -440,9 +424,9 @@ class FocusPreviousSearchResultCommand extends SettingsCommand { const focusPreviousSearchResultCommand = new FocusPreviousSearchResultCommand({ id: SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING, precondition: CONTEXT_SETTINGS_SEARCH_FOCUS, - kbOpts: { primary: KeyMod.Shift | KeyCode.Enter } + kbOpts: { primary: KeyMod.Shift | KeyCode.Enter, weight: KeybindingWeight.EditorContrib } }); -KeybindingsRegistry.registerCommandAndKeybindingRule(focusPreviousSearchResultCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); +focusPreviousSearchResultCommand.register(); class EditFocusedSettingCommand extends SettingsCommand { @@ -456,26 +440,9 @@ class EditFocusedSettingCommand extends SettingsCommand { const editFocusedSettingCommand = new EditFocusedSettingCommand({ id: SETTINGS_EDITOR_COMMAND_EDIT_FOCUSED_SETTING, precondition: CONTEXT_SETTINGS_SEARCH_FOCUS, - kbOpts: { primary: KeyMod.CtrlCmd | KeyCode.US_DOT } + kbOpts: { primary: KeyMod.CtrlCmd | KeyCode.US_DOT, weight: KeybindingWeight.EditorContrib } }); -KeybindingsRegistry.registerCommandAndKeybindingRule(editFocusedSettingCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); - -class EditFocusedSettingCommand2 extends SettingsCommand { - - public runCommand(accessor: ServicesAccessor, args: any): void { - const preferencesEditor = this.getPreferencesEditor(accessor); - if (preferencesEditor instanceof SettingsEditor2) { - preferencesEditor.editSelectedSetting(); - } - } -} - -const editFocusedSettingCommand2 = new EditFocusedSettingCommand2({ - id: SETTINGS_EDITOR_COMMAND_EDIT_FOCUSED_SETTING, - precondition: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS), - kbOpts: { primary: KeyCode.Enter } -}); -KeybindingsRegistry.registerCommandAndKeybindingRule(editFocusedSettingCommand2.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.workbenchContrib())); +editFocusedSettingCommand.register(); class FocusSettingsListCommand extends SettingsCommand { @@ -490,6 +457,26 @@ class FocusSettingsListCommand extends SettingsCommand { const focusSettingsListCommand = new FocusSettingsListCommand({ id: SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_LIST, precondition: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_TOC_ROW_FOCUS), - kbOpts: { primary: KeyCode.Enter } + kbOpts: { primary: KeyCode.Enter, weight: KeybindingWeight.WorkbenchContrib } +}); +focusSettingsListCommand.register(); + +// Preferences menu + +MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { + group: '1_settings', + command: { + id: OpenSettingsAction.ID, + title: nls.localize({ key: 'miOpenSettings', comment: ['&& denotes a mnemonic'] }, "&&Settings") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { + group: '2_keybindings', + command: { + id: OpenGlobalKeybindingsAction.ID, + title: nls.localize({ key: 'miOpenKeymap', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts") + }, + order: 1 }); -KeybindingsRegistry.registerCommandAndKeybindingRule(focusSettingsListCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.workbenchContrib())); diff --git a/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts b/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts index f0037613a93..1fffc1a2884 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts @@ -90,6 +90,9 @@ export class PreferencesSearchService extends Disposable implements IPreferences } export class LocalSearchProvider implements ISearchProvider { + static readonly EXACT_MATCH_SCORE = 10000; + static readonly START_SCORE = 1000; + constructor(private _filter: string) { // Remove " and : which are likely to be copypasted as part of a setting name. // Leave other special characters which the user might want to search for. @@ -104,25 +107,36 @@ export class LocalSearchProvider implements ISearchProvider { return TPromise.wrap(null); } - let score = 1000; // Sort is not stable + let orderedScore = LocalSearchProvider.START_SCORE; // Sort is not stable const settingMatcher = (setting: ISetting) => { const matches = new SettingMatches(this._filter, setting, true, true, (filter, setting) => preferencesModel.findValueMatches(filter, setting)).matches; + const score = this._filter === setting.key ? + LocalSearchProvider.EXACT_MATCH_SCORE : + orderedScore--; + return matches && matches.length ? { matches, - score: score-- + score } : null; }; const filterMatches = preferencesModel.filterSettings(this._filter, this.getGroupFilter(this._filter), settingMatcher); - return TPromise.wrap({ - filterMatches - }); + if (filterMatches[0] && filterMatches[0].score === LocalSearchProvider.EXACT_MATCH_SCORE) { + return TPromise.wrap({ + filterMatches: filterMatches.slice(0, 1), + exactMatch: true + }); + } else { + return TPromise.wrap({ + filterMatches + }); + } } private getGroupFilter(filter: string): IGroupFilter { - const regex = strings.createRegExp(this._filter, false, { global: true }); + const regex = strings.createRegExp(filter, false, { global: true }); return (group: ISettingsGroup) => { return regex.test(group.title); }; @@ -391,6 +405,7 @@ function escapeSpecialChars(query: string): string { function remoteSettingToISetting(remoteSetting: IRemoteSetting): IExtensionSetting { return { description: remoteSetting.description.split('\n'), + descriptionIsMarkdown: false, descriptionRanges: null, key: remoteSetting.key, keyRange: null, diff --git a/src/vs/workbench/parts/preferences/test/browser/settingsTree.test.ts b/src/vs/workbench/parts/preferences/test/browser/settingsTree.test.ts index a1d32f71ac6..06517a42795 100644 --- a/src/vs/workbench/parts/preferences/test/browser/settingsTree.test.ts +++ b/src/vs/workbench/parts/preferences/test/browser/settingsTree.test.ts @@ -47,6 +47,13 @@ suite('SettingsTree', () => { label: 'Bar' }); + assert.deepEqual( + settingKeyToDisplayFormat('disableligatures.ligatures', 'disableligatures'), + { + category: '', + label: 'Ligatures' + }); + assert.deepEqual( settingKeyToDisplayFormat('foo.bar.etc', 'foo'), { @@ -62,14 +69,14 @@ suite('SettingsTree', () => { }); assert.deepEqual( - settingKeyToDisplayFormat('foo.bar.etc', 'foo.bar'), + settingKeyToDisplayFormat('foo.bar.etc', 'foo/bar'), { category: '', label: 'Etc' }); assert.deepEqual( - settingKeyToDisplayFormat('foo.bar.etc', 'something.foo'), + settingKeyToDisplayFormat('foo.bar.etc', 'something/foo'), { category: 'Bar', label: 'Etc' diff --git a/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts b/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts index 32464070e4e..2880d33cb38 100644 --- a/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts @@ -5,7 +5,7 @@ 'use strict'; -import 'vs/css!./media/gotoSymbolHandler'; +import 'vs/css!vs/editor/contrib/documentSymbols/media/symbol-icons'; import { TPromise } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import * as types from 'vs/base/common/types'; @@ -239,9 +239,10 @@ class SymbolEntry extends EditorQuickOpenEntryGroup { private icon: string; private description: string; private range: IRange; + private revealRange: IRange; private handler: GotoSymbolHandler; - constructor(index: number, name: string, type: string, description: string, icon: string, range: IRange, highlights: IHighlight[], editorService: IEditorService, handler: GotoSymbolHandler) { + constructor(index: number, name: string, type: string, description: string, icon: string, range: IRange, revealRange: IRange, highlights: IHighlight[], editorService: IEditorService, handler: GotoSymbolHandler) { super(); this.index = index; @@ -250,6 +251,7 @@ class SymbolEntry extends EditorQuickOpenEntryGroup { this.icon = icon; this.description = description; this.range = range; + this.revealRange = revealRange || range; this.setHighlights(highlights); this.editorService = editorService; this.handler = handler; @@ -342,10 +344,10 @@ class SymbolEntry extends EditorQuickOpenEntryGroup { private toSelection(): IRange { return { - startLineNumber: this.range.startLineNumber, - startColumn: this.range.startColumn || 1, - endLineNumber: this.range.startLineNumber, - endColumn: this.range.startColumn || 1 + startLineNumber: this.revealRange.startLineNumber, + startColumn: this.revealRange.startColumn || 1, + endLineNumber: this.revealRange.startLineNumber, + endColumn: this.revealRange.startColumn || 1 }; } } @@ -449,8 +451,8 @@ export class GotoSymbolHandler extends QuickOpenHandler { // Add results.push(new SymbolEntry(i, - label, icon, description, icon, - element.range, null, this.editorService, this + label, icon, description, `symbol-icon ${icon}`, + element.range, element.selectionRange, null, this.editorService, this )); } diff --git a/src/vs/workbench/parts/quickopen/browser/media/Constant_16x.svg b/src/vs/workbench/parts/quickopen/browser/media/Constant_16x.svg deleted file mode 100644 index ed2a1751005..00000000000 --- a/src/vs/workbench/parts/quickopen/browser/media/Constant_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/Constant_16x_inverse.svg b/src/vs/workbench/parts/quickopen/browser/media/Constant_16x_inverse.svg deleted file mode 100644 index 173e427f964..00000000000 --- a/src/vs/workbench/parts/quickopen/browser/media/Constant_16x_inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/EnumItem_16x.svg b/src/vs/workbench/parts/quickopen/browser/media/EnumItem_16x.svg deleted file mode 100755 index aa901ec1934..00000000000 --- a/src/vs/workbench/parts/quickopen/browser/media/EnumItem_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/EnumItem_inverse_16x.svg b/src/vs/workbench/parts/quickopen/browser/media/EnumItem_inverse_16x.svg deleted file mode 100755 index 791759092fc..00000000000 --- a/src/vs/workbench/parts/quickopen/browser/media/EnumItem_inverse_16x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/Event_16x_vscode.svg b/src/vs/workbench/parts/quickopen/browser/media/Event_16x_vscode.svg deleted file mode 100644 index 0e202ec10be..00000000000 --- a/src/vs/workbench/parts/quickopen/browser/media/Event_16x_vscode.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/Event_16x_vscode_inverse.svg b/src/vs/workbench/parts/quickopen/browser/media/Event_16x_vscode_inverse.svg deleted file mode 100644 index a508edcd3d6..00000000000 --- a/src/vs/workbench/parts/quickopen/browser/media/Event_16x_vscode_inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/Operator_16x_vscode.svg b/src/vs/workbench/parts/quickopen/browser/media/Operator_16x_vscode.svg deleted file mode 100644 index ba2f2d091cf..00000000000 --- a/src/vs/workbench/parts/quickopen/browser/media/Operator_16x_vscode.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/Operator_16x_vscode_inverse.svg b/src/vs/workbench/parts/quickopen/browser/media/Operator_16x_vscode_inverse.svg deleted file mode 100644 index 21e1e814b2e..00000000000 --- a/src/vs/workbench/parts/quickopen/browser/media/Operator_16x_vscode_inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/Structure_16x_vscode.svg b/src/vs/workbench/parts/quickopen/browser/media/Structure_16x_vscode.svg deleted file mode 100644 index e776cbc5651..00000000000 --- a/src/vs/workbench/parts/quickopen/browser/media/Structure_16x_vscode.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/Structure_16x_vscode_inverse.svg b/src/vs/workbench/parts/quickopen/browser/media/Structure_16x_vscode_inverse.svg deleted file mode 100644 index 1b76b62be9a..00000000000 --- a/src/vs/workbench/parts/quickopen/browser/media/Structure_16x_vscode_inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/Template_16x_vscode.svg b/src/vs/workbench/parts/quickopen/browser/media/Template_16x_vscode.svg deleted file mode 100644 index 788cc8d6450..00000000000 --- a/src/vs/workbench/parts/quickopen/browser/media/Template_16x_vscode.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/Template_16x_vscode_inverse.svg b/src/vs/workbench/parts/quickopen/browser/media/Template_16x_vscode_inverse.svg deleted file mode 100644 index 6cec71cb033..00000000000 --- a/src/vs/workbench/parts/quickopen/browser/media/Template_16x_vscode_inverse.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/media/gotoSymbolHandler.css b/src/vs/workbench/parts/quickopen/browser/media/gotoSymbolHandler.css deleted file mode 100644 index 4d99facd57b..00000000000 --- a/src/vs/workbench/parts/quickopen/browser/media/gotoSymbolHandler.css +++ /dev/null @@ -1,154 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.constant { - background-image: url('Constant_16x.svg'); - background-repeat: no-repeat; - background-position: 0 -2px; -} -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.constant, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.constant { - background-image: url('Constant_16x_inverse.svg'); -} - -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.enum-member { - background-image: url('EnumItem_16x.svg'); - background-repeat: no-repeat; - background-position: 0 -2px; -} -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.enum-member, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.enum-member { - background-image: url('EnumItem_inverse_16x.svg'); -} - -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.struct { - background-image: url('Structure_16x_vscode.svg'); - background-repeat: no-repeat; - background-position: 0 -2px; -} -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.struct, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.struct { - background-image: url('Structure_16x_vscode_inverse.svg'); -} - -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.event { - background-image: url('Event_16x_vscode.svg'); - background-repeat: no-repeat; - background-position: 0 -2px; -} -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.event, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.event { - background-image: url('Event_16x_vscode_inverse.svg'); -} - -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.operator { - background-image: url('Operator_16x_vscode.svg'); - background-repeat: no-repeat; - background-position: 0 -2px; -} -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.operator, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.operator { - background-image: url('Operator_16x_vscode_inverse.svg'); -} - -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.type-parameter { - background-image: url('Template_16x_vscode.svg'); - background-repeat: no-repeat; - background-position: 0 -2px; -} -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.type-parameter, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.type-parameter { - background-image: url('Template_16x_vscode_inverse.svg'); -} - -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.method, -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.function, -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.constructor, -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.field, -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.variable, -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.class, -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.interface, -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.object, -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.namespace, -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.package, -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.module, -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.property, -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.enum, -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.key, -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.string, -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.rule, -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.file, -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.array, -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.number, -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.null, -.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.boolean { - background-image: url('symbol-sprite.svg'); - background-repeat: no-repeat; -} - -.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.method, -.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.function, -.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.constructor { background-position: 0 -4px; } -.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.field, -.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.variable { background-position: -22px -4px; } -.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.class { background-position: -43px -3px; } -.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.interface { background-position: -63px -4px; } -.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.object, -.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.namespace, -.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.package, -.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.module { background-position: -82px -4px; } -.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.property { background-position: -102px -3px; } -.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.enum { background-position: -122px -3px; } -.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.key, -.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.string { background-position: -202px -3px; } -.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.rule { background-position: -242px -4px; } -.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.file { background-position: -262px -4px; } -.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.array { background-position: -302px -4px; } -.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.number { background-position: -322px -4px; } -.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.null, -.vs .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.boolean { background-position: -343px -4px; } - -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.method, -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.function, -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.constructor, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.method, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.function, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.constructor { background-position: 0 -24px; } -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.field, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.field, -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.variable, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.variable { background-position: -22px -24px; } -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.class, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.class { background-position: -43px -23px; } -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.interface, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.interface { background-position: -63px -24px; } -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.object, -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.namespace, -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.package, -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.module, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.object, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.namespace, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.package, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.module { background-position: -82px -24px; } -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.property, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.property { background-position: -102px -23px; } -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.key, -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.string, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.key, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.string { background-position: -202px -23px; } -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.enum, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.enum { background-position: -122px -23px; } -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.rule, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.rule { background-position: -242px -24px; } -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.file, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.file { background-position: -262px -24px; } -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.array, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.array { background-position: -302px -24px; } -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.number, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.number { background-position: -322px -24px; } -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.null, -.vs-dark .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.boolean, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.null, -.hc-black .monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon.boolean { background-position: -342px -24px; } diff --git a/src/vs/workbench/parts/quickopen/browser/media/symbol-sprite.svg b/src/vs/workbench/parts/quickopen/browser/media/symbol-sprite.svg deleted file mode 100644 index ee9a63dcf6f..00000000000 --- a/src/vs/workbench/parts/quickopen/browser/media/symbol-sprite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts b/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts index be4d6379082..638f3f36c58 100644 --- a/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts +++ b/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts @@ -9,7 +9,7 @@ import * as env from 'vs/base/common/platform'; import * as nls from 'vs/nls'; import { QuickOpenHandlerDescriptor, IQuickOpenRegistry, Extensions as QuickOpenExtensions } from 'vs/workbench/browser/quickopen'; import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { GotoSymbolAction, GOTO_SYMBOL_PREFIX, SCOPE_PREFIX, GotoSymbolHandler } from 'vs/workbench/parts/quickopen/browser/gotoSymbolHandler'; @@ -19,7 +19,7 @@ import { HELP_PREFIX, HelpHandler } from 'vs/workbench/parts/quickopen/browser/h import { VIEW_PICKER_PREFIX, OpenViewPickerAction, QuickOpenViewPickerAction, ViewPickerHandler } from 'vs/workbench/parts/quickopen/browser/viewPickerHandler'; import { inQuickOpenContext, getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; // Register Actions const registry = Registry.as(ActionExtensions.WorkbenchActions); @@ -50,7 +50,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenViewPickerAct const quickOpenNavigateNextInViewPickerId = 'workbench.action.quickOpenNavigateNextInViewPicker'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: quickOpenNavigateNextInViewPickerId, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50), + weight: KeybindingWeight.WorkbenchContrib + 50, handler: getQuickNavigateHandler(quickOpenNavigateNextInViewPickerId, true), when: inViewsPickerContext, primary: viewPickerKeybinding.primary, @@ -61,7 +61,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const quickOpenNavigatePreviousInViewPickerId = 'workbench.action.quickOpenNavigatePreviousInViewPicker'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: quickOpenNavigatePreviousInViewPickerId, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50), + weight: KeybindingWeight.WorkbenchContrib + 50, handler: getQuickNavigateHandler(quickOpenNavigatePreviousInViewPickerId, false), when: inViewsPickerContext, primary: viewPickerKeybinding.primary | KeyMod.Shift, @@ -144,4 +144,44 @@ Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpen } ] ) -); \ No newline at end of file +); + +// View menu + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '1_open', + command: { + id: ShowAllCommandsAction.ID, + title: nls.localize({ key: 'miCommandPalette', comment: ['&& denotes a mnemonic'] }, "&&Command Palette...") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '1_open', + command: { + id: OpenViewPickerAction.ID, + title: nls.localize({ key: 'miOpenView', comment: ['&& denotes a mnemonic'] }, "&&Open View...") + }, + order: 2 +}); + +// Go to menu + +MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { + group: 'z_go_to', + command: { + id: 'workbench.action.gotoSymbol', + title: nls.localize({ key: 'miGotoSymbolInFile', comment: ['&& denotes a mnemonic'] }, "Go to &&Symbol in File...") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { + group: 'z_go_to', + command: { + id: 'workbench.action.gotoLine', + title: nls.localize({ key: 'miGotoLine', comment: ['&& denotes a mnemonic'] }, "Go to &&Line...") + }, + order: 7 +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts b/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts index 90a6893cdf4..505f3c8a672 100644 --- a/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts @@ -33,7 +33,7 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Position } from 'vs/editor/common/core/position'; import { rot } from 'vs/base/common/numbers'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { peekViewBorder, peekViewTitleBackground, peekViewTitleForeground, peekViewTitleInfoForeground } from 'vs/editor/contrib/referenceSearch/referencesWidget'; import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; @@ -372,7 +372,7 @@ export class ShowPreviousChangeAction extends EditorAction { label: nls.localize('show previous change', "Show Previous Change"), alias: 'Show Previous Change', precondition: null, - kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.F3 } + kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.F3, weight: KeybindingWeight.EditorContrib } }); } @@ -406,7 +406,7 @@ export class ShowNextChangeAction extends EditorAction { label: nls.localize('show next change', "Show Next Change"), alias: 'Show Next Change', precondition: null, - kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Alt | KeyCode.F3 } + kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Alt | KeyCode.F3, weight: KeybindingWeight.EditorContrib } }); } @@ -440,7 +440,7 @@ export class MoveToPreviousChangeAction extends EditorAction { label: nls.localize('move to previous change', "Move to Previous Change"), alias: 'Move to Previous Change', precondition: null, - kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.F5 } + kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Shift | KeyMod.Alt | KeyCode.F5, weight: KeybindingWeight.EditorContrib } }); } @@ -482,7 +482,7 @@ export class MoveToNextChangeAction extends EditorAction { label: nls.localize('move to next change', "Move to Next Change"), alias: 'Move to Next Change', precondition: null, - kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Alt | KeyCode.F5 } + kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, primary: KeyMod.Alt | KeyCode.F5, weight: KeybindingWeight.EditorContrib } }); } @@ -518,7 +518,7 @@ registerEditorAction(MoveToNextChangeAction); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'closeDirtyDiff', - weight: KeybindingsRegistry.WEIGHT.editorContrib(50), + weight: KeybindingWeight.EditorContrib + 50, primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape], when: ContextKeyExpr.and(isDirtyDiffVisible), diff --git a/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css b/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css index 8638431c24b..108bf404719 100644 --- a/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css +++ b/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css @@ -15,7 +15,6 @@ .scm-viewlet .empty-message { padding: 10px 22px 0 22px; - opacity: 0.5; } .scm-viewlet:not(.empty) .empty-message, diff --git a/src/vs/workbench/parts/scm/electron-browser/scm.contribution.ts b/src/vs/workbench/parts/scm/electron-browser/scm.contribution.ts index 56943a892a0..453a5aa6da3 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scm.contribution.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scm.contribution.ts @@ -13,7 +13,7 @@ import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, To import { VIEWLET_ID } from 'vs/workbench/parts/scm/common/scm'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { StatusUpdater, StatusBarController } from './scmActivity'; import { SCMViewlet } from 'vs/workbench/parts/scm/electron-browser/scmViewlet'; @@ -71,7 +71,7 @@ Registry.as(ConfigurationExtensions.Configuration).regis properties: { 'scm.alwaysShowProviders': { type: 'boolean', - description: localize('alwaysShowProviders', "Whether to always show the Source Control Provider section."), + description: localize('alwaysShowProviders', "Controls whether to always show the Source Control Provider section."), default: false }, 'scm.diffDecorations': { @@ -88,3 +88,14 @@ Registry.as(ConfigurationExtensions.Configuration).regis } } }); + +// View menu + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '3_views', + command: { + id: VIEWLET_ID, + title: localize({ key: 'miViewSCM', comment: ['&& denotes a mnemonic'] }, "S&&CM") + }, + order: 3 +}); diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index 6d60c47a9f4..4a9da7a25f7 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -1285,7 +1285,8 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle this.addPanels([{ panel, size: panel.minimumSize, index: index++ }]); panel.repository.focus(); panel.onDidFocus(() => this.lastFocusedRepository = panel.repository); - if (newRepositoryPanels.length === 1 || this.lastFocusedRepository === panel.repository) { + + if (this.lastFocusedRepository === panel.repository) { panel.focus(); } }); @@ -1320,6 +1321,11 @@ export class SCMViewlet extends PanelViewlet implements IViewModel, IViewsViewle this.updateTitleArea(); } + + if (this.isVisible()) { + panelsToRemove.forEach(p => p.repository.setSelected(false)); + newRepositoryPanels.forEach(p => p.repository.setSelected(true)); + } } private getContributableViewsSize(): number { diff --git a/src/vs/workbench/parts/search/browser/openFileHandler.ts b/src/vs/workbench/parts/search/browser/openFileHandler.ts index 179ea2e6ec3..baf0b860cc8 100644 --- a/src/vs/workbench/parts/search/browser/openFileHandler.ts +++ b/src/vs/workbench/parts/search/browser/openFileHandler.ts @@ -8,7 +8,6 @@ import { TPromise } from 'vs/base/common/winjs.base'; import * as errors from 'vs/base/common/errors'; import * as nls from 'vs/nls'; import * as paths from 'vs/base/common/paths'; -import * as labels from 'vs/base/common/labels'; import * as objects from 'vs/base/common/objects'; import { defaultGenerator } from 'vs/base/common/idGenerator'; import URI from 'vs/base/common/uri'; @@ -34,6 +33,8 @@ import { getOutOfWorkspaceEditorResources } from 'vs/workbench/parts/search/comm import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { prepareQuery, IPreparedQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer'; import { IFileService } from 'vs/platform/files/common/files'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; +import { untildify } from 'vs/base/common/labels'; export class FileQuickOpenModel extends QuickOpenModel { @@ -125,7 +126,8 @@ export class OpenFileHandler extends QuickOpenHandler { @IWorkspaceContextService private contextService: IWorkspaceContextService, @ISearchService private searchService: ISearchService, @IEnvironmentService private environmentService: IEnvironmentService, - @IFileService private fileService: IFileService + @IFileService private fileService: IFileService, + @IUriDisplayService private uriDisplayService: IUriDisplayService ) { super(); @@ -145,7 +147,7 @@ export class OpenFileHandler extends QuickOpenHandler { } // Untildify file pattern - query.value = labels.untildify(query.value, this.environmentService.userHome); + query.value = untildify(query.value, this.environmentService.userHome); // Do find results return this.doFindResults(query, this.cacheState.cacheKey, maxSortedResults); @@ -171,7 +173,7 @@ export class OpenFileHandler extends QuickOpenHandler { const fileMatch = complete.results[i]; const label = paths.basename(fileMatch.resource.fsPath); - const description = labels.getPathLabel(resources.dirname(fileMatch.resource), this.environmentService, this.contextService); + const description = this.uriDisplayService.getLabel(resources.dirname(fileMatch.resource), true); results.push(this.instantiationService.createInstance(FileEntry, fileMatch.resource, label, description, iconClass)); } diff --git a/src/vs/workbench/parts/search/browser/openSymbolHandler.ts b/src/vs/workbench/parts/search/browser/openSymbolHandler.ts index 8882abb8fc5..17472a73e75 100644 --- a/src/vs/workbench/parts/search/browser/openSymbolHandler.ts +++ b/src/vs/workbench/parts/search/browser/openSymbolHandler.ts @@ -16,16 +16,14 @@ import * as filters from 'vs/base/common/filters'; import * as strings from 'vs/base/common/strings'; import { Range } from 'vs/editor/common/core/range'; import { EditorInput, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor'; -import * as labels from 'vs/base/common/labels'; import { symbolKindToCssClass } from 'vs/editor/common/modes'; import { IResourceInput } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkspaceSymbolProvider, getWorkspaceSymbols, IWorkspaceSymbol } from 'vs/workbench/parts/search/common/search'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { basename } from 'vs/base/common/paths'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; class SymbolEntry extends EditorQuickOpenEntry { @@ -35,9 +33,8 @@ class SymbolEntry extends EditorQuickOpenEntry { private _bearing: IWorkspaceSymbol, private _provider: IWorkspaceSymbolProvider, @IConfigurationService private readonly _configurationService: IConfigurationService, - @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, @IEditorService editorService: IEditorService, - @IEnvironmentService private readonly _environmentService: IEnvironmentService + @IUriDisplayService private _uriDisplayService: IUriDisplayService ) { super(editorService); } @@ -56,7 +53,7 @@ class SymbolEntry extends EditorQuickOpenEntry { if (containerName) { return `${containerName} — ${basename(this._bearing.location.uri.fsPath)}`; } else { - return labels.getPathLabel(this._bearing.location.uri, this._environmentService, this._contextService); + return this._uriDisplayService.getLabel(this._bearing.location.uri, true); } } return containerName; diff --git a/src/vs/workbench/parts/search/browser/searchActions.ts b/src/vs/workbench/parts/search/browser/searchActions.ts index f9483bb4466..b0c4c900a2e 100644 --- a/src/vs/workbench/parts/search/browser/searchActions.ts +++ b/src/vs/workbench/parts/search/browser/searchActions.ts @@ -233,7 +233,34 @@ export class CollapseDeepestExpandedLevelAction extends Action { return TPromise.as(null); // Global action disabled if user is in edit mode from another action } - viewer.collapseDeepestExpandedLevel(); + /** + * The hierarchy is FolderMatch, FileMatch, Match. If the top level is FileMatches, then there is only + * one level to collapse so collapse everything. If FolderMatch, check if there are visible grandchildren, + * i.e. if Matches are returned by the navigator, and if so, collapse to them, otherwise collapse all levels. + */ + const navigator = viewer.getNavigator(); + let node = navigator.first(); + let collapseFileMatchLevel = false; + if (node instanceof FolderMatch) { + while (node = navigator.next()) { + if (node instanceof Match) { + collapseFileMatchLevel = true; + break; + } + } + } + + if (collapseFileMatchLevel) { + node = navigator.first(); + do { + if (node instanceof FileMatch) { + viewer.collapse(node); + } + } while (node = navigator.next()); + } else { + viewer.collapseAll(); + } + viewer.clearSelection(); viewer.clearFocus(); viewer.domFocus(); diff --git a/src/vs/workbench/parts/search/browser/searchResultsView.ts b/src/vs/workbench/parts/search/browser/searchResultsView.ts index dd4b0933013..4a27ccf12d5 100644 --- a/src/vs/workbench/parts/search/browser/searchResultsView.ts +++ b/src/vs/workbench/parts/search/browser/searchResultsView.ts @@ -21,14 +21,13 @@ import { RemoveAction, ReplaceAllAction, ReplaceAction, ReplaceAllInFolderAction import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { getPathLabel } from 'vs/base/common/labels'; import { FileKind } from 'vs/platform/files/common/files'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; import { WorkbenchTreeController, WorkbenchTree } from 'vs/platform/list/browser/listService'; import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; export class SearchDataSource implements IDataSource { @@ -238,11 +237,11 @@ export class SearchRenderer extends Disposable implements IRenderer { private renderFolderMatch(tree: ITree, folderMatch: FolderMatch, templateData: IFolderMatchTemplate): void { if (folderMatch.hasRoot()) { const workspaceFolder = this.contextService.getWorkspaceFolder(folderMatch.resource()); - const fileKind = workspaceFolder && resources.isEqual(workspaceFolder.uri, folderMatch.resource()) ? - FileKind.ROOT_FOLDER : - FileKind.FOLDER; - - templateData.label.setFile(folderMatch.resource(), { fileKind }); + if (workspaceFolder && resources.isEqual(workspaceFolder.uri, folderMatch.resource())) { + templateData.label.setFile(folderMatch.resource(), { fileKind: FileKind.ROOT_FOLDER, hidePath: true }); + } else { + templateData.label.setFile(folderMatch.resource(), { fileKind: FileKind.FOLDER }); + } } else { templateData.label.setValue(nls.localize('searchFolderMatch.other.label', "Other files")); } @@ -263,10 +262,8 @@ export class SearchRenderer extends Disposable implements IRenderer { } private renderFileMatch(tree: ITree, fileMatch: FileMatch, templateData: IFileMatchTemplate): void { - const folderMatch = fileMatch.parent(); - const root = folderMatch.hasRoot() ? folderMatch.resource() : undefined; templateData.el.setAttribute('data-resource', fileMatch.resource().toString()); - templateData.label.setFile(fileMatch.resource(), { root }); + templateData.label.setFile(fileMatch.resource(), { hideIcon: false }); let count = fileMatch.count(); templateData.badge.setCount(count); templateData.badge.setTitleFormat(count > 1 ? nls.localize('searchMatches', "{0} matches found", count) : nls.localize('searchMatch', "{0} match found", count)); @@ -321,8 +318,7 @@ export class SearchRenderer extends Disposable implements IRenderer { export class SearchAccessibilityProvider implements IAccessibilityProvider { constructor( - @IWorkspaceContextService private contextService: IWorkspaceContextService, - @IEnvironmentService private environmentService: IEnvironmentService + @IUriDisplayService private uriDisplayService: IUriDisplayService ) { } @@ -332,7 +328,7 @@ export class SearchAccessibilityProvider implements IAccessibilityProvider { } if (element instanceof FileMatch) { - const path = getPathLabel(element.resource(), this.environmentService, this.contextService) || element.resource().fsPath; + const path = this.uriDisplayService.getLabel(element.resource(), true) || element.resource().fsPath; return nls.localize('fileMatchAriaLabel', "{0} matches in file {1} of folder {2}, Search result", element.count(), element.name(), paths.dirname(path)); } diff --git a/src/vs/workbench/parts/search/browser/searchView.ts b/src/vs/workbench/parts/search/browser/searchView.ts index fb02650821e..fe81d90a0f5 100644 --- a/src/vs/workbench/parts/search/browser/searchView.ts +++ b/src/vs/workbench/parts/search/browser/searchView.ts @@ -5,6 +5,7 @@ 'use strict'; +import 'vs/css!./media/searchview'; import { $, Builder } from 'vs/base/browser/builder'; import * as dom from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -14,15 +15,14 @@ import { MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; import { IAction } from 'vs/base/common/actions'; import { Delayer } from 'vs/base/common/async'; import * as errors from 'vs/base/common/errors'; -import { debounceEvent, Emitter } from 'vs/base/common/event'; +import { debounceEvent, Emitter, anyEvent } from 'vs/base/common/event'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import * as paths from 'vs/base/common/paths'; import * as env from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IFocusEvent, ITree } from 'vs/base/parts/tree/browser/tree'; -import 'vs/css!./media/searchview'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import * as nls from 'vs/nls'; @@ -582,14 +582,9 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { } })); - let treeHasFocus = false; - this.tree.onDidFocus(() => { - treeHasFocus = true; - }); - - this._register(this.tree.onDidChangeFocus((e: IFocusEvent) => { - if (treeHasFocus) { - const focus = e.focus; + this._register(anyEvent(this.tree.onDidFocus, this.tree.onDidChangeFocus)(() => { + if (this.tree.isDOMFocused()) { + const focus = this.tree.getFocus(); this.firstMatchFocused.set(this.tree.getNavigator().first() === focus); this.fileMatchOrMatchFocused.set(!!focus); this.fileMatchFocused.set(focus instanceof FileMatch); @@ -600,7 +595,6 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { })); this._register(this.tree.onDidBlur(e => { - treeHasFocus = false; this.firstMatchFocused.reset(); this.fileMatchOrMatchFocused.reset(); this.fileMatchFocused.reset(); @@ -1075,7 +1069,6 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { isRegExp: isRegex, isCaseSensitive: isCaseSensitive, isWordMatch: isWholeWords, - wordSeparators: this.configurationService.getValue().editor.wordSeparators, isSmartCase: this.configurationService.getValue().search.smartCase }; @@ -1359,7 +1352,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this.searchWidget.setReplaceAllActionState(false); - this.viewModel.search(query).done(onComplete, onError, onProgress); + this.viewModel.search(query, onProgress).done(onComplete, onError); } private updateSearchResultCount(): void { diff --git a/src/vs/workbench/parts/search/browser/searchWidget.ts b/src/vs/workbench/parts/search/browser/searchWidget.ts index 15342de2365..86bf8193435 100644 --- a/src/vs/workbench/parts/search/browser/searchWidget.ts +++ b/src/vs/workbench/parts/search/browser/searchWidget.ts @@ -14,7 +14,7 @@ import { FindInput, IFindInputOptions } from 'vs/base/browser/ui/findinput/findI import { IMessage, HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { Button, IButtonOptions } from 'vs/base/browser/ui/button/button'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ContextKeyExpr, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; @@ -473,7 +473,7 @@ export class SearchWidget extends Widget { export function registerContributions() { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: ReplaceAllAction.ID, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceActiveKey, CONTEXT_FIND_WIDGET_NOT_VISIBLE), primary: KeyMod.Alt | KeyMod.CtrlCmd | KeyCode.Enter, handler: accessor => { diff --git a/src/vs/workbench/parts/search/common/queryBuilder.ts b/src/vs/workbench/parts/search/common/queryBuilder.ts index 52392ff3903..08bea8ac1cd 100644 --- a/src/vs/workbench/parts/search/common/queryBuilder.ts +++ b/src/vs/workbench/parts/search/common/queryBuilder.ts @@ -11,6 +11,7 @@ import * as collections from 'vs/base/common/collections'; import * as strings from 'vs/base/common/strings'; import * as glob from 'vs/base/common/glob'; import * as paths from 'vs/base/common/paths'; +import * as resources from 'vs/base/common/resources'; import uri from 'vs/base/common/uri'; import { untildify } from 'vs/base/common/labels'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; @@ -73,20 +74,24 @@ export class QueryBuilder { if (contentPattern) { this.resolveSmartCaseToCaseSensitive(contentPattern); + + contentPattern.wordSeparators = this.configurationService.getValue().editor.wordSeparators; } - const query = { + const query: ISearchQuery = { type, folderQueries, usingSearchPaths: !!(searchPaths && searchPaths.length), extraFileResources: options.extraFileResources, - filePattern: options.filePattern, + filePattern: options.filePattern + ? options.filePattern.trim() + : options.filePattern, excludePattern, includePattern, maxResults: options.maxResults, sortByScore: options.sortByScore, cacheKey: options.cacheKey, - contentPattern: contentPattern, + contentPattern, useRipgrep, disregardIgnoreFiles: options.disregardIgnoreFiles || !useIgnoreFiles, disregardExcludeSettings: options.disregardExcludeSettings, @@ -270,18 +275,18 @@ export class QueryBuilder { if (this.workspaceContextService.getWorkbenchState() === WorkbenchState.FOLDER) { // TODO: @Sandy Try checking workspace folders length instead. const workspaceUri = this.workspaceContextService.getWorkspace().folders[0].uri; - return [workspaceUri.with({ path: paths.normalize(paths.join(workspaceUri.path, searchPath)) })]; + return [resources.joinPath(workspaceUri, searchPath)]; } else if (searchPath === './') { return []; // ./ or ./**/foo makes sense for single-folder but not multi-folder workspaces } else { const relativeSearchPathMatch = searchPath.match(/\.[\/\\]([^\/\\]+)([\/\\].+)?/); if (relativeSearchPathMatch) { const searchPathRoot = relativeSearchPathMatch[1]; - const matchingRoots = this.workspaceContextService.getWorkspace().folders.filter(folder => paths.basename(folder.uri.fsPath) === searchPathRoot || folder.name === searchPathRoot); + const matchingRoots = this.workspaceContextService.getWorkspace().folders.filter(folder => resources.basename(folder.uri) === searchPathRoot || folder.name === searchPathRoot); if (matchingRoots.length) { return matchingRoots.map(root => { return relativeSearchPathMatch[2] ? - root.uri.with({ path: paths.normalize(paths.join(root.uri.path, relativeSearchPathMatch[2])) }) : + resources.joinPath(root.uri, relativeSearchPathMatch[2]) : root.uri; }); } else { diff --git a/src/vs/workbench/parts/search/common/searchModel.ts b/src/vs/workbench/parts/search/common/searchModel.ts index 186d45a23a6..0705774d83c 100644 --- a/src/vs/workbench/parts/search/common/searchModel.ts +++ b/src/vs/workbench/parts/search/common/searchModel.ts @@ -8,7 +8,7 @@ import * as strings from 'vs/base/common/strings'; import * as errors from 'vs/base/common/errors'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; -import { TPromise, PPromise } from 'vs/base/common/winjs.base'; +import { TPromise } from 'vs/base/common/winjs.base'; import URI from 'vs/base/common/uri'; import { values, ResourceMap, TernarySearchTree } from 'vs/base/common/map'; import { Event, Emitter, fromPromise, stopwatch, anyEvent } from 'vs/base/common/event'; @@ -708,7 +708,7 @@ export class SearchModel extends Disposable { private readonly _onReplaceTermChanged: Emitter = this._register(new Emitter()); public readonly onReplaceTermChanged: Event = this._onReplaceTermChanged.event; - private currentRequest: PPromise; + private currentRequest: TPromise; constructor(@ISearchService private searchService: ISearchService, @ITelemetryService private telemetryService: ITelemetryService, @IInstantiationService private instantiationService: IInstantiationService) { super(); @@ -743,18 +743,26 @@ export class SearchModel extends Disposable { return this._searchResult; } - public search(query: ISearchQuery): PPromise { + public search(query: ISearchQuery, onProgress?: (result: ISearchProgressItem) => void): TPromise { this.cancelSearch(); + this._searchQuery = query; - this.currentRequest = this.searchService.search(this._searchQuery); - this.searchResult.clear(); - this._searchResult.query = this._searchQuery; + + const progressEmitter = new Emitter(); this._replacePattern = new ReplacePattern(this._replaceString, this._searchQuery.contentPattern); + this.currentRequest = this.searchService.search(this._searchQuery, p => { + progressEmitter.fire(); + this.onSearchProgress(p); + + if (onProgress) { + onProgress(p); + } + }); + const onDone = fromPromise(this.currentRequest); - const progressEmitter = new Emitter(); const onFirstRender = anyEvent(onDone, progressEmitter.event); const onFirstRenderStopwatch = stopwatch(onFirstRender); /* __GDPR__ @@ -777,12 +785,7 @@ export class SearchModel extends Disposable { const currentRequest = this.currentRequest; this.currentRequest.then( value => this.onSearchCompleted(value, Date.now() - start), - e => this.onSearchError(e, Date.now() - start), - p => { - progressEmitter.fire(); - this.onSearchProgress(p); - } - ); + e => this.onSearchError(e, Date.now() - start)); // this.currentRequest may be completed (and nulled) immediately return currentRequest; diff --git a/src/vs/workbench/parts/search/electron-browser/search.contribution.ts b/src/vs/workbench/parts/search/electron-browser/search.contribution.ts index 6aabebef7d9..407f714fbf4 100644 --- a/src/vs/workbench/parts/search/electron-browser/search.contribution.ts +++ b/src/vs/workbench/parts/search/electron-browser/search.contribution.ts @@ -19,7 +19,7 @@ import { ExplorerFolderContext, ExplorerRootContext } from 'vs/workbench/parts/f import { SyncActionDescriptor, MenuRegistry, MenuId, ICommandAction } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { QuickOpenHandlerDescriptor, IQuickOpenRegistry, Extensions as QuickOpenExtensions } from 'vs/workbench/browser/quickopen'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; @@ -68,7 +68,7 @@ const category = nls.localize('search', "Search"); KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'workbench.action.search.toggleQueryDetails', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: Constants.SearchViewVisibleKey, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_J, handler: accessor => { @@ -81,7 +81,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Constants.FocusSearchFromResults, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.FirstMatchFocusKey), primary: KeyMod.CtrlCmd | KeyCode.UpArrow, handler: (accessor, args: any) => { @@ -92,7 +92,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Constants.OpenMatchToSide, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.FileMatchOrMatchFocusKey), primary: KeyMod.CtrlCmd | KeyCode.Enter, mac: { @@ -107,7 +107,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Constants.CancelActionId, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, WorkbenchListFocusContextKey), primary: KeyCode.Escape, handler: (accessor, args: any) => { @@ -118,7 +118,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Constants.RemoveActionId, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.FileMatchOrMatchFocusKey), primary: KeyCode.Delete, mac: { @@ -133,7 +133,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Constants.ReplaceActionId, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceActiveKey, Constants.MatchFocusKey), primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.KEY_1, handler: (accessor, args: any) => { @@ -145,7 +145,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Constants.ReplaceAllInFileActionId, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceActiveKey, Constants.FileFocusKey), primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.KEY_1, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter], @@ -158,7 +158,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Constants.ReplaceAllInFolderActionId, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceActiveKey, Constants.FolderFocusKey), primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.KEY_1, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter], @@ -171,7 +171,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Constants.CloseReplaceWidgetActionId, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.ReplaceInputBoxFocusedKey), primary: KeyCode.Escape, handler: (accessor, args: any) => { @@ -181,7 +181,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: FocusNextInputAction.ID, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.InputBoxFocusedKey), primary: KeyMod.CtrlCmd | KeyCode.DownArrow, handler: (accessor, args: any) => { @@ -191,7 +191,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: FocusPreviousInputAction.ID, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.InputBoxFocusedKey, Constants.SearchInputBoxFocusedKey.toNegated()), primary: KeyMod.CtrlCmd | KeyCode.UpArrow, handler: (accessor, args: any) => { @@ -241,7 +241,7 @@ MenuRegistry.appendMenuItem(MenuId.SearchContext, { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Constants.CopyMatchCommandId, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: Constants.FileMatchOrMatchFocusKey, primary: KeyMod.CtrlCmd | KeyCode.KEY_C, handler: copyMatchCommand @@ -259,7 +259,7 @@ MenuRegistry.appendMenuItem(MenuId.SearchContext, { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: Constants.CopyPathCommandId, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: Constants.FileMatchOrFolderMatchFocusKey, primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_C, win: { @@ -476,29 +476,45 @@ const registry = Registry.as(ActionExtensions.Workbenc // Show Search 'when' is redundant but if the two conflict with exactly the same keybinding and 'when' clause, then they can show up as "unbound" - #51780 registry.registerWorkbenchAction(new SyncActionDescriptor(FindInFilesAction, VIEW_ID, nls.localize('showSearchViewl', "Show Search"), { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_F }, Constants.SearchViewVisibleKey.toNegated()), 'View: Show Search', nls.localize('view', "View")); registry.registerWorkbenchAction(new SyncActionDescriptor(FindInFilesAction, Constants.FindInFilesActionId, nls.localize('findInFiles', "Find in Files"), { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_F }), 'Find in Files', category); +MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { + group: '4_find_global', + command: { + id: Constants.FindInFilesActionId, + title: nls.localize({ key: 'miFindInFiles', comment: ['&& denotes a mnemonic'] }, "Find &&in Files") + }, + order: 1 +}); registry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextSearchResultAction, FocusNextSearchResultAction.ID, FocusNextSearchResultAction.LABEL, { primary: KeyCode.F4 }, ContextKeyExpr.and(Constants.HasSearchResults)), 'Focus Next Search Result', category); registry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousSearchResultAction, FocusPreviousSearchResultAction.ID, FocusPreviousSearchResultAction.LABEL, { primary: KeyMod.Shift | KeyCode.F4 }, ContextKeyExpr.and(Constants.HasSearchResults)), 'Focus Previous Search Result', category); registry.registerWorkbenchAction(new SyncActionDescriptor(ReplaceInFilesAction, ReplaceInFilesAction.ID, ReplaceInFilesAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_H }), 'Replace in Files', category); +MenuRegistry.appendMenuItem(MenuId.MenubarEditMenu, { + group: '4_find_global', + command: { + id: ReplaceInFilesAction.ID, + title: nls.localize({ key: 'miReplaceInFiles', comment: ['&& denotes a mnemonic'] }, "Replace &&in Files") + }, + order: 2 +}); KeybindingsRegistry.registerCommandAndKeybindingRule(objects.assign({ id: Constants.ToggleCaseSensitiveCommandId, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.SearchInputBoxFocusedKey), handler: toggleCaseSensitiveCommand }, ToggleCaseSensitiveKeybinding)); KeybindingsRegistry.registerCommandAndKeybindingRule(objects.assign({ id: Constants.ToggleWholeWordCommandId, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.SearchInputBoxFocusedKey), handler: toggleWholeWordCommand }, ToggleWholeWordKeybinding)); KeybindingsRegistry.registerCommandAndKeybindingRule(objects.assign({ id: Constants.ToggleRegexCommandId, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.SearchInputBoxFocusedKey), handler: toggleRegexCommand }, ToggleRegexKeybinding)); @@ -544,7 +560,7 @@ configurationRegistry.registerConfiguration({ properties: { 'search.exclude': { type: 'object', - description: nls.localize('exclude', "Configure glob patterns for excluding files and folders in searches. Inherits all glob patterns from the [`files.exclude`](#files-exclude) setting. Read more about glob patterns [here](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options)."), + markdownDescription: nls.localize('exclude', "Configure glob patterns for excluding files and folders in searches. Inherits all glob patterns from the `#files.exclude#` setting. Read more about glob patterns [here](https://code.visualstudio.com/docs/editor/codebasics#_advanced-search-options)."), default: { '**/node_modules': true, '**/bower_components': true }, additionalProperties: { anyOf: [ @@ -569,18 +585,18 @@ configurationRegistry.registerConfiguration({ }, 'search.useRipgrep': { type: 'boolean', - description: nls.localize('useRipgrep', "Controls whether to use ripgrep in text and file search"), + description: nls.localize('useRipgrep', "Controls whether to use ripgrep in text and file search."), default: true }, 'search.useIgnoreFiles': { type: 'boolean', - description: nls.localize('useIgnoreFiles', "Controls whether to use .gitignore and .ignore files when searching for files."), + markdownDescription: nls.localize('useIgnoreFiles', "Controls whether to use `.gitignore` and `.ignore` files when searching for files."), default: true, scope: ConfigurationScope.RESOURCE }, 'search.quickOpen.includeSymbols': { type: 'boolean', - description: nls.localize('search.quickOpen.includeSymbols', "Configure to include results from a global symbol search in the file results for Quick Open."), + description: nls.localize('search.quickOpen.includeSymbols', "Whether to include results from a global symbol search in the file results for Quick Open."), default: false }, 'search.followSymlinks': { @@ -590,20 +606,20 @@ configurationRegistry.registerConfiguration({ }, 'search.smartCase': { type: 'boolean', - description: nls.localize('search.smartCase', "Searches case-insensitively if the pattern is all lowercase, otherwise, searches case-sensitively"), + description: nls.localize('search.smartCase', "Search case-insensitively if the pattern is all lowercase, otherwise, search case-sensitively."), default: false }, 'search.globalFindClipboard': { type: 'boolean', default: false, - description: nls.localize('search.globalFindClipboard', "Controls if the search view should read or modify the shared find clipboard on macOS"), + description: nls.localize('search.globalFindClipboard', "Controls whether the search view should read or modify the shared find clipboard on macOS."), included: platform.isMacintosh }, 'search.location': { type: 'string', enum: ['sidebar', 'panel'], default: 'sidebar', - description: nls.localize('search.location', "Controls if the search will be shown as a view in the sidebar or as a panel in the panel area for more horizontal space."), + description: nls.localize('search.location', "Controls whether the search will be shown as a view in the sidebar or as a panel in the panel area for more horizontal space."), } } }); @@ -615,3 +631,25 @@ registerLanguageCommand('_executeWorkspaceSymbolProvider', function (accessor, a } return getWorkspaceSymbols(query); }); + +// View menu + +MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '3_views', + command: { + id: VIEW_ID, + title: nls.localize({ key: 'miViewSearch', comment: ['&& denotes a mnemonic'] }, "&&Search") + }, + order: 2 +}); + +// Go to menu + +MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { + group: 'z_go_to', + command: { + id: 'workbench.action.showAllSymbols', + title: nls.localize({ key: 'miGotoSymbolInWorkspace', comment: ['&& denotes a mnemonic'] }, "Go to Symbol in &&Workspace...") + }, + order: 3 +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts index c11c6994b96..4ac216e7e23 100644 --- a/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts +++ b/src/vs/workbench/parts/search/test/common/queryBuilder.test.ts @@ -17,6 +17,7 @@ import { IWorkspaceContextService, toWorkspaceFolders, Workspace } from 'vs/plat import { ISearchPathsResult, QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder'; import { TestContextService, TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices'; +const DEFAULT_EDITOR_CONFIG = {}; const DEFAULT_USER_CONFIG = { useRipgrep: true, useIgnoreFiles: true }; const DEFAULT_QUERY_PROPS = { useRipgrep: true, disregardIgnoreFiles: false }; @@ -36,6 +37,7 @@ suite('QueryBuilder', () => { mockConfigService = new TestConfigurationService(); mockConfigService.setUserConfiguration('search', DEFAULT_USER_CONFIG); + mockConfigService.setUserConfiguration('editor', DEFAULT_EDITOR_CONFIG); instantiationService.stub(IConfigurationService, mockConfigService); mockContextService = new TestContextService(); @@ -233,6 +235,21 @@ suite('QueryBuilder', () => { }); }); + test('file pattern trimming', () => { + const content = 'content'; + assertEqualQueries( + queryBuilder.text( + PATTERN_INFO, + undefined, + { filePattern: ` ${content} ` } + ), + { + contentPattern: PATTERN_INFO, + filePattern: content, + type: QueryType.Text + }); + }); + test('exclude ./ syntax', () => { assertEqualQueries( queryBuilder.text( diff --git a/src/vs/workbench/parts/search/test/common/searchModel.test.ts b/src/vs/workbench/parts/search/test/common/searchModel.test.ts index ae9d4349c90..589973b27a6 100644 --- a/src/vs/workbench/parts/search/test/common/searchModel.test.ts +++ b/src/vs/workbench/parts/search/test/common/searchModel.test.ts @@ -6,20 +6,20 @@ import * as assert from 'assert'; import * as sinon from 'sinon'; -import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { DeferredPPromise } from 'vs/base/test/common/utils'; -import { PPromise } from 'vs/base/common/winjs.base'; -import { SearchModel } from 'vs/workbench/parts/search/common/searchModel'; +import { timeout } from 'vs/base/common/async'; import URI from 'vs/base/common/uri'; -import { IFileMatch, IFolderQuery, ILineMatch, ISearchService, ISearchComplete, ISearchProgressItem, IUncachedSearchStats } from 'vs/platform/search/common/search'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { DeferredTPromise } from 'vs/base/test/common/utils'; import { Range } from 'vs/editor/common/core/range'; import { IModelService } from 'vs/editor/common/services/modelService'; +import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; -import { timeout } from 'vs/base/common/async'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { IFileMatch, IFolderQuery, ILineMatch, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchService, IUncachedSearchStats } from 'vs/platform/search/common/search'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; +import { SearchModel } from 'vs/workbench/parts/search/common/searchModel'; const nullEvent = new class { @@ -68,7 +68,7 @@ suite('SearchModel', () => { instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(IModelService, stubModelService(instantiationService)); instantiationService.stub(ISearchService, {}); - instantiationService.stub(ISearchService, 'search', PPromise.as({ results: [] })); + instantiationService.stub(ISearchService, 'search', TPromise.as({ results: [] })); }); teardown(() => { @@ -77,18 +77,32 @@ suite('SearchModel', () => { }); }); - function ppromiseWithProgress(results: IFileMatch[]): () => PPromise { - return () => new PPromise((resolve, reject, progress) => { - process.nextTick(() => { - results.forEach(progress); - resolve(null); - }); - }); + function searchServiceWithResults(results: IFileMatch[], complete: ISearchComplete = null): ISearchService { + return { + search(query: ISearchQuery, onProgress: (result: ISearchProgressItem) => void): TPromise { + return new TPromise(resolve => { + process.nextTick(() => { + results.forEach(onProgress); + resolve(complete); + }); + }); + } + }; + } + + function searchServiceWithError(error: Error): ISearchService { + return { + search(query: ISearchQuery, onProgress: (result: ISearchProgressItem) => void): TPromise { + return new TPromise((resolve, reject) => { + reject(error); + }); + } + }; } test('Search Model: Search adds to results', async () => { let results = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]])), aRawMatch('file://c:/2', aLineMatch('preview 2'))]; - instantiationService.stub(ISearchService, 'search', ppromiseWithProgress(results)); + instantiationService.stub(ISearchService, searchServiceWithResults(results)); let testObject: SearchModel = instantiationService.createInstance(SearchModel); await testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); @@ -114,9 +128,9 @@ suite('SearchModel', () => { test('Search Model: Search reports telemetry on search completed', async () => { let target = instantiationService.spy(ITelemetryService, 'publicLog'); let results = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]])), aRawMatch('file://c:/2', aLineMatch('preview 2'))]; - instantiationService.stub(ISearchService, 'search', ppromiseWithProgress(results)); + instantiationService.stub(ISearchService, searchServiceWithResults(results)); - let testObject = instantiationService.createInstance(SearchModel); + let testObject: SearchModel = instantiationService.createInstance(SearchModel); await testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); assert.ok(target.calledThrice); @@ -131,7 +145,7 @@ suite('SearchModel', () => { let target1 = sinon.stub().returns(nullEvent); instantiationService.stub(ITelemetryService, 'publicLog', target1); - instantiationService.stub(ISearchService, 'search', ppromiseWithProgress([])); + instantiationService.stub(ISearchService, searchServiceWithResults([])); let testObject = instantiationService.createInstance(SearchModel); const result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); @@ -150,17 +164,17 @@ suite('SearchModel', () => { let target1 = sinon.stub().returns(nullEvent); instantiationService.stub(ITelemetryService, 'publicLog', target1); - let promise = new DeferredPPromise(); - instantiationService.stub(ISearchService, 'search', promise); + instantiationService.stub(ISearchService, searchServiceWithResults( + [aRawMatch('file://c:/1', aLineMatch('some preview'))], + { results: [], stats: testSearchStats })); let testObject = instantiationService.createInstance(SearchModel); let result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); - promise.progress(aRawMatch('file://c:/1', aLineMatch('some preview'))); - promise.complete({ results: [], stats: testSearchStats }); - - return timeout(1).then(() => { - return result.then(() => { + return result.then(() => { + return timeout(1).then(() => { + // timeout because promise handlers may run in a different order. We only care that these + // are fired at some point. assert.ok(target1.calledWith('searchResultsFirstRender')); assert.ok(target1.calledWith('searchResultsFinished')); // assert.equal(1, target2.callCount); @@ -174,14 +188,11 @@ suite('SearchModel', () => { let target1 = sinon.stub().returns(nullEvent); instantiationService.stub(ITelemetryService, 'publicLog', target1); - let promise = new DeferredPPromise(); - instantiationService.stub(ISearchService, 'search', promise); + instantiationService.stub(ISearchService, searchServiceWithError(new Error('error'))); let testObject = instantiationService.createInstance(SearchModel); let result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); - promise.error('error'); - return timeout(1).then(() => { return result.then(() => { }, () => { assert.ok(target1.calledWith('searchResultsFirstRender')); @@ -197,7 +208,7 @@ suite('SearchModel', () => { let target1 = sinon.stub().returns(nullEvent); instantiationService.stub(ITelemetryService, 'publicLog', target1); - let promise = new DeferredPPromise(); + let promise = new DeferredTPromise(); instantiationService.stub(ISearchService, 'search', promise); let testObject = instantiationService.createInstance(SearchModel); @@ -216,12 +227,12 @@ suite('SearchModel', () => { test('Search Model: Search results are cleared during search', async () => { let results = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]])), aRawMatch('file://c:/2', aLineMatch('preview 2'))]; - instantiationService.stub(ISearchService, 'search', ppromiseWithProgress(results)); + instantiationService.stub(ISearchService, searchServiceWithResults(results)); let testObject: SearchModel = instantiationService.createInstance(SearchModel); await testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); assert.ok(!testObject.searchResult.isEmpty()); - instantiationService.stub(ISearchService, 'search', new DeferredPPromise()); + instantiationService.stub(ISearchService, searchServiceWithResults([])); testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); assert.ok(testObject.searchResult.isEmpty()); @@ -229,11 +240,11 @@ suite('SearchModel', () => { test('Search Model: Previous search is cancelled when new search is called', async () => { let target = sinon.spy(); - instantiationService.stub(ISearchService, 'search', new DeferredPPromise((c, e, p) => { }, target)); + instantiationService.stub(ISearchService, 'search', new DeferredTPromise(target)); let testObject: SearchModel = instantiationService.createInstance(SearchModel); testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); - instantiationService.stub(ISearchService, 'search', new DeferredPPromise()); + instantiationService.stub(ISearchService, searchServiceWithResults([])); testObject.search({ contentPattern: { pattern: 'somestring' }, type: 1, folderQueries }); assert.ok(target.calledOnce); @@ -241,7 +252,7 @@ suite('SearchModel', () => { test('getReplaceString returns proper replace string for regExpressions', async () => { let results = [aRawMatch('file://c:/1', aLineMatch('preview 1', 1, [[1, 3], [4, 7]]))]; - instantiationService.stub(ISearchService, 'search', ppromiseWithProgress(results)); + instantiationService.stub(ISearchService, searchServiceWithResults(results)); let testObject: SearchModel = instantiationService.createInstance(SearchModel); await testObject.search({ contentPattern: { pattern: 're' }, type: 1, folderQueries }); diff --git a/src/vs/workbench/parts/snippets/electron-browser/configureSnippets.ts b/src/vs/workbench/parts/snippets/electron-browser/configureSnippets.ts index 71045971e74..c7398954035 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/configureSnippets.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/configureSnippets.ts @@ -217,3 +217,12 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { category: nls.localize('preferences', "Preferences") } }); + +MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { + group: '3_snippets', + command: { + id, + title: nls.localize({ key: 'miOpenSnippets', comment: ['&& denotes a mnemonic'] }, "User &&Snippets") + }, + order: 1 +}); diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippetsFile.ts b/src/vs/workbench/parts/snippets/electron-browser/snippetsFile.ts index e624664bbe6..699d144f02b 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippetsFile.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetsFile.ts @@ -33,7 +33,7 @@ export class Snippet { readonly isFromExtension?: boolean, ) { // - this.prefixLow = prefix.toLowerCase(); + this.prefixLow = prefix ? prefix.toLowerCase() : prefix; } get codeSnippet(): string { @@ -195,7 +195,7 @@ export class SnippetFile { load(): Promise { if (!this._loadPromise) { - this._loadPromise = Promise.resolve(this._fileService.resolveContent(this.location)).then(content => { + this._loadPromise = Promise.resolve(this._fileService.resolveContent(this.location, { encoding: 'utf8' })).then(content => { const data = jsonParse(content.value.toString()); if (typeof data === 'object') { forEach(data, entry => { diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts index 047e0c2ae96..69dc72ef055 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts @@ -7,7 +7,7 @@ import { basename, extname, join } from 'path'; import { MarkdownString } from 'vs/base/common/htmlContent'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { values } from 'vs/base/common/map'; import * as resources from 'vs/base/common/resources'; import { compare, endsWith, isFalsyOrWhitespace } from 'vs/base/common/strings'; @@ -253,14 +253,12 @@ class SnippetsService implements ISnippetsService { } }); }, (error: string) => this._logService.error(error)); - this._disposables.push({ - dispose: () => { - if (watcher) { - watcher.removeAllListeners(); - watcher.close(); - } + this._disposables.push(toDisposable(() => { + if (watcher) { + watcher.removeAllListeners(); + watcher.close(); } - }); + })); }).then(undefined, err => { this._logService.error('Failed to load user snippets', err); diff --git a/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts b/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts index 9b2b047160c..93c0e82a043 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts @@ -8,7 +8,7 @@ import { localize } from 'vs/nls'; import { KeyCode } from 'vs/base/common/keyCodes'; import { RawContextKey, IContextKeyService, ContextKeyExpr, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ISnippetsService } from 'vs/workbench/parts/snippets/electron-browser/snippets.contribution'; import { getNonWhitespacePrefix, SnippetSuggestion } from 'vs/workbench/parts/snippets/electron-browser/snippetsService'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -142,7 +142,7 @@ registerEditorCommand(new TabCompletionCommand({ precondition: TabCompletionController.ContextKey, handler: x => x.performSnippetCompletions(), kbOpts: { - weight: KeybindingsRegistry.WEIGHT.editorContrib(), + weight: KeybindingWeight.EditorContrib, kbExpr: ContextKeyExpr.and( EditorContextKeys.editorTextFocus, EditorContextKeys.tabDoesNotMoveFocus, diff --git a/src/vs/workbench/parts/splash/electron-browser/partsSplash.contribution.ts b/src/vs/workbench/parts/splash/electron-browser/partsSplash.contribution.ts new file mode 100644 index 00000000000..4632aaf6efb --- /dev/null +++ b/src/vs/workbench/parts/splash/electron-browser/partsSplash.contribution.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { getTotalHeight, getTotalWidth } from 'vs/base/browser/dom'; +import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; +import * as themes from 'vs/workbench/common/theme'; +import { IPartService, Parts, Position } from 'vs/workbench/services/part/common/partService'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { debounceEvent } from 'vs/base/common/event'; +import { DEFAULT_EDITOR_MIN_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor'; +import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; + +class PartsSplash { + + private static readonly _splashElementId = 'monaco-parts-splash'; + + private readonly _disposables: IDisposable[] = []; + + constructor( + @IThemeService private readonly _themeService: IThemeService, + @IPartService private readonly _partService: IPartService, + @IStorageService private readonly _storageService: IStorageService, + @ILifecycleService lifecycleService: ILifecycleService, + ) { + lifecycleService.when(LifecyclePhase.Running).then(_ => this._removePartsSplash()); + debounceEvent(_partService.onEditorLayout, () => { }, 50)(this._savePartsSplash, this, this._disposables); + } + + dispose(): void { + dispose(this._disposables); + } + + private _savePartsSplash() { + const colorInfo = { + titleBarBackground: this._getThemeColor(themes.TITLE_BAR_ACTIVE_BACKGROUND), + activityBarBackground: this._getThemeColor(themes.ACTIVITY_BAR_BACKGROUND), + sideBarBackground: this._getThemeColor(themes.SIDE_BAR_BACKGROUND), + statusBarBackground: this._getThemeColor(themes.STATUS_BAR_BACKGROUND), + statusBarNoFolderBackground: this._getThemeColor(themes.STATUS_BAR_NO_FOLDER_BACKGROUND), + }; + const layoutInfo = { + sideBarSide: this._partService.getSideBarPosition() === Position.RIGHT ? 'right' : 'left', + editorPartMinWidth: DEFAULT_EDITOR_MIN_DIMENSIONS.width, + titleBarHeight: getTotalHeight(this._partService.getContainer(Parts.TITLEBAR_PART)), + activityBarWidth: getTotalWidth(this._partService.getContainer(Parts.ACTIVITYBAR_PART)), + sideBarWidth: getTotalWidth(this._partService.getContainer(Parts.SIDEBAR_PART)), + statusBarHeight: getTotalHeight(this._partService.getContainer(Parts.STATUSBAR_PART)), + }; + this._storageService.store('parts-splash-data', JSON.stringify({ id: PartsSplash._splashElementId, colorInfo, layoutInfo }), StorageScope.GLOBAL); + } + + private _getThemeColor(id: ColorIdentifier): string { + const theme = this._themeService.getTheme(); + const color = theme.getColor(id); + return color ? color.toString() : undefined; + } + + private _removePartsSplash(): void { + let element = document.getElementById(PartsSplash._splashElementId); + if (element) { + element.remove(); + } + } +} + +Registry.as(Extensions.Workbench).registerWorkbenchContribution(PartsSplash, LifecyclePhase.Starting); diff --git a/src/vs/workbench/parts/stats/node/workspaceStats.ts b/src/vs/workbench/parts/stats/node/workspaceStats.ts index 60b4979fd47..19bc5120d59 100644 --- a/src/vs/workbench/parts/stats/node/workspaceStats.ts +++ b/src/vs/workbench/parts/stats/node/workspaceStats.ts @@ -16,6 +16,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IWindowConfiguration, IWindowService } from 'vs/platform/windows/common/windows'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { endsWith } from 'vs/base/common/strings'; +import { Schemas } from 'vs/base/common/network'; const SshProtocolMatcher = /^([^@:]+@)?([^:]+):/; const SshUrlMatcher = /^([^@:]+@)?([^:]+):(.+)$/; @@ -240,7 +241,8 @@ export class WorkspaceStats implements IWorkbenchContribution { workspaceId = void 0; break; case WorkbenchState.FOLDER: - workspaceId = crypto.createHash('sha1').update(workspace.folders[0].uri.fsPath).digest('hex'); + // TODO: #54483 @Ben + workspaceId = crypto.createHash('sha1').update(workspace.folders[0].uri.scheme === Schemas.file ? workspace.folders[0].uri.fsPath : workspace.folders[0].uri.toString()).digest('hex'); break; case WorkbenchState.WORKSPACE: workspaceId = crypto.createHash('sha1').update(workspace.configuration.fsPath).digest('hex'); diff --git a/src/vs/workbench/parts/tasks/common/taskTemplates.ts b/src/vs/workbench/parts/tasks/common/taskTemplates.ts index e19cfd42648..77998b88ac6 100644 --- a/src/vs/workbench/parts/tasks/common/taskTemplates.ts +++ b/src/vs/workbench/parts/tasks/common/taskTemplates.ts @@ -6,9 +6,9 @@ import * as nls from 'vs/nls'; -import { IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; +import { IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -export interface TaskEntry extends IPickOpenEntry { +export interface TaskEntry extends IQuickPickItem { sort?: string; autoDetect: boolean; content: string; diff --git a/src/vs/workbench/parts/tasks/common/tasks.ts b/src/vs/workbench/parts/tasks/common/tasks.ts index 7d92c1af7da..9a771032c99 100644 --- a/src/vs/workbench/parts/tasks/common/tasks.ts +++ b/src/vs/workbench/parts/tasks/common/tasks.ts @@ -12,6 +12,9 @@ import { UriComponents } from 'vs/base/common/uri'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ProblemMatcher } from 'vs/workbench/parts/tasks/common/problemMatcher'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; + +export const TASK_RUNNING_STATE = new RawContextKey('taskRunning', false); export enum ShellQuoting { /** diff --git a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts index 1f274236426..9f8b91f0d45 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts @@ -17,7 +17,7 @@ import URI from 'vs/base/common/uri'; import { IStringDictionary } from 'vs/base/common/collections'; import { Action } from 'vs/base/common/actions'; import * as Dom from 'vs/base/browser/dom'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import * as Types from 'vs/base/common/types'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; @@ -31,7 +31,7 @@ import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; import { Registry } from 'vs/platform/registry/common/platform'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; -import { MenuRegistry } from 'vs/platform/actions/common/actions'; +import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IMarkerService, MarkerStatistics } from 'vs/platform/markers/common/markers'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -39,7 +39,7 @@ import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configur import { IFileService, IFileStat } from 'vs/platform/files/common/files'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ProblemMatcherRegistry, NamedProblemMatcher } from 'vs/workbench/parts/tasks/common/problemMatcher'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IProgressService2, IProgressOptions, ProgressLocation } from 'vs/workbench/services/progress/common/progress'; @@ -75,7 +75,7 @@ import { ITaskSystem, ITaskResolver, ITaskSummary, TaskExecuteKind, TaskError, T import { Task, CustomTask, ConfiguringTask, ContributedTask, InMemoryTask, TaskEvent, TaskEventKind, TaskSet, TaskGroup, GroupType, ExecutionEngine, JsonSchemaVersion, TaskSourceKind, - TaskSorter, TaskIdentifier, KeyedTaskIdentifier + TaskSorter, TaskIdentifier, KeyedTaskIdentifier, TASK_RUNNING_STATE } from 'vs/workbench/parts/tasks/common/tasks'; import { ITaskService, ITaskProvider, RunOptions, CustomizationProperties, TaskFilter } from 'vs/workbench/parts/tasks/common/taskService'; import { getTemplates as getTaskTemplates } from 'vs/workbench/parts/tasks/common/taskTemplates'; @@ -90,6 +90,7 @@ import { QuickOpenActionContributor } from '../browser/quickOpen'; import { Themable, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND } from 'vs/workbench/common/theme'; import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; let tasksCategory = nls.localize('tasksCategory', "Tasks"); @@ -253,11 +254,9 @@ class BuildStatusBarItem extends Themable implements IStatusbarItem { this.updateStyles(); - return { - dispose: () => { - callOnDispose = dispose(callOnDispose); - } - }; + return toDisposable(() => { + callOnDispose = dispose(callOnDispose); + }); } private ignoreEvent(event: TaskEvent): boolean { @@ -459,6 +458,8 @@ class TaskService implements ITaskService { private _taskSystemListener: IDisposable; private _recentlyUsedTasks: LinkedMap; + private _taskRunningState: IContextKey; + private _outputChannel: IOutputChannel; private readonly _onDidStateChange: Emitter; @@ -475,6 +476,7 @@ class TaskService implements ITaskService { @IModelService private modelService: IModelService, @IExtensionService private extensionService: IExtensionService, @IQuickOpenService private quickOpenService: IQuickOpenService, + @IQuickInputService private quickInputService: IQuickInputService, @IConfigurationResolverService private configurationResolverService: IConfigurationResolverService, @ITerminalService private terminalService: ITerminalService, @IStorageService private storageService: IStorageService, @@ -482,7 +484,9 @@ class TaskService implements ITaskService { @IOpenerService private openerService: IOpenerService, @IWindowService private readonly _windowService: IWindowService, @IDialogService private dialogService: IDialogService, - @INotificationService private notificationService: INotificationService + @INotificationService private notificationService: INotificationService, + @IContextKeyService contextKeyService: IContextKeyService, + ) { this._configHasErrors = false; this._workspaceTasksPromise = undefined; @@ -521,6 +525,7 @@ class TaskService implements ITaskService { this.updateSetup(folderSetup); this.updateWorkspaceTasks(); }); + this._taskRunningState = TASK_RUNNING_STATE.bindTo(contextKeyService); lifecycleService.onWillShutdown(event => event.veto(this.beforeShutdown())); this._onDidStateChange = new Emitter(); this.registerCommands(); @@ -563,7 +568,7 @@ class TaskService implements ITaskService { KeybindingsRegistry.registerKeybindingRule({ id: 'workbench.action.tasks.build', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: undefined, primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_B }); @@ -1070,7 +1075,7 @@ class TaskService implements ITaskService { }); } - private writeConfiguration(workspaceFolder: IWorkspaceFolder, key: string, value: any): TPromise { + private writeConfiguration(workspaceFolder: IWorkspaceFolder, key: string, value: any): TPromise { if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) { return this.configurationService.updateValue(key, value, { resource: workspaceFolder.uri }, ConfigurationTarget.WORKSPACE); } else if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { @@ -1292,6 +1297,9 @@ class TaskService implements ITaskService { this._taskSystem = system; } this._taskSystemListener = this._taskSystem.onDidStateChange((event) => { + if (this._taskSystem) { + this._taskRunningState.set(this._taskSystem.isActiveSync()); + } this._onDidStateChange.fire(event); }); return this._taskSystem; @@ -2199,7 +2207,7 @@ class TaskService implements ITaskService { if (stat) { return stat.resource; } - return this.quickOpenService.pick(getTaskTemplates(), { placeHolder: nls.localize('TaskService.template', 'Select a Task Template') }).then((selection) => { + return this.quickInputService.pick(getTaskTemplates(), { placeHolder: nls.localize('TaskService.template', 'Select a Task Template') }).then((selection) => { if (!selection) { return undefined; } @@ -2420,6 +2428,75 @@ class TaskService implements ITaskService { } } +MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { + group: '1_run', + command: { + id: 'workbench.action.tasks.runTask', + title: nls.localize({ key: 'miRunTask', comment: ['&& denotes a mnemonic'] }, "&&Run Task...") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { + group: '1_run', + command: { + id: 'workbench.action.tasks.build', + title: nls.localize({ key: 'miBuildTask', comment: ['&& denotes a mnemonic'] }, "Run &&Build Task...") + }, + order: 2 +}); + +// Manage Tasks +MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { + group: '2_manage', + command: { + precondition: TASK_RUNNING_STATE, + id: 'workbench.action.tasks.showTasks', + title: nls.localize({ key: 'miRunningTask', comment: ['&& denotes a mnemonic'] }, "Show Runnin&&g Tasks...") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { + group: '2_manage', + command: { + precondition: TASK_RUNNING_STATE, + id: 'workbench.action.tasks.restartTask', + title: nls.localize({ key: 'miRestartTask', comment: ['&& denotes a mnemonic'] }, "R&&estart Running Task...") + }, + order: 2 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { + group: '2_manage', + command: { + precondition: TASK_RUNNING_STATE, + id: 'workbench.action.tasks.terminate', + title: nls.localize({ key: 'miTerminateTask', comment: ['&& denotes a mnemonic'] }, "&&Terminate Task...") + }, + order: 3 +}); + +// Configure Tasks +MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { + group: '3_configure', + command: { + id: 'workbench.action.tasks.configureTaskRunner', + title: nls.localize({ key: 'miConfigureTask', comment: ['&& denotes a mnemonic'] }, "&&Configure Tasks...") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarTasksMenu, { + group: '3_configure', + command: { + id: 'workbench.action.tasks.configureDefaultBuildTask', + title: nls.localize({ key: 'miConfigureBuildTask', comment: ['&& denotes a mnemonic'] }, "Configure De&&fault Build Task...") + }, + order: 2 +}); + + MenuRegistry.addCommand({ id: ConfigureTaskAction.ID, title: { value: ConfigureTaskAction.TEXT, original: 'Configure Task' }, category: { value: tasksCategory, original: 'Tasks' } }); MenuRegistry.addCommand({ id: 'workbench.action.tasks.showLog', title: { value: nls.localize('ShowLogAction.label', "Show Task Log"), original: 'Show Task Log' }, category: { value: tasksCategory, original: 'Tasks' } }); MenuRegistry.addCommand({ id: 'workbench.action.tasks.runTask', title: { value: nls.localize('RunTaskAction.label', "Run Task"), original: 'Run Task' }, category: { value: tasksCategory, original: 'Tasks' } }); @@ -2488,6 +2565,7 @@ let schema: IJSONSchema = { import schemaVersion1 from './jsonSchema_v1'; import schemaVersion2 from './jsonSchema_v2'; import { TaskDefinitionRegistry } from 'vs/workbench/parts/tasks/common/taskDefinitionRegistry'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; schema.definitions = { ...schemaVersion1.definitions, ...schemaVersion2.definitions, diff --git a/src/vs/workbench/parts/terminal/common/terminal.ts b/src/vs/workbench/parts/terminal/common/terminal.ts index ffcd27e8f46..b7a2c8e5915 100644 --- a/src/vs/workbench/parts/terminal/common/terminal.ts +++ b/src/vs/workbench/parts/terminal/common/terminal.ts @@ -14,9 +14,11 @@ export const TERMINAL_PANEL_ID = 'workbench.panel.terminal'; export const TERMINAL_SERVICE_ID = 'terminalService'; -/** A context key that is set when the integrated terminal has focus. */ +/** A context key that is set when there is at least one opened integrated terminal. */ +export const KEYBINDING_CONTEXT_TERMINAL_IS_OPEN = new RawContextKey('terminalIsOpen', false); +/** A context key that is set when the integrated terminal has focus. */ export const KEYBINDING_CONTEXT_TERMINAL_FOCUS = new RawContextKey('terminalFocus', undefined); -/** A context key that is set when the integrated terminal does not have focus. */ +/** A context key that is set when the integrated terminal does not have focus. */ export const KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED: ContextKeyExpr = KEYBINDING_CONTEXT_TERMINAL_FOCUS.toNegated(); /** A keybinding context key that is set when the integrated terminal has text selected. */ @@ -95,8 +97,6 @@ export interface ITerminalConfiguration { windows: { [key: string]: string }; }; showExitAlert: boolean; - experimentalRestore: boolean; - experimentalTextureCachingStrategy: 'static' | 'dynamic'; } export interface ITerminalConfigHelper { @@ -540,6 +540,8 @@ export interface ITerminalInstance { setDimensions(dimensions: ITerminalDimensions): void; addDisposable(disposable: IDisposable): void; + + toggleEscapeSequenceLogging(): void; } export interface ITerminalCommandTracker { diff --git a/src/vs/workbench/parts/terminal/common/terminalCommands.ts b/src/vs/workbench/parts/terminal/common/terminalCommands.ts index 828588d03b1..8d5345ed224 100644 --- a/src/vs/workbench/parts/terminal/common/terminalCommands.ts +++ b/src/vs/workbench/parts/terminal/common/terminalCommands.ts @@ -3,10 +3,62 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ITerminalService } from 'vs/workbench/parts/terminal/common/terminal'; -export function setup(): void { +export const enum TERMINAL_COMMAND_ID { + TOGGLE = 'workbench.action.terminal.toggleTerminal', + KILL = 'workbench.action.terminal.kill', + QUICK_KILL = 'workbench.action.terminal.quickKill', + COPY_SELECTION = 'workbench.action.terminal.copySelection', + SELECT_ALL = 'workbench.action.terminal.selectAll', + DELETE_WORD_LEFT = 'workbench.action.terminal.deleteWordLeft', + DELETE_WORD_RIGHT = 'workbench.action.terminal.deleteWordRight', + MOVE_TO_LINE_START = 'workbench.action.terminal.moveToLineStart', + MOVE_TO_LINE_END = 'workbench.action.terminal.moveToLineEnd', + NEW = 'workbench.action.terminal.new', + NEW_IN_ACTIVE_WORKSPACE = 'workbench.action.terminal.newInActiveWorkspace', + SPLIT = 'workbench.action.terminal.split', + SPLIT_IN_ACTIVE_WORKSPACE = 'workbench.action.terminal.splitInActiveWorkspace', + FOCUS_PREVIOUS_PANE = 'workbench.action.terminal.focusPreviousPane', + FOCUS_NEXT_PANE = 'workbench.action.terminal.focusNextPane', + RESIZE_PANE_LEFT = 'workbench.action.terminal.resizePaneLeft', + RESIZE_PANE_RIGHT = 'workbench.action.terminal.resizePaneRight', + RESIZE_PANE_UP = 'workbench.action.terminal.resizePaneUp', + RESIZE_PANE_DOWN = 'workbench.action.terminal.resizePaneDown', + FOCUS = 'workbench.action.terminal.focus', + FOCUS_NEXT = 'workbench.action.terminal.focusNext', + FOCUS_PREVIOUS = 'workbench.action.terminal.focusPrevious', + PASTE = 'workbench.action.terminal.paste', + SELECT_DEFAULT_SHELL = 'workbench.action.terminal.selectDefaultShell', + RUN_SELECTED_TEXT = 'workbench.action.terminal.runSelectedText', + RUN_ACTIVE_FILE = 'workbench.action.terminal.runActiveFile', + SWITCH_TERMINAL = 'workbench.action.terminal.switchTerminal', + SCROLL_DOWN_LINE = 'workbench.action.terminal.scrollDown', + SCROLL_DOWN_PAGE = 'workbench.action.terminal.scrollDownPage', + SCROLL_TO_BOTTOM = 'workbench.action.terminal.scrollToBottom', + SCROLL_UP_LINE = 'workbench.action.terminal.scrollUp', + SCROLL_UP_PAGE = 'workbench.action.terminal.scrollUpPage', + SCROLL_TO_TOP = 'workbench.action.terminal.scrollToTop', + CLEAR = 'workbench.action.terminal.clear', + CLEAR_SELECTION = 'workbench.action.terminal.clearSelection', + WORKSPACE_SHELL_ALLOW = 'workbench.action.terminal.allowWorkspaceShell', + WORKSPACE_SHELL_DISALLOW = 'workbench.action.terminal.disallowWorkspaceShell', + RENAME = 'workbench.action.terminal.rename', + FIND_WIDGET_FOCUS = 'workbench.action.terminal.focusFindWidget', + FIND_WIDGET_HIDE = 'workbench.action.terminal.hideFindWidget', + QUICK_OPEN_TERM = 'workbench.action.quickOpenTerm', + SCROLL_TO_PREVIOUS_COMMAND = 'workbench.action.terminal.scrollToPreviousCommand', + SCROLL_TO_NEXT_COMMAND = 'workbench.action.terminal.scrollToNextCommand', + SELECT_TO_PREVIOUS_COMMAND = 'workbench.action.terminal.selectToPreviousCommand', + SELECT_TO_NEXT_COMMAND = 'workbench.action.terminal.selectToNextCommand', + SELECT_TO_PREVIOUS_LINE = 'workbench.action.terminal.selectToPreviousLine', + SELECT_TO_NEXT_LINE = 'workbench.action.terminal.selectToNextLine', + TOGGLE_ESCAPE_SEQUENCE_LOGGING = 'toggleEscapeSequenceLogging' +} + + +export function setupTerminalCommands(): void { registerOpenTerminalAtIndexCommands(); } @@ -17,7 +69,7 @@ function registerOpenTerminalAtIndexCommands(): void { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: `workbench.action.terminal.focusAtIndex${visibleIndex}`, - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: void 0, primary: null, handler: accessor => { diff --git a/src/vs/workbench/parts/terminal/common/terminalMenu.ts b/src/vs/workbench/parts/terminal/common/terminalMenu.ts new file mode 100644 index 00000000000..52498a02f51 --- /dev/null +++ b/src/vs/workbench/parts/terminal/common/terminalMenu.ts @@ -0,0 +1,120 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { TERMINAL_COMMAND_ID } from 'vs/workbench/parts/terminal/common/terminalCommands'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; + +export function setupTerminalMenu() { + + // View menu + + MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { + group: '4_panels', + command: { + id: TERMINAL_COMMAND_ID.TOGGLE, + title: nls.localize({ key: 'miToggleIntegratedTerminal', comment: ['&& denotes a mnemonic'] }, "&&Integrated Terminal") + }, + order: 3 + }); + + // Manage + const manageGroup = '1_manage'; + MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { + group: manageGroup, + command: { + id: TERMINAL_COMMAND_ID.NEW, + title: nls.localize({ key: 'miNewTerminal', comment: ['&& denotes a mnemonic'] }, "&&New Terminal") + }, + order: 1 + }); + MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { + group: manageGroup, + command: { + id: TERMINAL_COMMAND_ID.SPLIT, + title: nls.localize({ key: 'miSplitTerminal', comment: ['&& denotes a mnemonic'] }, "&&Split Terminal"), + precondition: ContextKeyExpr.has('terminalIsOpen') + }, + order: 2 + }); + + MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { + group: manageGroup, + command: { + id: TERMINAL_COMMAND_ID.KILL, + title: nls.localize({ key: 'miKillTerminal', comment: ['&& denotes a mnemonic'] }, "&&Kill Terminal"), + precondition: ContextKeyExpr.has('terminalIsOpen') + }, + order: 3 + }); + + // Run + const runGroup = '2_run'; + MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { + group: runGroup, + command: { + id: TERMINAL_COMMAND_ID.CLEAR, + title: nls.localize({ key: 'miClear', comment: ['&& denotes a mnemonic'] }, "&&Clear"), + precondition: ContextKeyExpr.has('terminalIsOpen') + }, + order: 1 + }); + MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { + group: runGroup, + command: { + id: TERMINAL_COMMAND_ID.RUN_ACTIVE_FILE, + title: nls.localize({ key: 'miRunActiveFile', comment: ['&& denotes a mnemonic'] }, "Run &&Active File") + }, + order: 2 + }); + MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { + group: runGroup, + command: { + id: TERMINAL_COMMAND_ID.RUN_SELECTED_TEXT, + title: nls.localize({ key: 'miRunSelectedText', comment: ['&& denotes a mnemonic'] }, "Run &&Selected Text") + }, + order: 3 + }); + + // Navigation + const navigationGroup = '3_navigation'; + MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { + group: navigationGroup, + command: { + id: TERMINAL_COMMAND_ID.SCROLL_TO_PREVIOUS_COMMAND, + title: nls.localize({ key: 'miScrollToPreviousCommand', comment: ['&& denotes a mnemonic'] }, "Scroll To Previous Command"), + precondition: ContextKeyExpr.has('terminalIsOpen') + }, + order: 1 + }); + MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { + group: navigationGroup, + command: { + id: TERMINAL_COMMAND_ID.SCROLL_TO_NEXT_COMMAND, + title: nls.localize({ key: 'miScrollToNextCommand', comment: ['&& denotes a mnemonic'] }, "Scroll To Next Command"), + precondition: ContextKeyExpr.has('terminalIsOpen') + }, + order: 2 + }); + MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { + group: navigationGroup, + command: { + id: TERMINAL_COMMAND_ID.SELECT_TO_PREVIOUS_COMMAND, + title: nls.localize({ key: 'miSelectToPreviousCommand', comment: ['&& denotes a mnemonic'] }, "Select To Previous Command"), + precondition: ContextKeyExpr.has('terminalIsOpen') + }, + order: 3 + }); + MenuRegistry.appendMenuItem(MenuId.MenubarTerminalMenu, { + group: navigationGroup, + command: { + id: TERMINAL_COMMAND_ID.SELECT_TO_NEXT_COMMAND, + title: nls.localize({ key: 'miSelectToNextCommand', comment: ['&& denotes a mnemonic'] }, "Select To Next Command"), + precondition: ContextKeyExpr.has('terminalIsOpen') + }, + order: 4 + }); +} diff --git a/src/vs/workbench/parts/terminal/common/terminalService.ts b/src/vs/workbench/parts/terminal/common/terminalService.ts index 20383cf4d66..c1650928b9d 100644 --- a/src/vs/workbench/parts/terminal/common/terminalService.ts +++ b/src/vs/workbench/parts/terminal/common/terminalService.ts @@ -6,14 +6,12 @@ import * as errors from 'vs/base/common/errors'; import { Event, Emitter } from 'vs/base/common/event'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IPartService } from 'vs/workbench/services/part/common/partService'; -import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalConfigHelper, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, TERMINAL_PANEL_ID, ITerminalTab, ITerminalProcessExtHostProxy, ITerminalProcessExtHostRequest } from 'vs/workbench/parts/terminal/common/terminal'; +import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalConfigHelper, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, TERMINAL_PANEL_ID, ITerminalTab, ITerminalProcessExtHostProxy, ITerminalProcessExtHostRequest, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN } from 'vs/workbench/parts/terminal/common/terminal'; import { TPromise } from 'vs/base/common/winjs.base'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; - -const TERMINAL_STATE_STORAGE_KEY = 'terminal.state'; +import { IStorageService } from 'vs/platform/storage/common/storage'; export abstract class TerminalService implements ITerminalService { public _serviceBrand: any; @@ -70,7 +68,17 @@ export abstract class TerminalService implements ITerminalService { this._findWidgetVisible = KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE.bindTo(this._contextKeyService); this.onTabDisposed(tab => this._removeTab(tab)); - lifecycleService.when(LifecyclePhase.Restoring).then(() => this._restoreTabs()); + this._handleContextKeys(); + } + + private _handleContextKeys(): void { + const terminalIsOpenContext = KEYBINDING_CONTEXT_TERMINAL_IS_OPEN.bindTo(this._contextKeyService); + + const updateTerminalContextKeys = () => { + terminalIsOpenContext.set(this.terminalInstances.length > 0); + }; + + this.onInstancesChanged(() => updateTerminalContextKeys()); } protected abstract _showTerminalCloseConfirmation(): TPromise; @@ -82,29 +90,6 @@ export abstract class TerminalService implements ITerminalService { public abstract setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; public abstract requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, cols: number, rows: number): void; - private _restoreTabs(): void { - if (!this.configHelper.config.experimentalRestore) { - return; - } - - const tabConfigsJson = this._storageService.get(TERMINAL_STATE_STORAGE_KEY, StorageScope.WORKSPACE); - if (!tabConfigsJson) { - return; - } - - const tabConfigs = <{ instances: IShellLaunchConfig[] }[]>JSON.parse(tabConfigsJson); - if (!Array.isArray(tabConfigs)) { - return; - } - - tabConfigs.forEach(tabConfig => { - const instance = this.createTerminal(tabConfig.instances[0]); - for (let i = 1; i < tabConfig.instances.length; i++) { - this.splitInstance(instance, tabConfig.instances[i]); - } - }); - } - private _onWillShutdown(): boolean | TPromise { if (this.terminalInstances.length === 0) { // No terminal instances, don't veto @@ -127,16 +112,6 @@ export abstract class TerminalService implements ITerminalService { } private _onShutdown(): void { - // Store terminal tab layout - if (this.configHelper.config.experimentalRestore) { - const configs = this.terminalTabs.map(tab => { - return { - instances: tab.terminalInstances.map(instance => instance.shellLaunchConfig) - }; - }); - this._storageService.store(TERMINAL_STATE_STORAGE_KEY, JSON.stringify(configs), StorageScope.WORKSPACE); - } - // Dispose of all instances this.terminalInstances.forEach(instance => instance.dispose()); } diff --git a/src/vs/workbench/parts/terminal/electron-browser/media/terminal.css b/src/vs/workbench/parts/terminal/electron-browser/media/terminal.css index 42d4a6496db..c5da665535e 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/media/terminal.css +++ b/src/vs/workbench/parts/terminal/electron-browser/media/terminal.css @@ -17,6 +17,7 @@ height: 100%; width: 100%; box-sizing: border-box; + overflow: hidden; } .monaco-workbench .panel.integrated-terminal .terminal-tab { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts index 5ba5b9659d1..9ef058f2994 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts @@ -11,21 +11,20 @@ import * as debugActions from 'vs/workbench/parts/debug/browser/debugActions'; import * as nls from 'vs/nls'; import * as panel from 'vs/workbench/browser/panel'; import * as platform from 'vs/base/common/platform'; -import * as terminalCommands from 'vs/workbench/parts/terminal/common/terminalCommands'; import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { ITerminalService, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, TerminalCursorStyle, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE, DEFAULT_LINE_HEIGHT, DEFAULT_LETTER_SPACING } from 'vs/workbench/parts/terminal/common/terminal'; import { getTerminalDefaultShellUnixLike, getTerminalDefaultShellWindows } from 'vs/workbench/parts/terminal/node/terminal'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { KillTerminalAction, ClearSelectionTerminalAction, CopyTerminalSelectionAction, CreateNewTerminalAction, CreateNewInActiveWorkspaceTerminalAction, FocusActiveTerminalAction, FocusNextTerminalAction, FocusPreviousTerminalAction, SelectDefaultShellWindowsTerminalAction, RunSelectedTextInTerminalAction, RunActiveFileInTerminalAction, ScrollDownTerminalAction, ScrollDownPageTerminalAction, ScrollToBottomTerminalAction, ScrollUpTerminalAction, ScrollUpPageTerminalAction, ScrollToTopTerminalAction, TerminalPasteAction, ToggleTerminalAction, ClearTerminalAction, AllowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand, RenameTerminalAction, SelectAllTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, TERMINAL_PICKER_PREFIX, MoveToLineStartTerminalAction, MoveToLineEndTerminalAction, SplitTerminalAction, SplitInActiveWorkspaceTerminalAction, FocusPreviousPaneTerminalAction, FocusNextPaneTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, ResizePaneDownTerminalAction, ScrollToPreviousCommandAction, ScrollToNextCommandAction, SelectToPreviousCommandAction, SelectToNextCommandAction, SelectToPreviousLineAction, SelectToNextLineAction } from 'vs/workbench/parts/terminal/electron-browser/terminalActions'; +import { KillTerminalAction, ClearSelectionTerminalAction, CopyTerminalSelectionAction, CreateNewTerminalAction, CreateNewInActiveWorkspaceTerminalAction, FocusActiveTerminalAction, FocusNextTerminalAction, FocusPreviousTerminalAction, SelectDefaultShellWindowsTerminalAction, RunSelectedTextInTerminalAction, RunActiveFileInTerminalAction, ScrollDownTerminalAction, ScrollDownPageTerminalAction, ScrollToBottomTerminalAction, ScrollUpTerminalAction, ScrollUpPageTerminalAction, ScrollToTopTerminalAction, TerminalPasteAction, ToggleTerminalAction, ClearTerminalAction, AllowWorkspaceShellTerminalCommand, DisallowWorkspaceShellTerminalCommand, RenameTerminalAction, SelectAllTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, TERMINAL_PICKER_PREFIX, MoveToLineStartTerminalAction, MoveToLineEndTerminalAction, SplitTerminalAction, SplitInActiveWorkspaceTerminalAction, FocusPreviousPaneTerminalAction, FocusNextPaneTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, ResizePaneDownTerminalAction, ScrollToPreviousCommandAction, ScrollToNextCommandAction, SelectToPreviousCommandAction, SelectToNextCommandAction, SelectToPreviousLineAction, SelectToNextLineAction, ToggleEscapeSequenceLoggingAction } from 'vs/workbench/parts/terminal/electron-browser/terminalActions'; import { Registry } from 'vs/platform/registry/common/platform'; import { ShowAllCommandsAction } from 'vs/workbench/parts/quickopen/browser/commandsHandler'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { TerminalService } from 'vs/workbench/parts/terminal/electron-browser/terminalService'; import { ToggleTabFocusModeAction } from 'vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { OpenNextRecentlyUsedEditorInGroupAction, OpenPreviousRecentlyUsedEditorInGroupAction, FocusActiveGroupAction, FocusFirstGroupAction, FocusLastGroupAction, OpenFirstEditorInGroup, OpenLastEditorInGroup } from 'vs/workbench/browser/parts/editor/editorActions'; import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; import { registerColors } from 'vs/workbench/parts/terminal/common/terminalColorRegistry'; @@ -37,6 +36,8 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { TogglePanelAction } from 'vs/workbench/browser/parts/panel/panelActions'; import { TerminalPanel } from 'vs/workbench/parts/terminal/electron-browser/terminalPanel'; import { TerminalPickerHandler } from 'vs/workbench/parts/terminal/browser/terminalQuickOpen'; +import { setupTerminalCommands, TERMINAL_COMMAND_ID } from 'vs/workbench/parts/terminal/common/terminalCommands'; +import { setupTerminalMenu } from 'vs/workbench/parts/terminal/common/terminalMenu'; const quickOpenRegistry = (Registry.as(QuickOpenExtensions.Quickopen)); @@ -68,76 +69,76 @@ actionBarRegistry.registerActionBarContributor(Scope.VIEWER, QuickOpenActionTerm const configurationRegistry = Registry.as(Extensions.Configuration); configurationRegistry.registerConfiguration({ - 'id': 'terminal', - 'order': 100, - 'title': nls.localize('terminalIntegratedConfigurationTitle', "Integrated Terminal"), - 'type': 'object', - 'properties': { + id: 'terminal', + order: 100, + title: nls.localize('terminalIntegratedConfigurationTitle', "Integrated Terminal"), + type: 'object', + properties: { 'terminal.integrated.shell.linux': { - 'description': nls.localize('terminal.integrated.shell.linux', "The path of the shell that the terminal uses on Linux."), - 'type': 'string', - 'default': getTerminalDefaultShellUnixLike() + markdownDescription: nls.localize('terminal.integrated.shell.linux', "The path of the shell that the terminal uses on Linux. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), + type: 'string', + default: getTerminalDefaultShellUnixLike() }, 'terminal.integrated.shellArgs.linux': { - 'description': nls.localize('terminal.integrated.shellArgs.linux', "The command line arguments to use when on the Linux terminal."), - 'type': 'array', - 'items': { - 'type': 'string' + markdownDescription: nls.localize('terminal.integrated.shellArgs.linux', "The command line arguments to use when on the Linux terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), + type: 'array', + items: { + type: 'string' }, - 'default': [] + default: [] }, 'terminal.integrated.shell.osx': { - 'description': nls.localize('terminal.integrated.shell.osx', "The path of the shell that the terminal uses on OS X."), - 'type': 'string', - 'default': getTerminalDefaultShellUnixLike() + markdownDescription: nls.localize('terminal.integrated.shell.osx', "The path of the shell that the terminal uses on macOS. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), + type: 'string', + default: getTerminalDefaultShellUnixLike() }, 'terminal.integrated.shellArgs.osx': { - 'description': nls.localize('terminal.integrated.shellArgs.osx', "The command line arguments to use when on the OS X terminal."), - 'type': 'array', - 'items': { - 'type': 'string' + markdownDescription: nls.localize('terminal.integrated.shellArgs.osx', "The command line arguments to use when on the macOS terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), + type: 'array', + items: { + type: 'string' }, // Unlike on Linux, ~/.profile is not sourced when logging into a macOS session. This // is the reason terminals on macOS typically run login shells by default which set up // the environment. See http://unix.stackexchange.com/a/119675/115410 - 'default': ['-l'] + default: ['-l'] }, 'terminal.integrated.shell.windows': { - 'description': nls.localize('terminal.integrated.shell.windows', "The path of the shell that the terminal uses on Windows. When using shells shipped with Windows (cmd, PowerShell or Bash on Ubuntu)."), - 'type': 'string', - 'default': getTerminalDefaultShellWindows() + markdownDescription: nls.localize('terminal.integrated.shell.windows', "The path of the shell that the terminal uses on Windows. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), + type: 'string', + default: getTerminalDefaultShellWindows() }, 'terminal.integrated.shellArgs.windows': { - 'description': nls.localize('terminal.integrated.shellArgs.windows', "The command line arguments to use when on the Windows terminal."), - 'type': 'array', - 'items': { - 'type': 'string' + markdownDescription: nls.localize('terminal.integrated.shellArgs.windows', "The command line arguments to use when on the Windows terminal. [Read more about configuring the shell](https://code.visualstudio.com/docs/editor/integrated-terminal#_configuration)."), + type: 'array', + items: { + type: 'string' }, - 'default': [] + default: [] }, 'terminal.integrated.macOptionIsMeta': { - 'description': nls.localize('terminal.integrated.macOptionIsMeta', "Treat the option key as the meta key in the terminal on macOS."), - 'type': 'boolean', - 'default': false + description: nls.localize('terminal.integrated.macOptionIsMeta', "Controls whether to treat the option key as the meta key in the terminal on macOS."), + type: 'boolean', + default: false }, 'terminal.integrated.macOptionClickForcesSelection': { - 'description': nls.localize('terminal.integrated.macOptionClickForcesSelection', "Whether to force selection when using Option+click on macOS. This will force a regular (line) selection and disallow the use of column selection mode. This enables copying and pasting using the regular terminal selection, for example, when mouse mode is enabled in tmux."), - 'type': 'boolean', - 'default': false + description: nls.localize('terminal.integrated.macOptionClickForcesSelection', "Controls whether to force selection when using Option+click on macOS. This will force a regular (line) selection and disallow the use of column selection mode. This enables copying and pasting using the regular terminal selection, for example, when mouse mode is enabled in tmux."), + type: 'boolean', + default: false }, 'terminal.integrated.copyOnSelection': { - 'description': nls.localize('terminal.integrated.copyOnSelection', "When set, text selected in the terminal will be copied to the clipboard."), - 'type': 'boolean', - 'default': false + description: nls.localize('terminal.integrated.copyOnSelection', "Controls whether text selected in the terminal will be copied to the clipboard."), + type: 'boolean', + default: false }, 'terminal.integrated.drawBoldTextInBrightColors': { - 'description': nls.localize('terminal.integrated.drawBoldTextInBrightColors', "When set, bold text in the terminal will always use the \"bright\" ANSI color variant."), - 'type': 'boolean', - 'default': true + description: nls.localize('terminal.integrated.drawBoldTextInBrightColors', "Controls whether bold text in the terminal will always use the \"bright\" ANSI color variant."), + type: 'boolean', + default: true }, 'terminal.integrated.fontFamily': { - 'description': nls.localize('terminal.integrated.fontFamily', "Controls the font family of the terminal, this defaults to editor.fontFamily's value."), - 'type': 'string' + markdownDescription: nls.localize('terminal.integrated.fontFamily', "Controls the font family of the terminal, this defaults to `#editor.fontFamily#`'s value."), + type: 'string' }, // TODO: Support font ligatures // 'terminal.integrated.fontLigatures': { @@ -146,97 +147,140 @@ configurationRegistry.registerConfiguration({ // 'default': false // }, 'terminal.integrated.fontSize': { - 'description': nls.localize('terminal.integrated.fontSize', "Controls the font size in pixels of the terminal."), - 'type': 'number', - 'default': EDITOR_FONT_DEFAULTS.fontSize + description: nls.localize('terminal.integrated.fontSize', "Controls the font size in pixels of the terminal."), + type: 'number', + default: EDITOR_FONT_DEFAULTS.fontSize }, 'terminal.integrated.letterSpacing': { - 'description': nls.localize('terminal.integrated.letterSpacing', "Controls the letter spacing of the terminal, this is an integer value which represents the amount of additional pixels to add between characters."), - 'type': 'number', - 'default': DEFAULT_LETTER_SPACING + description: nls.localize('terminal.integrated.letterSpacing', "Controls the letter spacing of the terminal, this is an integer value which represents the amount of additional pixels to add between characters."), + type: 'number', + default: DEFAULT_LETTER_SPACING }, 'terminal.integrated.lineHeight': { - 'description': nls.localize('terminal.integrated.lineHeight', "Controls the line height of the terminal, this number is multiplied by the terminal font size to get the actual line-height in pixels."), - 'type': 'number', - 'default': DEFAULT_LINE_HEIGHT + description: nls.localize('terminal.integrated.lineHeight', "Controls the line height of the terminal, this number is multiplied by the terminal font size to get the actual line-height in pixels."), + type: 'number', + default: DEFAULT_LINE_HEIGHT }, 'terminal.integrated.fontWeight': { - 'type': 'string', - 'enum': ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], - 'description': nls.localize('terminal.integrated.fontWeight', "The font weight to use within the terminal for non-bold text."), - 'default': 'normal' + type: 'string', + enum: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], + description: nls.localize('terminal.integrated.fontWeight', "The font weight to use within the terminal for non-bold text."), + default: 'normal' }, 'terminal.integrated.fontWeightBold': { - 'type': 'string', - 'enum': ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], - 'description': nls.localize('terminal.integrated.fontWeightBold', "The font weight to use within the terminal for bold text."), - 'default': 'bold' + type: 'string', + enum: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], + description: nls.localize('terminal.integrated.fontWeightBold', "The font weight to use within the terminal for bold text."), + default: 'bold' }, 'terminal.integrated.cursorBlinking': { - 'description': nls.localize('terminal.integrated.cursorBlinking', "Controls whether the terminal cursor blinks."), - 'type': 'boolean', - 'default': false + description: nls.localize('terminal.integrated.cursorBlinking', "Controls whether the terminal cursor blinks."), + type: 'boolean', + default: false }, 'terminal.integrated.cursorStyle': { - 'description': nls.localize('terminal.integrated.cursorStyle', "Controls the style of terminal cursor."), - 'enum': [TerminalCursorStyle.BLOCK, TerminalCursorStyle.LINE, TerminalCursorStyle.UNDERLINE], - 'default': TerminalCursorStyle.BLOCK + description: nls.localize('terminal.integrated.cursorStyle', "Controls the style of terminal cursor."), + enum: [TerminalCursorStyle.BLOCK, TerminalCursorStyle.LINE, TerminalCursorStyle.UNDERLINE], + default: TerminalCursorStyle.BLOCK }, 'terminal.integrated.scrollback': { - 'description': nls.localize('terminal.integrated.scrollback', "Controls the maximum amount of lines the terminal keeps in its buffer."), - 'type': 'number', - 'default': 1000 + description: nls.localize('terminal.integrated.scrollback', "Controls the maximum amount of lines the terminal keeps in its buffer."), + type: 'number', + default: 1000 }, 'terminal.integrated.setLocaleVariables': { - 'description': nls.localize('terminal.integrated.setLocaleVariables', "Controls whether locale variables are set at startup of the terminal, this defaults to true on OS X, false on other platforms."), - 'type': 'boolean', - 'default': platform.isMacintosh + markdownDescription: nls.localize('terminal.integrated.setLocaleVariables', "Controls whether locale variables are set at startup of the terminal, this defaults to `true` on macOS, `false` on other platforms."), + type: 'boolean', + default: platform.isMacintosh }, 'terminal.integrated.rendererType': { - 'type': 'string', - 'enum': ['auto', 'canvas', 'dom'], + type: 'string', + enum: ['auto', 'canvas', 'dom'], + enumDescriptions: [ + nls.localize('terminal.integrated.rendererType.auto', "Let VS Code guess which renderer to use."), + nls.localize('terminal.integrated.rendererType.canvas', "Use the standard GPU/canvas-based renderer"), + nls.localize('terminal.integrated.rendererType.dom', "Use the fallback DOM-based renderer.") + ], default: 'auto', - description: nls.localize('terminal.integrated.rendererType', "Controls how the terminal is rendered, the options are \"canvas\" for the standard (fast) canvas renderer, \"dom\" for the fallback DOM-based renderer or \"auto\" which lets VS Code guess which will be best. This setting needs VS Code to reload in order to take effect.") + description: nls.localize('terminal.integrated.rendererType', "Controls how the terminal is rendered.") }, 'terminal.integrated.rightClickBehavior': { - 'type': 'string', - 'enum': ['default', 'copyPaste', 'selectWord'], + type: 'string', + enum: ['default', 'copyPaste', 'selectWord'], + enumDescriptions: [ + nls.localize('terminal.integrated.rightClickBehavior.default', "Show the context menu."), + nls.localize('terminal.integrated.rightClickBehavior.copyPaste', "Copy when there is a selection, otherwise paste."), + nls.localize('terminal.integrated.rightClickBehavior.selectWord', "Select the word under the cursor and show the context menu.") + ], default: platform.isMacintosh ? 'selectWord' : platform.isWindows ? 'copyPaste' : 'default', - description: nls.localize('terminal.integrated.rightClickBehavior', "Controls how terminal reacts to right click, possibilities are \"default\", \"copyPaste\", and \"selectWord\". \"default\" will show the context menu, \"copyPaste\" will copy when there is a selection otherwise paste, \"selectWord\" will select the word under the cursor and show the context menu.") + description: nls.localize('terminal.integrated.rightClickBehavior', "Controls how terminal reacts to right click.") }, 'terminal.integrated.cwd': { - 'description': nls.localize('terminal.integrated.cwd', "An explicit start path where the terminal will be launched, this is used as the current working directory (cwd) for the shell process. This may be particularly useful in workspace settings if the root directory is not a convenient cwd."), - 'type': 'string', - 'default': undefined + description: nls.localize('terminal.integrated.cwd', "An explicit start path where the terminal will be launched, this is used as the current working directory (cwd) for the shell process. This may be particularly useful in workspace settings if the root directory is not a convenient cwd."), + type: 'string', + default: undefined }, 'terminal.integrated.confirmOnExit': { - 'description': nls.localize('terminal.integrated.confirmOnExit', "Whether to confirm on exit if there are active terminal sessions."), - 'type': 'boolean', - 'default': false + description: nls.localize('terminal.integrated.confirmOnExit', "Controls whether to confirm on exit if there are active terminal sessions."), + type: 'boolean', + default: false }, 'terminal.integrated.enableBell': { - 'description': nls.localize('terminal.integrated.enableBell', "Whether the terminal bell is enabled or not."), - 'type': 'boolean', - 'default': false + description: nls.localize('terminal.integrated.enableBell', "Controls whether the terminal bell is enabled."), + type: 'boolean', + default: false }, 'terminal.integrated.commandsToSkipShell': { - 'description': nls.localize('terminal.integrated.commandsToSkipShell', "A set of command IDs whose keybindings will not be sent to the shell and instead always be handled by Code. This allows the use of keybindings that would normally be consumed by the shell to act the same as when the terminal is not focused, for example ctrl+p to launch Quick Open."), - 'type': 'array', - 'items': { - 'type': 'string' + description: nls.localize('terminal.integrated.commandsToSkipShell', "A set of command IDs whose keybindings will not be sent to the shell and instead always be handled by Code. This allows the use of keybindings that would normally be consumed by the shell to act the same as when the terminal is not focused, for example ctrl+p to launch Quick Open."), + type: 'array', + items: { + type: 'string' }, - 'default': [ + default: [ + TERMINAL_COMMAND_ID.CLEAR_SELECTION, + TERMINAL_COMMAND_ID.CLEAR, + TERMINAL_COMMAND_ID.COPY_SELECTION, + TERMINAL_COMMAND_ID.DELETE_WORD_LEFT, + TERMINAL_COMMAND_ID.DELETE_WORD_RIGHT, + TERMINAL_COMMAND_ID.FIND_WIDGET_FOCUS, + TERMINAL_COMMAND_ID.FIND_WIDGET_HIDE, + TERMINAL_COMMAND_ID.FOCUS_NEXT_PANE, + TERMINAL_COMMAND_ID.FOCUS_NEXT, + TERMINAL_COMMAND_ID.FOCUS_PREVIOUS_PANE, + TERMINAL_COMMAND_ID.FOCUS_PREVIOUS, + TERMINAL_COMMAND_ID.FOCUS, + TERMINAL_COMMAND_ID.KILL, + TERMINAL_COMMAND_ID.MOVE_TO_LINE_END, + TERMINAL_COMMAND_ID.MOVE_TO_LINE_START, + TERMINAL_COMMAND_ID.NEW_IN_ACTIVE_WORKSPACE, + TERMINAL_COMMAND_ID.NEW, + TERMINAL_COMMAND_ID.PASTE, + TERMINAL_COMMAND_ID.RESIZE_PANE_DOWN, + TERMINAL_COMMAND_ID.RESIZE_PANE_LEFT, + TERMINAL_COMMAND_ID.RESIZE_PANE_RIGHT, + TERMINAL_COMMAND_ID.RESIZE_PANE_UP, + TERMINAL_COMMAND_ID.RUN_ACTIVE_FILE, + TERMINAL_COMMAND_ID.RUN_SELECTED_TEXT, + TERMINAL_COMMAND_ID.SCROLL_DOWN_LINE, + TERMINAL_COMMAND_ID.SCROLL_DOWN_PAGE, + TERMINAL_COMMAND_ID.SCROLL_TO_BOTTOM, + TERMINAL_COMMAND_ID.SCROLL_TO_NEXT_COMMAND, + TERMINAL_COMMAND_ID.SCROLL_TO_PREVIOUS_COMMAND, + TERMINAL_COMMAND_ID.SCROLL_TO_TOP, + TERMINAL_COMMAND_ID.SCROLL_UP_LINE, + TERMINAL_COMMAND_ID.SCROLL_UP_PAGE, + TERMINAL_COMMAND_ID.SELECT_ALL, + TERMINAL_COMMAND_ID.SELECT_TO_NEXT_COMMAND, + TERMINAL_COMMAND_ID.SELECT_TO_NEXT_LINE, + TERMINAL_COMMAND_ID.SELECT_TO_PREVIOUS_COMMAND, + TERMINAL_COMMAND_ID.SELECT_TO_PREVIOUS_LINE, + TERMINAL_COMMAND_ID.SPLIT_IN_ACTIVE_WORKSPACE, + TERMINAL_COMMAND_ID.SPLIT, + TERMINAL_COMMAND_ID.TOGGLE, ToggleTabFocusModeAction.ID, QUICKOPEN_ACTION_ID, QUICKOPEN_FOCUS_SECONDARY_ACTION_ID, ShowAllCommandsAction.ID, - CreateNewTerminalAction.ID, - CreateNewInActiveWorkspaceTerminalAction.ID, - CopyTerminalSelectionAction.ID, - KillTerminalAction.ID, - FocusActiveTerminalAction.ID, - FocusPreviousTerminalAction.ID, - FocusNextTerminalAction.ID, 'workbench.action.tasks.build', 'workbench.action.tasks.restartTask', 'workbench.action.tasks.runTask', @@ -260,18 +304,6 @@ configurationRegistry.registerConfiguration({ 'workbench.action.focusSixthEditorGroup', 'workbench.action.focusSeventhEditorGroup', 'workbench.action.focusEighthEditorGroup', - TerminalPasteAction.ID, - RunSelectedTextInTerminalAction.ID, - RunActiveFileInTerminalAction.ID, - ToggleTerminalAction.ID, - ScrollDownTerminalAction.ID, - ScrollDownPageTerminalAction.ID, - ScrollToBottomTerminalAction.ID, - ScrollUpTerminalAction.ID, - ScrollUpPageTerminalAction.ID, - ScrollToTopTerminalAction.ID, - ClearTerminalAction.ID, - ClearSelectionTerminalAction.ID, debugActions.StartAction.ID, debugActions.StopAction.ID, debugActions.RunAction.ID, @@ -288,75 +320,43 @@ configurationRegistry.registerConfiguration({ FocusLastGroupAction.ID, OpenFirstEditorInGroup.ID, OpenLastEditorInGroup.ID, - SelectAllTerminalAction.ID, - FocusTerminalFindWidgetAction.ID, - HideTerminalFindWidgetAction.ID, NavigateUpAction.ID, NavigateDownAction.ID, NavigateRightAction.ID, NavigateLeftAction.ID, - DeleteWordLeftTerminalAction.ID, - DeleteWordRightTerminalAction.ID, - MoveToLineStartTerminalAction.ID, - MoveToLineEndTerminalAction.ID, TogglePanelAction.ID, - 'workbench.action.quickOpenView', - SplitTerminalAction.ID, - SplitInActiveWorkspaceTerminalAction.ID, - FocusPreviousPaneTerminalAction.ID, - FocusNextPaneTerminalAction.ID, - ResizePaneLeftTerminalAction.ID, - ResizePaneRightTerminalAction.ID, - ResizePaneUpTerminalAction.ID, - ResizePaneDownTerminalAction.ID, - ScrollToPreviousCommandAction.ID, - ScrollToNextCommandAction.ID, - SelectToPreviousCommandAction.ID, - SelectToNextCommandAction.ID, - SelectToPreviousLineAction.ID, - SelectToNextLineAction.ID + 'workbench.action.quickOpenView' ].sort() }, 'terminal.integrated.env.osx': { - 'description': nls.localize('terminal.integrated.env.osx', "Object with environment variables that will be added to the VS Code process to be used by the terminal on OS X"), - 'type': 'object', - 'additionalProperties': { - 'type': ['string', 'null'] + markdownDescription: nls.localize('terminal.integrated.env.osx', "Object with environment variables that will be added to the VS Code process to be used by the terminal on macOS. Set to `null` to delete the environment variable."), + type: 'object', + additionalProperties: { + type: ['string', 'null'] }, - 'default': {} + default: {} }, 'terminal.integrated.env.linux': { - 'description': nls.localize('terminal.integrated.env.linux', "Object with environment variables that will be added to the VS Code process to be used by the terminal on Linux"), - 'type': 'object', - 'additionalProperties': { - 'type': ['string', 'null'] + markdownDescription: nls.localize('terminal.integrated.env.linux', "Object with environment variables that will be added to the VS Code process to be used by the terminal on Linux. Set to `null` to delete the environment variable."), + type: 'object', + additionalProperties: { + type: ['string', 'null'] }, - 'default': {} + default: {} }, 'terminal.integrated.env.windows': { - 'description': nls.localize('terminal.integrated.env.windows', "Object with environment variables that will be added to the VS Code process to be used by the terminal on Windows"), - 'type': 'object', - 'additionalProperties': { - 'type': ['string', 'null'] + markdownDescription: nls.localize('terminal.integrated.env.windows', "Object with environment variables that will be added to the VS Code process to be used by the terminal on Windows. Set to `null` to delete the environment variable."), + type: 'object', + additionalProperties: { + type: ['string', 'null'] }, - 'default': {} + default: {} }, 'terminal.integrated.showExitAlert': { - 'description': nls.localize('terminal.integrated.showExitAlert', "Show alert `The terminal process terminated with exit code` when exit code is non-zero."), - 'type': 'boolean', - 'default': true - }, - 'terminal.integrated.experimentalRestore': { - 'description': nls.localize('terminal.integrated.experimentalRestore', "Whether to restore terminal sessions for the workspace automatically when launching VS Code. This is an experimental setting; it may be buggy and could change in the future."), - 'type': 'boolean', - 'default': false - }, - 'terminal.integrated.experimentalTextureCachingStrategy': { - 'description': nls.localize('terminal.integrated.experimentalTextureCachingStrategy', "Controls how the terminal stores glyph textures. `static` is the default and uses a fixed texture to draw the characters from. `dynamic` will draw the characters to the texture as they are needed, this should boost overall performance at the cost of slightly increased draw time the first time a character is drawn. `dynamic` will eventually become the default and this setting will be removed. Changes to this setting will only apply to new terminals."), - 'type': 'string', - 'enum': ['static', 'dynamic'], - 'default': 'dynamic' - }, + description: nls.localize('terminal.integrated.showExitAlert', "Controls whether to show the alert \"The terminal process terminated with exit code\" when exit code is non-zero."), + type: 'boolean', + default: true + } } }); @@ -368,7 +368,7 @@ registerSingleton(ITerminalService, TerminalService); nls.localize('terminal', "Terminal"), 'terminal', 40, - ToggleTerminalAction.ID + TERMINAL_COMMAND_ID.TOGGLE )); // On mac cmd+` is reserved to cycle between windows, that's why the keybindings use WinCtrl @@ -439,7 +439,7 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ScrollToTopTermi actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ClearTerminalAction, ClearTerminalAction.ID, ClearTerminalAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_K, linux: { primary: null } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KeybindingsRegistry.WEIGHT.workbenchContrib(1)), 'Terminal: Clear', category); +}, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KeybindingWeight.WorkbenchContrib + 1), 'Terminal: Clear', category); if (platform.isWindows) { actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectDefaultShellWindowsTerminalAction, SelectDefaultShellWindowsTerminalAction.ID, SelectDefaultShellWindowsTerminalAction.LABEL), 'Terminal: Select Default Shell', category); } @@ -532,7 +532,9 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToNextComm }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Select To Next Command', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToPreviousLineAction, SelectToPreviousLineAction.ID, SelectToPreviousLineAction.LABEL), 'Terminal: Select To Previous Line', category); actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(SelectToNextLineAction, SelectToNextLineAction.ID, SelectToNextLineAction.LABEL), 'Terminal: Select To Next Line', category); +actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEscapeSequenceLoggingAction, ToggleEscapeSequenceLoggingAction.ID, ToggleEscapeSequenceLoggingAction.LABEL), 'Terminal: Toggle Escape Sequence Logging', category); -terminalCommands.setup(); +setupTerminalCommands(); +setupTerminalMenu(); registerColors(); diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts index e5df662157c..25b65e9eafc 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts @@ -26,12 +26,13 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { TERMINAL_COMMAND_ID } from 'vs/workbench/parts/terminal/common/terminalCommands'; export const TERMINAL_PICKER_PREFIX = 'term '; export class ToggleTerminalAction extends TogglePanelAction { - public static readonly ID = 'workbench.action.terminal.toggleTerminal'; + public static readonly ID = TERMINAL_COMMAND_ID.TOGGLE; public static readonly LABEL = nls.localize('workbench.action.terminal.toggleTerminal', "Toggle Integrated Terminal"); constructor( @@ -59,7 +60,7 @@ export class ToggleTerminalAction extends TogglePanelAction { export class KillTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.kill'; + public static readonly ID = TERMINAL_COMMAND_ID.KILL; public static readonly LABEL = nls.localize('workbench.action.terminal.kill', "Kill the Active Terminal Instance"); public static readonly PANEL_LABEL = nls.localize('workbench.action.terminal.kill.short', "Kill Terminal"); @@ -84,7 +85,7 @@ export class KillTerminalAction extends Action { export class QuickKillTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.quickKill'; + public static readonly ID = TERMINAL_COMMAND_ID.QUICK_KILL; public static readonly LABEL = nls.localize('workbench.action.terminal.quickKill', "Kill Terminal Instance"); constructor( @@ -110,8 +111,9 @@ export class QuickKillTerminalAction extends Action { */ export class CopyTerminalSelectionAction extends Action { - public static readonly ID = 'workbench.action.terminal.copySelection'; + public static readonly ID = TERMINAL_COMMAND_ID.COPY_SELECTION; public static readonly LABEL = nls.localize('workbench.action.terminal.copySelection', "Copy Selection"); + public static readonly SHORT_LABEL = nls.localize('workbench.action.terminal.copySelection.short', "Copy"); constructor( id: string, label: string, @@ -131,7 +133,7 @@ export class CopyTerminalSelectionAction extends Action { export class SelectAllTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.selectAll'; + public static readonly ID = TERMINAL_COMMAND_ID.SELECT_ALL; public static readonly LABEL = nls.localize('workbench.action.terminal.selectAll', "Select All"); constructor( @@ -170,7 +172,7 @@ export abstract class BaseSendTextTerminalAction extends Action { } export class DeleteWordLeftTerminalAction extends BaseSendTextTerminalAction { - public static readonly ID = 'workbench.action.terminal.deleteWordLeft'; + public static readonly ID = TERMINAL_COMMAND_ID.DELETE_WORD_LEFT; public static readonly LABEL = nls.localize('workbench.action.terminal.deleteWordLeft', "Delete Word Left"); constructor( @@ -184,7 +186,7 @@ export class DeleteWordLeftTerminalAction extends BaseSendTextTerminalAction { } export class DeleteWordRightTerminalAction extends BaseSendTextTerminalAction { - public static readonly ID = 'workbench.action.terminal.deleteWordRight'; + public static readonly ID = TERMINAL_COMMAND_ID.DELETE_WORD_RIGHT; public static readonly LABEL = nls.localize('workbench.action.terminal.deleteWordRight', "Delete Word Right"); constructor( @@ -198,7 +200,7 @@ export class DeleteWordRightTerminalAction extends BaseSendTextTerminalAction { } export class MoveToLineStartTerminalAction extends BaseSendTextTerminalAction { - public static readonly ID = 'workbench.action.terminal.moveToLineStart'; + public static readonly ID = TERMINAL_COMMAND_ID.MOVE_TO_LINE_START; public static readonly LABEL = nls.localize('workbench.action.terminal.moveToLineStart', "Move To Line Start"); constructor( @@ -212,7 +214,7 @@ export class MoveToLineStartTerminalAction extends BaseSendTextTerminalAction { } export class MoveToLineEndTerminalAction extends BaseSendTextTerminalAction { - public static readonly ID = 'workbench.action.terminal.moveToLineEnd'; + public static readonly ID = TERMINAL_COMMAND_ID.MOVE_TO_LINE_END; public static readonly LABEL = nls.localize('workbench.action.terminal.moveToLineEnd', "Move To Line End"); constructor( @@ -227,9 +229,9 @@ export class MoveToLineEndTerminalAction extends BaseSendTextTerminalAction { export class CreateNewTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.new'; + public static readonly ID = TERMINAL_COMMAND_ID.NEW; public static readonly LABEL = nls.localize('workbench.action.terminal.new', "Create New Integrated Terminal"); - public static readonly PANEL_LABEL = nls.localize('workbench.action.terminal.new.short', "New Terminal"); + public static readonly SHORT_LABEL = nls.localize('workbench.action.terminal.new.short', "New Terminal"); constructor( id: string, label: string, @@ -280,7 +282,7 @@ export class CreateNewTerminalAction extends Action { export class CreateNewInActiveWorkspaceTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.newInActiveWorkspace'; + public static readonly ID = TERMINAL_COMMAND_ID.NEW_IN_ACTIVE_WORKSPACE; public static readonly LABEL = nls.localize('workbench.action.terminal.newInActiveWorkspace', "Create New Integrated Terminal (In Active Workspace)"); constructor( @@ -301,8 +303,9 @@ export class CreateNewInActiveWorkspaceTerminalAction extends Action { } export class SplitTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.split'; + public static readonly ID = TERMINAL_COMMAND_ID.SPLIT; public static readonly LABEL = nls.localize('workbench.action.terminal.split', "Split Terminal"); + public static readonly SHORT_LABEL = nls.localize('workbench.action.terminal.split.short', "Split"); constructor( id: string, label: string, @@ -347,7 +350,7 @@ export class SplitTerminalAction extends Action { } export class SplitInActiveWorkspaceTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.splitInActiveWorkspace'; + public static readonly ID = TERMINAL_COMMAND_ID.SPLIT_IN_ACTIVE_WORKSPACE; public static readonly LABEL = nls.localize('workbench.action.terminal.splitInActiveWorkspace', "Split Terminal (In Active Workspace)"); constructor( @@ -368,7 +371,7 @@ export class SplitInActiveWorkspaceTerminalAction extends Action { } export class FocusPreviousPaneTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.focusPreviousPane'; + public static readonly ID = TERMINAL_COMMAND_ID.FOCUS_PREVIOUS_PANE; public static readonly LABEL = nls.localize('workbench.action.terminal.focusPreviousPane', "Focus Previous Pane"); constructor( @@ -389,7 +392,7 @@ export class FocusPreviousPaneTerminalAction extends Action { } export class FocusNextPaneTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.focusNextPane'; + public static readonly ID = TERMINAL_COMMAND_ID.FOCUS_NEXT_PANE; public static readonly LABEL = nls.localize('workbench.action.terminal.focusNextPane', "Focus Next Pane"); constructor( @@ -428,7 +431,7 @@ export abstract class BaseFocusDirectionTerminalAction extends Action { } export class ResizePaneLeftTerminalAction extends BaseFocusDirectionTerminalAction { - public static readonly ID = 'workbench.action.terminal.resizePaneLeft'; + public static readonly ID = TERMINAL_COMMAND_ID.RESIZE_PANE_LEFT; public static readonly LABEL = nls.localize('workbench.action.terminal.resizePaneLeft', "Resize Pane Left"); constructor( @@ -440,7 +443,7 @@ export class ResizePaneLeftTerminalAction extends BaseFocusDirectionTerminalActi } export class ResizePaneRightTerminalAction extends BaseFocusDirectionTerminalAction { - public static readonly ID = 'workbench.action.terminal.resizePaneRight'; + public static readonly ID = TERMINAL_COMMAND_ID.RESIZE_PANE_RIGHT; public static readonly LABEL = nls.localize('workbench.action.terminal.resizePaneRight', "Resize Pane Right"); constructor( @@ -452,7 +455,7 @@ export class ResizePaneRightTerminalAction extends BaseFocusDirectionTerminalAct } export class ResizePaneUpTerminalAction extends BaseFocusDirectionTerminalAction { - public static readonly ID = 'workbench.action.terminal.resizePaneUp'; + public static readonly ID = TERMINAL_COMMAND_ID.RESIZE_PANE_UP; public static readonly LABEL = nls.localize('workbench.action.terminal.resizePaneUp', "Resize Pane Up"); constructor( @@ -464,7 +467,7 @@ export class ResizePaneUpTerminalAction extends BaseFocusDirectionTerminalAction } export class ResizePaneDownTerminalAction extends BaseFocusDirectionTerminalAction { - public static readonly ID = 'workbench.action.terminal.resizePaneDown'; + public static readonly ID = TERMINAL_COMMAND_ID.RESIZE_PANE_DOWN; public static readonly LABEL = nls.localize('workbench.action.terminal.resizePaneDown', "Resize Pane Down"); constructor( @@ -477,7 +480,7 @@ export class ResizePaneDownTerminalAction extends BaseFocusDirectionTerminalActi export class FocusActiveTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.focus'; + public static readonly ID = TERMINAL_COMMAND_ID.FOCUS; public static readonly LABEL = nls.localize('workbench.action.terminal.focus', "Focus Terminal"); constructor( @@ -499,7 +502,7 @@ export class FocusActiveTerminalAction extends Action { export class FocusNextTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.focusNext'; + public static readonly ID = TERMINAL_COMMAND_ID.FOCUS_NEXT; public static readonly LABEL = nls.localize('workbench.action.terminal.focusNext', "Focus Next Terminal"); constructor( @@ -517,7 +520,7 @@ export class FocusNextTerminalAction extends Action { export class FocusPreviousTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.focusPrevious'; + public static readonly ID = TERMINAL_COMMAND_ID.FOCUS_PREVIOUS; public static readonly LABEL = nls.localize('workbench.action.terminal.focusPrevious', "Focus Previous Terminal"); constructor( @@ -535,8 +538,9 @@ export class FocusPreviousTerminalAction extends Action { export class TerminalPasteAction extends Action { - public static readonly ID = 'workbench.action.terminal.paste'; + public static readonly ID = TERMINAL_COMMAND_ID.PASTE; public static readonly LABEL = nls.localize('workbench.action.terminal.paste', "Paste into Active Terminal"); + public static readonly SHORT_LABEL = nls.localize('workbench.action.terminal.paste.short', "Paste"); constructor( id: string, label: string, @@ -556,8 +560,8 @@ export class TerminalPasteAction extends Action { export class SelectDefaultShellWindowsTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.selectDefaultShell'; - public static readonly LABEL = nls.localize('workbench.action.terminal.DefaultShell', "Select Default Shell"); + public static readonly ID = TERMINAL_COMMAND_ID.SELECT_DEFAULT_SHELL; + public static readonly LABEL = nls.localize('workbench.action.terminal.selectDefaultShell', "Select Default Shell"); constructor( id: string, label: string, @@ -573,7 +577,7 @@ export class SelectDefaultShellWindowsTerminalAction extends Action { export class RunSelectedTextInTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.runSelectedText'; + public static readonly ID = TERMINAL_COMMAND_ID.RUN_SELECTED_TEXT; public static readonly LABEL = nls.localize('workbench.action.terminal.runSelectedText', "Run Selected Text In Active Terminal"); constructor( @@ -608,7 +612,7 @@ export class RunSelectedTextInTerminalAction extends Action { export class RunActiveFileInTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.runActiveFile'; + public static readonly ID = TERMINAL_COMMAND_ID.RUN_ACTIVE_FILE; public static readonly LABEL = nls.localize('workbench.action.terminal.runActiveFile', "Run Active File In Active Terminal"); constructor( @@ -641,14 +645,14 @@ export class RunActiveFileInTerminalAction extends Action { export class SwitchTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.switchTerminal'; + public static readonly ID = TERMINAL_COMMAND_ID.SWITCH_TERMINAL; public static readonly LABEL = nls.localize('workbench.action.terminal.switchTerminal', "Switch Terminal"); constructor( id: string, label: string, @ITerminalService private terminalService: ITerminalService ) { - super(SwitchTerminalAction.ID, SwitchTerminalAction.LABEL, 'terminal-action switch-terminal'); + super(id, label, 'terminal-action switch-terminal'); } public run(item?: string): TPromise { @@ -684,7 +688,7 @@ export class SwitchTerminalActionItem extends SelectActionItem { export class ScrollDownTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.scrollDown'; + public static readonly ID = TERMINAL_COMMAND_ID.SCROLL_DOWN_LINE; public static readonly LABEL = nls.localize('workbench.action.terminal.scrollDown', "Scroll Down (Line)"); constructor( @@ -705,7 +709,7 @@ export class ScrollDownTerminalAction extends Action { export class ScrollDownPageTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.scrollDownPage'; + public static readonly ID = TERMINAL_COMMAND_ID.SCROLL_DOWN_PAGE; public static readonly LABEL = nls.localize('workbench.action.terminal.scrollDownPage', "Scroll Down (Page)"); constructor( @@ -726,7 +730,7 @@ export class ScrollDownPageTerminalAction extends Action { export class ScrollToBottomTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.scrollToBottom'; + public static readonly ID = TERMINAL_COMMAND_ID.SCROLL_TO_BOTTOM; public static readonly LABEL = nls.localize('workbench.action.terminal.scrollToBottom', "Scroll to Bottom"); constructor( @@ -747,7 +751,7 @@ export class ScrollToBottomTerminalAction extends Action { export class ScrollUpTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.scrollUp'; + public static readonly ID = TERMINAL_COMMAND_ID.SCROLL_UP_LINE; public static readonly LABEL = nls.localize('workbench.action.terminal.scrollUp', "Scroll Up (Line)"); constructor( @@ -768,7 +772,7 @@ export class ScrollUpTerminalAction extends Action { export class ScrollUpPageTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.scrollUpPage'; + public static readonly ID = TERMINAL_COMMAND_ID.SCROLL_UP_PAGE; public static readonly LABEL = nls.localize('workbench.action.terminal.scrollUpPage', "Scroll Up (Page)"); constructor( @@ -789,7 +793,7 @@ export class ScrollUpPageTerminalAction extends Action { export class ScrollToTopTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.scrollToTop'; + public static readonly ID = TERMINAL_COMMAND_ID.SCROLL_TO_TOP; public static readonly LABEL = nls.localize('workbench.action.terminal.scrollToTop', "Scroll to Top"); constructor( @@ -810,7 +814,7 @@ export class ScrollToTopTerminalAction extends Action { export class ClearTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.clear'; + public static readonly ID = TERMINAL_COMMAND_ID.CLEAR; public static readonly LABEL = nls.localize('workbench.action.terminal.clear', "Clear"); constructor( @@ -831,7 +835,7 @@ export class ClearTerminalAction extends Action { export class ClearSelectionTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.clearSelection'; + public static readonly ID = TERMINAL_COMMAND_ID.CLEAR_SELECTION; public static readonly LABEL = nls.localize('workbench.action.terminal.clearSelection', "Clear Selection"); constructor( @@ -852,7 +856,7 @@ export class ClearSelectionTerminalAction extends Action { export class AllowWorkspaceShellTerminalCommand extends Action { - public static readonly ID = 'workbench.action.terminal.allowWorkspaceShell'; + public static readonly ID = TERMINAL_COMMAND_ID.WORKSPACE_SHELL_ALLOW; public static readonly LABEL = nls.localize('workbench.action.terminal.allowWorkspaceShell', "Allow Workspace Shell Configuration"); constructor( @@ -870,7 +874,7 @@ export class AllowWorkspaceShellTerminalCommand extends Action { export class DisallowWorkspaceShellTerminalCommand extends Action { - public static readonly ID = 'workbench.action.terminal.disallowWorkspaceShell'; + public static readonly ID = TERMINAL_COMMAND_ID.WORKSPACE_SHELL_DISALLOW; public static readonly LABEL = nls.localize('workbench.action.terminal.disallowWorkspaceShell', "Disallow Workspace Shell Configuration"); constructor( @@ -888,7 +892,7 @@ export class DisallowWorkspaceShellTerminalCommand extends Action { export class RenameTerminalAction extends Action { - public static readonly ID = 'workbench.action.terminal.rename'; + public static readonly ID = TERMINAL_COMMAND_ID.RENAME; public static readonly LABEL = nls.localize('workbench.action.terminal.rename', "Rename"); constructor( @@ -918,7 +922,7 @@ export class RenameTerminalAction extends Action { export class FocusTerminalFindWidgetAction extends Action { - public static readonly ID = 'workbench.action.terminal.focusFindWidget'; + public static readonly ID = TERMINAL_COMMAND_ID.FIND_WIDGET_FOCUS; public static readonly LABEL = nls.localize('workbench.action.terminal.focusFindWidget', "Focus Find Widget"); constructor( @@ -935,7 +939,7 @@ export class FocusTerminalFindWidgetAction extends Action { export class HideTerminalFindWidgetAction extends Action { - public static readonly ID = 'workbench.action.terminal.hideFindWidget'; + public static readonly ID = TERMINAL_COMMAND_ID.FIND_WIDGET_HIDE; public static readonly LABEL = nls.localize('workbench.action.terminal.hideFindWidget', "Hide Find Widget"); constructor( @@ -974,7 +978,7 @@ export class QuickOpenActionTermContributor extends ActionBarContributor { export class QuickOpenTermAction extends Action { - public static readonly ID = 'workbench.action.quickOpenTerm'; + public static readonly ID = TERMINAL_COMMAND_ID.QUICK_OPEN_TERM; public static readonly LABEL = nls.localize('quickOpenTerm', "Switch Active Terminal"); constructor( @@ -1013,7 +1017,7 @@ export class RenameTerminalQuickOpenAction extends RenameTerminalAction { } export class ScrollToPreviousCommandAction extends Action { - public static readonly ID = 'workbench.action.terminal.scrollToPreviousCommand'; + public static readonly ID = TERMINAL_COMMAND_ID.SCROLL_TO_PREVIOUS_COMMAND; public static readonly LABEL = nls.localize('workbench.action.terminal.scrollToPreviousCommand', "Scroll To Previous Command"); constructor( @@ -1034,7 +1038,7 @@ export class ScrollToPreviousCommandAction extends Action { } export class ScrollToNextCommandAction extends Action { - public static readonly ID = 'workbench.action.terminal.scrollToNextCommand'; + public static readonly ID = TERMINAL_COMMAND_ID.SCROLL_TO_NEXT_COMMAND; public static readonly LABEL = nls.localize('workbench.action.terminal.scrollToNextCommand', "Scroll To Next Command"); constructor( @@ -1055,7 +1059,7 @@ export class ScrollToNextCommandAction extends Action { } export class SelectToPreviousCommandAction extends Action { - public static readonly ID = 'workbench.action.terminal.selectToPreviousCommand'; + public static readonly ID = TERMINAL_COMMAND_ID.SELECT_TO_PREVIOUS_COMMAND; public static readonly LABEL = nls.localize('workbench.action.terminal.selectToPreviousCommand', "Select To Previous Command"); constructor( @@ -1076,7 +1080,7 @@ export class SelectToPreviousCommandAction extends Action { } export class SelectToNextCommandAction extends Action { - public static readonly ID = 'workbench.action.terminal.selectToNextCommand'; + public static readonly ID = TERMINAL_COMMAND_ID.SELECT_TO_NEXT_COMMAND; public static readonly LABEL = nls.localize('workbench.action.terminal.selectToNextCommand', "Select To Next Command"); constructor( @@ -1097,7 +1101,7 @@ export class SelectToNextCommandAction extends Action { } export class SelectToPreviousLineAction extends Action { - public static readonly ID = 'workbench.action.terminal.selectToPreviousLine'; + public static readonly ID = TERMINAL_COMMAND_ID.SELECT_TO_PREVIOUS_LINE; public static readonly LABEL = nls.localize('workbench.action.terminal.selectToPreviousLine', "Select To Previous Line"); constructor( @@ -1118,7 +1122,7 @@ export class SelectToPreviousLineAction extends Action { } export class SelectToNextLineAction extends Action { - public static readonly ID = 'workbench.action.terminal.selectToNextLine'; + public static readonly ID = TERMINAL_COMMAND_ID.SELECT_TO_NEXT_LINE; public static readonly LABEL = nls.localize('workbench.action.terminal.selectToNextLine', "Select To Next Line"); constructor( @@ -1136,4 +1140,25 @@ export class SelectToNextLineAction extends Action { } return TPromise.as(void 0); } -} \ No newline at end of file +} + + +export class ToggleEscapeSequenceLoggingAction extends Action { + public static readonly ID = TERMINAL_COMMAND_ID.TOGGLE_ESCAPE_SEQUENCE_LOGGING; + public static readonly LABEL = nls.localize('workbench.action.terminal.toggleEscapeSequenceLogging', "Toggle Escape Sequence Logging"); + + constructor( + id: string, label: string, + @ITerminalService private terminalService: ITerminalService + ) { + super(id, label); + } + + public run(): TPromise { + const instance = this.terminalService.getActiveInstance(); + if (instance) { + instance.toggleEscapeSequenceLogging(); + } + return TPromise.as(void 0); + } +} diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 7d1a41951b3..0b7ed17b6dc 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -293,7 +293,8 @@ export class TerminalInstance implements ITerminalInstance { rightClickSelectsWord: config.rightClickBehavior === 'selectWord', // TODO: Guess whether to use canvas or dom better rendererType: config.rendererType === 'auto' ? 'canvas' : config.rendererType, - experimentalCharAtlas: config.experimentalTextureCachingStrategy + // TODO: Remove this once the setting is removed upstream + experimentalCharAtlas: 'dynamic' }); if (this._shellLaunchConfig.initialText) { this._xterm.writeln(this._shellLaunchConfig.initialText); @@ -481,7 +482,7 @@ export class TerminalInstance implements ITerminalInstance { label: nls.localize('yes', "Yes"), run: () => { this._configurationService.updateValue('terminal.integrated.rendererType', 'dom', ConfigurationTarget.USER).then(() => { - this._notificationService.info(nls.localize('terminal.rendererInAllNewTerminals', "All newly created terminals will use the non-GPU renderer.")); + this._notificationService.info(nls.localize('terminal.rendererInAllNewTerminals', "The termnial is now using the fallback renderer.")); }); } } as IPromptChoice, @@ -665,6 +666,10 @@ export class TerminalInstance implements ITerminalInstance { const width = parseInt(computedStyle.getPropertyValue('width').replace('px', ''), 10); const height = parseInt(computedStyle.getPropertyValue('height').replace('px', ''), 10); this.layout(new dom.Dimension(width, height)); + // HACK: Trigger another async layout to ensure xterm's CharMeasure is ready to use, + // this hack can be removed when https://github.com/xtermjs/xterm.js/issues/702 is + // supported. + setTimeout(() => this.layout(new dom.Dimension(width, height)), 0); } } } @@ -880,6 +885,7 @@ export class TerminalInstance implements ITerminalInstance { this._safeSetOption('macOptionIsMeta', config.macOptionIsMeta); this._safeSetOption('macOptionClickForcesSelection', config.macOptionClickForcesSelection); this._safeSetOption('rightClickSelectsWord', config.rightClickBehavior === 'selectWord'); + this._safeSetOption('rendererType', config.rendererType === 'auto' ? 'canvas' : config.rendererType); } public updateAccessibilitySupport(): void { @@ -1064,6 +1070,11 @@ export class TerminalInstance implements ITerminalInstance { private _updateTheme(theme?: ITheme): void { this._xterm.setOption('theme', this._getXtermTheme(theme)); } + + public toggleEscapeSequenceLogging(): void { + this._xterm._core.debug = !this._xterm._core.debug; + this._xterm.setOption('debug', this._xterm._core.debug); + } } registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts index 76750995eac..4e1b4891f83 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalPanel.ts @@ -134,7 +134,7 @@ export class TerminalPanel extends Panel { if (!this._actions) { this._actions = [ this._instantiationService.createInstance(SwitchTerminalAction, SwitchTerminalAction.ID, SwitchTerminalAction.LABEL), - this._instantiationService.createInstance(CreateNewTerminalAction, CreateNewTerminalAction.ID, CreateNewTerminalAction.PANEL_LABEL), + this._instantiationService.createInstance(CreateNewTerminalAction, CreateNewTerminalAction.ID, CreateNewTerminalAction.SHORT_LABEL), this._instantiationService.createInstance(SplitTerminalAction, SplitTerminalAction.ID, SplitTerminalAction.LABEL), this._instantiationService.createInstance(KillTerminalAction, KillTerminalAction.ID, KillTerminalAction.PANEL_LABEL) ]; @@ -147,16 +147,16 @@ export class TerminalPanel extends Panel { private _getContextMenuActions(): IAction[] { if (!this._contextMenuActions) { - this._copyContextMenuAction = this._instantiationService.createInstance(CopyTerminalSelectionAction, CopyTerminalSelectionAction.ID, nls.localize('copy', "Copy")); + this._copyContextMenuAction = this._instantiationService.createInstance(CopyTerminalSelectionAction, CopyTerminalSelectionAction.ID, CopyTerminalSelectionAction.SHORT_LABEL); this._contextMenuActions = [ - this._instantiationService.createInstance(CreateNewTerminalAction, CreateNewTerminalAction.ID, CreateNewTerminalAction.PANEL_LABEL), - this._instantiationService.createInstance(SplitTerminalAction, SplitTerminalAction.ID, nls.localize('split', "Split")), + this._instantiationService.createInstance(CreateNewTerminalAction, CreateNewTerminalAction.ID, CreateNewTerminalAction.SHORT_LABEL), + this._instantiationService.createInstance(SplitTerminalAction, SplitTerminalAction.ID, SplitTerminalAction.SHORT_LABEL), new Separator(), this._copyContextMenuAction, - this._instantiationService.createInstance(TerminalPasteAction, TerminalPasteAction.ID, nls.localize('paste', "Paste")), - this._instantiationService.createInstance(SelectAllTerminalAction, SelectAllTerminalAction.ID, nls.localize('selectAll', "Select All")), + this._instantiationService.createInstance(TerminalPasteAction, TerminalPasteAction.ID, TerminalPasteAction.SHORT_LABEL), + this._instantiationService.createInstance(SelectAllTerminalAction, SelectAllTerminalAction.ID, SelectAllTerminalAction.LABEL), new Separator(), - this._instantiationService.createInstance(ClearTerminalAction, ClearTerminalAction.ID, nls.localize('clear', "Clear")) + this._instantiationService.createInstance(ClearTerminalAction, ClearTerminalAction.ID, ClearTerminalAction.LABEL) ]; this._contextMenuActions.forEach(a => { this._register(a); diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts index 5b865bd3a86..fb61123763f 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalService.ts @@ -13,7 +13,6 @@ import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { IQuickOpenService, IPickOpenEntry, IPickOptions } from 'vs/platform/quickOpen/common/quickOpen'; import { ITerminalInstance, ITerminalService, IShellLaunchConfig, ITerminalConfigHelper, NEVER_SUGGEST_SELECT_WINDOWS_SHELL_STORAGE_KEY, TERMINAL_PANEL_ID, ITerminalProcessExtHostProxy } from 'vs/workbench/parts/terminal/common/terminal'; import { TerminalService as AbstractTerminalService } from 'vs/workbench/parts/terminal/common/terminalService'; import { TerminalConfigHelper } from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper'; @@ -29,6 +28,7 @@ import { ipcRenderer as ipc } from 'electron'; import { IOpenFileRequest } from 'vs/platform/windows/common/windows'; import { TerminalInstance } from 'vs/workbench/parts/terminal/electron-browser/terminalInstance'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IQuickInputService, IQuickPickItem, IPickOptions } from 'vs/platform/quickinput/common/quickInput'; export class TerminalService extends AbstractTerminalService implements ITerminalService { private _configHelper: TerminalConfigHelper; @@ -47,7 +47,7 @@ export class TerminalService extends AbstractTerminalService implements ITermina @ILifecycleService lifecycleService: ILifecycleService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IQuickOpenService private readonly _quickOpenService: IQuickOpenService, + @IQuickInputService private readonly _quickInputService: IQuickInputService, @INotificationService private readonly _notificationService: INotificationService, @IDialogService private readonly _dialogService: IDialogService, @IExtensionService private readonly _extensionService: IExtensionService @@ -184,10 +184,10 @@ export class TerminalService extends AbstractTerminalService implements ITermina public selectDefaultWindowsShell(): TPromise { return this._detectWindowsShells().then(shells => { - const options: IPickOptions = { + const options: IPickOptions = { placeHolder: nls.localize('terminal.integrated.chooseWindowsShell', "Select your preferred terminal shell, you can change this later in your settings") }; - return this._quickOpenService.pick(shells, options).then(value => { + return this._quickInputService.pick(shells, options).then(value => { if (!value) { return null; } @@ -197,7 +197,7 @@ export class TerminalService extends AbstractTerminalService implements ITermina }); } - private _detectWindowsShells(): TPromise { + private _detectWindowsShells(): TPromise { // Determine the correct System32 path. We want to point to Sysnative // when the 32-bit version of VS Code is running on a 64-bit machine. // The reason for this is because PowerShell's important PSReadline @@ -231,7 +231,7 @@ export class TerminalService extends AbstractTerminalService implements ITermina Object.keys(expectedLocations).forEach(key => promises.push(this._validateShellPaths(key, expectedLocations[key]))); return TPromise.join(promises).then(results => { return results.filter(result => !!result).map(result => { - return { + return { label: result[0], description: result[1] }; diff --git a/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts b/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts index 4d2291134ef..dfa15ae450f 100644 --- a/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts +++ b/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts @@ -10,7 +10,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { Action } from 'vs/base/common/actions'; import { firstIndex } from 'vs/base/common/arrays'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { IQuickOpenService, IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen'; @@ -228,3 +228,21 @@ const developerCategory = localize('developer', "Developer"); const generateColorThemeDescriptor = new SyncActionDescriptor(GenerateColorThemeAction, GenerateColorThemeAction.ID, GenerateColorThemeAction.LABEL); Registry.as(Extensions.WorkbenchActions).registerWorkbenchAction(generateColorThemeDescriptor, 'Developer: Generate Color Theme From Current Settings', developerCategory); + +MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { + group: '4_themes', + command: { + id: SelectColorThemeAction.ID, + title: localize({ key: 'miSelectColorTheme', comment: ['&& denotes a mnemonic'] }, "&&Color Theme") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { + group: '4_themes', + command: { + id: SelectIconThemeAction.ID, + title: localize({ key: 'miSelectIconTheme', comment: ['&& denotes a mnemonic'] }, "File &&Icon Theme") + }, + order: 2 +}); diff --git a/src/vs/workbench/parts/update/electron-browser/media/update.contribution.css b/src/vs/workbench/parts/update/electron-browser/media/update.contribution.css index 4e602f932a2..54537cdcfa7 100644 --- a/src/vs/workbench/parts/update/electron-browser/media/update.contribution.css +++ b/src/vs/workbench/parts/update/electron-browser/media/update.contribution.css @@ -7,9 +7,3 @@ -webkit-mask: url('update.svg') no-repeat 50% 50%; -webkit-mask-size: 22px; } - -/* TODO@Ben this is a hack to overwrite the icon for release notes eitor */ -.file-icons-enabled .show-file-icons .release-notes-ext-file-icon.file-icon::before { - content: ' '; - background-image: url('code-icon.svg'); -} \ No newline at end of file diff --git a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts index b50ecda1765..c3c0cf180a6 100644 --- a/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts +++ b/src/vs/workbench/parts/update/electron-browser/releaseNotesEditor.ts @@ -99,6 +99,11 @@ export class ReleaseNotesManager { onDispose: () => { this._currentReleaseNotes = undefined; } }); + const iconPath = URI.parse(require.toUrl('./media/code-icon.svg')); + this._currentReleaseNotes.iconPath = { + light: iconPath, + dark: iconPath + }; this._currentReleaseNotes.html = html; } @@ -153,6 +158,13 @@ export class ReleaseNotesManager { if (!this._releaseNotesCache[version]) { this._releaseNotesCache[version] = this._requestService.request({ url }) .then(asText) + .then(text => { + if (!/^#\s/.test(text)) { // release notes always starts with `#` followed by whitespace + return TPromise.wrapError(new Error('Invalid release notes')); + } + + return TPromise.wrap(text); + }) .then(text => patchKeybindings(text)); } diff --git a/src/vs/workbench/parts/update/electron-browser/update.ts b/src/vs/workbench/parts/update/electron-browser/update.ts index 941351830b6..04edd26fd77 100644 --- a/src/vs/workbench/parts/update/electron-browser/update.ts +++ b/src/vs/workbench/parts/update/electron-browser/update.ts @@ -209,9 +209,28 @@ export class Win3264BitContribution implements IWorkbenchContribution { } } +async function isUserSetupInstalled(): Promise { + const rawUserAppId = process.arch === 'x64' ? product.win32x64UserAppId : product.win32UserAppId; + const userAppId = rawUserAppId.replace(/^\{\{/, '{'); + const Registry = await import('winreg'); + const key = new Registry({ + hive: Registry.HKCU, + key: `\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${userAppId}_is1` + }); + + try { + await new Promise((c, e) => key.get('', (err, result) => err ? e(err) : c(result))); + } catch (err) { + return false; + } + + return true; +} + export class WinUserSetupContribution implements IWorkbenchContribution { private static readonly KEY = 'update/win32-usersetup'; + private static readonly KEY_BOTH = 'update/win32-usersetup-both'; private static readonly STABLE_URL = 'https://vscode-update.azurewebsites.net/latest/win32-x64-user/stable'; private static readonly STABLE_URL_32BIT = 'https://vscode-update.azurewebsites.net/latest/win32-user/stable'; @@ -230,8 +249,34 @@ export class WinUserSetupContribution implements IWorkbenchContribution { @IOpenerService private openerService: IOpenerService, @IUpdateService private updateService: IUpdateService ) { - updateService.onStateChange(this.onUpdateStateChange, this, this.disposables); - this.onUpdateStateChange(this.updateService.state); + const neverShowAgain = new NeverShowAgain(WinUserSetupContribution.KEY_BOTH, this.storageService); + + if (!neverShowAgain.shouldShow()) { + return; + } + + isUserSetupInstalled().then(userSetupIsInstalled => { + if (!userSetupIsInstalled) { + updateService.onStateChange(this.onUpdateStateChange, this, this.disposables); + this.onUpdateStateChange(this.updateService.state); + return; + } + + const handle = this.notificationService.prompt( + severity.Warning, + nls.localize('usersetupsystem', "You are running the system-wide installation of {0}, while having the user-wide distribution installed as well. Make sure you're running the {0} version you expect.", product.nameShort), + [ + { label: nls.localize('ok', "OK"), run: () => null }, + { + label: nls.localize('neveragain', "Don't Show Again"), + isSecondary: true, + run: () => { + neverShowAgain.action.run(handle); + neverShowAgain.action.dispose(); + } + }] + ); + }); } private onUpdateStateChange(state: UpdateState): void { @@ -297,7 +342,7 @@ class CommandAction extends Action { export class UpdateContribution implements IGlobalActivity { private static readonly showCommandsId = 'workbench.action.showCommands'; - private static readonly openSettingsId = 'workbench.action.openSettings2'; + 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'; diff --git a/src/vs/workbench/parts/watermark/electron-browser/watermark.ts b/src/vs/workbench/parts/watermark/electron-browser/watermark.ts index 44449b32df2..0c2214ba8ef 100644 --- a/src/vs/workbench/parts/watermark/electron-browser/watermark.ts +++ b/src/vs/workbench/parts/watermark/electron-browser/watermark.ts @@ -24,9 +24,9 @@ import { ShowAllCommandsAction } from 'vs/workbench/parts/quickopen/browser/comm import { Parts, IPartService, IDimension } from 'vs/workbench/services/part/common/partService'; import { StartAction } from 'vs/workbench/parts/debug/browser/debugActions'; import { FindInFilesActionId } from 'vs/workbench/parts/search/common/constants'; -import { ToggleTerminalAction } from 'vs/workbench/parts/terminal/electron-browser/terminalActions'; import { escape } from 'vs/base/common/strings'; import { QUICKOPEN_ACTION_ID } from 'vs/workbench/browser/parts/quickopen/quickopen'; +import { TERMINAL_COMMAND_ID } from 'vs/workbench/parts/terminal/common/terminalCommands'; interface WatermarkEntry { text: string; @@ -68,7 +68,7 @@ const newUntitledFile: WatermarkEntry = { const newUntitledFileMacOnly: WatermarkEntry = assign({ mac: true }, newUntitledFile); const toggleTerminal: WatermarkEntry = { text: nls.localize({ key: 'watermark.toggleTerminal', comment: ['toggle is a verb here'] }, "Toggle Terminal"), - ids: [ToggleTerminalAction.ID] + ids: [TERMINAL_COMMAND_ID.TOGGLE] }; const findInFiles: WatermarkEntry = { diff --git a/src/vs/workbench/parts/webview/electron-browser/baseWebviewEditor.ts b/src/vs/workbench/parts/webview/electron-browser/baseWebviewEditor.ts index aca88aff9df..7cbbbb07f67 100644 --- a/src/vs/workbench/parts/webview/electron-browser/baseWebviewEditor.ts +++ b/src/vs/workbench/parts/webview/electron-browser/baseWebviewEditor.ts @@ -77,4 +77,10 @@ export abstract class BaseWebviewEditor extends BaseEditor { this._webview.focus(); } } + + public selectAll(): void { + if (this._webview) { + this._webview.selectAll(); + } + } } diff --git a/src/vs/workbench/parts/webview/electron-browser/webview.contribution.ts b/src/vs/workbench/parts/webview/electron-browser/webview.contribution.ts index 3b34b5184ee..45e3dab5b9a 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webview.contribution.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webview.contribution.ts @@ -9,14 +9,14 @@ import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor'; 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/parts/webview/electron-browser/webviewEditorInputFactory'; import { KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE } from './baseWebviewEditor'; -import { HideWebViewEditorFindCommand, OpenWebviewDeveloperToolsAction, ReloadWebviewAction, ShowWebViewEditorFindWidgetCommand } from './webviewCommands'; +import { HideWebViewEditorFindCommand, OpenWebviewDeveloperToolsAction, ReloadWebviewAction, ShowWebViewEditorFindWidgetCommand, SelectAllWebviewEditorCommand } from './webviewCommands'; import { WebviewEditor } from './webviewEditor'; import { WebviewEditorInput } from './webviewEditorInput'; import { IWebviewEditorService, WebviewEditorService } from './webviewEditorService'; @@ -42,10 +42,11 @@ const showNextFindWdigetCommand = new ShowWebViewEditorFindWidgetCommand({ id: ShowWebViewEditorFindWidgetCommand.ID, precondition: KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS, kbOpts: { - primary: KeyMod.CtrlCmd | KeyCode.KEY_F + primary: KeyMod.CtrlCmd | KeyCode.KEY_F, + weight: KeybindingWeight.EditorContrib } }); -KeybindingsRegistry.registerCommandAndKeybindingRule(showNextFindWdigetCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); +showNextFindWdigetCommand.register(); const hideCommand = new HideWebViewEditorFindCommand({ id: HideWebViewEditorFindCommand.ID, @@ -53,11 +54,21 @@ const hideCommand = new HideWebViewEditorFindCommand({ KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE), kbOpts: { - primary: KeyCode.Escape + primary: KeyCode.Escape, + weight: KeybindingWeight.EditorContrib } }); -KeybindingsRegistry.registerCommandAndKeybindingRule(hideCommand.toCommandAndKeybindingRule(KeybindingsRegistry.WEIGHT.editorContrib())); +hideCommand.register(); +const selectAllCommand = new SelectAllWebviewEditorCommand({ + id: SelectAllWebviewEditorCommand.ID, + precondition: KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS, + kbOpts: { + primary: KeyMod.CtrlCmd | KeyCode.KEY_A, + weight: KeybindingWeight.EditorContrib + } +}); +selectAllCommand.register(); actionRegistry.registerWorkbenchAction( new SyncActionDescriptor(OpenWebviewDeveloperToolsAction, OpenWebviewDeveloperToolsAction.ID, OpenWebviewDeveloperToolsAction.LABEL), diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewCommands.ts b/src/vs/workbench/parts/webview/electron-browser/webviewCommands.ts index c30d4fa8c85..5866fcdbec6 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewCommands.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewCommands.ts @@ -33,6 +33,17 @@ export class HideWebViewEditorFindCommand extends Command { } } +export class SelectAllWebviewEditorCommand extends Command { + public static readonly ID = 'editor.action.webvieweditor.selectAll'; + + public runCommand(accessor: ServicesAccessor, args: any): void { + const webViewEditor = getActiveWebviewEditor(accessor); + if (webViewEditor) { + webViewEditor.selectAll(); + } + } +} + export class OpenWebviewDeveloperToolsAction extends Action { static readonly ID = 'workbench.action.webview.openDeveloperTools'; static readonly LABEL = nls.localize('openToolsLabel', "Open Webview Developer Tools"); diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewEditor.ts b/src/vs/workbench/parts/webview/electron-browser/webviewEditor.ts index 82252859c77..8eb67ef6b68 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewEditor.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewEditor.ts @@ -5,6 +5,7 @@ import * as DOM from 'vs/base/browser/dom'; import { domEvent } from 'vs/base/browser/event'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import URI from 'vs/base/common/uri'; @@ -14,13 +15,12 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { EditorOptions } from 'vs/workbench/common/editor'; -import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; import { WebviewEditorInput } from 'vs/workbench/parts/webview/electron-browser/webviewEditorInput'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; import { BaseWebviewEditor, KEYBINDING_CONTEXT_WEBVIEWEDITOR_FIND_WIDGET_INPUT_FOCUSED, KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE } from './baseWebviewEditor'; import { WebviewElement } from './webviewElement'; -import { CancellationToken } from 'vs/base/common/cancellation'; export class WebviewEditor extends BaseWebviewEditor { diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewEditorInput.ts b/src/vs/workbench/parts/webview/electron-browser/webviewEditorInput.ts index 6d48bf36101..912f937e607 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewEditorInput.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewEditorInput.ts @@ -2,23 +2,59 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import * as dom from 'vs/base/browser/dom'; +import { Emitter } from 'vs/base/common/event'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { IEditorModel } from 'vs/platform/editor/common/editor'; -import { EditorInput, EditorModel, IEditorInput, GroupIdentifier } from 'vs/workbench/common/editor'; +import { EditorInput, EditorModel, GroupIdentifier, IEditorInput } from 'vs/workbench/common/editor'; import { IPartService, Parts } from 'vs/workbench/services/part/common/partService'; +import * as vscode from 'vscode'; import { WebviewEvents, WebviewInputOptions, WebviewReviver } from './webviewEditorService'; import { WebviewElement } from './webviewElement'; -import * as vscode from 'vscode'; + export class WebviewEditorInput extends EditorInput { private static handlePool = 0; + + private static _styleElement?: HTMLStyleElement; + + private static _icons = new Map(); + + private static updateStyleElement( + id: number, + iconPath: { light: URI, dark: URI } | undefined + ) { + if (!this._styleElement) { + this._styleElement = dom.createStyleSheet(); + this._styleElement.className = 'webview-icons'; + } + + if (!iconPath) { + this._icons.delete(id); + } else { + this._icons.set(id, iconPath); + } + + const cssRules: string[] = []; + this._icons.forEach((value, key) => { + const webviewSelector = `.show-file-icons .webview-${key}-name-file-icon::before`; + if (URI.isUri(value)) { + cssRules.push(`${webviewSelector} { content: ""; background-image: url(${value.toString()}); }`); + } else { + cssRules.push(`${webviewSelector} { content: ""; background-image: url(${value.light.toString()}); }`); + cssRules.push(`.vs-dark ${webviewSelector} { content: ""; background-image: url(${value.dark.toString()}); }`); + } + }); + this._styleElement.innerHTML = cssRules.join('\n'); + } + public static readonly typeId = 'workbench.editors.webviewInput'; private _name: string; + private _iconPath?: { light: URI, dark: URI }; private _options: WebviewInputOptions; private _html: string = ''; private _currentWebviewHtml: string = ''; @@ -35,6 +71,7 @@ export class WebviewEditorInput extends EditorInput { private _revived: boolean = false; public readonly extensionLocation: URI | undefined; + private readonly _id: number; constructor( public readonly viewType: string, @@ -47,6 +84,7 @@ export class WebviewEditorInput extends EditorInput { @IPartService private readonly _partService: IPartService, ) { super(); + this._id = WebviewEditorInput.handlePool++; this._name = name; this._options = options; this._events = events; @@ -58,6 +96,13 @@ export class WebviewEditorInput extends EditorInput { return WebviewEditorInput.typeId; } + public getId(): number { + return this._id; + } + + private readonly _onDidChangeIcon = this._register(new Emitter()); + public readonly onDidChangeIcon = this._onDidChangeIcon.event; + public dispose() { this.disposeWebview(); @@ -76,7 +121,10 @@ export class WebviewEditorInput extends EditorInput { } public getResource(): URI { - return null; + return URI.from({ + scheme: 'webview-panel', + path: `webview-panel/webview-${this._id}` + }); } public getName(): string { @@ -96,6 +144,15 @@ export class WebviewEditorInput extends EditorInput { this._onDidChangeLabel.fire(); } + public get iconPath() { + return this._iconPath; + } + + public set iconPath(value: { light: URI, dark: URI } | undefined) { + this._iconPath = value; + WebviewEditorInput.updateStyleElement(this._id, value); + } + public matches(other: IEditorInput): boolean { return other && other === this; } @@ -154,7 +211,7 @@ export class WebviewEditorInput extends EditorInput { } } - public resolve(): TPromise { + public resolve(): TPromise { if (this.reviver && !this._revived) { this._revived = true; return this.reviver.reviveWebview(this).then(() => new EditorModel()); @@ -169,9 +226,8 @@ export class WebviewEditorInput extends EditorInput { public get container(): HTMLElement { if (!this._container) { - const id = WebviewEditorInput.handlePool++; this._container = document.createElement('div'); - this._container.id = `webview-${id}`; + this._container.id = `webview-${this._id}`; this._partService.getContainer(Parts.EDITOR_PART).appendChild(this._container); } return this._container; diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewEditorInputFactory.ts b/src/vs/workbench/parts/webview/electron-browser/webviewEditorInputFactory.ts index 094c71d62a2..fc973d68479 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewEditorInputFactory.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewEditorInputFactory.ts @@ -13,12 +13,9 @@ interface SerializedWebview { readonly viewType: string; readonly title: string; readonly options: WebviewInputOptions; - /** - * compatibility with previous versions - */ - readonly extensionFolderPath?: string; readonly extensionLocation: string; readonly state: any; + readonly iconPath: { light: string, dark: string } | undefined; } export class WebviewEditorInputFactory implements IEditorInputFactory { @@ -47,24 +44,19 @@ export class WebviewEditorInputFactory implements IEditorInputFactory { title: input.getName(), options: input.options, extensionLocation: input.extensionLocation.toString(), - state: input.state + state: input.state, + iconPath: input.iconPath ? { light: input.iconPath.light.toString(), dark: input.iconPath.dark.toString(), } : undefined, }; return JSON.stringify(data); } public deserialize( - instantiationService: IInstantiationService, + _instantiationService: IInstantiationService, serializedEditorInput: string ): WebviewEditorInput { const data: SerializedWebview = JSON.parse(serializedEditorInput); - let extensionLocation: URI; - if (typeof data.extensionLocation === 'string') { - extensionLocation = URI.parse(data.extensionLocation); - } - if (typeof data.extensionFolderPath === 'string') { - // compatibility with previous versions - extensionLocation = URI.file(data.extensionFolderPath); - } - return this._webviewService.reviveWebview(data.viewType, data.title, data.state, data.options, extensionLocation); + const extensionLocation = URI.parse(data.extensionLocation); + const iconPath = data.iconPath ? { light: URI.parse(data.iconPath.light), dark: URI.parse(data.iconPath.dark) } : undefined; + return this._webviewService.reviveWebview(data.viewType, data.title, iconPath, data.state, data.options, extensionLocation); } } diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewEditorService.ts b/src/vs/workbench/parts/webview/electron-browser/webviewEditorService.ts index 3c4228078b6..99695243653 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewEditorService.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewEditorService.ts @@ -36,6 +36,7 @@ export interface IWebviewEditorService { reviveWebview( viewType: string, title: string, + iconPath: { light: URI, dark: URI } | undefined, state: any, options: WebviewInputOptions, extensionLocation: URI @@ -126,6 +127,7 @@ export class WebviewEditorService implements IWebviewEditorService { reviveWebview( viewType: string, title: string, + iconPath: { light: URI, dark: URI } | undefined, state: any, options: WebviewInputOptions, extensionLocation: URI @@ -148,7 +150,7 @@ export class WebviewEditorService implements IWebviewEditorService { }); } }); - + webviewInput.iconPath = iconPath; return webviewInput; } diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts b/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts index a97e8f8fd1c..a82c36d8793 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts @@ -6,9 +6,6 @@ import { addClass, addDisposableListener } from 'vs/base/browser/dom'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { getMediaMime, guessMimeTypes } from 'vs/base/common/mime'; -import { extname, nativeSep } from 'vs/base/common/paths'; -import { startsWith } from 'vs/base/common/strings'; import URI from 'vs/base/common/uri'; import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -16,8 +13,9 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import * as colorRegistry from 'vs/platform/theme/common/colorRegistry'; import { DARK, ITheme, IThemeService, LIGHT } from 'vs/platform/theme/common/themeService'; -import { WebviewFindWidget } from './webviewFindWidget'; +import { registerFileProtocol, WebviewProtocol } from 'vs/workbench/parts/webview/electron-browser/webviewProtocols'; import { areWebviewInputOptionsEqual } from './webviewEditorService'; +import { WebviewFindWidget } from './webviewFindWidget'; export interface WebviewOptions { readonly allowScripts?: boolean; @@ -28,9 +26,6 @@ export interface WebviewOptions { readonly localResourceRoots?: ReadonlyArray; } -const CORE_RESOURCE_PROTOCOL = 'vscode-core-resource'; -const VSCODE_RESOURCE_PROTOCOL = 'vscode-resource'; - export class WebviewElement extends Disposable { private _webview: Electron.WebviewTag; private _ready: Promise; @@ -376,11 +371,11 @@ export class WebviewElement extends Disposable { const appRootUri = URI.file(this._environmentService.appRoot); - registerFileProtocol(contents, CORE_RESOURCE_PROTOCOL, this._fileService, () => [ + registerFileProtocol(contents, WebviewProtocol.CoreResource, this._fileService, () => [ appRootUri ]); - registerFileProtocol(contents, VSCODE_RESOURCE_PROTOCOL, this._fileService, () => + registerFileProtocol(contents, WebviewProtocol.VsCodeResource, this._fileService, () => (this._options.localResourceRoots || []) ); } @@ -443,6 +438,10 @@ export class WebviewElement extends Disposable { public reload() { this.contents = this._contents; } + + public selectAll() { + this._webview.selectAll(); + } } @@ -463,44 +462,3 @@ namespace ApiThemeClassName { } } } - -function registerFileProtocol( - contents: Electron.WebContents, - protocol: string, - fileService: IFileService, - 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 + nativeSep)) { - fileService.resolveContent(normalizedPath, { encoding: 'binary' }).then(contents => { - const mime = getMimeType(normalizedPath); - callback({ - data: Buffer.from(contents.value, contents.encoding), - mimeType: mime - }); - }, () => { - callback({ error: -2 /* FAILED: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ }); - }); - return; - } - } - 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 */ }); - }, (error) => { - if (error) { - console.error('Failed to register protocol ' + protocol); - } - }); -} - -const webviewMimeTypes = { - '.svg': 'image/svg+xml' -}; - -function getMimeType(normalizedPath: URI) { - const ext = extname(normalizedPath.fsPath).toLowerCase(); - return webviewMimeTypes[ext] || getMediaMime(normalizedPath.fsPath) || guessMimeTypes(normalizedPath.fsPath)[0]; -} diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewFindWidget.ts b/src/vs/workbench/parts/webview/electron-browser/webviewFindWidget.ts index 3529b7b8a10..4b0a2c6bbaa 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewFindWidget.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewFindWidget.ts @@ -11,7 +11,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; export class WebviewFindWidget extends SimpleFindWidget { constructor( - private webview: WebviewElement, + private _webview: WebviewElement, @IContextViewService contextViewService: IContextViewService, @IContextKeyService contextKeyService: IContextKeyService ) { @@ -19,45 +19,45 @@ export class WebviewFindWidget extends SimpleFindWidget { } dispose() { - this.webview = undefined; + this._webview = undefined; super.dispose(); } public find(previous: boolean) { const val = this.inputValue; if (val) { - this.webview.find(val, { findNext: true, forward: !previous }); + this._webview.find(val, { findNext: true, forward: !previous }); } } public hide() { super.hide(); - this.webview.stopFind(true); - this.webview.focus(); + this._webview.stopFind(true); + this._webview.focus(); } public onInputChanged() { const val = this.inputValue; if (val) { - this.webview.startFind(val); + this._webview.startFind(val); } else { - this.webview.stopFind(false); + this._webview.stopFind(false); } } protected onFocusTrackerFocus() { - this.webview.notifyFindWidgetFocusChanged(true); + this._webview.notifyFindWidgetFocusChanged(true); } protected onFocusTrackerBlur() { - this.webview.notifyFindWidgetFocusChanged(false); + this._webview.notifyFindWidgetFocusChanged(false); } protected onFindInputFocusTrackerFocus() { - this.webview.notifyFindWidgetInputFocusChanged(true); + this._webview.notifyFindWidgetInputFocusChanged(true); } protected onFindInputFocusTrackerBlur() { - this.webview.notifyFindWidgetInputFocusChanged(false); + this._webview.notifyFindWidgetInputFocusChanged(false); } } \ No newline at end of file diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewProtocols.ts b/src/vs/workbench/parts/webview/electron-browser/webviewProtocols.ts new file mode 100644 index 00000000000..0fa492620f8 --- /dev/null +++ b/src/vs/workbench/parts/webview/electron-browser/webviewProtocols.ts @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { extname } from 'path'; +import { getMediaMime, guessMimeTypes } from 'vs/base/common/mime'; +import { nativeSep } from 'vs/base/common/paths'; +import { startsWith } from 'vs/base/common/strings'; +import URI from 'vs/base/common/uri'; +import { IFileService } from 'vs/platform/files/common/files'; + +export enum WebviewProtocol { + CoreResource = 'vscode-core-resource', + VsCodeResource = 'vscode-resource' +} + +export function registerFileProtocol( + contents: Electron.WebContents, + protocol: WebviewProtocol, + fileService: IFileService, + 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 + nativeSep)) { + fileService.resolveContent(normalizedPath, { encoding: 'binary' }).then(contents => { + const mime = getMimeType(normalizedPath); + callback({ + data: Buffer.from(contents.value, contents.encoding), + mimeType: mime + }); + }, () => { + callback({ error: -2 /* FAILED: https://cs.chromium.org/chromium/src/net/base/net_error_list.h */ }); + }); + return; + } + } + 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 */ }); + }, (error) => { + if (error) { + console.error('Failed to register protocol ' + protocol); + } + }); +} + +const webviewMimeTypes = { + '.svg': 'image/svg+xml' +}; + +function getMimeType(normalizedPath: URI) { + const ext = extname(normalizedPath.fsPath).toLowerCase(); + return webviewMimeTypes[ext] || getMediaMime(normalizedPath.fsPath) || guessMimeTypes(normalizedPath.fsPath)[0]; +} diff --git a/src/vs/workbench/parts/welcome/gettingStarted/electron-browser/telemetryOptOut.ts b/src/vs/workbench/parts/welcome/gettingStarted/electron-browser/telemetryOptOut.ts index 49a9d309bbf..62b0d5e5916 100644 --- a/src/vs/workbench/parts/welcome/gettingStarted/electron-browser/telemetryOptOut.ts +++ b/src/vs/workbench/parts/welcome/gettingStarted/electron-browser/telemetryOptOut.ts @@ -14,45 +14,132 @@ 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/parts/experiments/node/experimentService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { language, locale } from 'vs/base/common/platform'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; export class TelemetryOptOut implements IWorkbenchContribution { private static TELEMETRY_OPT_OUT_SHOWN = 'workbench.telemetryOptOutShown'; + private privacyUrl: string; + private optOutUrl: string; constructor( @IStorageService storageService: IStorageService, @IOpenerService openerService: IOpenerService, - @INotificationService notificationService: INotificationService, + @INotificationService private notificationService: INotificationService, @IWindowService windowService: IWindowService, @IWindowsService windowsService: IWindowsService, - @ITelemetryService telemetryService: ITelemetryService + @ITelemetryService private telemetryService: ITelemetryService, + @IExperimentService private experimentService: IExperimentService, + @IConfigurationService private configurationService: IConfigurationService, + @IExtensionGalleryService private galleryService: IExtensionGalleryService ) { if (!product.telemetryOptOutUrl || storageService.get(TelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN)) { return; } + const experimentId = 'telemetryOptOut'; Promise.all([ windowService.isFocused(), - windowsService.getWindowCount() - ]).then(([focused, count]) => { + windowsService.getWindowCount(), + experimentService.getExperimentById(experimentId) + ]).then(([focused, count, experimentState]) => { if (!focused && count > 1) { return null; } storageService.store(TelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, true); - const optOutUrl = product.telemetryOptOutUrl; - const privacyUrl = product.privacyStatementUrl || product.telemetryOptOutUrl; - const optOutNotice = localize('telemetryOptOut.optOutNotice', "Help improve VS Code by allowing Microsoft to collect usage data. Read our [privacy statement]({0}) and learn how to [opt out]({1}).", privacyUrl, optOutUrl); - const optInNotice = localize('telemetryOptOut.optInNotice', "Help improve VS Code by allowing Microsoft to collect usage data. Read our [privacy statement]({0}) and learn how to [opt in]({1}).", privacyUrl, optOutUrl); + this.optOutUrl = product.telemetryOptOutUrl; + this.privacyUrl = product.privacyStatementUrl || product.telemetryOptOutUrl; + + if (experimentState && experimentState.state === ExperimentState.Run && telemetryService.isOptedIn) { + this.runExperiment(experimentId); + return; + } + + const optOutNotice = localize('telemetryOptOut.optOutNotice', "Help improve VS Code by allowing Microsoft to collect usage data. Read our [privacy statement]({0}) and learn how to [opt out]({1}).", this.privacyUrl, this.optOutUrl); + const optInNotice = localize('telemetryOptOut.optInNotice', "Help improve VS Code by allowing Microsoft to collect usage data. Read our [privacy statement]({0}) and learn how to [opt in]({1}).", this.privacyUrl, this.optOutUrl); notificationService.prompt( Severity.Info, telemetryService.isOptedIn ? optOutNotice : optInNotice, [{ label: localize('telemetryOptOut.readMore', "Read More"), - run: () => openerService.open(URI.parse(optOutUrl)) + run: () => openerService.open(URI.parse(this.optOutUrl)) }] ); }) .then(null, onUnexpectedError); } + + private runExperiment(experimentId: string) { + const promptMessageKey = 'telemetryOptOut.optOutOption'; + const yesLabelKey = 'telemetryOptOut.OptIn'; + const noLabelKey = 'telemetryOptOut.OptOut'; + + let promptMessage = localize('telemetryOptOut.optOutOption', "Please help Microsoft improve Visual Studio Code by allowing the collection of usage data. Read our [privacy statement]({0}) for more details.", this.privacyUrl); + let yesLabel = localize('telemetryOptOut.OptIn', "Yes, glad to help"); + let noLabel = localize('telemetryOptOut.OptOut', "No, thanks"); + + let queryPromise = TPromise.as(undefined); + if ((locale !== language && locale !== 'en' && locale.indexOf('en-') === -1)) { + queryPromise = this.galleryService.query({ text: `tag:lp-${locale}` }).then(tagResult => { + if (!tagResult || !tagResult.total) { + return undefined; + } + const extensionToFetchTranslationsFrom = tagResult.firstPage.filter(e => e.publisher === 'MS-CEINTL' && e.name.indexOf('vscode-language-pack') === 0)[0] || tagResult.firstPage[0]; + if (!extensionToFetchTranslationsFrom.assets || !extensionToFetchTranslationsFrom.assets.coreTranslations) { + return undefined; + } + + return this.galleryService.getCoreTranslation(extensionToFetchTranslationsFrom, locale) + .then(translation => { + const translationsFromPack = translation && translation.contents ? translation.contents['vs/workbench/parts/welcome/gettingStarted/electron-browser/telemetryOptOut'] : {}; + if (!!translationsFromPack[promptMessageKey] && !!translationsFromPack[yesLabelKey] && !!translationsFromPack[noLabelKey]) { + promptMessage = translationsFromPack[promptMessageKey].replace('{0}', this.privacyUrl) + ' (Please help Microsoft improve Visual Studio Code by allowing the collection of usage data.)'; + yesLabel = translationsFromPack[yesLabelKey] + ' (Yes)'; + noLabel = translationsFromPack[noLabelKey] + ' (No)'; + } + return undefined; + }); + + }); + } + + const logTelemetry = (optout?: boolean) => { + /* __GDPR__ + "experiments:optout" : { + "optOut": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ + this.telemetryService.publicLog('experiments:optout', typeof optout === 'boolean' ? { optout } : {}); + }; + + queryPromise.then(() => { + this.notificationService.prompt( + Severity.Info, + promptMessage, + [ + { + label: yesLabel, + run: () => { + logTelemetry(false); + } + }, + { + label: noLabel, + run: () => { + logTelemetry(true); + this.configurationService.updateValue('telemetry.enableTelemetry', false); + this.configurationService.updateValue('telemetry.enableCrashReporter', false); + } + } + ], + logTelemetry + ); + this.experimentService.markAsCompleted(experimentId); + }); + } } diff --git a/src/vs/workbench/parts/welcome/overlay/browser/media/commandpalette.svg b/src/vs/workbench/parts/welcome/overlay/browser/media/commandpalette.svg index 56e91eb8410..5d4d668d107 100644 --- a/src/vs/workbench/parts/welcome/overlay/browser/media/commandpalette.svg +++ b/src/vs/workbench/parts/welcome/overlay/browser/media/commandpalette.svg @@ -1 +1 @@ -Asset 9 \ No newline at end of file +Asset 9 \ No newline at end of file diff --git a/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.css b/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.css index bc843e58ab3..e76b3e08093 100644 --- a/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.css +++ b/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.css @@ -97,6 +97,12 @@ left: 45px; } +.monaco-workbench > .welcomeOverlay > .key.terminal { + position: absolute; + bottom: 25px; + left: 50%; +} + .monaco-workbench > .welcomeOverlay > .key.notifications { position: absolute; bottom: 25px; diff --git a/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.ts b/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.ts index 8bff80b2110..9fbcb9ac51f 100644 --- a/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.ts +++ b/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.ts @@ -28,7 +28,7 @@ import { Color } from 'vs/base/common/color'; interface Key { id: string; - arrow: string; + arrow?: string; label: string; command?: string; arrowLast?: boolean; @@ -78,6 +78,11 @@ const keys: Key[] = [ label: localize('welcomeOverlay.problems', "View errors and warnings"), command: 'workbench.actions.view.problems' }, + { + id: 'terminal', + label: localize('welcomeOverlay.terminal', "Toggle integrated terminal"), + command: 'workbench.action.terminal.toggleTerminal' + }, // { // id: 'openfile', // arrow: '⤸', @@ -182,7 +187,7 @@ class WelcomeOverlay { keys.filter(key => !('withEditor' in key) || key.withEditor === editorOpen) .forEach(({ id, arrow, label, command, arrowLast }) => { const div = $(this._overlay).div({ 'class': ['key', id] }); - if (!arrowLast) { + if (arrow && !arrowLast) { $(div).span({ 'class': 'arrow' }).innerHtml(arrow); } $(div).span({ 'class': 'label' }).text(label); @@ -192,7 +197,7 @@ class WelcomeOverlay { $(div).span({ 'class': 'shortcut' }).text(shortcut.getLabel()); } } - if (arrowLast) { + if (arrow && arrowLast) { $(div).span({ 'class': 'arrow' }).innerHtml(arrow); } }); diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts b/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts index 27e73025e43..56ffc97bcb6 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts @@ -55,11 +55,11 @@ export default () => ` diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.ts b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.ts index 2e5f8844e43..99341e7cabe 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.ts @@ -9,7 +9,7 @@ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } fr import { Registry } from 'vs/platform/registry/common/platform'; import { WelcomePageContribution, WelcomePageAction, WelcomeInputFactory } from 'vs/workbench/parts/welcome/page/electron-browser/welcomePage'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; @@ -26,10 +26,10 @@ Registry.as(ConfigurationExtensions.Configuration) 'enumDescriptions': [ localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.none' }, "Start without an editor."), localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.welcomePage' }, "Open the Welcome page (default)."), - localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.newUntitledFile' }, "Open a new untitled file."), + localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.startupEditor.newUntitledFile' }, "Open a new untitled file (only applies when opening an empty workspace)."), ], 'default': 'welcomePage', - 'description': localize('workbench.startupEditor', "Controls which editor is shown at startup, if none is restored from the previous session. Select 'none' to start without an editor, 'welcomePage' to open the Welcome page (default), 'newUntitledFile' to open a new untitled file (only opening an empty workspace).") + 'description': localize('workbench.startupEditor', "Controls which editor is shown at startup, if none are restored from the previous session.") }, } }); @@ -41,3 +41,12 @@ Registry.as(ActionExtensions.WorkbenchActions) .registerWorkbenchAction(new SyncActionDescriptor(WelcomePageAction, WelcomePageAction.ID, WelcomePageAction.LABEL), 'Help: Welcome', localize('help', "Help")); Registry.as(EditorExtensions.EditorInputFactories).registerEditorInputFactory(WelcomeInputFactory.ID, WelcomeInputFactory); + +MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '1_welcome', + command: { + id: 'workbench.action.showWelcomePage', + title: localize({ key: 'miWelcome', comment: ['&& denotes a mnemonic'] }, "&&Welcome") + }, + order: 1 +}); diff --git a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts index 55cc22df61d..ad2032a68ca 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts @@ -34,12 +34,13 @@ import { registerColor, focusBorder, textLinkForeground, textLinkActiveForegroun import { getExtraColor } from 'vs/workbench/parts/welcome/walkThrough/node/walkThroughUtils'; import { IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { IWorkspaceIdentifier, getWorkspaceLabel, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, getWorkspaceLabel, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IEditorInputFactory, EditorInput } from 'vs/workbench/common/editor'; import { getIdAndVersionFromLocalExtensionId } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { TimeoutTimer } from 'vs/base/common/async'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; used(); @@ -225,6 +226,7 @@ class WelcomePage { @IWorkspaceContextService private contextService: IWorkspaceContextService, @IConfigurationService private configurationService: IConfigurationService, @IEnvironmentService private environmentService: IEnvironmentService, + @IUriDisplayService private uriDisplayService: IUriDisplayService, @INotificationService private notificationService: INotificationService, @IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService, @IExtensionGalleryService private extensionGalleryService: IExtensionGalleryService, @@ -278,33 +280,40 @@ class WelcomePage { const before = ul.firstElementChild; workspaces.slice(0, 5).forEach(workspace => { let label: string; - let parent: string; - let wsPath: string; + let resource: URI; if (isSingleFolderWorkspaceIdentifier(workspace)) { - label = getBaseLabel(workspace); - parent = path.dirname(workspace); - wsPath = workspace; + resource = workspace; + label = getWorkspaceLabel(workspace, this.environmentService, this.uriDisplayService); + } else if (isWorkspaceIdentifier(workspace)) { + label = getWorkspaceLabel(workspace, this.environmentService, this.uriDisplayService); + resource = URI.file(workspace.configPath); } else { - label = getWorkspaceLabel(workspace, this.environmentService); - parent = path.dirname(workspace.configPath); - wsPath = workspace.configPath; + label = getBaseLabel(workspace); + resource = URI.file(workspace); } const li = document.createElement('li'); const a = document.createElement('a'); let name = label; - let parentFolder = parent; - if (!name && parentFolder) { - const tmp = name; - name = parentFolder; - parentFolder = tmp; + let parentFolderPath: string; + + if (resource.scheme === Schemas.file) { + let parentFolder = path.dirname(resource.fsPath); + if (!name && parentFolder) { + const tmp = name; + name = parentFolder; + parentFolder = tmp; + } + parentFolderPath = tildify(parentFolder, this.environmentService.userHome); + } else { + parentFolderPath = this.uriDisplayService.getLabel(resource); } - const tildifiedParentFolder = tildify(parentFolder, this.environmentService.userHome); + a.innerText = name; a.title = label; - a.setAttribute('aria-label', localize('welcomePage.openFolderWithPath', "Open folder {0} with path {1}", name, tildifiedParentFolder)); + a.setAttribute('aria-label', localize('welcomePage.openFolderWithPath', "Open folder {0} with path {1}", name, parentFolderPath)); a.href = 'javascript:void(0)'; a.addEventListener('click', e => { /* __GDPR__ @@ -317,7 +326,7 @@ class WelcomePage { id: 'openRecentFolder', from: telemetryFrom }); - this.windowService.openWindow([wsPath], { forceNewWindow: e.ctrlKey || e.metaKey }); + this.windowService.openWindow([resource], { forceNewWindow: e.ctrlKey || e.metaKey }); e.preventDefault(); e.stopPropagation(); }); @@ -326,7 +335,7 @@ class WelcomePage { const span = document.createElement('span'); span.classList.add('path'); span.classList.add('detail'); - span.innerText = tildifiedParentFolder; + span.innerText = parentFolderPath; span.title = label; li.appendChild(span); diff --git a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.ts b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.ts index 335ba65618e..f0397cfb446 100644 --- a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.ts +++ b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution.ts @@ -14,7 +14,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { IEditorRegistry, Extensions as EditorExtensions, EditorDescriptor } from 'vs/workbench/browser/editor'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; @@ -48,3 +48,12 @@ KeybindingsRegistry.registerCommandAndKeybindingRule(WalkThroughArrowDown); KeybindingsRegistry.registerCommandAndKeybindingRule(WalkThroughPageUp); KeybindingsRegistry.registerCommandAndKeybindingRule(WalkThroughPageDown); + +MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '1_welcome', + command: { + id: 'workbench.action.showInteractivePlayground', + title: localize({ key: 'miInteractivePlayground', comment: ['&& denotes a mnemonic'] }, "&&Interactive Playground") + }, + order: 2 +}); \ No newline at end of file diff --git a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.ts b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.ts index 5a838046aa8..6fc211d61f6 100644 --- a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.ts +++ b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughActions.ts @@ -6,14 +6,14 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { WalkThroughPart, WALK_THROUGH_FOCUS } from 'vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart'; -import { ICommandAndKeybindingRule, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { ICommandAndKeybindingRule, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeyCode } from 'vs/base/common/keyCodes'; export const WalkThroughArrowUp: ICommandAndKeybindingRule = { id: 'workbench.action.interactivePlayground.arrowUp', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(WALK_THROUGH_FOCUS, EditorContextKeys.editorTextFocus.toNegated()), primary: KeyCode.UpArrow, handler: accessor => { @@ -27,7 +27,7 @@ export const WalkThroughArrowUp: ICommandAndKeybindingRule = { export const WalkThroughArrowDown: ICommandAndKeybindingRule = { id: 'workbench.action.interactivePlayground.arrowDown', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(WALK_THROUGH_FOCUS, EditorContextKeys.editorTextFocus.toNegated()), primary: KeyCode.DownArrow, handler: accessor => { @@ -41,7 +41,7 @@ export const WalkThroughArrowDown: ICommandAndKeybindingRule = { export const WalkThroughPageUp: ICommandAndKeybindingRule = { id: 'workbench.action.interactivePlayground.pageUp', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(WALK_THROUGH_FOCUS, EditorContextKeys.editorTextFocus.toNegated()), primary: KeyCode.PageUp, handler: accessor => { @@ -55,7 +55,7 @@ export const WalkThroughPageUp: ICommandAndKeybindingRule = { export const WalkThroughPageDown: ICommandAndKeybindingRule = { id: 'workbench.action.interactivePlayground.pageDown', - weight: KeybindingsRegistry.WEIGHT.workbenchContrib(), + weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(WALK_THROUGH_FOCUS, EditorContextKeys.editorTextFocus.toNegated()), primary: KeyCode.PageDown, handler: accessor => { diff --git a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts index 0c8665ff148..625b1675d72 100644 --- a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts +++ b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.ts @@ -10,7 +10,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 } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, toDisposable } 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'; @@ -117,7 +117,7 @@ export class WalkThroughPart extends BaseEditor { private addEventListener(element: E, type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): IDisposable; private addEventListener(element: E, type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): IDisposable { element.addEventListener(type, listener, useCapture); - return { dispose: () => { element.removeEventListener(type, listener, useCapture); } }; + return toDisposable(() => { element.removeEventListener(type, listener, useCapture); }); } private registerFocusHandlers() { @@ -517,7 +517,7 @@ export class WalkThroughPart extends BaseEditor { export const embeddedEditorBackground = registerColor('walkThrough.embeddedEditorBackground', { dark: null, light: null, hc: null }, localize('walkThrough.embeddedEditorBackground', 'Background color for the embedded editors on the Interactive Playground.')); registerThemingParticipant((theme, collector) => { - const color = getExtraColor(theme, embeddedEditorBackground, { dark: 'rgba(0, 0, 0, .4)', extra_dark: 'rgba(200, 235, 255, .064)', light: 'rgba(0,0,0,.08)', hc: null }); + const color = getExtraColor(theme, embeddedEditorBackground, { dark: 'rgba(0, 0, 0, .4)', extra_dark: 'rgba(200, 235, 255, .064)', light: '#f4f4f4', hc: null }); if (color) { collector.addRule(`.monaco-workbench > .part.editor > .content .walkThroughContent .monaco-editor-background, .monaco-workbench > .part.editor > .content .walkThroughContent .margin-view-overlays { background: ${color}; }`); diff --git a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts index a0a69bf0c88..7fb340f111a 100644 --- a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts @@ -6,7 +6,6 @@ import { mergeSort } from 'vs/base/common/arrays'; -import { getPathLabel } from 'vs/base/common/labels'; import { dispose, IDisposable, IReference } from 'vs/base/common/lifecycle'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; @@ -19,14 +18,13 @@ import { isResourceFileEdit, isResourceTextEdit, ResourceFileEdit, ResourceTextE import { IModelService } from 'vs/editor/common/services/modelService'; import { ITextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; import { localize } from 'vs/nls'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } 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 { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; abstract class Recording { @@ -47,11 +45,14 @@ abstract class Recording { abstract hasChanged(resource: URI): boolean; } +type ValidationResult = { canApply: true } | { canApply: false, reason: URI }; + class ModelEditTask implements IDisposable { private readonly _model: ITextModel; protected _edits: IIdentifiedSingleEditOperation[]; + private _expectedModelVersionId: number | undefined; protected _newEol: EndOfLineSequence; constructor(private readonly _modelReference: IReference) { @@ -64,6 +65,7 @@ class ModelEditTask implements IDisposable { } addEdit(resourceEdit: ResourceTextEdit): void { + this._expectedModelVersionId = resourceEdit.modelVersionId; for (const edit of resourceEdit.edits) { if (typeof edit.eol === 'number') { // honor eol-change @@ -82,6 +84,13 @@ class ModelEditTask implements IDisposable { } } + validate(): ValidationResult { + if (typeof this._expectedModelVersionId === 'undefined' || this._model.getVersionId() === this._expectedModelVersionId) { + return { canApply: true }; + } + return { canApply: false, reason: this._model.uri }; + } + apply(): void { if (this._edits.length > 0) { this._edits = mergeSort(this._edits, (a, b) => Range.compareRangesUsingStarts(a.range, b.range)); @@ -191,6 +200,16 @@ class BulkEditModel implements IDisposable { return this; } + validate(): ValidationResult { + for (const task of this._tasks) { + const result = task.validate(); + if (!result.canApply) { + return result; + } + } + return { canApply: true }; + } + apply(): void { for (const task of this._tasks) { task.apply(); @@ -214,8 +233,7 @@ export class BulkEdit { @ITextModelService private readonly _textModelService: ITextModelService, @IFileService private readonly _fileService: IFileService, @ITextFileService private readonly _textFileService: ITextFileService, - @IEnvironmentService private readonly _environmentService: IEnvironmentService, - @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService + @IUriDisplayService private readonly _uriDisplayServie: IUriDisplayService ) { this._editor = editor; this._progress = progress || emptyProgressRunner; @@ -321,7 +339,7 @@ export class BulkEdit { const conflicts = edits .filter(edit => recording.hasChanged(edit.resource)) - .map(edit => getPathLabel(edit.resource, this._environmentService, this._contextService)); + .map(edit => this._uriDisplayServie.getLabel(edit.resource, true)); recording.stop(); @@ -330,6 +348,11 @@ export class BulkEdit { throw new Error(localize('conflict', "These files have changed in the meantime: {0}", conflicts.join(', '))); } + const validationResult = model.validate(); + if (validationResult.canApply === false) { + throw new Error(`${validationResult.reason.toString()} has changed in the meantime`); + } + await model.apply(); model.dispose(); } @@ -346,8 +369,7 @@ export class BulkEditService implements IBulkEditService { @ITextModelService private readonly _textModelService: ITextModelService, @IFileService private readonly _fileService: IFileService, @ITextFileService private readonly _textFileService: ITextFileService, - @IEnvironmentService private readonly _environmentService: IEnvironmentService, - @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService + @IUriDisplayService private readonly _uriDisplayService: IUriDisplayService ) { } @@ -378,7 +400,7 @@ export class BulkEditService implements IBulkEditService { } } - const bulkEdit = new BulkEdit(options.editor, options.progress, this._logService, this._textModelService, this._fileService, this._textFileService, this._environmentService, this._contextService); + const bulkEdit = new BulkEdit(options.editor, options.progress, this._logService, this._textModelService, this._fileService, this._textFileService, this._uriDisplayService); bulkEdit.add(edits); return TPromise.wrap(bulkEdit.perform().then(() => { diff --git a/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts b/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts index 6705a89acda..6197ea71e29 100644 --- a/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts +++ b/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts @@ -46,6 +46,13 @@ const configurationEntrySchema: IJSONSchema = { nls.localize('scope.resource.description', "Resource specific configuration, which can be configured in the User, Workspace or Folder settings.") ], description: nls.localize('scope.description', "Scope in which the configuration is applicable. Available scopes are `window` and `resource`.") + }, + enumDescriptions: { + type: 'array', + items: { + type: 'string', + }, + description: nls.localize('scope.enumDescriptions', 'Descriptions for enum values') } } } @@ -106,7 +113,7 @@ configurationExtPoint.setHandler(extensions => { validateProperties(configuration, extension); - configuration.id = node.id || extension.description.uuid || extension.description.id; + configuration.id = node.id || extension.description.id || extension.description.uuid; configuration.contributedByExtension = true; configuration.title = configuration.title || extension.description.displayName || extension.description.id; configurations.push(configuration); diff --git a/src/vs/workbench/services/configuration/node/configuration.ts b/src/vs/workbench/services/configuration/node/configuration.ts index e09d62ad085..2df1c8ad96e 100644 --- a/src/vs/workbench/services/configuration/node/configuration.ts +++ b/src/vs/workbench/services/configuration/node/configuration.ts @@ -6,6 +6,7 @@ import URI from 'vs/base/common/uri'; import { createHash } from 'crypto'; import * as paths from 'vs/base/common/paths'; +import * as resources from 'vs/base/common/resources'; import { TPromise } from 'vs/base/common/winjs.base'; import { Event, Emitter } from 'vs/base/common/event'; import * as pfs from 'vs/base/node/pfs'; @@ -124,7 +125,7 @@ export class WorkspaceConfiguration extends Disposable { } function isFolderConfigurationFile(resource: URI): boolean { - const name = paths.basename(resource.path); + const name = resources.basename(resource); return [`${FOLDER_SETTINGS_NAME}.json`, `${TASKS_CONFIGURATION_KEY}.json`, `${LAUNCH_CONFIGURATION_KEY}.json`].some(p => p === name);// only workspace config files } @@ -192,7 +193,7 @@ export abstract class AbstractFolderConfiguration extends Disposable implements private parseContents(contents: { resource: URI, value: string }[]): void { for (const content of contents) { - const name = paths.basename(content.resource.path); + const name = resources.basename(content.resource); if (name === `${FOLDER_SETTINGS_NAME}.json`) { this._folderSettingsModelParser.parse(content.value); } else { @@ -215,7 +216,7 @@ export class NodeBasedFolderConfiguration extends AbstractFolderConfiguration { constructor(folder: URI, configFolderRelativePath: string, workbenchState: WorkbenchState) { super(folder, workbenchState); - this.folderConfigurationPath = URI.file(paths.join(this.folder.fsPath, configFolderRelativePath)); + this.folderConfigurationPath = resources.joinPath(folder, configFolderRelativePath); } protected loadFolderConfigurationContents(): TPromise<{ resource: URI, value: string }[]> { @@ -248,7 +249,7 @@ export class NodeBasedFolderConfiguration extends AbstractFolderConfiguration { c({ resource, isDirectory: true, - children: children.map(child => { return { resource: URI.file(paths.join(resource.fsPath, child)) }; }) + children: children.map(child => { return { resource: resources.joinPath(resource, child) }; }) }); } }); @@ -264,7 +265,7 @@ export class FileServiceBasedFolderConfiguration extends AbstractFolderConfigura constructor(folder: URI, private configFolderRelativePath: string, workbenchState: WorkbenchState, private fileService: IFileService, from?: AbstractFolderConfiguration) { super(folder, workbenchState, from); - this.folderConfigurationPath = folder.with({ path: paths.join(this.folder.path, configFolderRelativePath) }); + this.folderConfigurationPath = resources.joinPath(folder, configFolderRelativePath); this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this._onDidChange.fire(), 50)); this._register(fileService.onFileChanges(e => this.handleWorkspaceFileEvents(e))); } @@ -295,7 +296,7 @@ export class FileServiceBasedFolderConfiguration extends AbstractFolderConfigura for (let i = 0, len = events.length; i < len; i++) { const resource = events[i].resource; - const basename = paths.basename(resource.path); + const basename = resources.basename(resource); const isJson = paths.extname(basename) === '.json'; const isDeletedSettingsFolder = (events[i].type === FileChangeType.DELETED && basename === this.configFolderRelativePath); @@ -337,7 +338,7 @@ export class FileServiceBasedFolderConfiguration extends AbstractFolderConfigura return paths.normalize(relative(this.folderConfigurationPath.fsPath, resource.fsPath)); } } else { - if (paths.isEqualOrParent(resource.path, this.folderConfigurationPath.path, true /* ignorecase */)) { + if (resources.isEqualOrParent(resource, this.folderConfigurationPath, resources.hasToIgnoreCase(resource))) { return paths.normalize(relative(this.folderConfigurationPath.path, resource.path)); } } diff --git a/src/vs/workbench/services/configuration/node/configurationService.ts b/src/vs/workbench/services/configuration/node/configurationService.ts index 1aafc56a10f..02320795944 100644 --- a/src/vs/workbench/services/configuration/node/configurationService.ts +++ b/src/vs/workbench/services/configuration/node/configurationService.ts @@ -26,7 +26,7 @@ import { IWorkspaceConfigurationService, FOLDER_CONFIG_FOLDER_NAME, defaultSetti import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationNode, IConfigurationRegistry, Extensions, IConfigurationPropertySchema, allSettings, windowSettings, resourceSettings, applicationSettings } from 'vs/platform/configuration/common/configurationRegistry'; import { createHash } from 'crypto'; -import { getWorkspaceLabel, IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; +import { getWorkspaceLabel, IWorkspaceIdentifier, isWorkspaceIdentifier, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -38,9 +38,10 @@ import { JSONEditingService } from 'vs/workbench/services/configuration/node/jso import { Schemas } from 'vs/base/common/network'; import { massageFolderPathForWorkspace } from 'vs/platform/workspaces/node/workspaces'; import { UserConfiguration } from 'vs/platform/configuration/node/configuration'; -import { getBaseLabel } from 'vs/base/common/labels'; import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; import { localize } from 'vs/nls'; +import { isEqual, hasToIgnoreCase } from 'vs/base/common/resources'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; export class WorkspaceService extends Disposable implements IWorkspaceConfigurationService, IWorkspaceContextService { @@ -68,6 +69,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat public readonly onDidChangeWorkbenchState: Event = this._onDidChangeWorkbenchState.event; private fileService: IFileService; + private uriDisplayService: IUriDisplayService; private configurationEditingService: ConfigurationEditingService; private jsonEditingService: JSONEditingService; @@ -131,7 +133,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat public isCurrentWorkspace(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): boolean { switch (this.getWorkbenchState()) { case WorkbenchState.FOLDER: - return isSingleFolderWorkspaceIdentifier(workspaceIdentifier) && this.pathEquals(this.workspace.folders[0].uri.fsPath, workspaceIdentifier); + return isSingleFolderWorkspaceIdentifier(workspaceIdentifier) && isEqual(workspaceIdentifier, this.workspace.folders[0].uri, hasToIgnoreCase(workspaceIdentifier)); case WorkbenchState.WORKSPACE: return isWorkspaceIdentifier(workspaceIdentifier) && this.workspace.id === workspaceIdentifier.id; } @@ -317,12 +319,16 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat }); } + acquireUriDisplayService(uriDisplayService: IUriDisplayService): void { + this.uriDisplayService = uriDisplayService; + } + acquireInstantiationService(instantiationService: IInstantiationService): void { this.configurationEditingService = instantiationService.createInstance(ConfigurationEditingService); this.jsonEditingService = instantiationService.createInstance(JSONEditingService); } - private createWorkspace(arg: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWindowConfiguration): TPromise { + private createWorkspace(arg: IWorkspaceIdentifier | URI | IWindowConfiguration): TPromise { if (isWorkspaceIdentifier(arg)) { return this.createMulitFolderWorkspace(arg); } @@ -340,32 +346,35 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat .then(() => { const workspaceFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), URI.file(dirname(workspaceConfigPath.fsPath))); const workspaceId = workspaceIdentifier.id; - const workspaceName = getWorkspaceLabel({ id: workspaceId, configPath: workspaceConfigPath.fsPath }, this.environmentService); + const workspaceName = getWorkspaceLabel({ id: workspaceId, configPath: workspaceConfigPath.fsPath }, this.environmentService, this.uriDisplayService); return new Workspace(workspaceId, workspaceName, workspaceFolders, workspaceConfigPath); }); } - private createSingleFolderWorkspace(singleFolderWorkspaceIdentifier: ISingleFolderWorkspaceIdentifier): TPromise { - const folderPath = URI.file(singleFolderWorkspaceIdentifier); - return stat(folderPath.fsPath) - .then(workspaceStat => { - let ctime: number; - if (isLinux) { - ctime = workspaceStat.ino; // Linux: birthtime is ctime, so we cannot use it! We use the ino instead! - } else if (isMacintosh) { - ctime = workspaceStat.birthtime.getTime(); // macOS: birthtime is fine to use as is - } else if (isWindows) { - if (typeof workspaceStat.birthtimeMs === 'number') { - ctime = Math.floor(workspaceStat.birthtimeMs); // Windows: fix precision issue in node.js 8.x to get 7.x results (see https://github.com/nodejs/node/issues/19897) - } else { - ctime = workspaceStat.birthtime.getTime(); + private createSingleFolderWorkspace(folder: URI): TPromise { + if (folder.scheme === Schemas.file) { + return stat(folder.fsPath) + .then(workspaceStat => { + let ctime: number; + if (isLinux) { + ctime = workspaceStat.ino; // Linux: birthtime is ctime, so we cannot use it! We use the ino instead! + } else if (isMacintosh) { + ctime = workspaceStat.birthtime.getTime(); // macOS: birthtime is fine to use as is + } else if (isWindows) { + if (typeof workspaceStat.birthtimeMs === 'number') { + ctime = Math.floor(workspaceStat.birthtimeMs); // Windows: fix precision issue in node.js 8.x to get 7.x results (see https://github.com/nodejs/node/issues/19897) + } else { + ctime = workspaceStat.birthtime.getTime(); + } } - } - const id = createHash('md5').update(folderPath.fsPath).update(ctime ? String(ctime) : '').digest('hex'); - const folder = URI.file(folderPath.fsPath); - return new Workspace(id, getBaseLabel(folder), toWorkspaceFolders([{ path: folder.fsPath }]), null, ctime); - }); + const id = createHash('md5').update(folder.fsPath).update(ctime ? String(ctime) : '').digest('hex'); + return new Workspace(id, getWorkspaceLabel(folder, this.environmentService, this.uriDisplayService), toWorkspaceFolders([{ path: folder.fsPath }]), null, ctime); + }); + } else { + const id = createHash('md5').update(folder.toString()).digest('hex'); + return TPromise.as(new Workspace(id, getWorkspaceLabel(folder, this.environmentService, this.uriDisplayService), toWorkspaceFolders([{ uri: folder.toString() }]), null)); + } } private createEmptyWorkspace(configuration: IWindowConfiguration): TPromise { @@ -682,15 +691,6 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat } return {}; } - - private pathEquals(path1: string, path2: string): boolean { - if (!isLinux) { - path1 = path1.toLowerCase(); - path2 = path2.toLowerCase(); - } - - return path1 === path2; - } } interface IExportedConfigurationNode { 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 0e42aaccf0c..a6ab72cead7 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 @@ -38,6 +38,7 @@ import { mkdirp } from 'vs/base/node/pfs'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { CommandService } from 'vs/workbench/services/commands/common/commandService'; +import URI from 'vs/base/common/uri'; class SettingsTestEnvironmentService extends EnvironmentService { @@ -103,7 +104,7 @@ suite('ConfigurationEditingService', () => { instantiationService.stub(IEnvironmentService, environmentService); const workspaceService = new WorkspaceService(environmentService); instantiationService.stub(IWorkspaceContextService, workspaceService); - return workspaceService.initialize(noWorkspace ? {} as IWindowConfiguration : workspaceDir).then(() => { + return workspaceService.initialize(noWorkspace ? {} as IWindowConfiguration : URI.file(workspaceDir)).then(() => { instantiationService.stub(IConfigurationService, workspaceService); instantiationService.stub(IFileService, new FileService(workspaceService, TestEnvironmentService, new TestTextResourceConfigurationService(), new TestConfigurationService(), new TestLifecycleService(), new TestStorageService(), new TestNotificationService(), { disableWatcher: true })); instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); 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 a38560e14b4..64e9352f99a 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 @@ -87,7 +87,7 @@ suite('WorkspaceContextService - Folder', () => { const globalSettingsFile = path.join(parentDir, 'settings.json'); const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, globalSettingsFile); workspaceContextService = new WorkspaceService(environmentService); - return (workspaceContextService).initialize(folderDir); + return (workspaceContextService).initialize(URI.file(folderDir)); }); }); @@ -124,11 +124,11 @@ suite('WorkspaceContextService - Folder', () => { }); test('isCurrentWorkspace() => true', () => { - assert.ok(workspaceContextService.isCurrentWorkspace(workspaceResource)); + assert.ok(workspaceContextService.isCurrentWorkspace(URI.file(workspaceResource))); }); test('isCurrentWorkspace() => false', () => { - assert.ok(!workspaceContextService.isCurrentWorkspace(workspaceResource + 'abc')); + assert.ok(!workspaceContextService.isCurrentWorkspace(URI.file(workspaceResource + 'abc'))); }); }); @@ -445,7 +445,7 @@ suite('WorkspaceService - Initialization', () => { testObject.onDidChangeWorkspaceFolders(target); testObject.onDidChangeConfiguration(target); - return testObject.initialize(path.join(parentResource, '1')) + return testObject.initialize(URI.file(path.join(parentResource, '1'))) .then(() => { assert.equal(testObject.getValue('initialization.testSetting1'), 'userValue'); assert.equal(target.callCount, 3); @@ -474,7 +474,7 @@ suite('WorkspaceService - Initialization', () => { fs.writeFileSync(path.join(parentResource, '1', '.vscode', 'settings.json'), '{ "initialization.testSetting1": "workspaceValue" }'); - return testObject.initialize(path.join(parentResource, '1')) + return testObject.initialize(URI.file(path.join(parentResource, '1'))) .then(() => { assert.equal(testObject.getValue('initialization.testSetting1'), 'workspaceValue'); assert.equal(target.callCount, 4); @@ -548,7 +548,7 @@ suite('WorkspaceService - Initialization', () => { test('initialize a folder workspace from a folder workspace with no configuration changes', () => { - return testObject.initialize(path.join(parentResource, '1')) + return testObject.initialize(URI.file(path.join(parentResource, '1'))) .then(() => { fs.writeFileSync(globalSettingsFile, '{ "initialization.testSetting1": "userValue" }'); @@ -560,7 +560,7 @@ suite('WorkspaceService - Initialization', () => { testObject.onDidChangeWorkspaceFolders(target); testObject.onDidChangeConfiguration(target); - return testObject.initialize(path.join(parentResource, '2')) + return testObject.initialize(URI.file(path.join(parentResource, '2'))) .then(() => { assert.equal(testObject.getValue('initialization.testSetting1'), 'userValue'); assert.equal(target.callCount, 1); @@ -576,7 +576,7 @@ suite('WorkspaceService - Initialization', () => { test('initialize a folder workspace from a folder workspace with configuration changes', () => { - return testObject.initialize(path.join(parentResource, '1')) + return testObject.initialize(URI.file(path.join(parentResource, '1'))) .then(() => { const target = sinon.spy(); @@ -586,7 +586,7 @@ suite('WorkspaceService - Initialization', () => { testObject.onDidChangeConfiguration(target); fs.writeFileSync(path.join(parentResource, '2', '.vscode', 'settings.json'), '{ "initialization.testSetting1": "workspaceValue2" }'); - return testObject.initialize(path.join(parentResource, '2')) + return testObject.initialize(URI.file(path.join(parentResource, '2'))) .then(() => { assert.equal(testObject.getValue('initialization.testSetting1'), 'workspaceValue2'); assert.equal(target.callCount, 2); @@ -601,7 +601,7 @@ suite('WorkspaceService - Initialization', () => { test('initialize a multi folder workspace from a folder workspacce triggers change events in the right order', () => { const folderDir = path.join(parentResource, '1'); - return testObject.initialize(folderDir) + return testObject.initialize(URI.file(folderDir)) .then(() => { const target = sinon.spy(); @@ -666,7 +666,7 @@ suite('WorkspaceConfigurationService - Folder', () => { instantiationService.stub(IConfigurationService, workspaceService); instantiationService.stub(IEnvironmentService, environmentService); - return workspaceService.initialize(folderDir).then(() => { + return workspaceService.initialize(URI.file(folderDir)).then(() => { const fileService = new FileService(workspaceService, TestEnvironmentService, new TestTextResourceConfigurationService(), workspaceService, new TestLifecycleService(), new TestStorageService(), new TestNotificationService(), { disableWatcher: true }); instantiationService.stub(IFileService, fileService); instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); diff --git a/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts b/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts index 2c0ede219c9..124d039a302 100644 --- a/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts +++ b/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts @@ -14,6 +14,7 @@ import { ConfigurationResolverService } from 'vs/workbench/services/configuratio import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { TestEnvironmentService, TestEditorService, TestContextService } from 'vs/workbench/test/workbenchTestServices'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { Disposable } from 'vs/base/common/lifecycle'; suite('Configuration Resolver Service', () => { let configurationResolverService: IConfigurationResolverService; @@ -391,7 +392,7 @@ class MockCommandService implements ICommandService { public _serviceBrand: any; public callCount = 0; - onWillExecuteCommand = () => ({ dispose: () => { } }); + onWillExecuteCommand = () => Disposable.None; public executeCommand(commandId: string, ...args: any[]): TPromise { this.callCount++; diff --git a/src/vs/workbench/services/crashReporter/electron-browser/crashReporterService.ts b/src/vs/workbench/services/crashReporter/electron-browser/crashReporterService.ts index 2b63e252efb..13049eb3358 100644 --- a/src/vs/workbench/services/crashReporter/electron-browser/crashReporterService.ts +++ b/src/vs/workbench/services/crashReporter/electron-browser/crashReporterService.ts @@ -36,8 +36,9 @@ configurationRegistry.registerConfiguration({ 'properties': { 'telemetry.enableCrashReporter': { 'type': 'boolean', - 'description': nls.localize('telemetry.enableCrashReporting', "Enable crash reports to be sent to Microsoft.\nThis option requires restart to take effect."), - 'default': true + 'description': nls.localize('telemetry.enableCrashReporting', "Enable crash reports to be sent to a Microsoft online service. \nThis option requires restart to take effect."), + 'default': true, + 'tags': ['usesOnlineServices'] } } }); diff --git a/src/vs/workbench/services/decorations/browser/decorationsService.ts b/src/vs/workbench/services/decorations/browser/decorationsService.ts index 722deba856e..101a0f69d96 100644 --- a/src/vs/workbench/services/decorations/browser/decorationsService.ts +++ b/src/vs/workbench/services/decorations/browser/decorationsService.ts @@ -8,7 +8,7 @@ import URI from 'vs/base/common/uri'; import { Event, Emitter, debounceEvent, anyEvent } from 'vs/base/common/event'; import { IDecorationsService, IDecoration, IResourceDecorationChangeEvent, IDecorationsProvider, IDecorationData } from './decorations'; import { TernarySearchTree } from 'vs/base/common/map'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, toDisposable } 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'; @@ -402,15 +402,13 @@ export class FileDecorationsService implements IDecorationsService { affectsResource() { return true; } }); - return { - dispose: () => { - // fire event that says 'yes' for any resource - // known to this provider. then dispose and remove it. - remove(); - this._onDidChangeDecorations.fire({ affectsResource: uri => wrapper.knowsAbout(uri) }); - wrapper.dispose(); - } - }; + return toDisposable(() => { + // fire event that says 'yes' for any resource + // known to this provider. then dispose and remove it. + remove(); + this._onDidChangeDecorations.fire({ affectsResource: uri => wrapper.knowsAbout(uri) }); + wrapper.dispose(); + }); } getDecoration(uri: URI, includeChildren: boolean, overwrite?: IDecorationData): IDecoration { diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 7b29d31a644..6250d63c405 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -13,11 +13,8 @@ import { DataUriEditorInput } from 'vs/workbench/common/editor/dataUriEditorInpu import { Registry } from 'vs/platform/registry/common/platform'; import { ResourceMap } from 'vs/base/common/map'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { Schemas } from 'vs/base/common/network'; -import { getPathLabel } from 'vs/base/common/labels'; import { Event, once, Emitter } from 'vs/base/common/event'; import URI from 'vs/base/common/uri'; import { basename } from 'vs/base/common/paths'; @@ -31,6 +28,7 @@ import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/l import { coalesce } from 'vs/base/common/arrays'; import { isCodeEditor, isDiffEditor, ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorGroupView, IEditorOpeningEvent, EditorGroupsServiceImpl, EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; type ICachedEditorInput = ResourceEditorInput | IFileEditorInput | DataUriEditorInput; @@ -65,9 +63,8 @@ export class EditorService extends Disposable implements EditorServiceImpl { constructor( @IEditorGroupsService private editorGroupService: EditorGroupsServiceImpl, @IUntitledEditorService private untitledEditorService: IUntitledEditorService, - @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, @IInstantiationService private instantiationService: IInstantiationService, - @IEnvironmentService private environmentService: IEnvironmentService, + @IUriDisplayService private uriDisplayService: IUriDisplayService, @IFileService private fileService: IFileService, @IConfigurationService private configurationService: IConfigurationService ) { @@ -489,7 +486,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { if (resourceDiffInput.leftResource && resourceDiffInput.rightResource) { const leftInput = this.createInput({ resource: resourceDiffInput.leftResource }, options); const rightInput = this.createInput({ resource: resourceDiffInput.rightResource }, options); - const label = resourceDiffInput.label || localize('compareLabels', "{0} ↔ {1}", this.toDiffLabel(leftInput, this.workspaceContextService, this.environmentService), this.toDiffLabel(rightInput, this.workspaceContextService, this.environmentService)); + const label = resourceDiffInput.label || localize('compareLabels', "{0} ↔ {1}", this.toDiffLabel(leftInput), this.toDiffLabel(rightInput)); return new DiffEditorInput(label, resourceDiffInput.description, leftInput, rightInput); } @@ -557,7 +554,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { return input; } - private toDiffLabel(input: EditorInput, context: IWorkspaceContextService, environment: IEnvironmentService): string { + private toDiffLabel(input: EditorInput): string { const res = input.getResource(); // Do not try to extract any paths from simple untitled editors @@ -566,7 +563,7 @@ export class EditorService extends Disposable implements EditorServiceImpl { } // Otherwise: for diff labels prefer to see the path as part of the label - return getPathLabel(res.fsPath, environment, context); + return this.uriDisplayService.getLabel(res, true); } //#endregion @@ -586,18 +583,16 @@ export class DelegatingEditorService extends EditorService { constructor( @IEditorGroupsService editorGroupService: EditorGroupsServiceImpl, @IUntitledEditorService untitledEditorService: IUntitledEditorService, - @IWorkspaceContextService workspaceContextService: IWorkspaceContextService, @IInstantiationService instantiationService: IInstantiationService, - @IEnvironmentService environmentService: IEnvironmentService, + @IUriDisplayService uriDisplayService: IUriDisplayService, @IFileService fileService: IFileService, @IConfigurationService configurationService: IConfigurationService ) { super( editorGroupService, untitledEditorService, - workspaceContextService, instantiationService, - environmentService, + uriDisplayService, fileService, configurationService ); diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index 6b3a2b4c7d5..3c291c70b46 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -220,6 +220,16 @@ const schema: IJSONSchema = { description: nls.localize('vscode.extension.activationEvents.workspaceContains', 'An activation event emitted whenever a folder is opened that contains at least a file matching the specified glob pattern.'), body: 'workspaceContains:${4:filePattern}' }, + { + label: 'onFileSystem', + description: nls.localize('vscode.extension.activationEvents.onFileSystem', 'An activation event emitted whenever a file or folder is accessed with the given scheme.'), + body: 'onFileSystem:${1:scheme}' + }, + { + label: 'onSearch', + description: nls.localize('vscode.extension.activationEvents.onSearch', 'An activation event emitted whenever a search is started in the folder with the given scheme.'), + body: 'onSearch:${7:scheme}' + }, { label: 'onView', body: 'onView:${5:viewId}', diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index c5943fb7778..16a25f5dbea 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -8,7 +8,6 @@ import * as nls from 'vs/nls'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import * as objects from 'vs/base/common/objects'; -import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { isWindows, isLinux } from 'vs/base/common/platform'; import { findFreePort } from 'vs/base/node/ports'; @@ -31,11 +30,12 @@ import { ICrashReporterService } from 'vs/workbench/services/crashReporter/elect import { IBroadcastService, IBroadcast } from 'vs/platform/broadcast/electron-browser/broadcastService'; import { isEqual } from 'vs/base/common/paths'; import { EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { IRemoteConsoleLog, log, parse } from 'vs/base/node/console'; import { getScopes } from 'vs/platform/configuration/common/configurationRegistry'; import { ILogService } from 'vs/platform/log/common/log'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; export class ExtensionHostProcessWorker { @@ -96,11 +96,9 @@ export class ExtensionHostProcessWorker { const globalExitListener = () => this.terminate(); process.once('exit', globalExitListener); - this._toDispose.push({ - dispose: () => { - process.removeListener('exit', globalExitListener); - } - }); + this._toDispose.push(toDisposable(() => { + process.removeListener('exit', globalExitListener); + })); } public dispose(): void { @@ -143,7 +141,8 @@ export class ExtensionHostProcessWorker { VERBOSE_LOGGING: true, VSCODE_IPC_HOOK_EXTHOST: pipeName, VSCODE_HANDLES_UNCAUGHT_ERRORS: true, - VSCODE_LOG_STACK: !this._isExtensionDevTestFromCli && (this._isExtensionDevHost || !this._environmentService.isBuilt || product.quality !== 'stable' || this._environmentService.verbose) + VSCODE_LOG_STACK: !this._isExtensionDevTestFromCli && (this._isExtensionDevHost || !this._environmentService.isBuilt || product.quality !== 'stable' || this._environmentService.verbose), + VSCODE_LOG_LEVEL: this._environmentService.verbose ? 'trace' : this._environmentService.log }), // We only detach the extension host on windows. Linux and Mac orphan by default // and detach under Linux and Mac create another process group. @@ -172,7 +171,7 @@ export class ExtensionHostProcessWorker { } // Run Extension Host as fork of current process - this._extensionHostProcess = fork(URI.parse(require.toUrl('bootstrap')).fsPath, ['--type=extensionHost'], opts); + this._extensionHostProcess = fork(getPathFromAmdModule(require, 'bootstrap'), ['--type=extensionHost'], opts); // Catch all output coming from the extension host process type Output = { data: string, format: string[] }; @@ -369,7 +368,6 @@ export class ExtensionHostProcessWorker { isExtensionDevelopmentDebug: this._isExtensionDevDebug, appRoot: this._environmentService.appRoot, appSettingsHome: this._environmentService.appSettingsHome, - disableExtensions: this._environmentService.disableExtensions, extensionDevelopmentPath: this._environmentService.extensionDevelopmentPath, extensionTestsPath: this._environmentService.extensionTestsPath }, diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index c2f0c78bfe2..a46a116e22e 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -43,18 +43,19 @@ import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { isFalsyOrEmpty } from 'vs/base/common/arrays'; import { Schemas } from 'vs/base/common/network'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; let _SystemExtensionsRoot: string = null; function getSystemExtensionsRoot(): string { if (!_SystemExtensionsRoot) { - _SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions')); + _SystemExtensionsRoot = path.normalize(path.join(getPathFromAmdModule(require, ''), '..', 'extensions')); } return _SystemExtensionsRoot; } let _ExtraDevSystemExtensionsRoot: string = null; function getExtraDevSystemExtensionsRoot(): string { if (!_ExtraDevSystemExtensionsRoot) { - _ExtraDevSystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', '.build', 'builtInExtensions')); + _ExtraDevSystemExtensionsRoot = path.normalize(path.join(getPathFromAmdModule(require, ''), '..', '.build', 'builtInExtensions')); } return _ExtraDevSystemExtensionsRoot; } @@ -122,7 +123,7 @@ export class ExtensionHostProcessManager extends Disposable { /** * winjs believes a proxy is a promise because it has a `then` method, so wrap the result in an object. */ - private readonly _extensionHostProcessProxy: TPromise<{ value: ExtHostExtensionServiceShape; }>; + private _extensionHostProcessProxy: TPromise<{ value: ExtHostExtensionServiceShape; }>; constructor( extensionHostProcessWorker: ExtensionHostProcessWorker, @@ -134,7 +135,6 @@ export class ExtensionHostProcessManager extends Disposable { this._extensionHostProcessFinishedActivateEvents = Object.create(null); this._extensionHostProcessRPCProtocol = null; this._extensionHostProcessCustomers = []; - this._extensionHostProcessProxy = null; this._extensionHostProcessWorker = extensionHostProcessWorker; this.onDidCrash = this._extensionHostProcessWorker.onCrashed; @@ -168,6 +168,7 @@ export class ExtensionHostProcessManager extends Disposable { errors.onUnexpectedError(err); } } + this._extensionHostProcessProxy = null; super.dispose(); } @@ -218,6 +219,11 @@ export class ExtensionHostProcessManager extends Disposable { return NO_OP_VOID_PROMISE; } return this._extensionHostProcessProxy.then((proxy) => { + if (!proxy) { + // this case is already covered above and logged. + // i.e. the extension host could not be started + return NO_OP_VOID_PROMISE; + } return proxy.value.$activateByEvent(activationEvent); }).then(() => { this._extensionHostProcessFinishedActivateEvents[activationEvent] = true; @@ -281,8 +287,8 @@ export class ExtensionService extends Disposable implements IExtensionService { this.startDelayed(lifecycleService); - if (this._environmentService.disableExtensions) { - this._notificationService.prompt(Severity.Info, nls.localize('extensionsDisabled', "All extensions are temporarily disabled. Reload the window to return to the previous state."), [{ + 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."), [{ label: nls.localize('Reload', "Reload"), run: () => { this._windowService.reloadWindow(); @@ -513,7 +519,7 @@ export class ExtensionService extends Disposable implements IExtensionService { this._logOrShowMessage(severity, this._isDev ? messageWithSource(source, message) : message); }); - return ExtensionService._scanInstalledExtensions(this._windowService, this._notificationService, this._environmentService, log) + return ExtensionService._scanInstalledExtensions(this._windowService, this._notificationService, this._environmentService, this._extensionEnablementService, log) .then(({ system, user, development }) => { let result: { [extensionId: string]: IExtensionDescription; } = {}; system.forEach((systemExtension) => { @@ -536,7 +542,7 @@ export class ExtensionService extends Disposable implements IExtensionService { }); } - private _getRuntimeExtensions(allExtensions: IExtensionDescription[]): TPromise { + private _getRuntimeExtensions(allExtensions: IExtensionDescription[]): Promise { return this._extensionEnablementService.getDisabledExtensions() .then(disabledExtensions => { @@ -759,7 +765,7 @@ export class ExtensionService extends Disposable implements IExtensionService { return result; } - private static _scanInstalledExtensions(windowService: IWindowService, notificationService: INotificationService, environmentService: IEnvironmentService, log: ILog): TPromise<{ system: IExtensionDescription[], user: IExtensionDescription[], development: IExtensionDescription[] }> { + private static _scanInstalledExtensions(windowService: IWindowService, notificationService: INotificationService, environmentService: IEnvironmentService, extensionEnablementService: IExtensionEnablementService, log: ILog): TPromise<{ system: IExtensionDescription[], user: IExtensionDescription[], development: IExtensionDescription[] }> { const translationConfig: TPromise = platform.translationsConfigFile ? pfs.readFile(platform.translationsConfigFile, 'utf8').then((content) => { @@ -791,7 +797,7 @@ export class ExtensionService extends Disposable implements IExtensionService { let finalBuiltinExtensions: TPromise = TPromise.wrap(builtinExtensions); if (devMode) { - const builtInExtensionsFilePath = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'build', 'builtInExtensions.json')); + const builtInExtensionsFilePath = path.normalize(path.join(getPathFromAmdModule(require, ''), '..', 'build', 'builtInExtensions.json')); const builtInExtensions = pfs.readFile(builtInExtensionsFilePath, 'utf8') .then(raw => JSON.parse(raw)); @@ -831,7 +837,7 @@ export class ExtensionService extends Disposable implements IExtensionService { } const userExtensions = ( - environmentService.disableExtensions || !environmentService.extensionsPath + extensionEnablementService.allUserExtensionsDisabled || !environmentService.extensionsPath ? TPromise.as([]) : this._scanExtensionsWithCache( windowService, diff --git a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts index 3f2e2a1806f..0722e8dfc97 100644 --- a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts +++ b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { posix } from 'path'; import { flatten, isFalsyOrEmpty } from 'vs/base/common/arrays'; import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { TernarySearchTree, keys } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; +import * as resources from 'vs/base/common/resources'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { IDecodeStreamOptions, toDecodeStream, encodeStream } from 'vs/base/node/encoding'; @@ -42,7 +42,7 @@ function toIFileStat(provider: IFileSystemProvider, tuple: [URI, IStat], recurse const [resource, stat] = tuple; const fileStat: IFileStat = { resource, - name: posix.basename(resource.path), + name: resources.basename(resource), isDirectory: (stat.type & FileType.Directory) !== 0, isSymbolicLink: (stat.type & FileType.SymbolicLink) !== 0, isReadonly: !!(provider.capabilities & FileSystemProviderCapabilities.Readonly), @@ -58,7 +58,7 @@ function toIFileStat(provider: IFileSystemProvider, tuple: [URI, IStat], recurse // resolve children if requested return TPromise.join(entries.map(tuple => { const [name, type] = tuple; - const childResource = resource.with({ path: posix.join(resource.path, name) }); + const childResource = resources.joinPath(resource, name); return toIFileStat(provider, [childResource, new TypeOnlyStat(type)], recurse); })).then(children => { fileStat.children = children; @@ -261,7 +261,7 @@ export class RemoteFileService extends FileService { private _withProvider(resource: URI): TPromise { - if (!posix.isAbsolute(resource.path)) { + if (!resources.isAbsolutePath(resource)) { throw new FileOperationError( localize('invalidPath', "The path of resource '{0}' must be absolute", resource.toString(true)), FileOperationResult.FILE_INVALID_PATH @@ -282,7 +282,7 @@ export class RemoteFileService extends FileService { }); } - existsFile(resource: URI): TPromise { + existsFile(resource: URI): TPromise { if (resource.scheme === Schemas.file) { return super.existsFile(resource); } else { @@ -290,7 +290,7 @@ export class RemoteFileService extends FileService { } } - resolveFile(resource: URI, options?: IResolveFileOptions): TPromise { + resolveFile(resource: URI, options?: IResolveFileOptions): TPromise { if (resource.scheme === Schemas.file) { return super.resolveFile(resource, options); } else { @@ -307,7 +307,7 @@ export class RemoteFileService extends FileService { } } - resolveFiles(toResolve: { resource: URI; options?: IResolveFileOptions; }[]): TPromise { + resolveFiles(toResolve: { resource: URI; options?: IResolveFileOptions; }[]): TPromise { // soft-groupBy, keep order, don't rearrange/merge groups let groups: (typeof toResolve)[] = []; @@ -320,7 +320,7 @@ export class RemoteFileService extends FileService { group.push(request); } - const promises: TPromise[] = []; + const promises: TPromise[] = []; for (const group of groups) { if (group[0].resource.scheme === Schemas.file) { promises.push(super.resolveFiles(group)); @@ -331,7 +331,7 @@ export class RemoteFileService extends FileService { return TPromise.join(promises).then(data => flatten(data)); } - private _doResolveFiles(toResolve: { resource: URI; options?: IResolveFileOptions; }[]): TPromise { + private _doResolveFiles(toResolve: { resource: URI; options?: IResolveFileOptions; }[]): TPromise { return this._withProvider(toResolve[0].resource).then(provider => { let result: IResolveFileResult[] = []; let promises = toResolve.map((item, idx) => { @@ -434,12 +434,12 @@ export class RemoteFileService extends FileService { break; // we have hit a directory -> good } catch (e) { // ENOENT - basenames.push(posix.basename(directory.path)); - directory = directory.with({ path: posix.dirname(directory.path) }); + basenames.push(resources.basename(directory)); + directory = resources.dirname(directory); } } for (let i = basenames.length - 1; i >= 0; i--) { - directory = directory.with({ path: posix.join(directory.path, basenames[i]) }); + directory = resources.joinPath(directory, basenames[i]); await provider.mkdir(directory); } } @@ -458,7 +458,7 @@ export class RemoteFileService extends FileService { return this._withProvider(resource).then(RemoteFileService._throwIfFileSystemIsReadonly).then(provider => { - return RemoteFileService._mkdirp(provider, resource.with({ path: posix.dirname(resource.path) })).then(() => { + return RemoteFileService._mkdirp(provider, resources.dirname(resource)).then(() => { const encoding = this.encoding.getWriteEncoding(resource); return this._writeFile(provider, resource, new StringSnapshot(content), encoding, { create: true, overwrite: Boolean(options && options.overwrite) }); }); @@ -479,7 +479,7 @@ export class RemoteFileService extends FileService { return super.updateContent(resource, value, options); } else { return this._withProvider(resource).then(RemoteFileService._throwIfFileSystemIsReadonly).then(provider => { - return RemoteFileService._mkdirp(provider, resource.with({ path: posix.dirname(resource.path) })).then(() => { + return RemoteFileService._mkdirp(provider, resources.dirname(resource)).then(() => { const snapshot = typeof value === 'string' ? new StringSnapshot(value) : value; return this._writeFile(provider, resource, snapshot, options && options.encoding, { create: true, overwrite: true }); }); @@ -532,12 +532,12 @@ export class RemoteFileService extends FileService { } } - createFolder(resource: URI): TPromise { + createFolder(resource: URI): TPromise { if (resource.scheme === Schemas.file) { return super.createFolder(resource); } else { return this._withProvider(resource).then(RemoteFileService._throwIfFileSystemIsReadonly).then(provider => { - return RemoteFileService._mkdirp(provider, resource.with({ path: posix.dirname(resource.path) })).then(() => { + return RemoteFileService._mkdirp(provider, resources.dirname(resource)).then(() => { return provider.mkdir(resource).then(() => { return this.resolveFile(resource); }); 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 d2791142347..f522fba51c8 100644 --- a/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts @@ -7,7 +7,6 @@ import { getNextTickChannel } from 'vs/base/parts/ipc/common/ipc'; import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; -import uri from 'vs/base/common/uri'; import { toFileChangesEvent, IRawFileChange } from 'vs/workbench/services/files/node/watcher/common'; import { IWatcherChannel, WatcherChannelClient } from 'vs/workbench/services/files/node/watcher/nsfw/watcherIpc'; import { FileChangesEvent, IFilesConfiguration } from 'vs/platform/files/common/files'; @@ -17,6 +16,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { filterEvent } from 'vs/base/common/event'; import { IWatchError } from 'vs/workbench/services/files/node/watcher/nsfw/watcher'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; export class FileWatcher { private static readonly MAX_RESTARTS = 5; @@ -39,7 +39,7 @@ export class FileWatcher { public startWatching(): () => void { const client = new Client( - uri.parse(require.toUrl('bootstrap')).fsPath, + getPathFromAmdModule(require, 'bootstrap'), { serverName: 'Watcher', args: ['--type=watcherService'], 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 45ca79e02f5..2dc1effa571 100644 --- a/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts @@ -7,7 +7,6 @@ import { getNextTickChannel } from 'vs/base/parts/ipc/common/ipc'; import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; -import uri from 'vs/base/common/uri'; import { toFileChangesEvent, IRawFileChange } from 'vs/workbench/services/files/node/watcher/common'; import { IWatcherChannel, WatcherChannelClient } from 'vs/workbench/services/files/node/watcher/unix/watcherIpc'; import { FileChangesEvent, IFilesConfiguration } from 'vs/platform/files/common/files'; @@ -17,6 +16,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { Schemas } from 'vs/base/common/network'; import { filterEvent } from 'vs/base/common/event'; import { IWatchError } from 'vs/workbench/services/files/node/watcher/unix/watcher'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; export class FileWatcher { private static readonly MAX_RESTARTS = 5; @@ -42,7 +42,7 @@ export class FileWatcher { const args = ['--type=watcherService']; const client = new Client( - uri.parse(require.toUrl('bootstrap')).fsPath, + getPathFromAmdModule(require, 'bootstrap'), { serverName: 'Watcher', args, @@ -122,4 +122,4 @@ export class FileWatcher { this.isDisposed = true; this.toDispose = dispose(this.toDispose); } -} \ No newline at end of file +} 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 6243afe1972..93dc71bd159 100644 --- a/src/vs/workbench/services/files/node/watcher/win32/csharpWatcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/win32/csharpWatcherService.ts @@ -10,9 +10,9 @@ 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 uri from 'vs/base/common/uri'; import { IRawFileChange } from 'vs/workbench/services/files/node/watcher/common'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; export class OutOfProcessWin32FolderWatcher { @@ -41,7 +41,7 @@ export class OutOfProcessWin32FolderWatcher { args.push('-verbose'); } - this.handle = cp.spawn(uri.parse(require.toUrl('vs/workbench/services/files/node/watcher/win32/CodeHelper.exe')).fsPath, args); + this.handle = cp.spawn(getPathFromAmdModule(require, 'vs/workbench/services/files/node/watcher/win32/CodeHelper.exe'), args); const stdoutLineDecoder = new decoder.LineDecoder(); @@ -116,4 +116,4 @@ export class OutOfProcessWin32FolderWatcher { this.handle = null; } } -} \ No newline at end of file +} 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 26295cbc4a9..3672cdd0775 100644 --- a/src/vs/workbench/services/files/node/watcher/win32/watcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/win32/watcherService.ts @@ -12,6 +12,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { normalize } from 'path'; import { rtrim, endsWith } from 'vs/base/common/strings'; import { sep } from 'vs/base/common/paths'; +import { Schemas } from 'vs/base/common/network'; export class FileWatcher { private isDisposed: boolean; @@ -26,6 +27,9 @@ export class FileWatcher { } public startWatching(): () => void { + if (this.contextService.getWorkspace().folders[0].uri.scheme !== Schemas.file) { + return () => { }; + } let basePath: string = normalize(this.contextService.getWorkspace().folders[0].uri.fsPath); if (basePath && basePath.indexOf('\\\\') === 0 && endsWith(basePath, sep)) { diff --git a/src/vs/workbench/services/files/test/electron-browser/fileService.test.ts b/src/vs/workbench/services/files/test/electron-browser/fileService.test.ts index e57b8041f6b..6d834e52b6a 100644 --- a/src/vs/workbench/services/files/test/electron-browser/fileService.test.ts +++ b/src/vs/workbench/services/files/test/electron-browser/fileService.test.ts @@ -24,6 +24,7 @@ import { Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/work import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { TextModel } from 'vs/editor/common/model/textModel'; import { IEncodingOverride } from 'vs/workbench/services/files/electron-browser/encoding'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; suite('FileService', () => { let service: FileService; @@ -33,7 +34,7 @@ suite('FileService', () => { setup(function () { const id = uuid.generateUuid(); testDir = path.join(parentDir, id); - const sourceDir = require.toUrl('./fixtures/service'); + const sourceDir = getPathFromAmdModule(require, './fixtures/service'); return pfs.copy(sourceDir, testDir).then(() => { service = new FileService(new TestContextService(new Workspace(testDir, testDir, toWorkspaceFolders([{ path: testDir }]))), TestEnvironmentService, new TestTextResourceConfigurationService(), new TestConfigurationService(), new TestLifecycleService(), new TestStorageService(), new TestNotificationService(), { disableWatcher: true }); @@ -837,7 +838,7 @@ suite('FileService', () => { // setup const _id = uuid.generateUuid(); const _testDir = path.join(parentDir, _id); - const _sourceDir = require.toUrl('./fixtures/service'); + const _sourceDir = getPathFromAmdModule(require, './fixtures/service'); return pfs.copy(_sourceDir, _testDir).then(() => { const encodingOverride: IEncodingOverride[] = []; @@ -882,7 +883,7 @@ suite('FileService', () => { // setup const _id = uuid.generateUuid(); const _testDir = path.join(parentDir, _id); - const _sourceDir = require.toUrl('./fixtures/service'); + const _sourceDir = getPathFromAmdModule(require, './fixtures/service'); return pfs.copy(_sourceDir, _testDir).then(() => { const encodingOverride: IEncodingOverride[] = []; @@ -927,7 +928,7 @@ suite('FileService', () => { // setup const _id = uuid.generateUuid(); const _testDir = path.join(parentDir, _id); - const _sourceDir = require.toUrl('./fixtures/service'); + const _sourceDir = getPathFromAmdModule(require, './fixtures/service'); const resource = uri.file(path.join(testDir, 'index.html')); const _service = new FileService( diff --git a/src/vs/workbench/services/files/test/electron-browser/resolver.test.ts b/src/vs/workbench/services/files/test/electron-browser/resolver.test.ts index 3a9c19ad869..11222a1f411 100644 --- a/src/vs/workbench/services/files/test/electron-browser/resolver.test.ts +++ b/src/vs/workbench/services/files/test/electron-browser/resolver.test.ts @@ -13,9 +13,10 @@ import { StatResolver } from 'vs/workbench/services/files/electron-browser/fileS import uri from 'vs/base/common/uri'; import { isLinux } from 'vs/base/common/platform'; import * as utils from 'vs/workbench/services/files/test/electron-browser/utils'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; function create(relativePath: string): StatResolver { - let basePath = require.toUrl('./fixtures/resolver'); + let basePath = getPathFromAmdModule(require, './fixtures/resolver'); let absolutePath = relativePath ? path.join(basePath, relativePath) : basePath; let fsStat = fs.statSync(absolutePath); @@ -23,7 +24,7 @@ function create(relativePath: string): StatResolver { } function toResource(relativePath: string): uri { - let basePath = require.toUrl('./fixtures/resolver'); + let basePath = getPathFromAmdModule(require, './fixtures/resolver'); let absolutePath = relativePath ? path.join(basePath, relativePath) : basePath; return uri.file(absolutePath); diff --git a/src/vs/workbench/services/history/electron-browser/history.ts b/src/vs/workbench/services/history/electron-browser/history.ts index 0cb907bc631..13c20d4a51e 100644 --- a/src/vs/workbench/services/history/electron-browser/history.ts +++ b/src/vs/workbench/services/history/electron-browser/history.ts @@ -32,6 +32,8 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ResourceGlobMatcher } from 'vs/workbench/electron-browser/resources'; import { Schemas } from 'vs/base/common/network'; import { EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; /** * Stores the selection & view state of an editor and allows to compare it to other selection states. @@ -122,6 +124,9 @@ export class HistoryService extends Disposable implements IHistoryService { private fileInputFactory: IFileInputFactory; + private canNavigateBackContextKey: IContextKey; + private canNavigateForwardContextKey: IContextKey; + constructor( @IEditorService private editorService: EditorServiceImpl, @IEditorGroupsService private editorGroupService: IEditorGroupsService, @@ -132,11 +137,16 @@ export class HistoryService extends Disposable implements IHistoryService { @IFileService private fileService: IFileService, @IWindowsService private windowService: IWindowsService, @IInstantiationService private instantiationService: IInstantiationService, + @IPartService private partService: IPartService, + @IContextKeyService private contextKeyService: IContextKeyService ) { super(); this.activeEditorListeners = []; + this.canNavigateBackContextKey = (new RawContextKey('canNavigateBack', false)).bindTo(this.contextKeyService); + this.canNavigateForwardContextKey = (new RawContextKey('canNavigateForward', false)).bindTo(this.contextKeyService); + this.fileInputFactory = Registry.as(EditorInputExtensions.EditorInputFactories).getFileInputFactory(); this.index = -1; @@ -266,6 +276,8 @@ export class HistoryService extends Disposable implements IHistoryService { private setIndex(value: number): void { this.lastIndex = this.index; this.index = value; + + this.updateContextKeys(); } private doForwardAcrossEditors(): void { @@ -336,6 +348,13 @@ export class HistoryService extends Disposable implements IHistoryService { this.stack.splice(0); this.history = []; this.recentlyClosedFiles = []; + + this.updateContextKeys(); + } + + private updateContextKeys(): void { + this.canNavigateBackContextKey.set(this.stack.length > 0 && this.index > 0); + this.canNavigateForwardContextKey.set(this.stack.length > 0 && this.index < this.stack.length - 1); } private navigate(acrossEditors?: boolean): void { @@ -567,6 +586,9 @@ export class HistoryService extends Disposable implements IHistoryService { if (stackInput instanceof EditorInput) { once(stackInput.onDispose)(() => this.removeFromStack(input)); } + + // Context + this.updateContextKeys(); } private preferResourceInput(input: IEditorInput): IEditorInput | IResourceInput { @@ -593,6 +615,8 @@ export class HistoryService extends Disposable implements IHistoryService { this.stack = this.stack.filter(e => !this.matches(arg1, e.input)); this.index = this.stack.length - 1; // reset index this.lastIndex = -1; + + this.updateContextKeys(); } private removeFromRecentlyClosedFiles(arg1: IEditorInput | IResourceInput | FileChangesEvent): void { @@ -657,8 +681,15 @@ export class HistoryService extends Disposable implements IHistoryService { if (arg2 instanceof EditorInput) { const inputResource = arg2.getResource(); + if (!inputResource) { + return false; + } - return inputResource && this.fileService.canHandleResource(inputResource) && inputResource.toString() === resource.toString(); + if (this.partService.isCreated() && !this.fileService.canHandleResource(inputResource)) { + return false; // make sure to only check this when workbench has started (for https://github.com/Microsoft/vscode/issues/48275) + } + + return inputResource.toString() === resource.toString(); } const resourceInput = arg2 as IResourceInput; diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts index e7f59e3cd19..77a0eb4cd9e 100644 --- a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts @@ -16,7 +16,7 @@ import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingReso import { ICommandService } from 'vs/platform/commands/common/commands'; import { IKeybindingEvent, IUserFriendlyKeybinding, KeybindingSource, IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IKeybindingItem, KeybindingsRegistry, IKeybindingRule2, KeybindingRuleSource } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IKeybindingItem, KeybindingsRegistry, IKeybindingRule2, KeybindingRuleSource, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { keybindingsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; @@ -485,9 +485,9 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { let weight: number; if (isBuiltin) { - weight = KeybindingsRegistry.WEIGHT.builtinExtension(idx); + weight = KeybindingWeight.BuiltinExtension + idx; } else { - weight = KeybindingsRegistry.WEIGHT.externalExtension(idx); + weight = KeybindingWeight.ExternalExtension + idx; } let desc = { @@ -540,6 +540,27 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { let pretty = unboundCommands.sort().join('\n// - '); return '// ' + nls.localize('unboundCommands', "Here are other available commands: ") + '\n// - ' + pretty; } + + mightProducePrintableCharacter(event: IKeyboardEvent): boolean { + if (event.ctrlKey || event.metaKey) { + // ignore ctrl/cmd-combination but not shift/alt-combinatios + return false; + } + // consult the KeyboardMapperFactory to check the given event for + // a printable value. + const mapping = KeyboardMapperFactory.INSTANCE.getRawKeyboardMapping(); + if (!mapping) { + return false; + } + const keyInfo = mapping[event.code]; + if (!keyInfo) { + return false; + } + if (keyInfo.value) { + return true; + } + return false; + } } let schemaId = 'vscode://schemas/keybindings'; @@ -585,7 +606,7 @@ const keyboardConfiguration: IConfigurationNode = { 'type': 'string', 'enum': ['code', 'keyCode'], 'default': 'code', - 'description': nls.localize('dispatch', "Controls the dispatching logic for key presses to use either `code` (recommended) or `keyCode`."), + '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': { 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 71af610716f..92fba70ec97 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 @@ -16,7 +16,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { KeyCode, SimpleKeybinding, ChordKeybinding } from 'vs/base/common/keyCodes'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import * as extfs from 'vs/base/node/extfs'; -import { TestTextFileService, TestLifecycleService, TestBackupFileService, TestContextService, TestTextResourceConfigurationService, TestHashService, TestEnvironmentService, TestStorageService, TestEditorGroupsService, TestEditorService } from 'vs/workbench/test/workbenchTestServices'; +import { TestTextFileService, TestLifecycleService, TestBackupFileService, TestContextService, TestTextResourceConfigurationService, TestHashService, TestEnvironmentService, TestStorageService, TestEditorGroupsService, TestEditorService, TestLogService } from 'vs/workbench/test/workbenchTestServices'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; @@ -47,6 +47,7 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/ import { IHashService } from 'vs/workbench/services/hash/common/hashService'; import { mkdirp } from 'vs/base/node/pfs'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; +import { ILogService } from 'vs/platform/log/common/log'; interface Modifiers { metaKey?: boolean; @@ -68,7 +69,7 @@ suite('KeybindingsEditing', () => { instantiationService = new TestInstantiationService(); - instantiationService.stub(IEnvironmentService, { appKeybindingsPath: keybindingsFile }); + instantiationService.stub(IEnvironmentService, { appKeybindingsPath: keybindingsFile, appSettingsPath: path.join(testDir, 'settings.json') }); instantiationService.stub(IConfigurationService, ConfigurationService); instantiationService.stub(IConfigurationService, 'getValue', { 'eol': '\n' }); instantiationService.stub(IConfigurationService, 'onDidUpdateConfiguration', () => { }); @@ -82,6 +83,7 @@ suite('KeybindingsEditing', () => { instantiationService.stub(IEditorService, new TestEditorService()); instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(IModeService, ModeServiceImpl); + instantiationService.stub(ILogService, new TestLogService()); instantiationService.stub(IModelService, instantiationService.createInstance(ModelServiceImpl)); instantiationService.stub(IFileService, new FileService( new TestContextService(new Workspace(testDir, testDir, toWorkspaceFolders([{ path: testDir }]))), diff --git a/src/vs/workbench/services/keybinding/test/keyboardMapperTestUtils.ts b/src/vs/workbench/services/keybinding/test/keyboardMapperTestUtils.ts index 6cf36eab48f..5d5bf78f5af 100644 --- a/src/vs/workbench/services/keybinding/test/keyboardMapperTestUtils.ts +++ b/src/vs/workbench/services/keybinding/test/keyboardMapperTestUtils.ts @@ -12,6 +12,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { readFile, writeFile } from 'vs/base/node/pfs'; import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; import { ScanCodeBinding } from 'vs/workbench/services/keybinding/common/scanCode'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; export interface IResolvedKeybinding { label: string; @@ -51,7 +52,7 @@ export function assertResolveUserBinding(mapper: IKeyboardMapper, firstPart: Sim } export function readRawMapping(file: string): TPromise { - return readFile(require.toUrl(`vs/workbench/services/keybinding/test/${file}.js`)).then((buff) => { + return readFile(getPathFromAmdModule(require, `vs/workbench/services/keybinding/test/${file}.js`)).then((buff) => { let contents = buff.toString(); let func = new Function('define', contents); let rawMappings: T = null; @@ -63,7 +64,7 @@ export function readRawMapping(file: string): TPromise { } export function assertMapping(writeFileIfDifferent: boolean, mapper: IKeyboardMapper, file: string): TPromise { - const filePath = require.toUrl(`vs/workbench/services/keybinding/test/${file}`); + const filePath = getPathFromAmdModule(require, `vs/workbench/services/keybinding/test/${file}`); return readFile(filePath).then((buff) => { let expected = buff.toString(); diff --git a/src/vs/workbench/services/part/common/partService.ts b/src/vs/workbench/services/part/common/partService.ts index e157b37687f..887597600b8 100644 --- a/src/vs/workbench/services/part/common/partService.ts +++ b/src/vs/workbench/services/part/common/partService.ts @@ -7,6 +7,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { Event } from 'vs/base/common/event'; +import { MenuBarVisibility } from 'vs/platform/windows/common/windows'; export enum Parts { ACTIVITYBAR_PART, @@ -44,11 +45,6 @@ export interface IPartService { */ onTitleBarVisibilityChange: Event; - /** - * Emits when the visibility of the menubar changes. - */ - onMenubarVisibilityChange: Event; - /** * Emits when the editor part's layout changes. */ @@ -115,6 +111,11 @@ export interface IPartService { */ getSideBarPosition(): Position; + /** + * Gets the current menubar visibility. + */ + getMenubarVisibility(): MenuBarVisibility; + /** * Gets the current panel position. Note that the panel can be hidden too. */ diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index f963de4c85d..1c87721d27a 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -7,7 +7,6 @@ import * as network from 'vs/base/common/network'; import { TPromise } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import URI from 'vs/base/common/uri'; -import * as labels from 'vs/base/common/labels'; import * as strings from 'vs/base/common/strings'; import { Disposable } from 'vs/base/common/lifecycle'; import { Emitter } from 'vs/base/common/event'; @@ -37,6 +36,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { assign } from 'vs/base/common/objects'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroup, IEditorGroupsService, GroupDirection } from 'vs/workbench/services/group/common/editorGroupsService'; +import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; const emptyEditableSettingsContent = '{\n}'; @@ -69,7 +69,8 @@ export class PreferencesService extends Disposable implements IPreferencesServic @IKeybindingService keybindingService: IKeybindingService, @IModelService private modelService: IModelService, @IJSONEditingService private jsonEditingService: IJSONEditingService, - @IModeService private modeService: IModeService + @IModeService private modeService: IModeService, + @IUriDisplayService private uriDisplayService: IUriDisplayService ) { super(); // The default keybindings.json updates based on keyboard layouts, so here we make sure @@ -221,21 +222,30 @@ export class PreferencesService extends Disposable implements IPreferencesServic if (textual) { const emptyContents = '// ' + nls.localize('emptyKeybindingsHeader', "Place your key bindings in this file to overwrite the defaults") + '\n[\n]'; const editableKeybindings = URI.file(this.environmentService.appKeybindingsPath); + const openDefaultKeybindings = !!this.configurationService.getValue('workbench.settings.openDefaultKeybindings'); // Create as needed and open in editor return this.createIfNotExists(editableKeybindings, emptyContents).then(() => { - const activeEditorGroup = this.editorGroupService.activeGroup; - const sideEditorGroup = this.editorGroupService.addGroup(activeEditorGroup.id, GroupDirection.RIGHT); - - return TPromise.join([ - this.editorService.openEditor({ resource: this.defaultKeybindingsResource, options: { pinned: true, preserveFocus: true }, label: nls.localize('defaultKeybindings', "Default Keybindings"), description: '' }), - this.editorService.openEditor({ resource: editableKeybindings, options: { pinned: true } }, sideEditorGroup.id) - ]).then(editors => void 0); + if (openDefaultKeybindings) { + const activeEditorGroup = this.editorGroupService.activeGroup; + const sideEditorGroup = this.editorGroupService.addGroup(activeEditorGroup.id, GroupDirection.RIGHT); + return TPromise.join([ + this.editorService.openEditor({ resource: this.defaultKeybindingsResource, options: { pinned: true, preserveFocus: true }, label: nls.localize('defaultKeybindings', "Default Keybindings"), description: '' }), + this.editorService.openEditor({ resource: editableKeybindings, options: { pinned: true } }, sideEditorGroup.id) + ]).then(editors => void 0); + } else { + return this.editorService.openEditor({ resource: editableKeybindings, options: { pinned: true } }).then(() => void 0); + } }); } + return this.editorService.openEditor(this.instantiationService.createInstance(KeybindingsEditorInput), { pinned: true }).then(() => null); } + openDefaultKeybindingsFile(): TPromise { + return this.editorService.openEditor({ resource: this.defaultKeybindingsResource }); + } + configureSettingsForLanguage(language: string): void { this.openGlobalSettings() .then(editor => this.createPreferencesEditorModel(this.userSettingsResource) @@ -435,7 +445,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.fileService.resolveContent(resource, { acceptTextOnly: true }).then(null, error => { if ((error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND) { return this.fileService.updateContent(resource, contents).then(null, error => { - return TPromise.wrapError(new Error(nls.localize('fail.createSettings', "Unable to create '{0}' ({1}).", labels.getPathLabel(resource, this.environmentService, this.contextService), error))); + return TPromise.wrapError(new Error(nls.localize('fail.createSettings', "Unable to create '{0}' ({1}).", this.uriDisplayService.getLabel(resource, true), error))); }); } diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index 93be98d9f2b..79610895b83 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -41,14 +41,18 @@ export interface ISetting { value: any; valueRange: IRange; description: string[]; + descriptionIsMarkdown: boolean; descriptionRanges: IRange[]; overrides?: ISetting[]; overrideOf?: ISetting; + deprecationMessage?: string; // TODO@roblou maybe need new type and new EditorModel for GUI editor instead of ISetting which is used for text settings editor type?: string | string[]; enum?: string[]; enumDescriptions?: string[]; + enumDescriptionsAreMarkdown?: boolean; + tags?: string[]; } export interface IExtensionSetting extends ISetting { @@ -58,6 +62,7 @@ export interface IExtensionSetting extends ISetting { export interface ISearchResult { filterMatches: ISettingMatch[]; + exactMatch?: boolean; metadata?: IFilterMetadata; } @@ -74,6 +79,7 @@ export interface IFilterResult { allGroups: ISettingsGroup[]; matches: IRange[]; metadata?: IStringDictionary; + exactMatch?: boolean; } export interface ISettingMatch { @@ -152,6 +158,7 @@ export interface IPreferencesService { openFolderSettings(folder: URI, options?: IEditorOptions, group?: IEditorGroup): TPromise; switchSettings(target: ConfigurationTarget, resource: URI): TPromise; openGlobalKeybindingSettings(textual: boolean): TPromise; + openDefaultKeybindingsFile(): TPromise; configureSettingsForLanguage(language: string): void; } diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index a25e508960f..c762fddfdd8 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -267,6 +267,7 @@ function parse(model: ITextModel, isSettingsProperty: (currentProperty: string, let settingStartPosition = model.getPositionAt(offset); const setting: ISetting = { description: [], + descriptionIsMarkdown: false, key: name, keyRange: { startLineNumber: settingStartPosition.lineNumber, @@ -551,18 +552,50 @@ export class DefaultSettings extends Disposable { let result: ISetting[] = []; for (let key in settingsObject) { const prop = settingsObject[key]; - if (!prop.deprecationMessage && this.matchesScope(prop)) { + if (this.matchesScope(prop)) { const value = prop.default; - const description = (prop.description || '').split('\n'); + const description = (prop.description || prop.markdownDescription || '').split('\n'); + if (prop.deprecationMessage) { + description.push( + '', + prop.deprecationMessage, + nls.localize('deprecatedSetting.unstable', "This setting should not be used, and will be removed in a future release.")); + } const overrides = OVERRIDE_PROPERTY_PATTERN.test(key) ? this.parseOverrideSettings(prop.default) : []; - result.push({ key, value, description, range: null, keyRange: null, valueRange: null, descriptionRanges: [], overrides, type: prop.type, enum: prop.enum, enumDescriptions: prop.enumDescriptions }); + result.push({ + key, + value, + description, + descriptionIsMarkdown: !prop.description, + range: null, + keyRange: null, + valueRange: null, + descriptionRanges: [], + overrides, + type: prop.type, + enum: prop.enum, + enumDescriptions: prop.enumDescriptions || prop.markdownEnumDescriptions, + enumDescriptionsAreMarkdown: !prop.enumDescriptions, + tags: prop.tags, + deprecationMessage: prop.deprecationMessage, + }); } } return result; } private parseOverrideSettings(overrideSettings: any): ISetting[] { - return Object.keys(overrideSettings).map((key) => ({ key, value: overrideSettings[key], description: [], range: null, keyRange: null, valueRange: null, descriptionRanges: [], overrides: [] })); + return Object.keys(overrideSettings).map((key) => ({ + key, + value: overrideSettings[key], + description: [], + descriptionIsMarkdown: false, + range: null, + keyRange: null, + valueRange: null, + descriptionRanges: [], + overrides: [] + })); } private matchesScope(property: IConfigurationNode): boolean { @@ -741,11 +774,15 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements private copySetting(setting: ISetting): ISetting { return { description: setting.description, + type: setting.type, + enum: setting.enum, + enumDescriptions: setting.enumDescriptions, key: setting.key, value: setting.value, range: setting.range, overrides: [], - overrideOf: setting.overrideOf + overrideOf: setting.overrideOf, + tags: setting.tags }; } @@ -849,12 +886,8 @@ class SettingsContentBuilder { private pushSetting(setting: ISetting, indent: string): void { const settingStart = this.lineCountWithOffset + 1; - setting.descriptionRanges = []; - const descriptionPreValue = indent + '// '; - for (const line of setting.description) { - this._contentByLines.push(descriptionPreValue + line); - setting.descriptionRanges.push({ startLineNumber: this.lineCountWithOffset, startColumn: this.lastLine.indexOf(line) + 1, endLineNumber: this.lineCountWithOffset, endColumn: this.lastLine.length }); - } + + this.pushSettingDescription(setting, indent); let preValueConent = indent; const keyString = JSON.stringify(setting.key); @@ -871,6 +904,33 @@ class SettingsContentBuilder { setting.range = { startLineNumber: settingStart, startColumn: 1, endLineNumber: this.lineCountWithOffset, endColumn: this.lastLine.length }; } + private pushSettingDescription(setting: ISetting, indent: string): void { + const fixSettingLink = line => line.replace(/`#(.*)#`/g, (match, settingName) => `\`${settingName}\``); + + setting.descriptionRanges = []; + const descriptionPreValue = indent + '// '; + for (let line of setting.description) { + // Remove setting link tag + line = fixSettingLink(line); + + this._contentByLines.push(descriptionPreValue + line); + setting.descriptionRanges.push({ startLineNumber: this.lineCountWithOffset, startColumn: this.lastLine.indexOf(line) + 1, endLineNumber: this.lineCountWithOffset, endColumn: this.lastLine.length }); + } + + if (setting.enumDescriptions && setting.enumDescriptions.some(desc => !!desc)) { + setting.enumDescriptions.forEach((desc, i) => { + const displayEnum = escapeInvisibleChars(String(setting.enum[i])); + const line = desc ? + `${displayEnum}: ${fixSettingLink(desc)}` : + displayEnum; + + this._contentByLines.push(` // - ${line}`); + + setting.descriptionRanges.push({ startLineNumber: this.lineCountWithOffset, startColumn: this.lastLine.indexOf(line) + 1, endLineNumber: this.lineCountWithOffset, endColumn: this.lastLine.length }); + }); + } + } + private pushValue(setting: ISetting, preValueConent: string, indent: string): void { let valueString = JSON.stringify(setting.value, null, indent); if (valueString && (typeof setting.value === 'object')) { @@ -903,6 +963,12 @@ class SettingsContentBuilder { } } +function escapeInvisibleChars(enumValue: string): string { + return enumValue && enumValue + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r'); +} + export function defaultKeybindingsContents(keybindingService: IKeybindingService): string { const defaultsHeader = '// ' + nls.localize('defaultKeybindingsHeader', "Overwrite key bindings by placing them into your key bindings file."); return defaultsHeader + '\n' + keybindingService.getDefaultKeybindingsContent(); diff --git a/src/vs/workbench/services/scm/common/scm.ts b/src/vs/workbench/services/scm/common/scm.ts index 6a01974b0ee..40aba5a08c0 100644 --- a/src/vs/workbench/services/scm/common/scm.ts +++ b/src/vs/workbench/services/scm/common/scm.ts @@ -96,9 +96,12 @@ export interface ISCMInput { export interface ISCMRepository extends IDisposable { readonly onDidFocus: Event; + readonly selected: boolean; + readonly onDidChangeSelection: Event; readonly provider: ISCMProvider; readonly input: ISCMInput; focus(): void; + setSelected(selected: boolean): void; } export interface ISCMService { @@ -108,6 +111,8 @@ export interface ISCMService { readonly onDidRemoveRepository: Event; readonly repositories: ISCMRepository[]; + readonly selectedRepositories: ISCMRepository[]; + readonly onDidChangeSelectedRepositories: Event; registerSCMProvider(provider: ISCMProvider): ISCMRepository; } diff --git a/src/vs/workbench/services/scm/common/scmService.ts b/src/vs/workbench/services/scm/common/scmService.ts index d062ae05633..83b3e88e06d 100644 --- a/src/vs/workbench/services/scm/common/scmService.ts +++ b/src/vs/workbench/services/scm/common/scmService.ts @@ -10,6 +10,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { ISCMService, ISCMProvider, ISCMInput, ISCMRepository, IInputValidator } from './scm'; import { ILogService } from 'vs/platform/log/common/log'; import { TPromise } from 'vs/base/common/winjs.base'; +import { equals } from 'vs/base/common/arrays'; class SCMInput implements ISCMInput { @@ -61,6 +62,14 @@ class SCMRepository implements ISCMRepository { private _onDidFocus = new Emitter(); readonly onDidFocus: Event = this._onDidFocus.event; + private _selected = false; + get selected(): boolean { + return this._selected; + } + + private _onDidChangeSelection = new Emitter(); + readonly onDidChangeSelection: Event = this._onDidChangeSelection.event; + readonly input: ISCMInput = new SCMInput(); constructor( @@ -72,6 +81,11 @@ class SCMRepository implements ISCMRepository { this._onDidFocus.fire(); } + setSelected(selected: boolean): void { + this._selected = selected; + this._onDidChangeSelection.fire(selected); + } + dispose(): void { this.disposable.dispose(); this.provider.dispose(); @@ -86,6 +100,12 @@ export class SCMService implements ISCMService { private _repositories: ISCMRepository[] = []; get repositories(): ISCMRepository[] { return [...this._repositories]; } + private _selectedRepositories: ISCMRepository[] = []; + get selectedRepositories(): ISCMRepository[] { return [...this._selectedRepositories]; } + + private _onDidChangeSelectedRepositories = new Emitter(); + readonly onDidChangeSelectedRepositories: Event = this._onDidChangeSelectedRepositories.event; + private _onDidAddProvider = new Emitter(); get onDidAddRepository(): Event { return this._onDidAddProvider.event; } @@ -110,15 +130,35 @@ export class SCMService implements ISCMService { return; } + selectedDisposable.dispose(); this._providerIds.delete(provider.id); this._repositories.splice(index, 1); this._onDidRemoveProvider.fire(repository); + this.onDidChangeSelection(); }); const repository = new SCMRepository(provider, disposable); + const selectedDisposable = repository.onDidChangeSelection(this.onDidChangeSelection, this); + this._repositories.push(repository); this._onDidAddProvider.fire(repository); + // automatically select the first repository + if (this._repositories.length === 1) { + repository.setSelected(true); + } + return repository; } + + private onDidChangeSelection(): void { + const selectedRepositories = this._repositories.filter(r => r.selected); + + if (equals(this._selectedRepositories, selectedRepositories)) { + return; + } + + this._selectedRepositories = this._repositories.filter(r => r.selected); + this._onDidChangeSelectedRepositories.fire(this.selectedRepositories); + } } diff --git a/src/vs/workbench/services/search/node/rawSearchService.ts b/src/vs/workbench/services/search/node/rawSearchService.ts index 20bad052121..f2281f3d5c7 100644 --- a/src/vs/workbench/services/search/node/rawSearchService.ts +++ b/src/vs/workbench/services/search/node/rawSearchService.ts @@ -42,7 +42,7 @@ export class SearchService implements IRawSearchService { let promise: TPromise; const emitter = new Emitter({ - onFirstListenerAdd: () => { + onFirstListenerDidAdd: () => { promise = this.doFileSearch(FileSearchEngine, config, p => emitter.fire(p), batchSize) .then(c => emitter.fire(c), err => emitter.fire({ type: 'error', error: { message: err.message, stack: err.stack } })); }, @@ -58,7 +58,7 @@ export class SearchService implements IRawSearchService { let promise: TPromise; const emitter = new Emitter({ - onFirstListenerAdd: () => { + onFirstListenerDidAdd: () => { promise = (config.useRipgrep ? this.ripgrepTextSearch(config, p => emitter.fire(p)) : this.legacyTextSearch(config, p => emitter.fire(p))) .then(c => emitter.fire(c), err => emitter.fire({ type: 'error', error: { message: err.message, stack: err.stack } })); }, diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index bdc651e9950..5d0ce0948d9 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -4,37 +4,37 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { PPromise, TPromise } from 'vs/base/common/winjs.base'; -import uri from 'vs/base/common/uri'; import * as arrays from 'vs/base/common/arrays'; +import { Event } from 'vs/base/common/event'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { ResourceMap, values } from 'vs/base/common/map'; +import { Schemas } from 'vs/base/common/network'; import * as objects from 'vs/base/common/objects'; import * as strings from 'vs/base/common/strings'; +import uri from 'vs/base/common/uri'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * 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 { IProgress, LineMatch, FileMatch, ISearchComplete, ISearchProgressItem, QueryType, IFileMatch, ISearchQuery, IFolderQuery, ISearchConfiguration, ISearchService, pathIncludedInQuery, ISearchResultProvider } from 'vs/platform/search/common/search'; -import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IRawSearch, ISerializedSearchComplete, ISerializedSearchProgressItem, ISerializedFileMatch, IRawSearchService, ITelemetryEvent, isSerializedSearchComplete, isSerializedSearchSuccess, ISerializedSearchSuccess } from './search'; -import { ISearchChannel, SearchChannelClient } from './searchIpc'; -import { IEnvironmentService, IDebugParams } from 'vs/platform/environment/common/environment'; -import { ResourceMap } from 'vs/base/common/map'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { onUnexpectedError } from 'vs/base/common/errors'; -import { Schemas } from 'vs/base/common/network'; -import * as pfs from 'vs/base/node/pfs'; +import { IDebugParams, IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ILogService } from 'vs/platform/log/common/log'; +import { FileMatch, IFileMatch, IFolderQuery, IProgress, ISearchComplete, ISearchConfiguration, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, LineMatch, pathIncludedInQuery, QueryType, SearchProviderType } from 'vs/platform/search/common/search'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { Event } from 'vs/base/common/event'; +import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { IRawSearch, IRawSearchService, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess, ITelemetryEvent } from './search'; +import { ISearchChannel, SearchChannelClient } from './searchIpc'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; -export class SearchService implements ISearchService { +export class SearchService extends Disposable implements ISearchService { public _serviceBrand: any; private diskSearch: DiskSearch; - private readonly searchProviders: ISearchResultProvider[] = []; - private fileSearchProvider: ISearchResultProvider; - private forwardingTelemetry: PPromise; + private readonly fileSearchProviders = new Map(); + private readonly textSearchProviders = new Map(); + private readonly fileIndexProviders = new Map(); constructor( @IModelService private modelService: IModelService, @@ -45,28 +45,28 @@ export class SearchService implements ISearchService { @ILogService private logService: ILogService, @IExtensionService private extensionService: IExtensionService ) { + super(); this.diskSearch = new DiskSearch(!environmentService.isBuilt || environmentService.verbose, /*timeout=*/undefined, environmentService.debugSearch); + this._register(this.diskSearch.onTelemetry(event => { + this.telemetryService.publicLog(event.eventName, event.data); + })); } - public registerSearchResultProvider(scheme: string, provider: ISearchResultProvider): IDisposable { - if (scheme === 'file') { - this.fileSearchProvider = provider; - } else { - this.searchProviders.push(provider); + public registerSearchResultProvider(scheme: string, type: SearchProviderType, provider: ISearchResultProvider): IDisposable { + let list: Map; + if (type === SearchProviderType.file) { + list = this.fileSearchProviders; + } else if (type === SearchProviderType.text) { + list = this.textSearchProviders; + } else if (type === SearchProviderType.fileIndex) { + list = this.fileIndexProviders; } - return { - dispose: () => { - if (scheme === 'file') { - this.fileSearchProvider = null; - } else { - const idx = this.searchProviders.indexOf(provider); - if (idx >= 0) { - this.searchProviders.splice(idx, 1); - } - } - } - }; + list.set(scheme, provider); + + return toDisposable(() => { + list.delete(scheme); + }); } public extendQuery(query: ISearchQuery): void { @@ -91,69 +91,63 @@ export class SearchService implements ISearchService { } } - public search(query: ISearchQuery): PPromise { - this.forwardTelemetry(); - + public search(query: ISearchQuery, onProgress?: (item: ISearchProgressItem) => void): TPromise { let combinedPromise: TPromise; - return new PPromise((onComplete, onError, onProgress) => { + return new TPromise((onComplete, onError) => { // Get local results from dirty/untitled const localResults = this.getLocalResults(query); - // Allow caller to register progress callback - process.nextTick(() => localResults.values().filter((res) => !!res).forEach(onProgress)); + if (onProgress) { + localResults.values().filter((res) => !!res).forEach(onProgress); + } this.logService.trace('SearchService#search', JSON.stringify(query)); + const onProviderProgress = progress => { + if (progress.resource) { + // Match + if (!localResults.has(progress.resource) && onProgress) { // don't override local results + onProgress(progress); + } + } else if (onProgress) { + // Progress + onProgress(progress); + } + + if (progress.message) { + this.logService.debug('SearchService#search', progress.message); + } + }; + const startTime = Date.now(); - const searchWithProvider = (provider: ISearchResultProvider) => TPromise.wrap(provider.search(query)).then(e => e, - null, - progress => { - if (progress.resource) { - // Match - if (!localResults.has(progress.resource)) { // don't override local results - onProgress(progress); - } - } else { - // Progress - onProgress(progress); + + const schemesInQuery = query.folderQueries.map(fq => fq.folder.scheme); + const providerActivations = schemesInQuery.map(scheme => this.extensionService.activateByEvent(`onSearch:${scheme}`)); + + const providerPromise = TPromise.join(providerActivations) + .then(() => this.searchWithProviders(query, onProviderProgress)) + .then(completes => { + completes = completes.filter(c => !!c); + if (!completes.length) { + return null; } - if (progress.message) { - this.logService.debug('SearchService#search', progress.message); + return { + limitHit: completes[0] && completes[0].limitHit, + stats: completes[0].stats, + results: arrays.flatten(completes.map(c => c.results)) + }; + }, errs => { + if (!Array.isArray(errs)) { + errs = [errs]; } + + errs = errs.filter(e => !!e); + return TPromise.wrapError(errs[0]); }); - const providerPromise = this.extensionService.whenInstalledExtensionsRegistered().then(() => { - // TODO@roblou this is not properly waiting for search-rg to finish registering itself - // If no search provider has been registered for the 'file' schema, fall back on DiskSearch - const providers = [ - this.fileSearchProvider || this.diskSearch, - ...this.searchProviders - ]; - return TPromise.join(providers.map(p => searchWithProvider(p))) - .then(completes => { - completes = completes.filter(c => !!c); - if (!completes.length) { - return null; - } - - return { - limitHit: completes[0] && completes[0].limitHit, - stats: completes[0].stats, - results: arrays.flatten(completes.map(c => c.results)) - }; - }, errs => { - if (!Array.isArray(errs)) { - errs = [errs]; - } - - errs = errs.filter(e => !!e); - return TPromise.wrapError(errs[0]); - }); - }); - combinedPromise = providerPromise.then(value => { this.logService.debug(`SearchService#search: ${Date.now() - startTime}ms`); const values = [value]; @@ -188,6 +182,45 @@ export class SearchService implements ISearchService { }, () => combinedPromise && combinedPromise.cancel()); } + private searchWithProviders(query: ISearchQuery, onProviderProgress: (progress: ISearchProgressItem) => void) { + const diskSearchQueries: IFolderQuery[] = []; + const searchPs = []; + + query.folderQueries.forEach(fq => { + let provider = query.type === QueryType.File ? + this.fileSearchProviders.get(fq.folder.scheme) || this.fileIndexProviders.get(fq.folder.scheme) : + this.textSearchProviders.get(fq.folder.scheme); + + if (!provider && fq.folder.scheme === 'file') { + diskSearchQueries.push(fq); + } else if (!provider) { + throw new Error('No search provider registered for scheme: ' + fq.folder.scheme); + } else { + const oneFolderQuery = { + ...query, + ...{ + folderQueries: [fq] + } + }; + + searchPs.push(provider.search(oneFolderQuery, onProviderProgress)); + } + }); + + if (diskSearchQueries.length) { + const diskSearchQuery = { + ...query, + ...{ + folderQueries: diskSearchQueries + } + }; + + searchPs.push(this.diskSearch.search(diskSearchQuery, onProviderProgress)); + } + + return TPromise.join(searchPs); + } + private getLocalResults(query: ISearchQuery): ResourceMap { const localResults = new ResourceMap(); @@ -259,21 +292,13 @@ export class SearchService implements ISearchService { } public clearCache(cacheKey: string): TPromise { - return TPromise.join([ - ...this.searchProviders, - this.fileSearchProvider, - this.diskSearch - ].map(provider => provider && provider.clearCache(cacheKey))) - .then(() => { }); - } + const clearPs = [ + this.diskSearch, + ...values(this.fileIndexProviders) + ].map(provider => provider && provider.clearCache(cacheKey)); - private forwardTelemetry() { - if (!this.forwardingTelemetry) { - this.forwardingTelemetry = this.diskSearch.fetchTelemetry() - .then(null, onUnexpectedError, event => { - this.telemetryService.publicLog(event.eventName, event.data); - }); - } + return TPromise.join(clearPs) + .then(() => { }); } } @@ -307,14 +332,18 @@ export class DiskSearch implements ISearchResultProvider { } const client = new Client( - uri.parse(require.toUrl('bootstrap')).fsPath, + getPathFromAmdModule(require, 'bootstrap'), opts); const channel = getNextTickChannel(client.getChannel('search')); this.raw = new SearchChannelClient(channel); } - public search(query: ISearchQuery): PPromise { + public get onTelemetry(): Event { + return this.raw.onTelemetry; + } + + public search(query: ISearchQuery, onProgress?: (p: ISearchProgressItem) => void): TPromise { const folderQueries = query.folderQueries || []; return TPromise.join(folderQueries.map(q => q.folder.scheme === Schemas.file && pfs.exists(q.folder.fsPath))) .then(exists => { @@ -328,7 +357,7 @@ export class DiskSearch implements ISearchResultProvider { event = this.raw.textSearch(rawSearch); } - return DiskSearch.collectResultsFromEvent(event); + return DiskSearch.collectResultsFromEvent(event, onProgress); }); } @@ -373,58 +402,52 @@ export class DiskSearch implements ISearchResultProvider { return rawSearch; } - public static collectResultsFromEvent(event: Event): PPromise { - const promise = new PPromise((c, e, p) => { - setTimeout(() => { - const listener = event(ev => { - if (isSerializedSearchComplete(ev)) { - if (isSerializedSearchSuccess(ev)) { - c(ev); - } else { - e(ev.error); - } - listener.dispose(); - } else { - p(ev); - } - }); - }, 0); - }); - - return DiskSearch.collectResults(promise); - } - - public static collectResults(request: PPromise): PPromise { + public static collectResultsFromEvent(event: Event, onProgress?: (p: ISearchProgressItem) => void): TPromise { let result: IFileMatch[] = []; - return new PPromise((c, e, p) => { - request.done((complete) => { - c({ - limitHit: complete.limitHit, - results: result, - stats: complete.stats - }); - }, e, (data) => { - // Matches - if (Array.isArray(data)) { - const fileMatches = data.map(d => this.createFileMatch(d)); - result = result.concat(fileMatches); - fileMatches.forEach(p); - } + let listener: IDisposable; + return new TPromise((c, e) => { + listener = event(ev => { + if (isSerializedSearchComplete(ev)) { + if (isSerializedSearchSuccess(ev)) { + c({ + limitHit: ev.limitHit, + results: result, + stats: ev.stats + }); + } else { + e(ev.error); + } - // Match - else if ((data).path) { - const fileMatch = this.createFileMatch(data); - result.push(fileMatch); - p(fileMatch); - } + listener.dispose(); + } else { + // Matches + if (Array.isArray(ev)) { + const fileMatches = ev.map(d => this.createFileMatch(d)); + result = result.concat(fileMatches); + if (onProgress) { + fileMatches.forEach(onProgress); + } + } - // Progress - else { - p(data); + // Match + else if ((ev).path) { + const fileMatch = this.createFileMatch(ev); + result.push(fileMatch); + + if (onProgress) { + onProgress(fileMatch); + } + } + + // Progress + else if (onProgress) { + onProgress(ev); + } } }); - }, () => request.cancel()); + }, + () => listener && listener.dispose()); } private static createFileMatch(data: ISerializedFileMatch): FileMatch { @@ -440,10 +463,4 @@ export class DiskSearch implements ISearchResultProvider { public clearCache(cacheKey: string): TPromise { return this.raw.clearCache(cacheKey); } - - public fetchTelemetry(): PPromise { - return new PPromise((c, e, p) => { - this.raw.onTelemetry(p); - }); - } } diff --git a/src/vs/workbench/services/search/node/textSearchWorkerProvider.ts b/src/vs/workbench/services/search/node/textSearchWorkerProvider.ts index 1b7b03ef9a7..14a7e52b6d6 100644 --- a/src/vs/workbench/services/search/node/textSearchWorkerProvider.ts +++ b/src/vs/workbench/services/search/node/textSearchWorkerProvider.ts @@ -7,11 +7,11 @@ import * as os from 'os'; -import uri from 'vs/base/common/uri'; import * as ipc from 'vs/base/parts/ipc/common/ipc'; import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; import { ISearchWorker, ISearchWorkerChannel, SearchWorkerChannelClient } from './worker/searchWorkerIpc'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; export interface ITextSearchWorkerProvider { getWorkers(): ISearchWorker[]; @@ -31,7 +31,7 @@ export class TextSearchWorkerProvider implements ITextSearchWorkerProvider { private createWorker(): void { let client = new Client( - uri.parse(require.toUrl('bootstrap')).fsPath, + getPathFromAmdModule(require, 'bootstrap'), { serverName: 'Search Worker ' + this.workers.length, args: ['--type=searchWorker'], @@ -49,4 +49,4 @@ export class TextSearchWorkerProvider implements ITextSearchWorkerProvider { this.workers.push(channelClient); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/search/test/node/search.test.ts b/src/vs/workbench/services/search/test/node/search.test.ts index fda8eed27b0..7e8ded6fe5d 100644 --- a/src/vs/workbench/services/search/test/node/search.test.ts +++ b/src/vs/workbench/services/search/test/node/search.test.ts @@ -13,8 +13,9 @@ import * as platform from 'vs/base/common/platform'; import { FileWalker, Engine as FileSearchEngine } from 'vs/workbench/services/search/node/fileSearch'; import { IRawFileMatch, IFolderSearch } from 'vs/workbench/services/search/node/search'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; -const TEST_FIXTURES = path.normalize(require.toUrl('./fixtures')); +const TEST_FIXTURES = path.normalize(getPathFromAmdModule(require, './fixtures')); const EXAMPLES_FIXTURES = path.join(TEST_FIXTURES, 'examples'); const MORE_FIXTURES = path.join(TEST_FIXTURES, 'more'); const TEST_ROOT_FOLDER: IFolderSearch = { folder: TEST_FIXTURES }; @@ -23,7 +24,7 @@ const ROOT_FOLDER_QUERY: IFolderSearch[] = [ ]; const ROOT_FOLDER_QUERY_36438: IFolderSearch[] = [ - { folder: path.normalize(require.toUrl('./fixtures2/36438')) } + { folder: path.normalize(getPathFromAmdModule(require, './fixtures2/36438')) } ]; const MULTIROOT_QUERIES: IFolderSearch[] = [ @@ -629,9 +630,9 @@ suite('FileSearchEngine', () => { let engine = new FileSearchEngine({ folderQueries: [], extraFiles: [ - path.normalize(path.join(require.toUrl('./fixtures'), 'site.css')), - path.normalize(path.join(require.toUrl('./fixtures'), 'examples', 'company.js')), - path.normalize(path.join(require.toUrl('./fixtures'), 'index.html')) + path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'site.css')), + path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'examples', 'company.js')), + path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'index.html')) ], filePattern: '*.js' }); @@ -656,9 +657,9 @@ suite('FileSearchEngine', () => { let engine = new FileSearchEngine({ folderQueries: [], extraFiles: [ - path.normalize(path.join(require.toUrl('./fixtures'), 'site.css')), - path.normalize(path.join(require.toUrl('./fixtures'), 'examples', 'company.js')), - path.normalize(path.join(require.toUrl('./fixtures'), 'index.html')) + path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'site.css')), + path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'examples', 'company.js')), + path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'index.html')) ], filePattern: '*.*', includePattern: { '**/*.css': true } @@ -684,9 +685,9 @@ suite('FileSearchEngine', () => { let engine = new FileSearchEngine({ folderQueries: [], extraFiles: [ - path.normalize(path.join(require.toUrl('./fixtures'), 'site.css')), - path.normalize(path.join(require.toUrl('./fixtures'), 'examples', 'company.js')), - path.normalize(path.join(require.toUrl('./fixtures'), 'index.html')) + path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'site.css')), + path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'examples', 'company.js')), + path.normalize(path.join(getPathFromAmdModule(require, './fixtures'), 'index.html')) ], filePattern: '*.*', excludePattern: { '**/*.css': true } @@ -942,4 +943,4 @@ suite('FileWalker', () => { const lines = stdout.split('\n'); return files.every(file => lines.indexOf(file) >= 0); } -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/services/search/test/node/searchService.test.ts b/src/vs/workbench/services/search/test/node/searchService.test.ts index 9ceecbfa748..23a434569ea 100644 --- a/src/vs/workbench/services/search/test/node/searchService.test.ts +++ b/src/vs/workbench/services/search/test/node/searchService.test.ts @@ -14,12 +14,13 @@ import { SearchService as RawSearchService } from 'vs/workbench/services/search/ import { DiskSearch } from 'vs/workbench/services/search/node/searchService'; import { Emitter, Event } from 'vs/base/common/event'; import { TPromise } from 'vs/base/common/winjs.base'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; const TEST_FOLDER_QUERIES = [ { folder: path.normalize('/some/where') } ]; -const TEST_FIXTURES = path.normalize(require.toUrl('./fixtures')); +const TEST_FIXTURES = path.normalize(getPathFromAmdModule(require, './fixtures')); const MULTIROOT_QUERIES: IFolderSearch[] = [ { folder: path.join(TEST_FIXTURES, 'examples') }, { folder: path.join(TEST_FIXTURES, 'more') } @@ -165,13 +166,15 @@ suite('SearchService', () => { } const progressResults = []; - return DiskSearch.collectResultsFromEvent(fileSearch(rawSearch, 10)) + const onProgress = match => { + assert.strictEqual(match.resource.path, uriPath); + progressResults.push(match); + }; + + return DiskSearch.collectResultsFromEvent(fileSearch(rawSearch, 10), onProgress) .then(result => { assert.strictEqual(result.results.length, 25, 'Result'); assert.strictEqual(progressResults.length, 25, 'Progress'); - }, null, match => { - assert.strictEqual(match.resource.path, uriPath); - progressResults.push(match); }); }); @@ -348,4 +351,4 @@ suite('SearchService', () => { }); }); }); -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts index 93a86a86b12..21484acb566 100644 --- a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts +++ b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts @@ -15,12 +15,13 @@ import { ISerializedFileMatch, IRawSearch, IFolderSearch } from 'vs/workbench/se import { Engine as TextSearchEngine } from 'vs/workbench/services/search/node/textSearch'; import { RipgrepEngine } from 'vs/workbench/services/search/node/ripgrepTextSearch'; import { TextSearchWorkerProvider } from 'vs/workbench/services/search/node/textSearchWorkerProvider'; +import { getPathFromAmdModule } from 'vs/base/common/amd'; function countAll(matches: ISerializedFileMatch[]): number { return matches.reduce((acc, m) => acc + m.numMatches, 0); } -const TEST_FIXTURES = path.normalize(require.toUrl('./fixtures')); +const TEST_FIXTURES = path.normalize(getPathFromAmdModule(require, './fixtures')); const EXAMPLES_FIXTURES = path.join(TEST_FIXTURES, 'examples'); const MORE_FIXTURES = path.join(TEST_FIXTURES, 'more'); const TEST_ROOT_FOLDER: IFolderSearch = { folder: TEST_FIXTURES }; diff --git a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts index d4fca9d949e..5a8a60d7238 100644 --- a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts +++ b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts @@ -216,10 +216,10 @@ export class TextMateService implements ITextMateService { loadGrammar: (scopeName: string) => { const location = this._scopeRegistry.getGrammarLocation(scopeName); if (!location) { - this._logService.info(`No grammar found for scope ${scopeName}`); + this._logService.trace(`No grammar found for scope ${scopeName}`); return null; } - return this._fileService.resolveContent(location).then(content => { + return this._fileService.resolveContent(location, { encoding: 'utf8' }).then(content => { return parseRawGrammar(content.value, location.path); }, e => { this._logService.error(`Unable to load and parse grammar for scope ${scopeName} from ${location}`, e); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 92745915709..0c3642145b3 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -12,7 +12,6 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { guessMimeTypes } from 'vs/base/common/mime'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import URI from 'vs/base/common/uri'; -import * as diagnostics from 'vs/base/common/diagnostics'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { IMode } from 'vs/editor/common/modes'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -33,6 +32,8 @@ 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 { ILogService } from 'vs/platform/log/common/log'; +import { isEqual, isEqualOrParent, hasToIgnoreCase } from 'vs/base/common/resources'; /** * 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. @@ -41,6 +42,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil static DEFAULT_CONTENT_CHANGE_BUFFER_DELAY = CONTENT_CHANGE_EVENT_BUFFER_DELAY; static DEFAULT_ORPHANED_CHANGE_BUFFER_DELAY = 100; + static WHITELIST_JSON = ['package.json', 'package-lock.json', 'tsconfig.json', 'jsconfig.json', 'bower.json', '.eslintrc.json', 'tslint.json', 'composer.json']; + static WHITELIST_WORKSPACE_JSON = ['settings.json', 'extensions.json', 'tasks.json', 'launch.json']; private static saveErrorHandler: ISaveErrorHandler; static setSaveErrorHandler(handler: ISaveErrorHandler): void { TextFileEditorModel.saveErrorHandler = handler; } @@ -88,7 +91,8 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil @IBackupFileService private backupFileService: IBackupFileService, @IEnvironmentService private environmentService: IEnvironmentService, @IWorkspaceContextService private contextService: IWorkspaceContextService, - @IHashService private hashService: IHashService + @IHashService private hashService: IHashService, + @ILogService private logService: ILogService ) { super(modelService, modeService); @@ -235,13 +239,13 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } load(options?: ILoadOptions): TPromise { - diag('load() - enter', this.resource, new Date()); + this.logService.trace('load() - enter', this.resource); // It is very important to not reload the model when the model is dirty. // We also only want to reload the model from the disk if no save is pending // to avoid data loss. if (this.dirty || this.saveSequentializer.hasPendingSave()) { - diag('load() - exit - without loading because model is dirty or being saved', this.resource, new Date()); + this.logService.trace('load() - exit - without loading because model is dirty or being saved', this.resource); return TPromise.as(this); } @@ -348,28 +352,24 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private loadWithContent(content: IRawTextContent, options?: ILoadOptions, backup?: URI): TPromise { return this.doLoadWithContent(content, backup).then(model => { - // Telemetry: We log the fileGet telemetry event after the model has been loaded to ensure a good mimetype - if (this.isSettingsFile()) { + const settingsType = this.getTypeIfSettings(); + if (settingsType) { /* __GDPR__ - "settingsRead" : {} + "settingsRead" : { + "settingsType": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + } */ - this.telemetryService.publicLog('settingsRead'); // Do not log read to user settings.json and .vscode folder as a fileGet event as it ruins our JSON usage data + 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" : { - "mimeType" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "ext": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "path": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "reason": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + "${include}": [ + "${FileTelemetryData}" + ] } */ - this.telemetryService.publicLog('fileGet', { - mimeType: guessMimeTypes(this.resource.fsPath).join(', '), - ext: path.extname(this.resource.fsPath), - path: this.hashService.createSHA1(this.resource.fsPath), - reason: options && options.reason ? options.reason : LoadReason.OTHER - }); + this.telemetryService.publicLog('fileGet', this.getTelemetryData(options && options.reason ? options.reason : LoadReason.OTHER)); } return model; @@ -377,7 +377,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } private doLoadWithContent(content: IRawTextContent, backup?: URI): TPromise { - diag('load() - resolved content', this.resource, new Date()); + this.logService.trace('load() - resolved content', this.resource); // Update our resolved disk stat model this.updateLastResolvedDiskStat({ @@ -409,7 +409,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Join an existing request to create the editor model to avoid race conditions else if (this.createTextEditorModelPromise) { - diag('load() - join existing text editor model promise', this.resource, new Date()); + this.logService.trace('load() - join existing text editor model promise', this.resource); return this.createTextEditorModelPromise; } @@ -419,7 +419,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } private doUpdateTextModel(value: ITextBufferFactory): TPromise { - diag('load() - updated text editor model', this.resource, new Date()); + this.logService.trace('load() - updated text editor model', this.resource); // Ensure we are not tracking a stale state this.setDirty(false); @@ -439,7 +439,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } private doCreateTextModel(resource: URI, value: ITextBufferFactory, backup: URI): TPromise { - diag('load() - created text editor model', this.resource, new Date()); + this.logService.trace('load() - created text editor model', this.resource); this.createTextEditorModelPromise = this.doLoadBackup(backup).then(backupContent => { const hasBackupContent = !!backupContent; @@ -499,11 +499,11 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } private onModelContentChanged(): void { - diag(`onModelContentChanged() - enter`, this.resource, new Date()); + this.logService.trace(`onModelContentChanged() - enter`, this.resource); // In any case increment the version id because it tracks the textual content state of the model at all times this.versionId++; - diag(`onModelContentChanged() - new versionId ${this.versionId}`, this.resource, new Date()); + this.logService.trace(`onModelContentChanged() - new versionId ${this.versionId}`, this.resource); // Ignore if blocking model changes if (this.blockModelContentChange) { @@ -515,7 +515,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // 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.getAlternativeVersionId() === this.bufferSavedVersionId) { - diag('onModelContentChanged() - model content changed back to last saved version', this.resource, new Date()); + this.logService.trace('onModelContentChanged() - model content changed back to last saved version', this.resource); // Clear flags const wasDirty = this.dirty; @@ -529,7 +529,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return; } - diag('onModelContentChanged() - model content changed and marked as dirty', this.resource, new Date()); + this.logService.trace('onModelContentChanged() - model content changed and marked as dirty', this.resource); // Mark as dirty this.makeDirty(); @@ -539,7 +539,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil if (!this.inConflictMode) { this.doAutoSave(this.versionId); } else { - diag('makeDirty() - prevented save because we are in conflict resolution mode', this.resource, new Date()); + this.logService.trace('makeDirty() - prevented save because we are in conflict resolution mode', this.resource); } } @@ -560,7 +560,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil } private doAutoSave(versionId: number): void { - diag(`doAutoSave() - enter for versionId ${versionId}`, this.resource, new Date()); + 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(); @@ -589,7 +589,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return TPromise.wrap(null); } - diag('save() - enter', this.resource, new Date()); + this.logService.trace('save() - enter', this.resource); // Cancel any currently running auto saves to make this the one that succeeds this.cancelPendingAutoSave(); @@ -602,7 +602,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil options.reason = SaveReason.EXPLICIT; } - diag(`doSave(${versionId}) - enter with versionId ' + versionId`, this.resource, new Date()); + this.logService.trace(`doSave(${versionId}) - enter with versionId ' + versionId`, this.resource); // Lookup any running pending save for this versionId and return it if found // @@ -610,7 +610,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // while the save was not yet finished to disk // if (this.saveSequentializer.hasPendingSave(versionId)) { - diag(`doSave(${versionId}) - exit - found a pending save for versionId ${versionId}`, this.resource, new Date()); + this.logService.trace(`doSave(${versionId}) - exit - found a pending save for versionId ${versionId}`, this.resource); return this.saveSequentializer.pendingSave; } @@ -623,7 +623,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Thus we avoid spawning multiple auto saves and only take the latest. // if ((!options.force && !this.dirty) || versionId !== this.versionId) { - diag(`doSave(${versionId}) - exit - because not dirty and/or versionId is different (this.isDirty: ${this.dirty}, this.versionId: ${this.versionId})`, this.resource, new Date()); + 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 TPromise.wrap(null); } @@ -637,7 +637,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // while the first save has not returned yet. // if (this.saveSequentializer.hasPendingSave()) { - diag(`doSave(${versionId}) - exit - because busy saving`, this.resource, new Date()); + this.logService.trace(`doSave(${versionId}) - exit - because busy saving`, this.resource); // Register this as the next upcoming save and return return this.saveSequentializer.setNext(() => this.doSave(this.versionId /* make sure to use latest version id here */, options)); @@ -703,7 +703,7 @@ 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) - diag(`doSave(${versionId}) - before updateContent()`, this.resource, new Date()); + this.logService.trace(`doSave(${versionId}) - before updateContent()`, this.resource); return this.saveSequentializer.setPending(newVersionId, this.fileService.updateContent(this.lastResolvedDiskStat.resource, this.createSnapshot(), { overwriteReadonly: options.overwriteReadonly, overwriteEncoding: options.overwriteEncoding, @@ -712,35 +712,34 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil etag: this.lastResolvedDiskStat.etag, writeElevated: options.writeElevated }).then(stat => { - diag(`doSave(${versionId}) - after updateContent()`, this.resource, new Date()); + this.logService.trace(`doSave(${versionId}) - after updateContent()`, this.resource); // Telemetry - if (this.isSettingsFile()) { + const settingsType = this.getTypeIfSettings(); + if (settingsType) { /* __GDPR__ - "settingsWritten" : {} + "settingsWritten" : { + "settingsType": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + } */ - this.telemetryService.publicLog('settingsWritten'); // Do not log write to user settings.json and .vscode folder as a filePUT event as it ruins our JSON usage data + this.telemetryService.publicLog('settingsWritten', { settingsType }); // Do not log write to user settings.json and .vscode folder as a filePUT event as it ruins our JSON usage data } else { /* __GDPR__ "filePUT" : { - "mimeType" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "ext": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "reason": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + "${include}": [ + "${FileTelemetryData}" + ] } */ - this.telemetryService.publicLog('filePUT', { - mimeType: guessMimeTypes(this.resource.fsPath).join(', '), - ext: path.extname(this.resource.fsPath), - reason: options.reason - }); + this.telemetryService.publicLog('filePUT', this.getTelemetryData(options.reason)); } // Update dirty state unless model has changed meanwhile if (versionId === this.versionId) { - diag(`doSave(${versionId}) - setting dirty to false because versionId did not change`, this.resource, new Date()); + this.logService.trace(`doSave(${versionId}) - setting dirty to false because versionId did not change`, this.resource); this.setDirty(false); } else { - diag(`doSave(${versionId}) - not setting dirty to false because versionId did change meanwhile`, this.resource, new Date()); + this.logService.trace(`doSave(${versionId}) - not setting dirty to false because versionId did change meanwhile`, this.resource); } // Updated resolved stat with updated stat @@ -752,7 +751,11 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Emit File Saved Event this._onDidStateChange.fire(StateChange.SAVED); }, error => { - diag(`doSave(${versionId}) - exit - resulted in a save error: ${error.toString()}`, this.resource, new Date()); + if (!error) { + error = new Error('Unknown Save Error'); // TODO@remote we should never get null as error (https://github.com/Microsoft/vscode/issues/55051) + } + + this.logService.error(`doSave(${versionId}) - exit - resulted in a save error: ${error.toString()}`, this.resource); // Flag as error state in the model this.inErrorMode = true; @@ -771,20 +774,69 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil })); } - private isSettingsFile(): boolean { + private getTypeIfSettings(): string { if (path.extname(this.resource.fsPath) !== '.json') { - return false; + return ''; } // Check for global settings file - if (path.isEqual(this.resource.fsPath, this.environmentService.appSettingsPath, !isLinux)) { - return true; + if (isEqual(this.resource, URI.file(this.environmentService.appSettingsPath), !isLinux)) { + return 'global-settings'; + } + + // Check for keybindings file + if (isEqual(this.resource, URI.file(this.environmentService.appKeybindingsPath), !isLinux)) { + return 'keybindings'; + } + + // Check for locale file + if (isEqual(this.resource, URI.file(path.join(this.environmentService.appSettingsHome, 'locale.json')), !isLinux)) { + return 'locale'; + } + + // Check for snippets + if (isEqualOrParent(this.resource, URI.file(path.join(this.environmentService.appSettingsHome, 'snippets')), hasToIgnoreCase(this.resource))) { + return 'snippets'; } // Check for workspace settings file - return this.contextService.getWorkspace().folders.some(folder => { - return path.isEqualOrParent(this.resource.fsPath, path.join(folder.uri.fsPath, '.vscode')); - }); + const folders = this.contextService.getWorkspace().folders; + for (let i = 0; i < folders.length; i++) { + if (isEqualOrParent(this.resource, folders[i].toResource('.vscode'), hasToIgnoreCase(this.resource))) { + const filename = path.basename(this.resource.fsPath); + if (TextFileEditorModel.WHITELIST_WORKSPACE_JSON.indexOf(filename) > -1) { + return `.vscode/${filename}`; + } + } + } + + return ''; + } + + private getTelemetryData(reason: number): Object { + const ext = path.extname(this.resource.fsPath); + const fileName = path.basename(this.resource.fsPath); + const telemetryData = { + mimeType: guessMimeTypes(this.resource.fsPath).join(', '), + ext, + path: this.hashService.createSHA1(this.resource.fsPath), + reason + }; + + if (ext === '.json' && TextFileEditorModel.WHITELIST_JSON.indexOf(fileName) > -1) { + telemetryData['whitelistedjson'] = fileName; + } + + /* __GDPR__FRAGMENT__ + "FileTelemetryData" : { + "mimeType" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "ext": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "path": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "reason": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "whitelistedjson": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + return telemetryData; } private doTouch(versionId: number): TPromise { @@ -1086,11 +1138,3 @@ class DefaultSaveErrorHandler implements ISaveErrorHandler { this.notificationService.error(nls.localize('genericSaveError', "Failed to save '{0}': {1}", path.basename(model.getResource().fsPath), toErrorMessage(error, false))); } } - -// Diagnostics support -let diag: (...args: any[]) => void; -if (!diag) { - diag = diagnostics.register('TextFileEditorModelDiagnostics', function (...args: any[]) { - console.log(args[1] + ' - ' + args[0] + ' (time: ' + args[2].getTime() + ' [' + args[2].toUTCString() + '])'); - }); -} diff --git a/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts b/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts index 7a52cc4ef45..d9b1331feda 100644 --- a/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts +++ b/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts @@ -272,7 +272,7 @@ function toCSSSelector(str: string) { function _loadColorTheme(fileService: IFileService, themeLocation: URI, resultRules: ITokenColorizationRule[], resultColors: IColorMap): TPromise { if (Paths.extname(themeLocation.path) === '.json') { - return fileService.resolveContent(themeLocation).then(content => { + return fileService.resolveContent(themeLocation, { encoding: 'utf8' }).then(content => { let errors: Json.ParseError[] = []; let contentValue = Json.parse(content.value.toString(), errors); if (errors.length > 0) { @@ -325,7 +325,7 @@ function getPListParser() { } function _loadSyntaxTokens(fileService: IFileService, themeLocation: URI, resultRules: ITokenColorizationRule[], resultColors: IColorMap): TPromise { - return fileService.resolveContent(themeLocation).then(content => { + return fileService.resolveContent(themeLocation, { encoding: 'utf8' }).then(content => { return getPListParser().then(parser => { try { let contentValue = parser.parse(content.value.toString()); diff --git a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts index 9fe9957a0ec..1fda23ca011 100644 --- a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts +++ b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts @@ -159,7 +159,7 @@ interface IconThemeDocument extends IconsAssociation { } function _loadIconThemeDocument(fileService: IFileService, location: URI): TPromise { - return fileService.resolveContent(location).then((content) => { + return fileService.resolveContent(location, { encoding: 'utf8' }).then((content) => { let errors: Json.ParseError[] = []; let contentValue = Json.parse(content.value.toString(), errors); if (errors.length > 0 || !contentValue) { diff --git a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts index a7f6644ac34..6caf2e400a4 100644 --- a/src/vs/workbench/services/workspace/node/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/node/workspaceEditingService.ts @@ -26,7 +26,7 @@ import { BackupFileService } from 'vs/workbench/services/backup/node/backupFileS import { ICommandService } from 'vs/platform/commands/common/commands'; import { distinct } from 'vs/base/common/arrays'; import { isLinux } from 'vs/base/common/platform'; -import { isEqual } from 'vs/base/common/resources'; +import { isEqual, hasToIgnoreCase } from 'vs/base/common/resources'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; export class WorkspaceEditingService implements IWorkspaceEditingService { @@ -138,7 +138,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { private includesSingleFolderWorkspace(folders: URI[]): boolean { if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) { const workspaceFolder = this.contextService.getWorkspace().folders[0]; - return (folders.some(folder => isEqual(folder, workspaceFolder.uri, !isLinux))); + return (folders.some(folder => isEqual(folder, workspaceFolder.uri, hasToIgnoreCase(folder)))); } return false; diff --git a/src/vs/workbench/test/browser/parts/editor/breadcrumbModel.test.ts b/src/vs/workbench/test/browser/parts/editor/breadcrumbModel.test.ts index bb0721629f8..e524bcd9ef2 100644 --- a/src/vs/workbench/test/browser/parts/editor/breadcrumbModel.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/breadcrumbModel.test.ts @@ -11,6 +11,7 @@ import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspa import { EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel'; import { TestContextService } from 'vs/workbench/test/workbenchTestServices'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { FileKind } from 'vs/platform/files/common/files'; suite('Breadcrumb Model', function () { @@ -35,9 +36,9 @@ suite('Breadcrumb Model', function () { assert.equal(elements.length, 3); let [one, two, three] = elements as FileElement[]; - assert.equal(one.isFile, false); - assert.equal(two.isFile, false); - assert.equal(three.isFile, true); + assert.equal(one.kind, FileKind.FOLDER); + assert.equal(two.kind, FileKind.FOLDER); + assert.equal(three.kind, FileKind.FILE); assert.equal(one.uri.toString(), 'foo:/bar/baz/ws/some'); assert.equal(two.uri.toString(), 'foo:/bar/baz/ws/some/path'); assert.equal(three.uri.toString(), 'foo:/bar/baz/ws/some/path/file.ts'); @@ -50,8 +51,8 @@ suite('Breadcrumb Model', function () { assert.equal(elements.length, 2); let [one, two] = elements as FileElement[]; - assert.equal(one.isFile, false); - assert.equal(two.isFile, true); + assert.equal(one.kind, FileKind.FOLDER); + assert.equal(two.kind, FileKind.FILE); assert.equal(one.uri.toString(), 'foo:/outside'); assert.equal(two.uri.toString(), 'foo:/outside/file.ts'); }); 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 ef672a49dd1..cd4ed9a20d7 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts @@ -122,7 +122,7 @@ suite('ExtHostLanguageFeatureCommands', function () { const diagnostics = new ExtHostDiagnostics(rpcProtocol); rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, diagnostics); - extHost = new ExtHostLanguageFeatures(rpcProtocol, null, extHostDocuments, commands, heapService, diagnostics); + extHost = new ExtHostLanguageFeatures(rpcProtocol, null, extHostDocuments, commands, heapService, diagnostics, new NullLogService()); rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, extHost); mainThread = rpcProtocol.set(MainContext.MainThreadLanguageFeatures, inst.createInstance(MainThreadLanguageFeatures, rpcProtocol)); 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 3b96a8f3c01..a88b3af5604 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts @@ -112,7 +112,7 @@ suite('ExtHostLanguageFeatures', function () { const diagnostics = new ExtHostDiagnostics(rpcProtocol); rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, diagnostics); - extHost = new ExtHostLanguageFeatures(rpcProtocol, null, extHostDocuments, commands, heapService, diagnostics); + extHost = new ExtHostLanguageFeatures(rpcProtocol, null, extHostDocuments, commands, heapService, diagnostics, new NullLogService()); rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, extHost); mainThread = rpcProtocol.set(MainContext.MainThreadLanguageFeatures, inst.createInstance(MainThreadLanguageFeatures, rpcProtocol)); @@ -1011,7 +1011,7 @@ suite('ExtHostLanguageFeatures', function () { })); return rpcProtocol.sync().then(() => { - return getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }).then(value => { + return getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }, CancellationToken.None).then(value => { assert.equal(value.length, 2); let [first, second] = value; assert.equal(first.text, 'testing'); @@ -1032,7 +1032,7 @@ suite('ExtHostLanguageFeatures', function () { })); return rpcProtocol.sync().then(() => { - return getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }); + return getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }, CancellationToken.None); }); }); @@ -1057,7 +1057,7 @@ suite('ExtHostLanguageFeatures', function () { })); return rpcProtocol.sync().then(() => { - return getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }).then(value => { + return getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }, CancellationToken.None).then(value => { assert.equal(value.length, 1); let [first] = value; assert.equal(first.text, 'testing'); @@ -1074,7 +1074,7 @@ suite('ExtHostLanguageFeatures', function () { })); return rpcProtocol.sync().then(() => { - return getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }).then(value => { + return getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, CancellationToken.None).then(value => { assert.equal(value.length, 1); let [first] = value; assert.equal(first.text, 'testing'); @@ -1100,7 +1100,7 @@ suite('ExtHostLanguageFeatures', function () { } })); return rpcProtocol.sync().then(() => { - return getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }).then(value => { + return getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, CancellationToken.None).then(value => { assert.equal(value.length, 1); let [first] = value; assert.equal(first.text, 'range2'); @@ -1120,7 +1120,7 @@ suite('ExtHostLanguageFeatures', function () { })); return rpcProtocol.sync().then(() => { - return getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }); + return getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, CancellationToken.None); }); }); 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 bad1d62b9d7..d1a70c00f48 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts @@ -28,7 +28,15 @@ class MockMainThreadSearch implements MainThreadSearchShape { results: (UriComponents | IRawFileMatch2)[] = []; - $registerSearchProvider(handle: number, scheme: string): void { + $registerFileSearchProvider(handle: number, scheme: string): void { + this.lastHandle = handle; + } + + $registerFileIndexProvider(handle: number, scheme: string): void { + this.lastHandle = handle; + } + + $registerTextSearchProvider(handle: number, scheme: string): void { this.lastHandle = handle; } @@ -53,8 +61,13 @@ class MockMainThreadSearch implements MainThreadSearchShape { let mockExtfs: Partial; suite('ExtHostSearch', () => { - async function registerTestSearchProvider(provider: vscode.SearchProvider, scheme = 'file'): Promise { - disposables.push(extHostSearch.registerSearchProvider(scheme, provider)); + async function registerTestTextSearchProvider(provider: vscode.TextSearchProvider, scheme = 'file'): Promise { + disposables.push(extHostSearch.registerTextSearchProvider(scheme, provider)); + await rpcProtocol.sync(); + } + + async function registerTestFileSearchProvider(provider: vscode.FileSearchProvider, scheme = 'file'): Promise { + disposables.push(extHostSearch.registerFileSearchProvider(scheme, provider)); await rpcProtocol.sync(); } @@ -153,8 +166,8 @@ suite('ExtHostSearch', () => { } test('no results', async () => { - await registerTestSearchProvider({ - provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { + await registerTestFileSearchProvider({ + provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { return TPromise.wrap(null); } }); @@ -171,10 +184,9 @@ suite('ExtHostSearch', () => { joinPath(rootFolderA, 'file3.ts') ]; - await registerTestSearchProvider({ - provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { - reportedResults.forEach(r => progress.report(r)); - return TPromise.wrap(null); + await registerTestFileSearchProvider({ + provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { + return TPromise.wrap(reportedResults); } }); @@ -186,14 +198,13 @@ suite('ExtHostSearch', () => { test('Search canceled', async () => { let cancelRequested = false; - await registerTestSearchProvider({ - provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { + await registerTestFileSearchProvider({ + provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { return new TPromise((resolve, reject) => { token.onCancellationRequested(() => { cancelRequested = true; - progress.report(joinPath(options.folder, 'file1.ts')); - resolve(null); // or reject or nothing? + resolve([joinPath(options.folder, 'file1.ts')]); // or reject or nothing? }); }); } @@ -204,34 +215,9 @@ suite('ExtHostSearch', () => { assert(!results.length); }); - test('provider fail', async () => { - const reportedResults = [ - 'file1.ts', - 'file2.ts', - 'file3.ts', - ]; - - await registerTestSearchProvider({ - provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { - reportedResults - .map(relativePath => joinPath(options.folder, relativePath)) - .forEach(r => progress.report(r)); - - throw new Error('I broke'); - } - }); - - try { - await runFileSearch(getSimpleQuery()); - assert(false, 'Expected to fail'); - } catch { - // Expected to throw - } - }); - test('provider returns null', async () => { - await registerTestSearchProvider({ - provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { + await registerTestFileSearchProvider({ + provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { return null; } }); @@ -245,8 +231,8 @@ suite('ExtHostSearch', () => { }); test('all provider calls get global include/excludes', async () => { - await registerTestSearchProvider({ - provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { + await registerTestFileSearchProvider({ + provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { assert(options.excludes.length === 2 && options.includes.length === 2, 'Missing global include/excludes'); return TPromise.wrap(null); } @@ -274,8 +260,8 @@ suite('ExtHostSearch', () => { }); test('global/local include/excludes combined', async () => { - await registerTestSearchProvider({ - provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { + await registerTestFileSearchProvider({ + provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { if (options.folder.toString() === rootFolderA.toString()) { assert.deepEqual(options.includes.sort(), ['*.ts', 'foo']); assert.deepEqual(options.excludes.sort(), ['*.js', 'bar']); @@ -316,8 +302,8 @@ suite('ExtHostSearch', () => { }); test('include/excludes resolved correctly', async () => { - await registerTestSearchProvider({ - provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { + await registerTestFileSearchProvider({ + provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { assert.deepEqual(options.includes.sort(), ['*.jsx', '*.ts']); assert.deepEqual(options.excludes.sort(), []); @@ -359,12 +345,10 @@ suite('ExtHostSearch', () => { 'file1.js', ]; - await registerTestSearchProvider({ - provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { - reportedResults - .map(relativePath => joinPath(options.folder, relativePath)) - .forEach(r => progress.report(r)); - return TPromise.wrap(null); + await registerTestFileSearchProvider({ + provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { + return TPromise.wrap(reportedResults + .map(relativePath => joinPath(options.folder, relativePath))); } }); @@ -392,8 +376,8 @@ suite('ExtHostSearch', () => { test('multiroot sibling exclude clause', async () => { - await registerTestSearchProvider({ - provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { + await registerTestFileSearchProvider({ + provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { let reportedResults: URI[]; if (options.folder.fsPath === rootFolderA.fsPath) { reportedResults = [ @@ -409,8 +393,7 @@ suite('ExtHostSearch', () => { ].map(relativePath => joinPath(rootFolderB, relativePath)); } - reportedResults.forEach(r => progress.report(r)); - return TPromise.wrap(null); + return TPromise.wrap(reportedResults); } }); @@ -455,7 +438,7 @@ suite('ExtHostSearch', () => { ]); }); - test('max results = 1', async () => { + test.skip('max results = 1', async () => { const reportedResults = [ joinPath(rootFolderA, 'file1.ts'), joinPath(rootFolderA, 'file2.ts'), @@ -463,13 +446,11 @@ suite('ExtHostSearch', () => { ]; let wasCanceled = false; - await registerTestSearchProvider({ - provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { - reportedResults - .forEach(r => progress.report(r)); + await registerTestFileSearchProvider({ + provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => wasCanceled = true); - return TPromise.wrap(null); + return TPromise.wrap(reportedResults); } }); @@ -493,7 +474,7 @@ suite('ExtHostSearch', () => { assert(wasCanceled, 'Expected to be canceled when hitting limit'); }); - test('max results = 2', async () => { + test.skip('max results = 2', async () => { const reportedResults = [ joinPath(rootFolderA, 'file1.ts'), joinPath(rootFolderA, 'file2.ts'), @@ -501,12 +482,11 @@ suite('ExtHostSearch', () => { ]; let wasCanceled = false; - await registerTestSearchProvider({ - provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { - reportedResults.forEach(r => progress.report(r)); + await registerTestFileSearchProvider({ + provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => wasCanceled = true); - return TPromise.wrap(null); + return TPromise.wrap(reportedResults); } }); @@ -530,19 +510,18 @@ suite('ExtHostSearch', () => { assert(wasCanceled, 'Expected to be canceled when hitting limit'); }); - test('provider returns maxResults exactly', async () => { + test.skip('provider returns maxResults exactly', async () => { const reportedResults = [ joinPath(rootFolderA, 'file1.ts'), joinPath(rootFolderA, 'file2.ts'), ]; let wasCanceled = false; - await registerTestSearchProvider({ - provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { - reportedResults.forEach(r => progress.report(r)); + await registerTestFileSearchProvider({ + provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => wasCanceled = true); - return TPromise.wrap(null); + return TPromise.wrap(reportedResults); } }); @@ -568,19 +547,18 @@ suite('ExtHostSearch', () => { test('multiroot max results', async () => { let cancels = 0; - await registerTestSearchProvider({ - provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { + await registerTestFileSearchProvider({ + provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => cancels++); // Provice results async so it has a chance to invoke every provider return new TPromise(r => process.nextTick(r)) .then(() => { - [ + return [ 'file1.ts', 'file2.ts', 'file3.ts', - ].map(relativePath => joinPath(options.folder, relativePath)) - .forEach(r => progress.report(r)); + ].map(relativePath => joinPath(options.folder, relativePath)); }); } }); @@ -614,10 +592,9 @@ suite('ExtHostSearch', () => { ]; - await registerTestSearchProvider({ - provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { - reportedResults.forEach(r => progress.report(r)); - return TPromise.wrap(null); + await registerTestFileSearchProvider({ + provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Thenable { + return TPromise.wrap(reportedResults); } }, fancyScheme); @@ -634,34 +611,6 @@ suite('ExtHostSearch', () => { const { results } = await runFileSearch(query); compareURIs(results, reportedResults); }); - - test('uses different cache keys for different folders', async () => { - const cacheKeys: string[] = []; - await registerTestSearchProvider({ - provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { - cacheKeys.push(query.cacheKey); - return TPromise.wrap(null); - } - }); - - const query: ISearchQuery = { - type: QueryType.File, - filePattern: '', - cacheKey: 'cacheKey', - folderQueries: [ - { - folder: rootFolderA - }, - { - folder: rootFolderB - } - ] - }; - - await runFileSearch(query); - assert.equal(cacheKeys.length, 2); - assert.notEqual(cacheKeys[0], cacheKeys[1]); - }); }); suite('Text:', () => { @@ -734,7 +683,7 @@ suite('ExtHostSearch', () => { } test('no results', async () => { - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { return TPromise.wrap(null); } @@ -751,7 +700,7 @@ suite('ExtHostSearch', () => { makeTextResult(rootFolderA, 'file2.ts') ]; - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { providedResults.forEach(r => progress.report(r)); return TPromise.wrap(null); @@ -764,7 +713,7 @@ suite('ExtHostSearch', () => { }); test('all provider calls get global include/excludes', async () => { - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { assert.equal(options.includes.length, 1); assert.equal(options.excludes.length, 1); @@ -793,7 +742,7 @@ suite('ExtHostSearch', () => { }); test('global/local include/excludes combined', async () => { - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { if (options.folder.toString() === rootFolderA.toString()) { assert.deepEqual(options.includes.sort(), ['*.ts', 'foo']); @@ -834,7 +783,7 @@ suite('ExtHostSearch', () => { }); test('include/excludes resolved correctly', async () => { - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { assert.deepEqual(options.includes.sort(), ['*.jsx', '*.ts']); assert.deepEqual(options.excludes.sort(), []); @@ -871,7 +820,7 @@ suite('ExtHostSearch', () => { }); test('provider fail', async () => { - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { throw new Error('Provider fail'); } @@ -902,7 +851,7 @@ suite('ExtHostSearch', () => { makeTextResult(rootFolderA, 'file1.ts') ]; - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { providedResults.forEach(r => progress.report(r)); return TPromise.wrap(null); @@ -946,7 +895,7 @@ suite('ExtHostSearch', () => { } }; - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { let reportedResults; if (options.folder.fsPath === rootFolderA.fsPath) { @@ -1010,7 +959,7 @@ suite('ExtHostSearch', () => { makeTextResult(rootFolderA, 'file1.ts') ]; - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { providedResults.forEach(r => progress.report(r)); return TPromise.wrap(null); @@ -1040,7 +989,7 @@ suite('ExtHostSearch', () => { ]; let wasCanceled = false; - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => wasCanceled = true); providedResults.forEach(r => progress.report(r)); @@ -1072,7 +1021,7 @@ suite('ExtHostSearch', () => { ]; let wasCanceled = false; - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => wasCanceled = true); providedResults.forEach(r => progress.report(r)); @@ -1103,7 +1052,7 @@ suite('ExtHostSearch', () => { ]; let wasCanceled = false; - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => wasCanceled = true); providedResults.forEach(r => progress.report(r)); @@ -1129,7 +1078,7 @@ suite('ExtHostSearch', () => { test('multiroot max results', async () => { let cancels = 0; - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { token.onCancellationRequested(() => cancels++); return new TPromise(r => process.nextTick(r)) @@ -1166,7 +1115,7 @@ suite('ExtHostSearch', () => { makeTextResult(fancySchemeFolderA, 'file3.ts') ]; - await registerTestSearchProvider({ + await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Thenable { providedResults.forEach(r => progress.report(r)); return TPromise.wrap(null); diff --git a/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts index ff187f60156..0c74b367c0a 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts @@ -6,21 +6,22 @@ import * as assert from 'assert'; import { TPromise } from 'vs/base/common/winjs.base'; -import { TextEditorLineNumbersStyle } from 'vs/workbench/api/node/extHostTypes'; +import { TextEditorLineNumbersStyle, Range } from 'vs/workbench/api/node/extHostTypes'; import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; import { MainThreadTextEditorsShape, IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate } from 'vs/workbench/api/node/extHost.protocol'; import { ExtHostTextEditorOptions, ExtHostTextEditor } from 'vs/workbench/api/node/extHostTextEditor'; import { ExtHostDocumentData } from 'vs/workbench/api/node/extHostDocumentData'; import URI from 'vs/base/common/uri'; +import { mock } from 'vs/workbench/test/electron-browser/api/mock'; suite('ExtHostTextEditor', () => { let editor: ExtHostTextEditor; + let doc = new ExtHostDocumentData(undefined, URI.file(''), [ + 'aaaa bbbb+cccc abc' + ], '\n', 'text', 1, false); setup(() => { - let doc = new ExtHostDocumentData(undefined, URI.file(''), [ - 'aaaa bbbb+cccc abc' - ], '\n', 'text', 1, false); editor = new ExtHostTextEditor(null, 'fake', doc, [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4 }, [], 1); }); @@ -39,6 +40,25 @@ suite('ExtHostTextEditor', () => { assert.throws(() => editor._acceptOptions(null)); assert.throws(() => editor._acceptSelections([])); }); + + test('API [bug]: registerTextEditorCommand clears redo stack even if no edits are made #55163', async function () { + let applyCount = 0; + let editor = new ExtHostTextEditor(new class extends mock() { + $tryApplyEdits(): TPromise { + applyCount += 1; + return TPromise.wrap(true); + } + }, 'edt1', doc, [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4 }, [], 1); + + await editor.edit(edit => { }); + assert.equal(applyCount, 0); + + await editor.edit(edit => { edit.setEndOfLine(1); }); + assert.equal(applyCount, 1); + + await editor.edit(edit => { edit.delete(new Range(0, 0, 1, 1)); }); + assert.equal(applyCount, 2); + }); }); suite('ExtHostTextEditorOptions', () => { 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 57894ee26cd..a62fde4bcb8 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTreeViews.test.ts @@ -72,7 +72,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())); + testObject = new ExtHostTreeViews(target, new ExtHostCommands(rpcProtocol, new ExtHostHeapService(), new NullLogService()), new NullLogService()); onDidChangeTreeNode = new Emitter<{ key: string }>(); onDidChangeTreeNodeWithId = new Emitter<{ key: string }>(); testObject.createTreeView('testNodeTreeProvider', { treeDataProvider: aNodeTreeDataProvider() }); 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 0589271ab33..b9728fac684 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts @@ -26,6 +26,9 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { ResourceTextEdit } from 'vs/editor/common/modes'; import { BulkEditService } from 'vs/workbench/services/bulkEdit/electron-browser/bulkEditService'; import { NullLogService } from 'vs/platform/log/common/log'; +import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService'; +import { IReference, ImmortalReference } from 'vs/base/common/lifecycle'; +import { UriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; suite('MainThreadEditors', () => { @@ -71,8 +74,16 @@ suite('MainThreadEditors', () => { }; const workbenchEditorService = new TestEditorService(); const editorGroupService = new TestEditorGroupsService(); + const textModelService = new class extends mock() { + createModelReference(resource: URI): TPromise> { + const textEditorModel: ITextEditorModel = new class extends mock() { + textEditorModel = modelService.getModel(resource); + }; + return TPromise.as(new ImmortalReference(textEditorModel)); + } + }; - const bulkEditService = new BulkEditService(new NullLogService(), modelService, new TestEditorService(), null, new TestFileService(), textFileService, TestEnvironmentService, new TestContextService()); + const bulkEditService = new BulkEditService(new NullLogService(), modelService, new TestEditorService(), textModelService, new TestFileService(), textFileService, new UriDisplayService(TestEnvironmentService, new TestContextService())); const rpcProtocol = new TestRPCProtocol(); rpcProtocol.set(ExtHostContext.ExtHostDocuments, new class extends mock() { @@ -129,7 +140,39 @@ suite('MainThreadEditors', () => { }); }); - test(`pasero applyWorkspaceEdit with only resource edit`, () => { + test(`issue #54773: applyWorkspaceEdit checks model version in race situation`, () => { + + let model = modelService.createModel('something', null, resource); + + let workspaceResourceEdit1: ResourceTextEdit = { + resource: resource, + modelVersionId: model.getVersionId(), + edits: [{ + text: 'asdfg', + range: new Range(1, 1, 1, 1) + }] + }; + let workspaceResourceEdit2: ResourceTextEdit = { + resource: resource, + modelVersionId: model.getVersionId(), + edits: [{ + text: 'asdfg', + range: new Range(1, 1, 1, 1) + }] + }; + + let p1 = editors.$tryApplyWorkspaceEdit({ edits: [workspaceResourceEdit1] }).then((result) => { + // first edit request succeeds + assert.equal(result, true); + }); + let p2 = editors.$tryApplyWorkspaceEdit({ edits: [workspaceResourceEdit2] }).then((result) => { + // second edit request fails + assert.equal(result, false); + }); + return TPromise.join([p1, p2]); + }); + + test(`applyWorkspaceEdit with only resource edit`, () => { return editors.$tryApplyWorkspaceEdit({ edits: [ { oldUri: resource, newUri: resource, options: undefined }, diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index b0049a70ce3..326271a125f 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -7,9 +7,10 @@ import 'vs/workbench/parts/files/electron-browser/files.contribution'; // load our contribution into the test import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput'; -import { Promise, TPromise } from 'vs/base/common/winjs.base'; +import { TPromise } from 'vs/base/common/winjs.base'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import * as paths from 'vs/base/common/paths'; +import * as resources from 'vs/base/common/resources'; import URI from 'vs/base/common/uri'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; @@ -26,7 +27,7 @@ import { TextModelResolverService } from 'vs/workbench/services/textmodelResolve import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IEditorOptions, IResourceInput } from 'vs/platform/editor/common/editor'; import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; -import { IWorkspaceContextService, IWorkspace as IWorkbenchWorkspace, WorkbenchState, IWorkspaceFolder, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, IWorkspace as IWorkbenchWorkspace, WorkbenchState, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, Workspace } from 'vs/platform/workspace/common/workspace'; import { ILifecycleService, ShutdownEvent, ShutdownReason, StartupKind, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { TextFileService } from 'vs/workbench/services/textfile/common/textFileService'; @@ -41,15 +42,14 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IInstantiationService, ServicesAccessor, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -import { IWindowsService, IWindowService, INativeOpenDialogOptions, IEnterWorkspaceResult, IMessageBoxResult, IWindowConfiguration } from 'vs/platform/windows/common/windows'; +import { IWindowsService, IWindowService, INativeOpenDialogOptions, IEnterWorkspaceResult, IMessageBoxResult, IWindowConfiguration, MenuBarVisibility } from 'vs/platform/windows/common/windows'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { isLinux } from 'vs/base/common/platform'; import { generateUuid } from 'vs/base/common/uuid'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; -import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IRecentlyOpened } from 'vs/platform/history/common/history'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { IPosition, Position as EditorPosition } from 'vs/editor/common/core/position'; @@ -74,6 +74,8 @@ 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 { IUriDisplayService, UriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay'; export function createFileInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { return instantiationService.createInstance(FileEditorInput, resource, void 0); @@ -84,7 +86,7 @@ export const TestEnvironmentService = new EnvironmentService(parseArgs(process.a export class TestContextService implements IWorkspaceContextService { public _serviceBrand: any; - private workspace: IWorkbenchWorkspace; + private workspace: Workspace; private options: any; private readonly _onDidChangeWorkspaceName: Emitter; @@ -131,7 +133,7 @@ export class TestContextService implements IWorkspaceContextService { } public getWorkspaceFolder(resource: URI): IWorkspaceFolder { - return this.isInsideWorkspace(resource) ? this.workspace.folders[0] : null; + return this.workspace.getFolder(resource); } public setWorkspace(workspace: any): void { @@ -148,7 +150,7 @@ export class TestContextService implements IWorkspaceContextService { public isInsideWorkspace(resource: URI): boolean { if (resource && this.workspace) { - return paths.isEqualOrParent(resource.fsPath, this.workspace.folders[0].uri.fsPath, !isLinux /* ignorecase */); + return resources.isEqualOrParent(resource, this.workspace.folders[0].uri, resources.hasToIgnoreCase(resource)); } return false; @@ -159,16 +161,7 @@ export class TestContextService implements IWorkspaceContextService { } public isCurrentWorkspace(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): boolean { - return isSingleFolderWorkspaceIdentifier(workspaceIdentifier) && this.pathEquals(this.workspace.folders[0].uri.fsPath, workspaceIdentifier); - } - - private pathEquals(path1: string, path2: string): boolean { - if (!isLinux) { - path1 = path1.toLowerCase(); - path2 = path2.toLowerCase(); - } - - return path1 === path2; + return isSingleFolderWorkspaceIdentifier(workspaceIdentifier) && resources.isEqual(this.workspace.folders[0].uri, workspaceIdentifier, resources.hasToIgnoreCase(workspaceIdentifier)); } } @@ -249,7 +242,8 @@ export class TestTextFileService extends TextFileService { export function workbenchInstantiationService(): IInstantiationService { let instantiationService = new TestInstantiationService(new ServiceCollection([ILifecycleService, new TestLifecycleService()])); instantiationService.stub(IContextKeyService, instantiationService.createInstance(MockContextKeyService)); - instantiationService.stub(IWorkspaceContextService, new TestContextService(TestWorkspace)); + const workspaceContextService = new TestContextService(TestWorkspace); + instantiationService.stub(IWorkspaceContextService, workspaceContextService); const configService = new TestConfigurationService(); instantiationService.stub(IConfigurationService, configService); instantiationService.stub(ITextResourceConfigurationService, new TestTextResourceConfigurationService(configService)); @@ -275,7 +269,9 @@ export function workbenchInstantiationService(): IInstantiationService { instantiationService.stub(IEnvironmentService, TestEnvironmentService); instantiationService.stub(IThemeService, new TestThemeService()); instantiationService.stub(IHashService, new TestHashService()); + instantiationService.stub(ILogService, new TestLogService()); instantiationService.stub(IEditorGroupsService, new TestEditorGroupsService([new TestEditorGroup(0)])); + instantiationService.stub(IUriDisplayService, new UriDisplayService(TestEnvironmentService, workspaceContextService)); const editorService = new TestEditorService(); instantiationService.stub(IEditorService, editorService); instantiationService.stub(ICodeEditorService, new TestCodeEditorService()); @@ -283,6 +279,19 @@ export function workbenchInstantiationService(): IInstantiationService { return instantiationService; } +export class TestLogService implements ILogService { + _serviceBrand: any; onDidChangeLogLevel: Event; + 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 = Event.None; @@ -294,13 +303,13 @@ export class TestExtensionService implements IExtensionService { _serviceBrand: any; onDidRegisterExtensions: Event = Event.None; onDidChangeExtensionsStatus: Event = Event.None; - activateByEvent(activationEvent: string): Promise { return TPromise.as(void 0); } - whenInstalledExtensionsRegistered(): Promise { return TPromise.as(true); } - getExtensions(): Promise { return TPromise.as([]); } - readExtensionPointContributions(extPoint: IExtensionPoint): Promise[]> { return TPromise.as(Object.create(null)); } + activateByEvent(activationEvent: string): TPromise { return TPromise.as(void 0); } + whenInstalledExtensionsRegistered(): TPromise { return TPromise.as(true); } + getExtensions(): TPromise { return TPromise.as([]); } + readExtensionPointContributions(extPoint: IExtensionPoint): TPromise[]> { return TPromise.as(Object.create(null)); } getExtensionsStatus(): { [id: string]: IExtensionsStatus; } { return Object.create(null); } canProfileExtensionHost(): boolean { return false; } - startExtensionHostProfile(): Promise { return TPromise.as(Object.create(null)); } + startExtensionHostProfile(): TPromise { return TPromise.as(Object.create(null)); } restartExtensionHost(): void { } startExtensionHost(): void { } stopExtensionHost(): void { } @@ -361,11 +370,11 @@ export class TestDialogService implements IDialogService { public _serviceBrand: any; - public confirm(confirmation: IConfirmation): Promise { + public confirm(confirmation: IConfirmation): TPromise { return TPromise.as({ confirmed: false }); } - public show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise { + public show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): TPromise { return TPromise.as(0); } } @@ -444,6 +453,10 @@ export class TestPartService implements IPartService { return false; } + public getMenubarVisibility(): MenuBarVisibility { + return null; + } + public getSideBarPosition() { return 0; } @@ -590,7 +603,7 @@ export class TestEditorGroup implements IEditorGroupView { disposed: boolean; editors: ReadonlyArray = []; label: string; - whenRestored: Promise = TPromise.as(void 0); + whenRestored: TPromise = TPromise.as(void 0); element: HTMLElement; minimumWidth: number; maximumWidth: number; @@ -1049,7 +1062,7 @@ export class TestWindowService implements IWindowService { return TPromise.as(void 0); } - openWindow(paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean }): TPromise { + openWindow(paths: URI[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean }): TPromise { return TPromise.as(void 0); } @@ -1081,7 +1094,7 @@ export class TestWindowService implements IWindowService { return TPromise.wrap(void 0); } - updateTouchBar(items: ISerializableCommandAction[][]): Promise { + updateTouchBar(items: ISerializableCommandAction[][]): TPromise { return TPromise.as(void 0); } } @@ -1251,7 +1264,7 @@ export class TestWindowsService implements IWindowsService { } // Global methods - openWindow(windowId: number, paths: string[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean }): TPromise { + openWindow(windowId: number, paths: URI[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean }): TPromise { return TPromise.as(void 0); } @@ -1263,7 +1276,7 @@ export class TestWindowsService implements IWindowsService { return TPromise.as(void 0); } - getWindows(): TPromise<{ id: number; workspace?: IWorkspaceIdentifier; folderPath?: string; title: string; filename?: string; }[]> { + getWindows(): TPromise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]> { return TPromise.as(void 0); } @@ -1279,27 +1292,31 @@ export class TestWindowsService implements IWindowsService { return TPromise.as(void 0); } - showPreviousWindowTab(): Promise { + newWindowTab(): TPromise { return TPromise.as(void 0); } - showNextWindowTab(): Promise { + showPreviousWindowTab(): TPromise { return TPromise.as(void 0); } - moveWindowTabToNewWindow(): Promise { + showNextWindowTab(): TPromise { return TPromise.as(void 0); } - mergeAllWindowTabs(): Promise { + moveWindowTabToNewWindow(): TPromise { return TPromise.as(void 0); } - toggleWindowTabsBar(): Promise { + mergeAllWindowTabs(): TPromise { return TPromise.as(void 0); } - updateTouchBar(windowId: number, items: ISerializableCommandAction[][]): Promise { + toggleWindowTabsBar(): TPromise { + return TPromise.as(void 0); + } + + updateTouchBar(windowId: number, items: ISerializableCommandAction[][]): TPromise { return TPromise.as(void 0); } @@ -1330,10 +1347,6 @@ export class TestWindowsService implements IWindowsService { return TPromise.as(void 0); } - openAccessibilityOptions(): TPromise { - return TPromise.as(void 0); - } - openAboutDialog(): TPromise { return TPromise.as(void 0); } diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index fa632fcf4f3..bab2fdcc700 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -17,6 +17,7 @@ import 'vs/editor/editor.all'; // Platform import 'vs/platform/widget/browser/contextScopedHistoryWidget'; +import 'vs/platform/uriDisplay/electron-browser/uriDisplay.contribution'; // Menus/Actions import 'vs/workbench/services/actions/electron-browser/menusExtensionPoint'; @@ -54,6 +55,8 @@ import 'vs/workbench/parts/backup/common/backup.contribution'; import 'vs/workbench/parts/stats/node/stats.contribution'; +import 'vs/workbench/parts/splash/electron-browser/partsSplash.contribution'; + import 'vs/workbench/parts/search/electron-browser/search.contribution'; import 'vs/workbench/parts/search/browser/searchView'; // can be packaged separately import 'vs/workbench/parts/search/browser/openAnythingHandler'; // can be packaged separately @@ -64,7 +67,6 @@ import 'vs/workbench/parts/scm/electron-browser/scmViewlet'; // can be packaged import 'vs/workbench/parts/debug/electron-browser/debug.contribution'; import 'vs/workbench/parts/debug/browser/debugQuickOpen'; import 'vs/workbench/parts/debug/electron-browser/repl'; -import 'vs/workbench/parts/debug/browser/debugEditorActions'; import 'vs/workbench/parts/debug/browser/debugViewlet'; // can be packaged separately import 'vs/workbench/parts/markers/electron-browser/markers.contribution'; @@ -142,4 +144,4 @@ import 'vs/workbench/parts/navigation/common/navigation.contribution'; // services import 'vs/workbench/services/bulkEdit/electron-browser/bulkEditService'; -import 'vs/workbench/parts/experiments/electron-browser/experiments.contribution'; \ No newline at end of file +import 'vs/workbench/parts/experiments/electron-browser/experiments.contribution'; diff --git a/test/all.js b/test/all.js index 14c6897b197..2222c8995e9 100644 --- a/test/all.js +++ b/test/all.js @@ -49,6 +49,7 @@ function main() { nodeMain: __filename, baseUrl: path.join(path.dirname(__dirname), 'src'), paths: { + 'vs/css': '../test/css.mock', 'vs': `../${ out }/vs`, 'lib': `../${ out }/lib`, 'bootstrap': `../${ out }/bootstrap` diff --git a/test/css.mock.js b/test/css.mock.js new file mode 100644 index 00000000000..1829c6ae48e --- /dev/null +++ b/test/css.mock.js @@ -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. + *--------------------------------------------------------------------------------------------*/ + +define([], function() { + return { + load: function(name, req, load) { + load({}); + } + }; +}); diff --git a/test/electron/renderer.js b/test/electron/renderer.js index 63729010808..35f970c5c8c 100644 --- a/test/electron/renderer.js +++ b/test/electron/renderer.js @@ -23,6 +23,15 @@ let _tests_glob = '**/test/**/*.test.js'; let loader; let _out; +function uriFromPath(_path) { + var pathName = path.resolve(_path).replace(/\\/g, '/'); + if (pathName.length > 0 && pathName.charAt(0) !== '/') { + pathName = '/' + pathName; + } + + return encodeURI('file://' + pathName); +} + function initLoader(opts) { let outdir = opts.build ? 'out-build' : 'out'; _out = path.join(__dirname, `../../${outdir}`); @@ -33,7 +42,7 @@ function initLoader(opts) { nodeRequire: require, nodeMain: __filename, catchError: true, - baseUrl: path.join(__dirname, '../../src'), + baseUrl: uriFromPath(path.join(__dirname, '../../src')), paths: { 'vs': `../${outdir}/vs`, 'lib': `../${outdir}/lib`, diff --git a/test/smoke/package.json b/test/smoke/package.json index 0ce01ada8b8..c3d79b62111 100644 --- a/test/smoke/package.json +++ b/test/smoke/package.json @@ -22,7 +22,7 @@ "@types/webdriverio": "4.6.1", "concurrently": "^3.5.1", "cpx": "^1.5.0", - "electron": "1.7.7", + "electron": "^2.0.6", "htmlparser2": "^3.9.2", "mkdirp": "^0.5.1", "mocha": "^5.2.0", diff --git a/test/smoke/src/areas/extensions/extensions.ts b/test/smoke/src/areas/extensions/extensions.ts index c904df46862..be72895ae26 100644 --- a/test/smoke/src/areas/extensions/extensions.ts +++ b/test/smoke/src/areas/extensions/extensions.ts @@ -6,7 +6,7 @@ import { Viewlet } from '../workbench/viewlet'; import { Code } from '../../vscode/code'; -const SEARCH_BOX = 'div.extensions-viewlet[id="workbench.view.extensions"] input.search-box'; +const SEARCH_BOX = 'div.extensions-viewlet[id="workbench.view.extensions"] .monaco-editor textarea'; export class Extensions extends Viewlet { @@ -27,7 +27,7 @@ export class Extensions extends Viewlet { async searchForExtension(name: string): Promise { await this.code.waitAndClick(SEARCH_BOX); await this.code.waitForActiveElement(SEARCH_BOX); - await this.code.waitForSetValue(SEARCH_BOX, `name:"${name}"`); + await this.code.waitForTypeInEditor(SEARCH_BOX, `name:"${name}"`); } async installExtension(name: string): Promise { diff --git a/test/smoke/src/areas/preferences/keybindings.ts b/test/smoke/src/areas/preferences/keybindings.ts index 235621574f0..c9f1273d0c1 100644 --- a/test/smoke/src/areas/preferences/keybindings.ts +++ b/test/smoke/src/areas/preferences/keybindings.ts @@ -21,14 +21,14 @@ export class KeybindingsEditor { await this.code.waitForActiveElement(SEARCH_INPUT); await this.code.waitForSetValue(SEARCH_INPUT, command); - await this.code.waitAndClick('div[aria-label="Keybindings"] .monaco-list-row.keybinding-item'); - await this.code.waitForElement('div[aria-label="Keybindings"] .monaco-list-row.keybinding-item.focused.selected'); + await this.code.waitAndClick('.keybindings-list-container .monaco-list-row.keybinding-item'); + await this.code.waitForElement('.keybindings-list-container .monaco-list-row.keybinding-item.focused.selected'); - await this.code.waitAndClick('div[aria-label="Keybindings"] .monaco-list-row.keybinding-item .action-item .icon.add'); + await this.code.waitAndClick('.keybindings-list-container .monaco-list-row.keybinding-item .action-item .icon.add'); await this.code.waitForActiveElement('.defineKeybindingWidget .monaco-inputbox input'); await this.code.dispatchKeybinding(keybinding); await this.code.dispatchKeybinding('enter'); - await this.code.waitForElement(`div[aria-label="Keybindings"] div[aria-label="Keybinding is ${ariaLabel}."]`); + await this.code.waitForElement(`.keybindings-list-container div[aria-label="Keybinding is ${ariaLabel}."]`); } } \ No newline at end of file diff --git a/test/smoke/src/areas/quickinput/quickinput.ts b/test/smoke/src/areas/quickinput/quickinput.ts index c13b308c0d9..aab4253c970 100644 --- a/test/smoke/src/areas/quickinput/quickinput.ts +++ b/test/smoke/src/areas/quickinput/quickinput.ts @@ -25,4 +25,13 @@ export class QuickInput { private async waitForQuickInputClosed(): Promise { await this.code.waitForElement(QuickInput.QUICK_INPUT, r => !!r && r.attributes.style.indexOf('display: none;') !== -1); } + + async selectQuickInputElement(index: number): Promise { + await this.waitForQuickInputOpened(); + for (let from = 0; from < index; from++) { + await this.code.dispatchKeybinding('down'); + } + await this.code.dispatchKeybinding('enter'); + await this.waitForQuickInputClosed(); + } } diff --git a/test/smoke/src/areas/statusbar/statusbar.test.ts b/test/smoke/src/areas/statusbar/statusbar.test.ts index dfb1d7550d3..c9ba22c290a 100644 --- a/test/smoke/src/areas/statusbar/statusbar.test.ts +++ b/test/smoke/src/areas/statusbar/statusbar.test.ts @@ -38,11 +38,11 @@ export function setup() { await app.workbench.quickopen.waitForQuickOpenOpened(); await app.workbench.quickopen.closeQuickOpen(); await app.workbench.statusbar.clickOn(StatusBarElement.ENCODING_STATUS); - await app.workbench.quickopen.waitForQuickOpenOpened(); - await app.workbench.quickopen.closeQuickOpen(); + await app.workbench.quickinput.waitForQuickInputOpened(); + await app.workbench.quickinput.closeQuickInput(); await app.workbench.statusbar.clickOn(StatusBarElement.EOL_STATUS); - await app.workbench.quickopen.waitForQuickOpenOpened(); - await app.workbench.quickopen.closeQuickOpen(); + await app.workbench.quickinput.waitForQuickInputOpened(); + await app.workbench.quickinput.closeQuickInput(); await app.workbench.statusbar.clickOn(StatusBarElement.LANGUAGE_STATUS); await app.workbench.quickopen.waitForQuickOpenOpened(); await app.workbench.quickopen.closeQuickOpen(); @@ -84,8 +84,8 @@ export function setup() { await app.workbench.quickopen.openFile('app.js'); await app.workbench.statusbar.clickOn(StatusBarElement.EOL_STATUS); - await app.workbench.quickopen.waitForQuickOpenOpened(); - await app.workbench.quickopen.selectQuickOpenElement(1); + await app.workbench.quickinput.waitForQuickInputOpened(); + await app.workbench.quickinput.selectQuickInputElement(1); await app.workbench.statusbar.waitForEOL('CRLF'); }); diff --git a/test/smoke/yarn.lock b/test/smoke/yarn.lock index c628b2089e2..55ab35aceea 100644 --- a/test/smoke/yarn.lock +++ b/test/smoke/yarn.lock @@ -41,9 +41,9 @@ version "8.0.33" resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.33.tgz#1126e94374014e54478092830704f6ea89df04cd" -"@types/node@^7.0.18": - version "7.0.46" - resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.46.tgz#c3dedd25558c676b3d6303e51799abb9c3f8f314" +"@types/node@^8.0.24": + version "8.10.23" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.23.tgz#e5ccfdafff42af5397c29669b6d7d65f7d629a00" "@types/rimraf@2.0.2": version "2.0.2" @@ -493,11 +493,11 @@ electron-download@^3.0.1: semver "^5.3.0" sumchecker "^1.2.0" -electron@1.7.7: - version "1.7.7" - resolved "https://registry.yarnpkg.com/electron/-/electron-1.7.7.tgz#cfd89ca9eba79d763ac0b0c6dcc583792097b9b6" +electron@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/electron/-/electron-2.0.6.tgz#8e5c1bd2ebc08fa7a6ee906de3753c1ece9d7300" dependencies: - "@types/node" "^7.0.18" + "@types/node" "^8.0.24" electron-download "^3.0.1" extract-zip "^1.0.3" diff --git a/yarn.lock b/yarn.lock index 7663cf6bf35..a47ab096725 100644 --- a/yarn.lock +++ b/yarn.lock @@ -294,10 +294,6 @@ asar@^0.14.0: mksnapshot "^0.3.0" tmp "0.0.28" -asn1@0.1.11: - version "0.1.11" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.1.11.tgz#559be18376d08a4ec4dbe80877d27818639b2df7" - asn1@~0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" @@ -306,10 +302,6 @@ assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" -assert-plus@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.1.5.tgz#ee74009413002d84cec7219c6ac811812e723160" - assert-plus@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" @@ -336,10 +328,6 @@ async@~0.2.8: version "0.2.10" resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" -async@~0.9.0: - version "0.9.2" - resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -367,27 +355,10 @@ aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" -aws-sign@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/aws-sign/-/aws-sign-0.3.0.tgz#3d81ca69b474b1e16518728b51c24ff0bbedc6e9" - aws4@^1.2.1, aws4@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" -azure-storage@^0.3.1: - version "0.3.3" - resolved "https://registry.yarnpkg.com/azure-storage/-/azure-storage-0.3.3.tgz#5e1920ba75c678cb3f5e52a89136ef36210b58a1" - dependencies: - extend "~1.2.1" - mime "~1.2.4" - node-uuid "~1.4.0" - request "~2.27.0" - underscore "~1.4.4" - validator "~3.1.0" - xml2js "0.2.7" - xmlbuilder "0.4.3" - azure-storage@^1.3.1: version "1.4.0" resolved "https://registry.yarnpkg.com/azure-storage/-/azure-storage-1.4.0.tgz#fb52fa68b3efa6980c33fd7c5cd489b7adc46ed1" @@ -492,12 +463,6 @@ boolbase@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" -boom@0.4.x: - version "0.4.2" - resolved "https://registry.yarnpkg.com/boom/-/boom-0.4.2.tgz#7a636e9ded4efcefb19cef4947a3c67dfaee911b" - dependencies: - hoek "0.9.x" - boom@2.x.x: version "2.10.1" resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" @@ -851,12 +816,6 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -combined-stream@~0.0.4: - version "0.0.7" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-0.0.7.tgz#0137e657baa5a7541c57ac37ac5fc07d73b4dc1f" - dependencies: - delayed-stream "0.0.5" - commander@*, commander@^2.11.0: version "2.15.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.0.tgz#ad2a23a1c3b036e392469b8012cec6b33b4c1322" @@ -928,10 +887,6 @@ convert-source-map@1.X, convert-source-map@^1.1.1: version "1.5.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5" -cookie-jar@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/cookie-jar/-/cookie-jar-0.3.0.tgz#bc9a27d4e2b97e186cd57c9e2063cb99fa68cccc" - cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" @@ -973,12 +928,6 @@ crypt@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" -cryptiles@0.2.x: - version "0.2.2" - resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-0.2.2.tgz#ed91ff1f17ad13d3748288594f8a48a0d26f325c" - dependencies: - boom "0.4.x" - cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" @@ -1077,10 +1026,6 @@ cssom@0.3.x, "cssom@>= 0.3.0 < 0.4.0": dependencies: cssom "0.3.x" -ctype@0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/ctype/-/ctype-0.5.3.tgz#82c18c2461f74114ef16c135224ad0b9144ca12f" - cuint@^0.2.1: version "0.2.2" resolved "https://registry.yarnpkg.com/cuint/-/cuint-0.2.2.tgz#408086d409550c2631155619e9fa7bcadc3b991b" @@ -1204,10 +1149,6 @@ del@^2.0.2: pinkie-promise "^2.0.0" rimraf "^2.2.8" -delayed-stream@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-0.0.5.tgz#d4b1f43a93e8296dfe02694f4680bc37a313c73f" - delayed-stream@0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-0.0.6.tgz#a2646cb7ec3d5d7774614670a7a65de0c173edbc" @@ -1936,22 +1877,10 @@ for-own@^1.0.0: dependencies: for-in "^1.0.1" -forever-agent@~0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.5.2.tgz#6d0e09c4921f94a27f63d3b49c5feff1ea4c5130" - forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" -form-data@~0.1.0: - version "0.1.4" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-0.1.4.tgz#91abd788aba9702b1aabfa8bc01031a2ac9e3b12" - dependencies: - async "~0.9.0" - combined-stream "~0.0.4" - mime "~1.2.11" - form-data@~1.0.0-rc4: version "1.0.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-1.0.1.tgz#ae315db9a4907fa065502304a66d7733475ee37c" @@ -2699,15 +2628,6 @@ has@^1.0.1: dependencies: function-bind "^1.0.2" -hawk@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hawk/-/hawk-1.0.0.tgz#b90bb169807285411da7ffcb8dd2598502d3b52d" - dependencies: - boom "0.4.x" - cryptiles "0.2.x" - hoek "0.9.x" - sntp "0.2.x" - hawk@~3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" @@ -2726,10 +2646,6 @@ hawk@~6.0.2: hoek "4.x.x" sntp "2.x.x" -hoek@0.9.x: - version "0.9.1" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-0.9.1.tgz#3d322462badf07716ea7eb85baf88079cddce505" - hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" @@ -2779,14 +2695,6 @@ http-proxy-agent@^2.1.0: agent-base "4" debug "3.1.0" -http-signature@~0.10.0: - version "0.10.1" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-0.10.1.tgz#4fbdac132559aa8323121e540779c0a012b27e66" - dependencies: - asn1 "0.1.11" - assert-plus "^0.1.5" - ctype "0.5.3" - http-signature@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" @@ -3261,7 +3169,7 @@ json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: dependencies: jsonify "~0.0.0" -json-stringify-safe@~5.0.0, json-stringify-safe@~5.0.1: +json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -3778,14 +3686,14 @@ mime-types@^2.1.11, mime-types@^2.1.12, mime-types@~2.1.15, mime-types@~2.1.16, dependencies: mime-db "~1.30.0" -mime@1.2.11, mime@~1.2.11, mime@~1.2.4, mime@~1.2.9: - version "1.2.11" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.2.11.tgz#58203eed86e3a5ef17aed2b7d9ebd47f0a60dd10" - mime@1.4.1, mime@^1.3.4: version "1.4.1" resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" +mime@^1.4.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + mimic-fn@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" @@ -4085,10 +3993,6 @@ number-is-nan@^1.0.0: version "1.4.3" resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.3.tgz#64348e3b3d80f035b40ac11563d278f8b72db89c" -oauth-sign@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.3.0.tgz#cb540f93bb2b22a7d5941691a288d60e8ea9386e" - oauth-sign@~0.8.1, oauth-sign@~0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" @@ -4764,10 +4668,6 @@ qs@6.5.1, qs@~6.5.1: version "6.5.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" -qs@~0.6.0: - version "0.6.6" - resolved "https://registry.yarnpkg.com/qs/-/qs-0.6.6.tgz#6e015098ff51968b8a3c819001d5f2c89bc4b107" - qs@~6.2.0: version "6.2.3" resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.3.tgz#1cfcb25c10a9b2b483053ff39f5dfc9233908cfe" @@ -5095,23 +4995,6 @@ request@2.81.0: tunnel-agent "^0.6.0" uuid "^3.1.0" -request@~2.27.0: - version "2.27.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.27.0.tgz#dfb1a224dd3a5a9bade4337012503d710e538668" - dependencies: - aws-sign "~0.3.0" - cookie-jar "~0.3.0" - forever-agent "~0.5.0" - form-data "~0.1.0" - hawk "~1.0.0" - http-signature "~0.10.0" - json-stringify-safe "~5.0.0" - mime "~1.2.9" - node-uuid "~1.4.0" - oauth-sign "~0.3.0" - qs "~0.6.0" - tunnel-agent "~0.3.0" - request@~2.74.0: version "2.74.0" resolved "https://registry.yarnpkg.com/request/-/request-2.74.0.tgz#7693ca768bbb0ea5c8ce08c084a45efa05b892ab" @@ -5361,12 +5244,6 @@ slice-ansi@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" -sntp@0.2.x: - version "0.2.4" - resolved "https://registry.yarnpkg.com/sntp/-/sntp-0.2.4.tgz#fb885f18b0f3aad189f824862536bceeec750900" - dependencies: - hoek "0.9.x" - sntp@1.x.x: version "1.0.9" resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" @@ -5852,10 +5729,6 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" -tunnel-agent@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.3.0.tgz#ad681b68f5321ad2827c4cfb1b7d5df2cfe942ee" - tunnel-agent@~0.4.1: version "0.4.3" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" @@ -6048,10 +5921,6 @@ validate-npm-package-license@^3.0.1: spdx-correct "~1.0.0" spdx-expression-parse "~1.0.0" -validator@~3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/validator/-/validator-3.1.0.tgz#2ea1ff7e92254d69367f385f015299e5ead8755b" - validator@~3.22.2: version "3.22.2" resolved "https://registry.yarnpkg.com/validator/-/validator-3.22.2.tgz#6f297ae67f7f82acc76d0afdb49f18d9a09c18c0" @@ -6202,9 +6071,9 @@ vscode-chokidar@^1.6.2: optionalDependencies: vscode-fsevents "0.3.9" -vscode-debugprotocol@1.30.0: - version "1.30.0" - resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.30.0.tgz#ece6d8559733e87bc7a2147b385899777a92af69" +vscode-debugprotocol@1.31.0: + version "1.31.0" + resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.31.0.tgz#8467eeabeea65f52da5ac03b03c18e10e8b95eb4" vscode-fsevents@0.3.9: version "0.3.9" @@ -6248,9 +6117,9 @@ vscode-textmate@Microsoft/vscode-textmate#electron-3.0.x: dependencies: oniguruma bpasero/node-oniguruma#nan-2.10.0 -vscode-xterm@3.6.0-beta3: - version "3.6.0-beta3" - resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.6.0-beta3.tgz#fe383ff8df66603088e36c1f9ac987b0de68bd1b" +vscode-xterm@3.7.0-beta3: + version "3.7.0-beta3" + resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.7.0-beta3.tgz#2306f9650ee2f55637ba1804d10c5ffb9ef944f6" vso-node-api@^6.1.2-preview: version "6.1.2-preview" @@ -6306,6 +6175,10 @@ windows-process-tree@0.2.2: dependencies: nan "^2.6.2" +winreg@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/winreg/-/winreg-1.2.4.tgz#ba065629b7a925130e15779108cf540990e98d1b" + wordwrap@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"